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.async;
018
019import java.io.IOException;
020import java.util.HashMap;
021import java.util.Map;
022
023import org.apache.logging.log4j.Level;
024import org.apache.logging.log4j.Marker;
025import org.apache.logging.log4j.ThreadContext.ContextStack;
026import org.apache.logging.log4j.core.LogEvent;
027import org.apache.logging.log4j.core.config.Property;
028import org.apache.logging.log4j.core.impl.Log4jLogEvent;
029import org.apache.logging.log4j.core.impl.ThrowableProxy;
030import org.apache.logging.log4j.core.lookup.StrSubstitutor;
031import org.apache.logging.log4j.message.Message;
032import org.apache.logging.log4j.message.SimpleMessage;
033import org.apache.logging.log4j.util.Strings;
034
035import com.lmax.disruptor.EventFactory;
036
037/**
038 * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during
039 * the life of the RingBuffer.
040 */
041public class RingBufferLogEvent implements LogEvent {
042    private static final long serialVersionUID = 8462119088943934758L;
043
044    /**
045     * Creates the events that will be put in the RingBuffer.
046     */
047    private static class Factory implements EventFactory<RingBufferLogEvent> {
048
049        @Override
050        public RingBufferLogEvent newInstance() {
051            return new RingBufferLogEvent();
052        }
053    }
054
055    /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
056    public static final Factory FACTORY = new Factory();
057
058    private transient AsyncLogger asyncLogger;
059    private String loggerName;
060    private Marker marker;
061    private String fqcn;
062    private Level level;
063    private Message message;
064    private transient Throwable thrown;
065    private ThrowableProxy thrownProxy;
066    private Map<String, String> contextMap;
067    private ContextStack contextStack;
068    private String threadName;
069    private StackTraceElement location;
070    private long currentTimeMillis;
071    private boolean endOfBatch;
072    private boolean includeLocation;
073    private long nanoTime;
074
075    public void setValues(final AsyncLogger asyncLogger, final String loggerName, final Marker marker,
076            final String fqcn, final Level level, final Message data, final Throwable throwable,
077            final Map<String, String> map, final ContextStack contextStack, final String threadName,
078            final StackTraceElement location, final long currentTimeMillis, final long nanoTime) {
079        this.asyncLogger = asyncLogger;
080        this.loggerName = loggerName;
081        this.marker = marker;
082        this.fqcn = fqcn;
083        this.level = level;
084        this.message = data;
085        this.thrown = throwable;
086        this.thrownProxy = null;
087        this.contextMap = map;
088        this.contextStack = contextStack;
089        this.threadName = threadName;
090        this.location = location;
091        this.currentTimeMillis = currentTimeMillis;
092        this.nanoTime = nanoTime;
093    }
094
095    /**
096     * Event processor that reads the event from the ringbuffer can call this method.
097     * 
098     * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
099     */
100    public void execute(final boolean endOfBatch) {
101        this.endOfBatch = endOfBatch;
102        asyncLogger.actualAsyncLog(this);
103    }
104
105    /**
106     * Returns {@code true} if this event is the end of a batch, {@code false} otherwise.
107     * 
108     * @return {@code true} if this event is the end of a batch, {@code false} otherwise
109     */
110    @Override
111    public boolean isEndOfBatch() {
112        return endOfBatch;
113    }
114
115    @Override
116    public void setEndOfBatch(final boolean endOfBatch) {
117        this.endOfBatch = endOfBatch;
118    }
119
120    @Override
121    public boolean isIncludeLocation() {
122        return includeLocation;
123    }
124
125    @Override
126    public void setIncludeLocation(final boolean includeLocation) {
127        this.includeLocation = includeLocation;
128    }
129
130    @Override
131    public String getLoggerName() {
132        return loggerName;
133    }
134
135    @Override
136    public Marker getMarker() {
137        return marker;
138    }
139
140    @Override
141    public String getLoggerFqcn() {
142        return fqcn;
143    }
144
145    @Override
146    public Level getLevel() {
147        if (level == null) {
148            level = Level.OFF; // LOG4J2-462, LOG4J2-465
149        }
150        return level;
151    }
152
153    @Override
154    public Message getMessage() {
155        if (message == null) {
156            message = new SimpleMessage(Strings.EMPTY);
157        }
158        return message;
159    }
160
161    @Override
162    public Throwable getThrown() {
163        // after deserialization, thrown is null but thrownProxy may be non-null
164        if (thrown == null) {
165            if (thrownProxy != null) {
166                thrown = thrownProxy.getThrowable();
167            }
168        }
169        return thrown;
170    }
171
172    @Override
173    public ThrowableProxy getThrownProxy() {
174        // lazily instantiate the (expensive) ThrowableProxy
175        if (thrownProxy == null) {
176            if (thrown != null) {
177                thrownProxy = new ThrowableProxy(thrown);
178            }
179        }
180        return this.thrownProxy;
181    }
182
183    @Override
184    public Map<String, String> getContextMap() {
185        return contextMap;
186    }
187
188    @Override
189    public ContextStack getContextStack() {
190        return contextStack;
191    }
192
193    @Override
194    public String getThreadName() {
195        return threadName;
196    }
197
198    @Override
199    public StackTraceElement getSource() {
200        return location;
201    }
202
203    @Override
204    public long getTimeMillis() {
205        return currentTimeMillis;
206    }
207    
208    @Override
209    public long getNanoTime() {
210        return nanoTime;
211    }
212
213    /**
214     * Merges the contents of the specified map into the contextMap, after replacing any variables in the property
215     * values with the StrSubstitutor-supplied actual values.
216     * 
217     * @param properties configured properties
218     * @param strSubstitutor used to lookup values of variables in properties
219     */
220    public void mergePropertiesIntoContextMap(final Map<Property, Boolean> properties,
221            final StrSubstitutor strSubstitutor) {
222        if (properties == null) {
223            return; // nothing to do
224        }
225
226        final Map<String, String> map = contextMap == null ? new HashMap<String, String>()
227                : new HashMap<>(contextMap);
228
229        for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) {
230            final Property prop = entry.getKey();
231            if (map.containsKey(prop.getName())) {
232                continue; // contextMap overrides config properties
233            }
234            final String value = entry.getValue().booleanValue() ? strSubstitutor.replace(prop.getValue()) : prop
235                    .getValue();
236            map.put(prop.getName(), value);
237        }
238        contextMap = map;
239    }
240
241    /**
242     * Release references held by ring buffer to allow objects to be garbage-collected.
243     */
244    public void clear() {
245        setValues(null, // asyncLogger
246                null, // loggerName
247                null, // marker
248                null, // fqcn
249                null, // level
250                null, // data
251                null, // t
252                null, // map
253                null, // contextStack
254                null, // threadName
255                null, // location
256                0, // currentTimeMillis
257                0 // nanoTime
258        );
259    }
260
261    private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
262        getThrownProxy(); // initialize the ThrowableProxy before serializing
263        out.defaultWriteObject();
264    }
265
266    /**
267     * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}.
268     * 
269     * @return a new immutable copy of the data in this {@code RingBufferLogEvent}
270     */
271    public LogEvent createMemento() {
272        final LogEvent result = new Log4jLogEvent.Builder(this).build();
273        return result;
274    }
275
276    /**
277     * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}.
278     * @param builder the builder whose fields to populate
279     */
280    public void initializeBuilder(Log4jLogEvent.Builder builder) {
281        builder.setContextMap(contextMap) //
282                .setContextStack(contextStack) //
283                .setEndOfBatch(endOfBatch) //
284                .setIncludeLocation(includeLocation) //
285                .setLevel(getLevel()) // ensure non-null
286                .setLoggerFqcn(fqcn) //
287                .setLoggerName(loggerName) //
288                .setMarker(marker) //
289                .setMessage(getMessage()) // ensure non-null
290                .setNanoTime(nanoTime) //
291                .setSource(location) //
292                .setThreadName(threadName) //
293                .setThrown(getThrown()) // may deserialize from thrownProxy
294                .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy
295                .setTimeMillis(currentTimeMillis) //
296                ;        
297    }
298}