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.impl;
18  
19  import java.io.InvalidObjectException;
20  import java.io.ObjectInputStream;
21  import java.util.Arrays;
22  import java.util.Map;
23  
24  import org.apache.logging.log4j.Level;
25  import org.apache.logging.log4j.Marker;
26  import org.apache.logging.log4j.ThreadContext;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.async.InternalAsyncUtil;
29  import org.apache.logging.log4j.core.util.*;
30  import org.apache.logging.log4j.core.time.Instant;
31  import org.apache.logging.log4j.core.time.MutableInstant;
32  import org.apache.logging.log4j.message.*;
33  import org.apache.logging.log4j.util.ReadOnlyStringMap;
34  import org.apache.logging.log4j.util.StackLocatorUtil;
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  /**
40   * Mutable implementation of the {@code LogEvent} interface.
41   * @since 2.6
42   */
43  public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisitable {
44      private static final Message EMPTY = new SimpleMessage(Strings.EMPTY);
45  
46      private int threadPriority;
47      private long threadId;
48      private final MutableInstant instant = new MutableInstant();
49      private long nanoTime;
50      private short parameterCount;
51      private boolean includeLocation;
52      private boolean endOfBatch = false;
53      private Level level;
54      private String threadName;
55      private String loggerName;
56      private Message message;
57      private String messageFormat;
58      private StringBuilder messageText;
59      private Object[] parameters;
60      private Throwable thrown;
61      private ThrowableProxy thrownProxy;
62      private StringMap contextData = ContextDataFactory.createContextData();
63      private Marker marker;
64      private String loggerFqcn;
65      private StackTraceElement source;
66      private ThreadContext.ContextStack contextStack;
67      transient boolean reserved = false;
68  
69      public MutableLogEvent() {
70          // messageText and the parameter array are lazily initialized
71          this(null, null);
72      }
73  
74      public MutableLogEvent(final StringBuilder msgText, final Object[] replacementParameters) {
75          this.messageText = msgText;
76          this.parameters = replacementParameters;
77      }
78  
79      @Override
80      public Log4jLogEvent toImmutable() {
81          return createMemento();
82      }
83  
84      /**
85       * Initialize the fields of this {@code MutableLogEvent} from another event.
86       * Similar in purpose and usage as {@link org.apache.logging.log4j.core.impl.Log4jLogEvent.LogEventProxy},
87       * but a mutable version.
88       * <p>
89       * This method is used on async logger ringbuffer slots holding MutableLogEvent objects in each slot.
90       * </p>
91       *
92       * @param event the event to copy data from
93       */
94      public void initFrom(final LogEvent event) {
95          this.loggerFqcn = event.getLoggerFqcn();
96          this.marker = event.getMarker();
97          this.level = event.getLevel();
98          this.loggerName = event.getLoggerName();
99          this.thrown = event.getThrown();
100         this.thrownProxy = event.getThrownProxy();
101 
102         this.instant.initFrom(event.getInstant());
103 
104         // NOTE: this ringbuffer event SHOULD NOT keep a reference to the specified
105         // thread-local MutableLogEvent's context data, because then two threads would call
106         // ReadOnlyStringMap.clear() on the same shared instance, resulting in data corruption.
107         this.contextData.putAll(event.getContextData());
108 
109         this.contextStack = event.getContextStack();
110         this.source = event.isIncludeLocation() ? event.getSource() : null;
111         this.threadId = event.getThreadId();
112         this.threadName = event.getThreadName();
113         this.threadPriority = event.getThreadPriority();
114         this.endOfBatch = event.isEndOfBatch();
115         this.includeLocation = event.isIncludeLocation();
116         this.nanoTime = event.getNanoTime();
117         setMessage(event.getMessage());
118     }
119 
120     /**
121      * Clears all references this event has to other objects.
122      */
123     public void clear() {
124         loggerFqcn = null;
125         marker = null;
126         level = null;
127         loggerName = null;
128         message = null;
129         messageFormat = null;
130         thrown = null;
131         thrownProxy = null;
132         source = null;
133         if (contextData != null) {
134             if (contextData.isFrozen()) { // came from CopyOnWrite thread context
135                 contextData = null;
136             } else {
137                 contextData.clear();
138             }
139         }
140         contextStack = null;
141 
142         // ThreadName should not be cleared: this field is set in the ReusableLogEventFactory
143         // where this instance is kept in a ThreadLocal, so it usually does not change.
144         // threadName = null; // no need to clear threadName
145 
146         // ensure that excessively long char[] arrays are not kept in memory forever
147         StringBuilders.trimToMaxSize(messageText, Constants.MAX_REUSABLE_MESSAGE_SIZE);
148 
149         if (parameters != null) {
150             Arrays.fill(parameters, null);
151         }
152 
153         // primitive fields that cannot be cleared:
154         //timeMillis;
155         //threadId;
156         //threadPriority;
157         //includeLocation;
158         //endOfBatch;
159         //nanoTime;
160     }
161 
162     @Override
163     public String getLoggerFqcn() {
164         return loggerFqcn;
165     }
166 
167     public void setLoggerFqcn(final String loggerFqcn) {
168         this.loggerFqcn = loggerFqcn;
169     }
170 
171     @Override
172     public Marker getMarker() {
173         return marker;
174     }
175 
176     public void setMarker(final Marker marker) {
177         this.marker = marker;
178     }
179 
180     @Override
181     public Level getLevel() {
182         if (level == null) {
183             level = Level.OFF; // LOG4J2-462, LOG4J2-465
184         }
185         return level;
186     }
187 
188     public void setLevel(final Level level) {
189         this.level = level;
190     }
191 
192     @Override
193     public String getLoggerName() {
194         return loggerName;
195     }
196 
197     public void setLoggerName(final String loggerName) {
198         this.loggerName = loggerName;
199     }
200 
201     @Override
202     public Message getMessage() {
203         if (message == null) {
204             return messageText == null ? EMPTY : this;
205         }
206         return message;
207     }
208 
209     public void setMessage(final Message msg) {
210         if (msg instanceof ReusableMessage) {
211             final ReusableMessage reusable = (ReusableMessage) msg;
212             reusable.formatTo(getMessageTextForWriting());
213             this.messageFormat = msg.getFormat();
214             parameters = reusable.swapParameters(parameters == null ? new Object[10] : parameters);
215             parameterCount = reusable.getParameterCount();
216         } else {
217             this.message = InternalAsyncUtil.makeMessageImmutable(msg);
218         }
219     }
220 
221     private StringBuilder getMessageTextForWriting() {
222         if (messageText == null) {
223             // Happens the first time messageText is requested
224             messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE);
225         }
226         messageText.setLength(0);
227         return messageText;
228     }
229 
230     /**
231      * @see ReusableMessage#getFormattedMessage()
232      */
233     @Override
234     public String getFormattedMessage() {
235         return messageText.toString();
236     }
237 
238     /**
239      * @see ReusableMessage#getFormat()
240      */
241     @Override
242     public String getFormat() {
243         return messageFormat;
244     }
245 
246     /**
247      * @see ReusableMessage#getParameters()
248      */
249     @Override
250     public Object[] getParameters() {
251         return parameters == null ? null : Arrays.copyOf(parameters, parameterCount);
252     }
253 
254     @Override
255     public <S> void forEachParameter(final ParameterConsumer<S> action, final S state) {
256         if (parameters != null) {
257             for (short i = 0; i < parameterCount; i++) {
258                 action.accept(parameters[i], i, state);
259             }
260         }
261     }
262 
263     /**
264      * @see ReusableMessage#getThrowable()
265      */
266     @Override
267     public Throwable getThrowable() {
268         return getThrown();
269     }
270 
271     /**
272      * @see ReusableMessage#formatTo(StringBuilder)
273      */
274     @Override
275     public void formatTo(final StringBuilder buffer) {
276         buffer.append(messageText);
277     }
278 
279     /**
280      * Replaces this ReusableMessage's parameter array with the specified value and return the original array
281      * @param emptyReplacement the parameter array that can be used for subsequent uses of this reusable message
282      * @return the original parameter array
283      * @see ReusableMessage#swapParameters(Object[])
284      */
285     @Override
286     public Object[] swapParameters(final Object[] emptyReplacement) {
287         final Object[] result = this.parameters;
288         this.parameters = emptyReplacement;
289         return result;
290     }
291 
292     /*
293      * @see ReusableMessage#getParameterCount
294      */
295     @Override
296     public short getParameterCount() {
297         return parameterCount;
298     }
299 
300     @Override
301     public Message memento() {
302         if (message == null) {
303             message = new MementoMessage(String.valueOf(messageText), messageFormat, getParameters());
304         }
305         return message;
306     }
307 
308     @Override
309     public Throwable getThrown() {
310         return thrown;
311     }
312 
313     public void setThrown(final Throwable thrown) {
314         this.thrown = thrown;
315     }
316 
317     void initTime(final Clock clock, final NanoClock nanoClock) {
318         if (message instanceof TimestampMessage) {
319             instant.initFromEpochMilli(((TimestampMessage) message).getTimestamp(), 0);
320         } else {
321             instant.initFrom(clock);
322         }
323         nanoTime = nanoClock.nanoTime();
324     }
325 
326     @Override
327     public long getTimeMillis() {
328         return instant.getEpochMillisecond();
329     }
330 
331     public void setTimeMillis(final long timeMillis) {
332         this.instant.initFromEpochMilli(timeMillis, 0);
333     }
334 
335     @Override
336     public Instant getInstant() {
337         return instant;
338     }
339 
340     /**
341      * Returns the ThrowableProxy associated with the event, or null.
342      * @return The ThrowableProxy associated with the event.
343      */
344     @Override
345     public ThrowableProxy getThrownProxy() {
346         if (thrownProxy == null && thrown != null) {
347             thrownProxy = new ThrowableProxy(thrown);
348         }
349         return thrownProxy;
350     }
351 
352     public void setSource(StackTraceElement source) {
353         this.source = source;
354     }
355 
356     /**
357      * Returns the StackTraceElement for the caller. This will be the entry that occurs right
358      * before the first occurrence of FQCN as a class name.
359      * @return the StackTraceElement for the caller.
360      */
361     @Override
362     public StackTraceElement getSource() {
363         if (source != null) {
364             return source;
365         }
366         if (loggerFqcn == null || !includeLocation) {
367             return null;
368         }
369         source = StackLocatorUtil.calcLocation(loggerFqcn);
370         return source;
371     }
372 
373     @SuppressWarnings("unchecked")
374     @Override
375     public ReadOnlyStringMap getContextData() {
376         return contextData;
377     }
378 
379     @Override
380     public Map<String, String> getContextMap() {
381         return contextData.toMap();
382     }
383 
384     public void setContextData(final StringMap mutableContextData) {
385         this.contextData = mutableContextData;
386     }
387 
388     @Override
389     public ThreadContext.ContextStack getContextStack() {
390         return contextStack;
391     }
392 
393     public void setContextStack(final ThreadContext.ContextStack contextStack) {
394         this.contextStack = contextStack;
395     }
396 
397     @Override
398     public long getThreadId() {
399         return threadId;
400     }
401 
402     public void setThreadId(final long threadId) {
403         this.threadId = threadId;
404     }
405 
406     @Override
407     public String getThreadName() {
408         return threadName;
409     }
410 
411     public void setThreadName(final String threadName) {
412         this.threadName = threadName;
413     }
414 
415     @Override
416     public int getThreadPriority() {
417         return threadPriority;
418     }
419 
420     public void setThreadPriority(final int threadPriority) {
421         this.threadPriority = threadPriority;
422     }
423 
424     @Override
425     public boolean isIncludeLocation() {
426         return includeLocation;
427     }
428 
429     @Override
430     public void setIncludeLocation(final boolean includeLocation) {
431         this.includeLocation = includeLocation;
432     }
433 
434     @Override
435     public boolean isEndOfBatch() {
436         return endOfBatch;
437     }
438 
439     @Override
440     public void setEndOfBatch(final boolean endOfBatch) {
441         this.endOfBatch = endOfBatch;
442     }
443 
444     @Override
445     public long getNanoTime() {
446         return nanoTime;
447     }
448 
449     public void setNanoTime(final long nanoTime) {
450         this.nanoTime = nanoTime;
451     }
452 
453     /**
454      * Creates a LogEventProxy that can be serialized.
455      * @return a LogEventProxy.
456      */
457     protected Object writeReplace() {
458         return new Log4jLogEvent.LogEventProxy(this, this.includeLocation);
459     }
460 
461     private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
462         throw new InvalidObjectException("Proxy required");
463     }
464 
465     /**
466      * Creates and returns a new immutable copy of this {@code MutableLogEvent}.
467      * If {@link #isIncludeLocation()} is true, this will obtain caller location information.
468      *
469      * @return a new immutable copy of the data in this {@code MutableLogEvent}
470      */
471     public Log4jLogEvent createMemento() {
472         return Log4jLogEvent.deserialize(Log4jLogEvent.serialize(this, includeLocation));
473     }
474 
475     /**
476      * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code MutableLogEvent}.
477      * @param builder the builder whose fields to populate
478      */
479     public void initializeBuilder(final Log4jLogEvent.Builder builder) {
480         builder.setContextData(contextData) //
481                 .setContextStack(contextStack) //
482                 .setEndOfBatch(endOfBatch) //
483                 .setIncludeLocation(includeLocation) //
484                 .setLevel(getLevel()) // ensure non-null
485                 .setLoggerFqcn(loggerFqcn) //
486                 .setLoggerName(loggerName) //
487                 .setMarker(marker) //
488                 .setMessage(memento()) // ensure non-null & immutable
489                 .setNanoTime(nanoTime) //
490                 .setSource(source) //
491                 .setThreadId(threadId) //
492                 .setThreadName(threadName) //
493                 .setThreadPriority(threadPriority) //
494                 .setThrown(getThrown()) // may deserialize from thrownProxy
495                 .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy
496                 .setInstant(instant) //
497         ;
498     }
499 }