1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.async;
18
19 import java.util.List;
20
21 import org.apache.logging.log4j.Level;
22 import org.apache.logging.log4j.Marker;
23 import org.apache.logging.log4j.ThreadContext;
24 import org.apache.logging.log4j.ThreadContext.ContextStack;
25 import org.apache.logging.log4j.core.ContextDataInjector;
26 import org.apache.logging.log4j.core.Logger;
27 import org.apache.logging.log4j.core.LoggerContext;
28 import org.apache.logging.log4j.core.config.Configuration;
29 import org.apache.logging.log4j.core.config.Property;
30 import org.apache.logging.log4j.core.config.ReliabilityStrategy;
31 import org.apache.logging.log4j.core.impl.ContextDataFactory;
32 import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
33 import org.apache.logging.log4j.core.util.Clock;
34 import org.apache.logging.log4j.core.util.ClockFactory;
35 import org.apache.logging.log4j.core.util.NanoClock;
36 import org.apache.logging.log4j.message.Message;
37 import org.apache.logging.log4j.message.MessageFactory;
38 import org.apache.logging.log4j.message.ReusableMessage;
39 import org.apache.logging.log4j.status.StatusLogger;
40 import org.apache.logging.log4j.util.StackLocatorUtil;
41 import org.apache.logging.log4j.util.StringMap;
42
43 import com.lmax.disruptor.EventTranslatorVararg;
44 import com.lmax.disruptor.dsl.Disruptor;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBufferLogEvent> {
66
67
68
69
70
71 private static final StatusLogger LOGGER = StatusLogger.getLogger();
72 private static final Clock CLOCK = ClockFactory.getClock();
73 private static final ContextDataInjector CONTEXT_DATA_INJECTOR = ContextDataInjectorFactory.createInjector();
74
75 private static final ThreadNameCachingStrategy THREAD_NAME_CACHING_STRATEGY = ThreadNameCachingStrategy.create();
76
77 private final ThreadLocal<RingBufferLogEventTranslator> threadLocalTranslator = new ThreadLocal<>();
78 private final AsyncLoggerDisruptor loggerDisruptor;
79
80 private volatile boolean includeLocation;
81 private volatile NanoClock nanoClock;
82
83
84
85
86
87
88
89
90
91 public AsyncLogger(final LoggerContext context, final String name, final MessageFactory messageFactory,
92 final AsyncLoggerDisruptor loggerDisruptor) {
93 super(context, name, messageFactory);
94 this.loggerDisruptor = loggerDisruptor;
95 includeLocation = privateConfig.loggerConfig.isIncludeLocation();
96 nanoClock = context.getConfiguration().getNanoClock();
97 }
98
99
100
101
102
103
104 @Override
105 protected void updateConfiguration(final Configuration newConfig) {
106 nanoClock = newConfig.getNanoClock();
107 includeLocation = newConfig.getLoggerConfig(name).isIncludeLocation();
108 super.updateConfiguration(newConfig);
109 }
110
111
112 NanoClock getNanoClock() {
113 return nanoClock;
114 }
115
116 private RingBufferLogEventTranslator getCachedTranslator() {
117 RingBufferLogEventTranslator result = threadLocalTranslator.get();
118 if (result == null) {
119 result = new RingBufferLogEventTranslator();
120 threadLocalTranslator.set(result);
121 }
122 return result;
123 }
124
125 @Override
126 public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message,
127 final Throwable thrown) {
128
129 if (loggerDisruptor.isUseThreadLocals()) {
130 logWithThreadLocalTranslator(fqcn, level, marker, message, thrown);
131 } else {
132
133 logWithVarargTranslator(fqcn, level, marker, message, thrown);
134 }
135 }
136
137 private boolean isReused(final Message message) {
138 return message instanceof ReusableMessage;
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153 private void logWithThreadLocalTranslator(final String fqcn, final Level level, final Marker marker,
154 final Message message, final Throwable thrown) {
155
156
157 final RingBufferLogEventTranslator translator = getCachedTranslator();
158 initTranslator(translator, fqcn, level, marker, message, thrown);
159 initTranslatorThreadValues(translator);
160 publish(translator);
161 }
162
163 private void publish(final RingBufferLogEventTranslator translator) {
164 if (!loggerDisruptor.tryPublish(translator)) {
165 handleRingBufferFull(translator);
166 }
167 }
168
169 private void handleRingBufferFull(final RingBufferLogEventTranslator translator) {
170 if (Logger.getRecursionDepth() > 1) {
171
172 final Message message = AsyncQueueFullMessageUtil.transform(translator.message);
173 logMessageInCurrentThread(translator.fqcn, translator.level, translator.marker, message,
174 translator.thrown);
175 return;
176 }
177 final EventRoute eventRoute = loggerDisruptor.getEventRoute(translator.level);
178 switch (eventRoute) {
179 case ENQUEUE:
180 loggerDisruptor.enqueueLogMessageInfo(translator);
181 break;
182 case SYNCHRONOUS:
183 logMessageInCurrentThread(translator.fqcn, translator.level, translator.marker, translator.message,
184 translator.thrown);
185 break;
186 case DISCARD:
187 break;
188 default:
189 throw new IllegalStateException("Unknown EventRoute " + eventRoute);
190 }
191 }
192
193 private void initTranslator(final RingBufferLogEventTranslator translator, final String fqcn,
194 final Level level, final Marker marker, final Message message, final Throwable thrown) {
195
196 translator.setBasicValues(this, name, marker, fqcn, level, message,
197
198 thrown,
199
200
201 ThreadContext.getImmutableStack(),
202
203
204 calcLocationIfRequested(fqcn),
205 CLOCK.currentTimeMillis(),
206 nanoClock.nanoTime()
207 );
208 }
209
210 private void initTranslatorThreadValues(final RingBufferLogEventTranslator translator) {
211
212 if (THREAD_NAME_CACHING_STRATEGY == ThreadNameCachingStrategy.UNCACHED) {
213 translator.updateThreadValues();
214 }
215 }
216
217
218
219
220
221
222
223 private StackTraceElement calcLocationIfRequested(final String fqcn) {
224
225
226
227 return includeLocation ? StackLocatorUtil.calcLocation(fqcn) : null;
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242 private void logWithVarargTranslator(final String fqcn, final Level level, final Marker marker,
243 final Message message, final Throwable thrown) {
244
245
246 final Disruptor<RingBufferLogEvent> disruptor = loggerDisruptor.getDisruptor();
247 if (disruptor == null) {
248 LOGGER.error("Ignoring log event after Log4j has been shut down.");
249 return;
250 }
251
252 if (!isReused(message)) {
253 InternalAsyncUtil.makeMessageImmutable(message);
254 }
255 StackTraceElement location = null;
256
257 if (!disruptor.getRingBuffer().tryPublishEvent(this,
258 this,
259 (location = calcLocationIfRequested(fqcn)),
260 fqcn,
261 level,
262 marker,
263 message,
264 thrown)) {
265 handleRingBufferFull(location, fqcn, level, marker, message, thrown);
266 }
267 }
268
269
270
271
272
273
274 @Override
275 public void translateTo(final RingBufferLogEvent event, final long sequence, final Object... args) {
276
277 final AsyncLogger asyncLogger = (AsyncLogger) args[0];
278 final StackTraceElement location = (StackTraceElement) args[1];
279 final String fqcn = (String) args[2];
280 final Level level = (Level) args[3];
281 final Marker marker = (Marker) args[4];
282 final Message message = (Message) args[5];
283 final Throwable thrown = (Throwable) args[6];
284
285
286 final ContextStack contextStack = ThreadContext.getImmutableStack();
287
288 final Thread currentThread = Thread.currentThread();
289 final String threadName = THREAD_NAME_CACHING_STRATEGY.getThreadName();
290 event.setValues(asyncLogger, asyncLogger.getName(), marker, fqcn, level, message, thrown,
291
292
293 CONTEXT_DATA_INJECTOR.injectContextData(null, (StringMap) event.getContextData()),
294 contextStack, currentThread.getId(), threadName, currentThread.getPriority(), location,
295 CLOCK.currentTimeMillis(), nanoClock.nanoTime());
296 }
297
298
299
300
301
302
303
304
305
306
307
308 void logMessageInCurrentThread(final String fqcn, final Level level, final Marker marker,
309 final Message message, final Throwable thrown) {
310
311 final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
312 strategy.log(this, getName(), fqcn, marker, level, message, thrown);
313 }
314
315 private void handleRingBufferFull(final StackTraceElement location,
316 final String fqcn,
317 final Level level,
318 final Marker marker,
319 final Message msg,
320 final Throwable thrown) {
321 if (Logger.getRecursionDepth() > 1) {
322
323 final Message message = AsyncQueueFullMessageUtil.transform(msg);
324 logMessageInCurrentThread(fqcn, level, marker, message, thrown);
325 return;
326 }
327 final EventRoute eventRoute = loggerDisruptor.getEventRoute(level);
328 switch (eventRoute) {
329 case ENQUEUE:
330 loggerDisruptor.getDisruptor().getRingBuffer().publishEvent(this,
331 this,
332 location,
333 fqcn,
334 level,
335 marker,
336 msg,
337 thrown);
338 break;
339 case SYNCHRONOUS:
340 logMessageInCurrentThread(fqcn, level, marker, msg, thrown);
341 break;
342 case DISCARD:
343 break;
344 default:
345 throw new IllegalStateException("Unknown EventRoute " + eventRoute);
346 }
347 }
348
349
350
351
352
353
354
355
356 public void actualAsyncLog(final RingBufferLogEvent event) {
357 final List<Property> properties = privateConfig.loggerConfig.getPropertyList();
358
359 if (properties != null) {
360 StringMap contextData = (StringMap) event.getContextData();
361 if (contextData.isFrozen()) {
362 final StringMap temp = ContextDataFactory.createContextData();
363 temp.putAll(contextData);
364 contextData = temp;
365 }
366 for (int i = 0; i < properties.size(); i++) {
367 final Property prop = properties.get(i);
368 if (contextData.getValue(prop.getName()) != null) {
369 continue;
370 }
371 final String value = prop.isValueNeedsLookup()
372 ? privateConfig.config.getStrSubstitutor().replace(event, prop.getValue())
373 : prop.getValue();
374 contextData.putValue(prop.getName(), value);
375 }
376 event.setContextData(contextData);
377 }
378
379 final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
380 strategy.log(this, event);
381 }
382 }