View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.async;
18  
19  import java.io.IOException;
20  import java.util.Arrays;
21  import java.util.Map;
22  
23  import org.apache.logging.log4j.Level;
24  import org.apache.logging.log4j.Marker;
25  import org.apache.logging.log4j.ThreadContext.ContextStack;
26  import org.apache.logging.log4j.core.LogEvent;
27  import org.apache.logging.log4j.core.impl.ContextDataFactory;
28  import org.apache.logging.log4j.core.impl.Log4jLogEvent;
29  import org.apache.logging.log4j.core.impl.ThrowableProxy;
30  import org.apache.logging.log4j.core.util.*;
31  import org.apache.logging.log4j.core.time.Instant;
32  import org.apache.logging.log4j.core.time.MutableInstant;
33  import org.apache.logging.log4j.message.*;
34  import org.apache.logging.log4j.util.ReadOnlyStringMap;
35  import org.apache.logging.log4j.util.StringBuilders;
36  import org.apache.logging.log4j.util.StringMap;
37  import org.apache.logging.log4j.util.Strings;
38  
39  import com.lmax.disruptor.EventFactory;
40  
41  /**
42   * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during
43   * the life of the RingBuffer.
44   */
45  public class RingBufferLogEvent implements LogEvent, ReusableMessage, CharSequence, ParameterVisitable {
46  
47      /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
48      public static final Factory FACTORY = new Factory();
49  
50      private static final long serialVersionUID = 8462119088943934758L;
51      private static final Message EMPTY = new SimpleMessage(Strings.EMPTY);
52  
53      /**
54       * Creates the events that will be put in the RingBuffer.
55       */
56      private static class Factory implements EventFactory<RingBufferLogEvent> {
57  
58          @Override
59          public RingBufferLogEvent newInstance() {
60              final RingBufferLogEvent result = new RingBufferLogEvent();
61              if (Constants.ENABLE_THREADLOCALS) {
62                  result.messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
63                  result.parameters = new Object[10];
64              }
65              return result;
66          }
67      }
68  
69      private int threadPriority;
70      private long threadId;
71      private MutableInstant instant = new MutableInstant();
72      private long nanoTime;
73      private short parameterCount;
74      private boolean includeLocation;
75      private boolean endOfBatch = false;
76      private Level level;
77      private String threadName;
78      private String loggerName;
79      private Message message;
80      private String messageFormat;
81      private StringBuilder messageText;
82      private Object[] parameters;
83      private transient Throwable thrown;
84      private ThrowableProxy thrownProxy;
85      private StringMap contextData = ContextDataFactory.createContextData();
86      private Marker marker;
87      private String fqcn;
88      private StackTraceElement location;
89      private ContextStack contextStack;
90  
91      private transient AsyncLogger asyncLogger;
92  
93      public void setValues(final AsyncLogger anAsyncLogger, final String aLoggerName, final Marker aMarker,
94                            final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable,
95                            final StringMap mutableContextData, final ContextStack aContextStack, final long threadId,
96                            final String threadName, final int threadPriority, final StackTraceElement aLocation,
97                            final Clock clock, final NanoClock nanoClock) {
98          this.threadPriority = threadPriority;
99          this.threadId = threadId;
100         this.level = aLevel;
101         this.threadName = threadName;
102         this.loggerName = aLoggerName;
103         setMessage(msg);
104         initTime(clock);
105         this.nanoTime = nanoClock.nanoTime();
106         this.thrown = aThrowable;
107         this.thrownProxy = null;
108         this.marker = aMarker;
109         this.fqcn = theFqcn;
110         this.location = aLocation;
111         this.contextData = mutableContextData;
112         this.contextStack = aContextStack;
113         this.asyncLogger = anAsyncLogger;
114     }
115 
116     private void initTime(final Clock clock) {
117         if (message instanceof TimestampMessage) {
118             instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
119         } else {
120             instant.initFrom(clock);
121         }
122     }
123 
124     @Override
125     public LogEvent toImmutable() {
126         return createMemento();
127     }
128 
129     private void setMessage(final Message msg) {
130         if (msg instanceof ReusableMessage) {
131             final ReusableMessage reusable = (ReusableMessage) msg;
132             reusable.formatTo(getMessageTextForWriting());
133             messageFormat = reusable.getFormat();
134             if (parameters != null) {
135                 parameters = reusable.swapParameters(parameters);
136                 parameterCount = reusable.getParameterCount();
137             }
138         } else {
139             this.message = InternalAsyncUtil.makeMessageImmutable(msg);
140         }
141     }
142 
143     private StringBuilder getMessageTextForWriting() {
144         if (messageText == null) {
145             // Should never happen:
146             // only happens if user logs a custom reused message when Constants.ENABLE_THREADLOCALS is false
147             messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
148         }
149         messageText.setLength(0);
150         return messageText;
151     }
152 
153     /**
154      * Event processor that reads the event from the ringbuffer can call this method.
155      *
156      * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
157      */
158     public void execute(final boolean endOfBatch) {
159         this.endOfBatch = endOfBatch;
160         asyncLogger.actualAsyncLog(this);
161     }
162 
163     /**
164      * Returns {@code true} if this event is the end of a batch, {@code false} otherwise.
165      *
166      * @return {@code true} if this event is the end of a batch, {@code false} otherwise
167      */
168     @Override
169     public boolean isEndOfBatch() {
170         return endOfBatch;
171     }
172 
173     @Override
174     public void setEndOfBatch(final boolean endOfBatch) {
175         this.endOfBatch = endOfBatch;
176     }
177 
178     @Override
179     public boolean isIncludeLocation() {
180         return includeLocation;
181     }
182 
183     @Override
184     public void setIncludeLocation(final boolean includeLocation) {
185         this.includeLocation = includeLocation;
186     }
187 
188     @Override
189     public String getLoggerName() {
190         return loggerName;
191     }
192 
193     @Override
194     public Marker getMarker() {
195         return marker;
196     }
197 
198     @Override
199     public String getLoggerFqcn() {
200         return fqcn;
201     }
202 
203     @Override
204     public Level getLevel() {
205         if (level == null) {
206             level = Level.OFF; // LOG4J2-462, LOG4J2-465
207         }
208         return level;
209     }
210 
211     @Override
212     public Message getMessage() {
213         if (message == null) {
214             return messageText == null ? EMPTY : this;
215         }
216         return message;
217     }
218 
219     /**
220      * @see ReusableMessage#getFormattedMessage()
221      */
222     @Override
223     public String getFormattedMessage() {
224         return messageText != null // LOG4J2-1527: may be null in web apps
225                 ? messageText.toString() // note: please keep below "redundant" braces for readability
226                 : (message == null ? null : message.getFormattedMessage());
227     }
228 
229     /**
230      * @see ReusableMessage#getFormat()
231      */
232     @Override
233     public String getFormat() {
234         return messageFormat;
235     }
236 
237     /**
238      * @see ReusableMessage#getParameters()
239      */
240     @Override
241     public Object[] getParameters() {
242         return parameters == null ? null : Arrays.copyOf(parameters, parameterCount);
243     }
244 
245     /**
246      * @see ReusableMessage#getThrowable()
247      */
248     @Override
249     public Throwable getThrowable() {
250         return getThrown();
251     }
252 
253     /**
254      * @see ReusableMessage#formatTo(StringBuilder)
255      */
256     @Override
257     public void formatTo(final StringBuilder buffer) {
258         buffer.append(messageText);
259     }
260 
261     /**
262      * Replaces this ReusableMessage's parameter array with the specified value and return the original array
263      * @param emptyReplacement the parameter array that can be used for subsequent uses of this reusable message
264      * @return the original parameter array
265      * @see ReusableMessage#swapParameters(Object[])
266      */
267     @Override
268     public Object[] swapParameters(final Object[] emptyReplacement) {
269         final Object[] result = this.parameters;
270         this.parameters = emptyReplacement;
271         return result;
272     }
273 
274     /*
275      * @see ReusableMessage#getParameterCount
276      */
277     @Override
278     public short getParameterCount() {
279         return parameterCount;
280     }
281 
282     @Override
283     public <S> void forEachParameter(ParameterConsumer<S> action, S state) {
284         if (parameters != null) {
285             for (short i = 0; i < parameterCount; i++) {
286                 action.accept(parameters[i], i, state);
287             }
288         }
289     }
290 
291     @Override
292     public Message memento() {
293         if (message != null) {
294             return message;
295         }
296         final Object[] params = parameters == null ? new Object[0] : Arrays.copyOf(parameters, parameterCount);
297         return new ParameterizedMessage(messageText.toString(), params);
298     }
299 
300     // CharSequence impl
301 
302     @Override
303     public int length() {
304         return messageText.length();
305     }
306 
307     @Override
308     public char charAt(final int index) {
309         return messageText.charAt(index);
310     }
311 
312     @Override
313     public CharSequence subSequence(final int start, final int end) {
314         return messageText.subSequence(start, end);
315     }
316 
317 
318     private Message getNonNullImmutableMessage() {
319         return message != null ? message : new SimpleMessage(String.valueOf(messageText));
320     }
321 
322     @Override
323     public Throwable getThrown() {
324         // after deserialization, thrown is null but thrownProxy may be non-null
325         if (thrown == null) {
326             if (thrownProxy != null) {
327                 thrown = thrownProxy.getThrowable();
328             }
329         }
330         return thrown;
331     }
332 
333     @Override
334     public ThrowableProxy getThrownProxy() {
335         // lazily instantiate the (expensive) ThrowableProxy
336         if (thrownProxy == null) {
337             if (thrown != null) {
338                 thrownProxy = new ThrowableProxy(thrown);
339             }
340         }
341         return this.thrownProxy;
342     }
343 
344     @SuppressWarnings("unchecked")
345     @Override
346     public ReadOnlyStringMap getContextData() {
347         return contextData;
348     }
349 
350     void setContextData(final StringMap contextData) {
351         this.contextData = contextData;
352     }
353 
354     @SuppressWarnings("unchecked")
355     @Override
356     public Map<String, String> getContextMap() {
357         return contextData.toMap();
358     }
359 
360     @Override
361     public ContextStack getContextStack() {
362         return contextStack;
363     }
364 
365     @Override
366     public long getThreadId() {
367         return threadId;
368     }
369 
370     @Override
371     public String getThreadName() {
372         return threadName;
373     }
374 
375     @Override
376     public int getThreadPriority() {
377         return threadPriority;
378     }
379 
380     @Override
381     public StackTraceElement getSource() {
382         return location;
383     }
384 
385     @Override
386     public long getTimeMillis() {
387         return message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : instant.getEpochMillisecond();
388     }
389 
390     @Override
391     public Instant getInstant() {
392         return instant;
393     }
394 
395     @Override
396     public long getNanoTime() {
397         return nanoTime;
398     }
399 
400     /**
401      * Release references held by ring buffer to allow objects to be garbage-collected.
402      */
403     public void clear() {
404         this.asyncLogger = null;
405         this.loggerName = null;
406         this.marker = null;
407         this.fqcn = null;
408         this.level = null;
409         this.message = null;
410         this.messageFormat = null;
411         this.thrown = null;
412         this.thrownProxy = null;
413         this.contextStack = null;
414         this.location = null;
415         if (contextData != null) {
416             if (contextData.isFrozen()) { // came from CopyOnWrite thread context
417                 contextData = null;
418             } else {
419                 contextData.clear();
420             }
421         }
422 
423         // ensure that excessively long char[] arrays are not kept in memory forever
424         StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE);
425 
426         if (parameters != null) {
427             for (int i = 0; i < parameters.length; i++) {
428                 parameters[i] = null;
429             }
430         }
431     }
432 
433     private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
434         getThrownProxy(); // initialize the ThrowableProxy before serializing
435         out.defaultWriteObject();
436     }
437 
438     /**
439      * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}.
440      *
441      * @return a new immutable copy of the data in this {@code RingBufferLogEvent}
442      */
443     public LogEvent createMemento() {
444         return new Log4jLogEvent.Builder(this).build();
445 
446     }
447 
448     /**
449      * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}.
450      * @param builder the builder whose fields to populate
451      */
452     public void initializeBuilder(final Log4jLogEvent.Builder builder) {
453         builder.setContextData(contextData) //
454                 .setContextStack(contextStack) //
455                 .setEndOfBatch(endOfBatch) //
456                 .setIncludeLocation(includeLocation) //
457                 .setLevel(getLevel()) // ensure non-null
458                 .setLoggerFqcn(fqcn) //
459                 .setLoggerName(loggerName) //
460                 .setMarker(marker) //
461                 .setMessage(getNonNullImmutableMessage()) // ensure non-null & immutable
462                 .setNanoTime(nanoTime) //
463                 .setSource(location) //
464                 .setThreadId(threadId) //
465                 .setThreadName(threadName) //
466                 .setThreadPriority(threadPriority) //
467                 .setThrown(getThrown()) // may deserialize from thrownProxy
468                 .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy
469                 .setInstant(instant) //
470         ;
471     }
472 
473 }