1
2
3
4
5
6
7
8
9
10
11
12 package ch.qos.logback.classic.spi;
13
14 import java.io.IOException;
15 import java.io.ObjectOutputStream;
16 import java.lang.reflect.InvocationTargetException;
17 import java.lang.reflect.Method;
18 import java.time.Clock;
19 import java.time.Instant;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Map;
24
25 import ch.qos.logback.core.CoreConstants;
26 import ch.qos.logback.core.util.EnvUtil;
27 import ch.qos.logback.core.util.StringUtil;
28 import org.slf4j.Marker;
29 import org.slf4j.event.KeyValuePair;
30 import org.slf4j.helpers.MessageFormatter;
31 import org.slf4j.spi.MDCAdapter;
32
33 import ch.qos.logback.classic.Level;
34 import ch.qos.logback.classic.Logger;
35 import ch.qos.logback.classic.LoggerContext;
36 import ch.qos.logback.classic.util.LogbackMDCAdapter;
37 import ch.qos.logback.core.spi.SequenceNumberGenerator;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class LoggingEvent implements ILoggingEvent {
55
56 public static final String VIRTUAL_THREAD_NAME_PREFIX = "virtual-";
57 public static final String REGULAR_UNNAMED_THREAD_PREFIX = "unnamed-";
58
59
60
61
62
63
64
65 transient String fqnOfLoggerClass;
66
67
68
69
70 private String threadName;
71
72 private String loggerName;
73 private LoggerContext loggerContext;
74 private LoggerContextVO loggerContextVO;
75
76
77
78
79
80
81
82
83 private transient Level level;
84
85 private String message;
86
87
88
89
90 transient String formattedMessage;
91
92 private transient Object[] argumentArray;
93
94 private ThrowableProxy throwableProxy;
95
96 private StackTraceElement[] callerDataArray;
97
98 private List<Marker> markerList;
99
100 private Map<String, String> mdcPropertyMap;
101
102
103
104
105 List<KeyValuePair> keyValuePairs;
106
107
108
109
110 private Instant instant;
111
112 private long timeStamp;
113 private int nanoseconds;
114
115 private long sequenceNumber;
116
117 public LoggingEvent() {
118 }
119
120 public LoggingEvent(String fqcn, Logger logger, Level level, String message, Throwable throwable,
121 Object[] argArray) {
122 this.fqnOfLoggerClass = fqcn;
123 this.loggerName = logger.getName();
124 this.loggerContext = logger.getLoggerContext();
125 this.loggerContextVO = loggerContext.getLoggerContextRemoteView();
126 this.level = level;
127
128 this.message = message;
129 this.argumentArray = argArray;
130
131 Instant instant = Clock.systemUTC().instant();
132 initTmestampFields(instant);
133
134 if (loggerContext != null) {
135 SequenceNumberGenerator sequenceNumberGenerator = loggerContext.getSequenceNumberGenerator();
136 if (sequenceNumberGenerator != null)
137 sequenceNumber = sequenceNumberGenerator.nextSequenceNumber();
138 }
139
140 if (throwable == null) {
141 throwable = extractThrowableAnRearrangeArguments(argArray);
142 }
143
144 if (throwable != null) {
145 this.throwableProxy = new ThrowableProxy(throwable);
146
147 if (loggerContext != null && loggerContext.isPackagingDataEnabled()) {
148 this.throwableProxy.calculatePackagingData();
149 }
150 }
151 }
152
153 void initTmestampFields(Instant instant) {
154 this.instant = instant;
155 long epochSecond = instant.getEpochSecond();
156 this.nanoseconds = instant.getNano();
157 long milliseconds = nanoseconds / 1000_000;
158 this.timeStamp = (epochSecond * 1000) + (milliseconds);
159 }
160
161 private Throwable extractThrowableAnRearrangeArguments(Object[] argArray) {
162 Throwable extractedThrowable = EventArgUtil.extractThrowable(argArray);
163 if (EventArgUtil.successfulExtraction(extractedThrowable)) {
164 this.argumentArray = EventArgUtil.trimmedCopy(argArray);
165 }
166 return extractedThrowable;
167 }
168
169 public void setArgumentArray(Object[] argArray) {
170 if (this.argumentArray != null) {
171 throw new IllegalStateException("argArray has been already set");
172 }
173 this.argumentArray = argArray;
174 }
175
176 public Object[] getArgumentArray() {
177 return this.argumentArray;
178 }
179
180 public void addKeyValuePair(KeyValuePair kvp) {
181 if (keyValuePairs == null) {
182 keyValuePairs = new ArrayList<>(4);
183 }
184 keyValuePairs.add(kvp);
185 }
186
187 public void setKeyValuePairs(List<KeyValuePair> kvpList) {
188 this.keyValuePairs = kvpList;
189 }
190
191 @Override
192 public List<KeyValuePair> getKeyValuePairs() {
193 return this.keyValuePairs;
194 }
195
196 public Level getLevel() {
197 return level;
198 }
199
200 public String getLoggerName() {
201 return loggerName;
202 }
203
204 public void setLoggerName(String loggerName) {
205 this.loggerName = loggerName;
206 }
207
208 public String getThreadName() {
209 if (threadName == null) {
210 threadName = extractThreadName(Thread.currentThread());
211 }
212 return threadName;
213 }
214
215
216
217
218
219
220
221
222
223
224 private String extractThreadName(Thread aThread) {
225 if (aThread == null) {
226 return CoreConstants.NA;
227 }
228 String threadName = aThread.getName();
229 if (StringUtil.notNullNorEmpty(threadName))
230 return threadName;
231 Long virtualThreadId = getVirtualThreadId(aThread);
232 if (virtualThreadId != null) {
233 return VIRTUAL_THREAD_NAME_PREFIX + virtualThreadId;
234 } else {
235 return REGULAR_UNNAMED_THREAD_PREFIX + aThread.getId();
236 }
237 }
238
239
240
241
242
243
244
245
246 Long getVirtualThreadId(Thread aThread) {
247 if (EnvUtil.isJDK21OrHigher()) {
248 try {
249 Method isVirtualMethod = Thread.class.getMethod("isVirtual");
250 boolean isVirtual = (boolean) isVirtualMethod.invoke(aThread);
251 if (isVirtual)
252 return aThread.getId();
253 } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
254 return null;
255 }
256 }
257 return null;
258 }
259
260
261
262
263
264 public void setThreadName(String threadName) throws IllegalStateException {
265 if (this.threadName != null) {
266 throw new IllegalStateException("threadName has been already set");
267 }
268 this.threadName = threadName;
269 }
270
271
272
273
274
275 public IThrowableProxy getThrowableProxy() {
276 return throwableProxy;
277 }
278
279
280
281
282 public void setThrowableProxy(ThrowableProxy tp) {
283 if (throwableProxy != null) {
284 throw new IllegalStateException("ThrowableProxy has been already set.");
285 } else {
286 throwableProxy = tp;
287 }
288 }
289
290
291
292
293
294
295
296
297
298 public void prepareForDeferredProcessing() {
299 this.getFormattedMessage();
300 this.getThreadName();
301
302 this.getMDCPropertyMap();
303 }
304
305 public void setLoggerContext(LoggerContext lc) {
306 this.loggerContext = lc;
307 }
308
309 public LoggerContextVO getLoggerContextVO() {
310 return loggerContextVO;
311 }
312
313 public void setLoggerContextRemoteView(LoggerContextVO loggerContextVO) {
314 this.loggerContextVO = loggerContextVO;
315 }
316
317 public String getMessage() {
318 return message;
319 }
320
321 public void setMessage(String message) {
322 if (this.message != null) {
323 throw new IllegalStateException("The message for this event has been set already.");
324 }
325 this.message = message;
326 }
327
328
329
330
331
332
333
334 public Instant getInstant() {
335 return instant;
336 }
337
338
339
340
341
342
343 public void setInstant(Instant instant) {
344 initTmestampFields(instant);
345 }
346
347
348
349
350 public long getTimeStamp() {
351 return timeStamp;
352 }
353
354
355
356
357
358
359 @Override
360 public int getNanoseconds() {
361 return nanoseconds;
362 }
363
364
365
366
367
368
369
370 public void setTimeStamp(long timeStamp) {
371 Instant instant = Instant.ofEpochMilli(timeStamp);
372 setInstant(instant);
373 }
374
375 @Override
376 public long getSequenceNumber() {
377 return sequenceNumber;
378 }
379
380 public void setSequenceNumber(long sn) {
381 sequenceNumber = sn;
382 }
383
384 public void setLevel(Level level) {
385 if (this.level != null) {
386 throw new IllegalStateException("The level has been already set for this event.");
387 }
388 this.level = level;
389 }
390
391
392
393
394
395
396
397
398
399 public StackTraceElement[] getCallerData() {
400 if (callerDataArray == null) {
401 callerDataArray = CallerData.extract(new Throwable(), fqnOfLoggerClass,
402 loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages());
403 }
404 return callerDataArray;
405 }
406
407 public boolean hasCallerData() {
408 return (callerDataArray != null);
409 }
410
411 public void setCallerData(StackTraceElement[] callerDataArray) {
412 this.callerDataArray = callerDataArray;
413 }
414
415 public List<Marker> getMarkerList() {
416 return markerList;
417 }
418
419 public void addMarker(Marker marker) {
420 if (marker == null) {
421 return;
422 }
423 if (markerList == null) {
424 markerList = new ArrayList<>(4);
425 }
426 markerList.add(marker);
427 }
428
429 public long getContextBirthTime() {
430 return loggerContextVO.getBirthTime();
431 }
432
433
434 public String getFormattedMessage() {
435 if (formattedMessage != null) {
436 return formattedMessage;
437 }
438 if (argumentArray != null) {
439 formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage();
440 } else {
441 formattedMessage = message;
442 }
443
444 return formattedMessage;
445 }
446
447 public Map<String, String> getMDCPropertyMap() {
448
449 if (mdcPropertyMap == null) {
450 MDCAdapter mdcAdapter = loggerContext.getMDCAdapter();
451 if (mdcAdapter instanceof LogbackMDCAdapter)
452 mdcPropertyMap = ((LogbackMDCAdapter) mdcAdapter).getPropertyMap();
453 else
454 mdcPropertyMap = mdcAdapter.getCopyOfContextMap();
455 }
456
457 if (mdcPropertyMap == null)
458 mdcPropertyMap = Collections.emptyMap();
459
460 return mdcPropertyMap;
461 }
462
463
464
465
466
467
468
469 public void setMDCPropertyMap(Map<String, String> map) {
470 if (mdcPropertyMap != null) {
471 throw new IllegalStateException("The MDCPropertyMap has been already set for this event.");
472 }
473 this.mdcPropertyMap = map;
474
475 }
476
477
478
479
480
481
482 public Map<String, String> getMdc() {
483 return getMDCPropertyMap();
484 }
485
486 @Override
487 public String toString() {
488 StringBuilder sb = new StringBuilder();
489 sb.append('[');
490 sb.append(level).append("] ");
491 sb.append(getFormattedMessage());
492 return sb.toString();
493 }
494
495
496
497
498
499
500
501 private void writeObject(ObjectOutputStream out) throws IOException {
502 throw new UnsupportedOperationException(this.getClass() + " does not support serialization. "
503 + "Use LoggerEventVO instance instead. See also LoggerEventVO.build method.");
504 }
505
506 }