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 */
017 package org.apache.logging.log4j.core.async;
018
019 import java.io.IOException;
020 import java.util.HashMap;
021 import java.util.Map;
022
023 import org.apache.logging.log4j.Level;
024 import org.apache.logging.log4j.Marker;
025 import org.apache.logging.log4j.ThreadContext.ContextStack;
026 import org.apache.logging.log4j.core.LogEvent;
027 import org.apache.logging.log4j.core.config.Property;
028 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
029 import org.apache.logging.log4j.core.impl.ThrowableProxy;
030 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
031 import org.apache.logging.log4j.message.Message;
032 import org.apache.logging.log4j.message.SimpleMessage;
033 import org.apache.logging.log4j.util.Strings;
034
035 import 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 */
041 public 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
074 public void setValues(final AsyncLogger asyncLogger, final String loggerName, final Marker marker,
075 final String fqcn, final Level level, final Message data, final Throwable throwable,
076 final Map<String, String> map, final ContextStack contextStack, final String threadName,
077 final StackTraceElement location, final long currentTimeMillis) {
078 this.asyncLogger = asyncLogger;
079 this.loggerName = loggerName;
080 this.marker = marker;
081 this.fqcn = fqcn;
082 this.level = level;
083 this.message = data;
084 this.thrown = throwable;
085 this.thrownProxy = null;
086 this.contextMap = map;
087 this.contextStack = contextStack;
088 this.threadName = threadName;
089 this.location = location;
090 this.currentTimeMillis = currentTimeMillis;
091 }
092
093 /**
094 * Event processor that reads the event from the ringbuffer can call this method.
095 *
096 * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
097 */
098 public void execute(final boolean endOfBatch) {
099 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 }