001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.impl;
018
019import java.io.InvalidObjectException;
020import java.io.ObjectInputStream;
021import java.util.Arrays;
022import java.util.Map;
023
024import org.apache.logging.log4j.Level;
025import org.apache.logging.log4j.Marker;
026import org.apache.logging.log4j.ThreadContext;
027import org.apache.logging.log4j.core.LogEvent;
028import org.apache.logging.log4j.core.async.InternalAsyncUtil;
029import org.apache.logging.log4j.core.util.*;
030import org.apache.logging.log4j.core.time.Instant;
031import org.apache.logging.log4j.core.time.MutableInstant;
032import org.apache.logging.log4j.message.*;
033import org.apache.logging.log4j.util.ReadOnlyStringMap;
034import org.apache.logging.log4j.util.StackLocatorUtil;
035import org.apache.logging.log4j.util.StringBuilders;
036import org.apache.logging.log4j.util.StringMap;
037import org.apache.logging.log4j.util.Strings;
038
039/**
040 * Mutable implementation of the {@code LogEvent} interface.
041 * @since 2.6
042 */
043public class MutableLogEvent implements LogEvent, ReusableMessage, ParameterVisitable {
044    private static final Message EMPTY = new SimpleMessage(Strings.EMPTY);
045
046    private int threadPriority;
047    private long threadId;
048    private final MutableInstant instant = new MutableInstant();
049    private long nanoTime;
050    private short parameterCount;
051    private boolean includeLocation;
052    private boolean endOfBatch = false;
053    private Level level;
054    private String threadName;
055    private String loggerName;
056    private Message message;
057    private String messageFormat;
058    private StringBuilder messageText;
059    private Object[] parameters;
060    private Throwable thrown;
061    private ThrowableProxy thrownProxy;
062    private StringMap contextData = ContextDataFactory.createContextData();
063    private Marker marker;
064    private String loggerFqcn;
065    private StackTraceElement source;
066    private ThreadContext.ContextStack contextStack;
067    transient boolean reserved = false;
068
069    public MutableLogEvent() {
070        // messageText and the parameter array are lazily initialized
071        this(null, null);
072    }
073
074    public MutableLogEvent(final StringBuilder msgText, final Object[] replacementParameters) {
075        this.messageText = msgText;
076        this.parameters = replacementParameters;
077    }
078
079    @Override
080    public Log4jLogEvent toImmutable() {
081        return createMemento();
082    }
083
084    /**
085     * Initialize the fields of this {@code MutableLogEvent} from another event.
086     * Similar in purpose and usage as {@link org.apache.logging.log4j.core.impl.Log4jLogEvent.LogEventProxy},
087     * but a mutable version.
088     * <p>
089     * This method is used on async logger ringbuffer slots holding MutableLogEvent objects in each slot.
090     * </p>
091     *
092     * @param event the event to copy data from
093     */
094    public void initFrom(final LogEvent event) {
095        this.loggerFqcn = event.getLoggerFqcn();
096        this.marker = event.getMarker();
097        this.level = event.getLevel();
098        this.loggerName = event.getLoggerName();
099        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}