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