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.HashMap;
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.config.Property;
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.lookup.StrSubstitutor;
31  import org.apache.logging.log4j.message.Message;
32  import org.apache.logging.log4j.message.SimpleMessage;
33  import org.apache.logging.log4j.util.Strings;
34  
35  import com.lmax.disruptor.EventFactory;
36  
37  /**
38   * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during
39   * the life of the RingBuffer.
40   */
41  public class RingBufferLogEvent implements LogEvent {
42      private static final long serialVersionUID = 8462119088943934758L;
43  
44      /**
45       * Creates the events that will be put in the RingBuffer.
46       */
47      private static class Factory implements EventFactory<RingBufferLogEvent> {
48  
49          @Override
50          public RingBufferLogEvent newInstance() {
51              return new RingBufferLogEvent();
52          }
53      }
54  
55      /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
56      public static final Factory FACTORY = new Factory();
57  
58      private transient AsyncLogger asyncLogger;
59      private String loggerName;
60      private Marker marker;
61      private String fqcn;
62      private Level level;
63      private Message message;
64      private transient Throwable thrown;
65      private ThrowableProxy thrownProxy;
66      private Map<String, String> contextMap;
67      private ContextStack contextStack;
68      private String threadName;
69      private StackTraceElement location;
70      private long currentTimeMillis;
71      private boolean endOfBatch;
72      private boolean includeLocation;
73      private long nanoTime;
74  
75      public void setValues(final AsyncLogger asyncLogger, final String loggerName, final Marker marker,
76              final String fqcn, final Level level, final Message data, final Throwable throwable,
77              final Map<String, String> map, final ContextStack contextStack, final String threadName,
78              final StackTraceElement location, final long currentTimeMillis, final long nanoTime) {
79          this.asyncLogger = asyncLogger;
80          this.loggerName = loggerName;
81          this.marker = marker;
82          this.fqcn = fqcn;
83          this.level = level;
84          this.message = data;
85          this.thrown = throwable;
86          this.thrownProxy = null;
87          this.contextMap = map;
88          this.contextStack = contextStack;
89          this.threadName = threadName;
90          this.location = location;
91          this.currentTimeMillis = currentTimeMillis;
92          this.nanoTime = nanoTime;
93      }
94  
95      /**
96       * Event processor that reads the event from the ringbuffer can call this method.
97       * 
98       * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
99       */
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 }