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.message.TimestampMessage;
34  import org.apache.logging.log4j.util.Strings;
35  
36  import com.lmax.disruptor.EventFactory;
37  
38  /**
39   * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during
40   * the life of the RingBuffer.
41   */
42  public class RingBufferLogEvent implements LogEvent {
43      private static final long serialVersionUID = 8462119088943934758L;
44  
45      /**
46       * Creates the events that will be put in the RingBuffer.
47       */
48      private static class Factory implements EventFactory<RingBufferLogEvent> {
49  
50          @Override
51          public RingBufferLogEvent newInstance() {
52              return new RingBufferLogEvent();
53          }
54      }
55  
56      /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
57      public static final Factory FACTORY = new Factory();
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  
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) {
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      }
93  
94      /**
95       * Event processor that reads the event from the ringbuffer can call this method.
96       * 
97       * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
98       */
99      public void execute(final boolean endOfBatch) {
100         this.endOfBatch = endOfBatch;
101         asyncLogger.actualAsyncLog(this);
102     }
103 
104     /**
105      * Returns {@code true} if this event is the end of a batch, {@code false} otherwise.
106      * 
107      * @return {@code true} if this event is the end of a batch, {@code false} otherwise
108      */
109     @Override
110     public boolean isEndOfBatch() {
111         return endOfBatch;
112     }
113 
114     @Override
115     public void setEndOfBatch(final boolean endOfBatch) {
116         this.endOfBatch = endOfBatch;
117     }
118 
119     @Override
120     public boolean isIncludeLocation() {
121         return includeLocation;
122     }
123 
124     @Override
125     public void setIncludeLocation(final boolean includeLocation) {
126         this.includeLocation = includeLocation;
127     }
128 
129     @Override
130     public String getLoggerName() {
131         return loggerName;
132     }
133 
134     @Override
135     public Marker getMarker() {
136         return marker;
137     }
138 
139     @Override
140     public String getLoggerFqcn() {
141         return fqcn;
142     }
143 
144     @Override
145     public Level getLevel() {
146         if (level == null) {
147             level = Level.OFF; // LOG4J2-462, LOG4J2-465
148         }
149         return level;
150     }
151 
152     @Override
153     public Message getMessage() {
154         if (message == null) {
155             message = new SimpleMessage(Strings.EMPTY);
156         }
157         return message;
158     }
159 
160     @Override
161     public Throwable getThrown() {
162         // after deserialization, thrown is null but thrownProxy may be non-null
163         if (thrown == null) {
164             if (thrownProxy != null) {
165                 thrown = thrownProxy.getThrowable();
166             }
167         }
168         return thrown;
169     }
170 
171     @Override
172     public ThrowableProxy getThrownProxy() {
173         // lazily instantiate the (expensive) ThrowableProxy
174         if (thrownProxy == null) {
175             if (thrown != null) {
176                 thrownProxy = new ThrowableProxy(thrown);
177             }
178         }
179         return this.thrownProxy;
180     }
181 
182     @Override
183     public Map<String, String> getContextMap() {
184         return contextMap;
185     }
186 
187     @Override
188     public ContextStack getContextStack() {
189         return contextStack;
190     }
191 
192     @Override
193     public String getThreadName() {
194         return threadName;
195     }
196 
197     @Override
198     public StackTraceElement getSource() {
199         return location;
200     }
201 
202     @Override
203     public long getTimeMillis() {
204         final Message msg = getMessage();
205         if (msg instanceof TimestampMessage) { // LOG4J2-455
206             return ((TimestampMessage) msg).getTimestamp();
207         }
208         return currentTimeMillis;
209     }
210 
211     /**
212      * Merges the contents of the specified map into the contextMap, after replacing any variables in the property
213      * values with the StrSubstitutor-supplied actual values.
214      * 
215      * @param properties configured properties
216      * @param strSubstitutor used to lookup values of variables in properties
217      */
218     public void mergePropertiesIntoContextMap(final Map<Property, Boolean> properties,
219             final StrSubstitutor strSubstitutor) {
220         if (properties == null) {
221             return; // nothing to do
222         }
223 
224         final Map<String, String> map = contextMap == null ? new HashMap<String, String>()
225                 : new HashMap<String, String>(contextMap);
226 
227         for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) {
228             final Property prop = entry.getKey();
229             if (map.containsKey(prop.getName())) {
230                 continue; // contextMap overrides config properties
231             }
232             final String value = entry.getValue().booleanValue() ? strSubstitutor.replace(prop.getValue()) : prop
233                     .getValue();
234             map.put(prop.getName(), value);
235         }
236         contextMap = map;
237     }
238 
239     /**
240      * Release references held by ring buffer to allow objects to be garbage-collected.
241      */
242     public void clear() {
243         setValues(null, // asyncLogger
244                 null, // loggerName
245                 null, // marker
246                 null, // fqcn
247                 null, // level
248                 null, // data
249                 null, // t
250                 null, // map
251                 null, // contextStack
252                 null, // threadName
253                 null, // location
254                 0 // currentTimeMillis
255         );
256     }
257 
258     private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
259         getThrownProxy(); // initialize the ThrowableProxy before serializing
260         out.defaultWriteObject();
261     }
262 
263     /**
264      * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}.
265      * 
266      * @return a new immutable copy of the data in this {@code RingBufferLogEvent}
267      */
268     public LogEvent createMemento() {
269         // Ideally, would like to use the LogEventFactory here but signature does not match:
270         // results in factory re-creating the timestamp, context map and context stack, which we don't want.
271         return new Log4jLogEvent(loggerName, marker, fqcn, level, message, thrown, contextMap, contextStack,
272                 threadName, location, currentTimeMillis);
273     }
274 }