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