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  
43      /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
44      public static final Factory FACTORY = new Factory();
45  
46      private static final long serialVersionUID = 8462119088943934758L;
47  
48      /**
49       * Creates the events that will be put in the RingBuffer.
50       */
51      private static class Factory implements EventFactory<RingBufferLogEvent> {
52  
53          @Override
54          public RingBufferLogEvent newInstance() {
55              return new RingBufferLogEvent();
56          }
57      }
58  
59      private transient AsyncLogger asyncLogger;
60      private String loggerName;
61      private Marker marker;
62      private String fqcn;
63      private Level level;
64      private Message message;
65      private transient Throwable thrown;
66      private ThrowableProxy thrownProxy;
67      private Map<String, String> contextMap;
68      private ContextStack contextStack;
69      private String threadName;
70      private StackTraceElement location;
71      private long currentTimeMillis;
72      private boolean endOfBatch;
73      private boolean includeLocation;
74      private long nanoTime;
75  
76      public void setValues(final AsyncLogger anAsyncLogger, final String aLoggerName, final Marker aMarker,
77              final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable,
78              final Map<String, String> aMap, final ContextStack aContextStack, final String aThreadName,
79              final StackTraceElement aLocation, final long aCurrentTimeMillis, final long aNanoTime) {
80          this.asyncLogger = anAsyncLogger;
81          this.loggerName = aLoggerName;
82          this.marker = aMarker;
83          this.fqcn = theFqcn;
84          this.level = aLevel;
85          this.message = msg;
86          this.thrown = aThrowable;
87          this.thrownProxy = null;
88          this.contextMap = aMap;
89          this.contextStack = aContextStack;
90          this.threadName = aThreadName;
91          this.location = aLocation;
92          this.currentTimeMillis = aCurrentTimeMillis;
93          this.nanoTime = aNanoTime;
94      }
95  
96      /**
97       * Event processor that reads the event from the ringbuffer can call this method.
98       * 
99       * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
100      */
101     public void execute(final boolean endOfBatch) {
102         this.endOfBatch = endOfBatch;
103         asyncLogger.actualAsyncLog(this);
104     }
105 
106     /**
107      * Returns {@code true} if this event is the end of a batch, {@code false} otherwise.
108      * 
109      * @return {@code true} if this event is the end of a batch, {@code false} otherwise
110      */
111     @Override
112     public boolean isEndOfBatch() {
113         return endOfBatch;
114     }
115 
116     @Override
117     public void setEndOfBatch(final boolean endOfBatch) {
118         this.endOfBatch = endOfBatch;
119     }
120 
121     @Override
122     public boolean isIncludeLocation() {
123         return includeLocation;
124     }
125 
126     @Override
127     public void setIncludeLocation(final boolean includeLocation) {
128         this.includeLocation = includeLocation;
129     }
130 
131     @Override
132     public String getLoggerName() {
133         return loggerName;
134     }
135 
136     @Override
137     public Marker getMarker() {
138         return marker;
139     }
140 
141     @Override
142     public String getLoggerFqcn() {
143         return fqcn;
144     }
145 
146     @Override
147     public Level getLevel() {
148         if (level == null) {
149             level = Level.OFF; // LOG4J2-462, LOG4J2-465
150         }
151         return level;
152     }
153 
154     @Override
155     public Message getMessage() {
156         if (message == null) {
157             message = new SimpleMessage(Strings.EMPTY);
158         }
159         return message;
160     }
161 
162     @Override
163     public Throwable getThrown() {
164         // after deserialization, thrown is null but thrownProxy may be non-null
165         if (thrown == null) {
166             if (thrownProxy != null) {
167                 thrown = thrownProxy.getThrowable();
168             }
169         }
170         return thrown;
171     }
172 
173     @Override
174     public ThrowableProxy getThrownProxy() {
175         // lazily instantiate the (expensive) ThrowableProxy
176         if (thrownProxy == null) {
177             if (thrown != null) {
178                 thrownProxy = new ThrowableProxy(thrown);
179             }
180         }
181         return this.thrownProxy;
182     }
183 
184     @Override
185     public Map<String, String> getContextMap() {
186         return contextMap;
187     }
188 
189     @Override
190     public ContextStack getContextStack() {
191         return contextStack;
192     }
193 
194     @Override
195     public String getThreadName() {
196         return threadName;
197     }
198 
199     @Override
200     public StackTraceElement getSource() {
201         return location;
202     }
203 
204     @Override
205     public long getTimeMillis() {
206         return currentTimeMillis;
207     }
208     
209     @Override
210     public long getNanoTime() {
211         return nanoTime;
212     }
213 
214     /**
215      * Merges the contents of the specified map into the contextMap, after replacing any variables in the property
216      * values with the StrSubstitutor-supplied actual values.
217      * 
218      * @param properties configured properties
219      * @param strSubstitutor used to lookup values of variables in properties
220      */
221     public void mergePropertiesIntoContextMap(final Map<Property, Boolean> properties,
222             final StrSubstitutor strSubstitutor) {
223         if (properties == null) {
224             return; // nothing to do
225         }
226 
227         final Map<String, String> map = contextMap == null ? new HashMap<String, String>()
228                 : new HashMap<>(contextMap);
229 
230         for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) {
231             final Property prop = entry.getKey();
232             if (map.containsKey(prop.getName())) {
233                 continue; // contextMap overrides config properties
234             }
235             final String value = entry.getValue().booleanValue() ? strSubstitutor.replace(prop.getValue()) : prop
236                     .getValue();
237             map.put(prop.getName(), value);
238         }
239         contextMap = map;
240     }
241 
242     /**
243      * Release references held by ring buffer to allow objects to be garbage-collected.
244      */
245     public void clear() {
246         setValues(null, // asyncLogger
247                 null, // loggerName
248                 null, // marker
249                 null, // fqcn
250                 null, // level
251                 null, // data
252                 null, // t
253                 null, // map
254                 null, // contextStack
255                 null, // threadName
256                 null, // location
257                 0, // currentTimeMillis
258                 0 // nanoTime
259         );
260     }
261 
262     private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
263         getThrownProxy(); // initialize the ThrowableProxy before serializing
264         out.defaultWriteObject();
265     }
266 
267     /**
268      * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}.
269      *
270      * @return a new immutable copy of the data in this {@code RingBufferLogEvent}
271      */
272     public LogEvent createMemento() {
273         final LogEvent result = new Log4jLogEvent.Builder(this).build();
274         return result;
275     }
276 
277     /**
278      * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}.
279      * @param builder the builder whose fields to populate
280      */
281     public void initializeBuilder(Log4jLogEvent.Builder builder) {
282         builder.setContextMap(contextMap) //
283                 .setContextStack(contextStack) //
284                 .setEndOfBatch(endOfBatch) //
285                 .setIncludeLocation(includeLocation) //
286                 .setLevel(getLevel()) // ensure non-null
287                 .setLoggerFqcn(fqcn) //
288                 .setLoggerName(loggerName) //
289                 .setMarker(marker) //
290                 .setMessage(getMessage()) // ensure non-null
291                 .setNanoTime(nanoTime) //
292                 .setSource(location) //
293                 .setThreadName(threadName) //
294                 .setThrown(getThrown()) // may deserialize from thrownProxy
295                 .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy
296                 .setTimeMillis(currentTimeMillis);
297     }
298 }