1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ch.qos.logback.classic.encoder;
16
17 import ch.qos.logback.classic.spi.ILoggingEvent;
18 import ch.qos.logback.classic.spi.IThrowableProxy;
19 import ch.qos.logback.classic.spi.LoggerContextVO;
20 import ch.qos.logback.classic.spi.StackTraceElementProxy;
21 import ch.qos.logback.core.CoreConstants;
22 import ch.qos.logback.core.encoder.EncoderBase;
23 import org.slf4j.Marker;
24 import org.slf4j.event.KeyValuePair;
25
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29
30 import static ch.qos.logback.core.CoreConstants.COLON_CHAR;
31 import static ch.qos.logback.core.CoreConstants.COMMA_CHAR;
32 import static ch.qos.logback.core.CoreConstants.DOUBLE_QUOTE_CHAR;
33 import static ch.qos.logback.core.CoreConstants.UTF_8_CHARSET;
34 import static ch.qos.logback.core.encoder.JsonEscapeUtil.jsonEscapeString;
35 import static ch.qos.logback.core.model.ModelConstants.NULL_STR;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public class JsonEncoder extends EncoderBase<ILoggingEvent> {
79 static final boolean DO_NOT_ADD_QUOTE_KEY = false;
80 static final boolean ADD_QUOTE_KEY = true;
81 static int DEFAULT_SIZE = 1024;
82 static int DEFAULT_SIZE_WITH_THROWABLE = DEFAULT_SIZE * 8;
83
84 static byte[] EMPTY_BYTES = new byte[0];
85
86 public static final String CONTEXT_ATTR_NAME = "context";
87 public static final String NAME_ATTR_NAME = "name";
88 public static final String BIRTHDATE_ATTR_NAME = "birthdate";
89 public static final String CONTEXT_PROPERTIES_ATTR_NAME = "properties";
90
91 public static final String TIMESTAMP_ATTR_NAME = "timestamp";
92
93 public static final String NANOSECONDS_ATTR_NAME = "nanoseconds";
94
95 public static final String SEQUENCE_NUMBER_ATTR_NAME = "sequenceNumber";
96
97 public static final String LEVEL_ATTR_NAME = "level";
98 public static final String MARKERS_ATTR_NAME = "markers";
99 public static final String THREAD_NAME_ATTR_NAME = "threadName";
100 public static final String MDC_ATTR_NAME = "mdc";
101 public static final String LOGGER_ATTR_NAME = "loggerName";
102
103 public static final String MESSAGE_ATTR_NAME = "message";
104
105 public static final String FORMATTED_MESSAGE_ATTR_NAME = "formattedMessage";
106
107 public static final String ARGUMENT_ARRAY_ATTR_NAME = "arguments";
108 public static final String KEY_VALUE_PAIRS_ATTR_NAME = "kvpList";
109
110 public static final String THROWABLE_ATTR_NAME = "throwable";
111
112 private static final String CYCLIC_THROWABLE_ATTR_NAME = "cyclic";
113
114 public static final String CAUSE_ATTR_NAME = "cause";
115
116 public static final String SUPPRESSED_ATTR_NAME = "suppressed";
117
118 public static final String COMMON_FRAMES_COUNT_ATTR_NAME = "commonFramesCount";
119
120 public static final String CLASS_NAME_ATTR_NAME = "className";
121 public static final String METHOD_NAME_ATTR_NAME = "methodName";
122 private static final String FILE_NAME_ATTR_NAME = "fileName";
123 private static final String LINE_NUMBER_ATTR_NAME = "lineNumber";
124
125 public static final String STEP_ARRAY_NAME_ATTRIBUTE = "stepArray";
126
127 protected static final char OPEN_OBJ = '{';
128 protected static final char CLOSE_OBJ = '}';
129 protected static final char OPEN_ARRAY = '[';
130 protected static final char CLOSE_ARRAY = ']';
131
132 protected static final char QUOTE = DOUBLE_QUOTE_CHAR;
133 protected static final char SP = ' ';
134 protected static final char ENTRY_SEPARATOR = COLON_CHAR;
135
136 protected static final String COL_SP = ": ";
137
138 protected static final String QUOTE_COL = "\":";
139
140 protected static final char VALUE_SEPARATOR = COMMA_CHAR;
141
142 private boolean withSequenceNumber = true;
143
144 private boolean withTimestamp = true;
145 private boolean withNanoseconds = true;
146
147 private boolean withLevel = true;
148 private boolean withThreadName = true;
149 private boolean withLoggerName = true;
150 private boolean withContext = true;
151 private boolean withMarkers = true;
152 private boolean withMDC = true;
153 private boolean withKVPList = true;
154 private boolean withMessage = true;
155 private boolean withArguments = true;
156 private boolean withThrowable = true;
157 private boolean withFormattedMessage = false;
158
159 @Override
160 public byte[] headerBytes() {
161 return EMPTY_BYTES;
162 }
163
164 @Override
165 public byte[] encode(ILoggingEvent event) {
166 final int initialCapacity = event.getThrowableProxy() == null ? DEFAULT_SIZE : DEFAULT_SIZE_WITH_THROWABLE;
167 StringBuilder sb = new StringBuilder(initialCapacity);
168 sb.append(OPEN_OBJ);
169
170 if (withSequenceNumber) {
171 appenderMemberWithLongValue(sb, SEQUENCE_NUMBER_ATTR_NAME, event.getSequenceNumber());
172 }
173
174 if (withTimestamp) {
175 appendValueSeparator(sb, withSequenceNumber);
176 appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp());
177 }
178
179 if (withNanoseconds) {
180 appendValueSeparator(sb, withSequenceNumber, withTimestamp);
181 appenderMemberWithLongValue(sb, NANOSECONDS_ATTR_NAME, event.getNanoseconds());
182 }
183
184 if (withLevel) {
185 appendValueSeparator(sb, withNanoseconds, withSequenceNumber, withTimestamp);
186 String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR;
187 appenderMember(sb, LEVEL_ATTR_NAME, levelStr);
188 }
189
190 if (withThreadName) {
191 appendValueSeparator(sb, withLevel, withNanoseconds, withSequenceNumber, withTimestamp);
192 appenderMember(sb, THREAD_NAME_ATTR_NAME, jsonEscape(event.getThreadName()));
193 }
194
195 if (withLoggerName) {
196 appendValueSeparator(sb, withThreadName, withLevel, withNanoseconds, withSequenceNumber, withTimestamp);
197 appenderMember(sb, LOGGER_ATTR_NAME, event.getLoggerName());
198 }
199
200 if (withContext) {
201
202 sb.append(VALUE_SEPARATOR);
203 appendLoggerContext(sb, event.getLoggerContextVO());
204 }
205
206 if (withMarkers)
207 appendMarkers(sb, event);
208
209 if (withMDC)
210 appendMDC(sb, event);
211
212 if (withKVPList)
213 appendKeyValuePairs(sb, event);
214
215 if (withMessage) {
216 sb.append(VALUE_SEPARATOR);
217 appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage()));
218 }
219
220 if (withFormattedMessage) {
221 sb.append(VALUE_SEPARATOR);
222 appenderMember(sb, FORMATTED_MESSAGE_ATTR_NAME, jsonEscape(event.getFormattedMessage()));
223 }
224
225 if (withArguments) {
226 appendArgumentArray(sb, event);
227 }
228
229 if (withThrowable)
230 appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy());
231
232
233 appendCustomFields(sb, event);
234
235 sb.append(CLOSE_OBJ);
236 sb.append(CoreConstants.JSON_LINE_SEPARATOR);
237 return sb.toString().getBytes(UTF_8_CHARSET);
238 }
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259 protected void appendValueSeparator(StringBuilder sb, boolean... subsequentConditionals) {
260 boolean enabled = false;
261 for (boolean subsequent : subsequentConditionals) {
262 if (subsequent) {
263 enabled = true;
264 break;
265 }
266 }
267
268 if (enabled)
269 sb.append(VALUE_SEPARATOR);
270 }
271
272 protected void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) {
273
274 sb.append(QUOTE).append(CONTEXT_ATTR_NAME).append(QUOTE_COL);
275 if (loggerContextVO == null) {
276 sb.append(NULL_STR);
277 return;
278 }
279
280 sb.append(OPEN_OBJ);
281 appenderMember(sb, NAME_ATTR_NAME, nullSafeStr(loggerContextVO.getName()));
282 sb.append(VALUE_SEPARATOR);
283 appenderMemberWithLongValue(sb, BIRTHDATE_ATTR_NAME, loggerContextVO.getBirthTime());
284 sb.append(VALUE_SEPARATOR);
285
286 appendMap(sb, CONTEXT_PROPERTIES_ATTR_NAME, loggerContextVO.getPropertyMap());
287 sb.append(CLOSE_OBJ);
288
289 }
290
291 protected void appendMap(StringBuilder sb, String attrName, Map<String, String> map) {
292 sb.append(QUOTE).append(attrName).append(QUOTE_COL);
293 if (map == null) {
294 sb.append(NULL_STR);
295 return;
296 }
297
298 sb.append(OPEN_OBJ);
299
300 boolean addComma = false;
301 Set<Map.Entry<String, String>> entries = map.entrySet();
302 for (Map.Entry<String, String> entry : entries) {
303 if (addComma) {
304 sb.append(VALUE_SEPARATOR);
305 }
306 addComma = true;
307 appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue()));
308 }
309
310 sb.append(CLOSE_OBJ);
311 }
312
313 protected void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) {
314 appendThrowableProxy(sb, attributeName, itp, true);
315 }
316
317 protected void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp, boolean appendValueSeparator) {
318
319 if (appendValueSeparator)
320 sb.append(VALUE_SEPARATOR);
321
322
323
324 if (attributeName != null) {
325 sb.append(QUOTE).append(attributeName).append(QUOTE_COL);
326 if (itp == null) {
327 sb.append(NULL_STR);
328 return;
329 }
330 }
331
332 sb.append(OPEN_OBJ);
333
334 appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(itp.getClassName()));
335
336 sb.append(VALUE_SEPARATOR);
337 appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(itp.getMessage()));
338
339 if (itp.isCyclic()) {
340 sb.append(VALUE_SEPARATOR);
341 appenderMember(sb, CYCLIC_THROWABLE_ATTR_NAME, jsonEscape("true"));
342 }
343
344 sb.append(VALUE_SEPARATOR);
345 appendSTEPArray(sb, itp.getStackTraceElementProxyArray(), itp.getCommonFrames());
346
347 if (itp.getCommonFrames() != 0) {
348 sb.append(VALUE_SEPARATOR);
349 appenderMemberWithIntValue(sb, COMMON_FRAMES_COUNT_ATTR_NAME, itp.getCommonFrames());
350 }
351
352 IThrowableProxy cause = itp.getCause();
353 if (cause != null) {
354 appendThrowableProxy(sb, CAUSE_ATTR_NAME, cause);
355 }
356
357 IThrowableProxy[] suppressedArray = itp.getSuppressed();
358 if (suppressedArray != null && suppressedArray.length != 0) {
359 sb.append(VALUE_SEPARATOR);
360 sb.append(QUOTE).append(SUPPRESSED_ATTR_NAME).append(QUOTE_COL);
361 sb.append(OPEN_ARRAY);
362
363 boolean first = true;
364 for (IThrowableProxy suppressedITP : suppressedArray) {
365 appendThrowableProxy(sb, null, suppressedITP, !first);
366 if (first)
367 first = false;
368 }
369 sb.append(CLOSE_ARRAY);
370 }
371
372 sb.append(CLOSE_OBJ);
373
374 }
375
376 protected void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArray, int commonFrames) {
377 sb.append(QUOTE).append(STEP_ARRAY_NAME_ATTRIBUTE).append(QUOTE_COL).append(OPEN_ARRAY);
378
379
380 if (stepArray == null || stepArray.length == 0) {
381 sb.append(CLOSE_ARRAY);
382 return;
383 }
384
385 int len = stepArray.length;
386
387 if (commonFrames >= len) {
388 commonFrames = 0;
389 }
390
391 for (int i = 0; i < len - commonFrames; i++) {
392 if (i != 0)
393 sb.append(VALUE_SEPARATOR);
394
395 StackTraceElementProxy step = stepArray[i];
396
397 sb.append(OPEN_OBJ);
398 StackTraceElement ste = step.getStackTraceElement();
399
400 appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(ste.getClassName()));
401 sb.append(VALUE_SEPARATOR);
402
403 appenderMember(sb, METHOD_NAME_ATTR_NAME, nullSafeStr(ste.getMethodName()));
404 sb.append(VALUE_SEPARATOR);
405
406 appenderMember(sb, FILE_NAME_ATTR_NAME, nullSafeStr(ste.getFileName()));
407 sb.append(VALUE_SEPARATOR);
408
409 appenderMemberWithIntValue(sb, LINE_NUMBER_ATTR_NAME, ste.getLineNumber());
410 sb.append(CLOSE_OBJ);
411
412 }
413
414 sb.append(CLOSE_ARRAY);
415 }
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430 protected void appendCustomFields(StringBuilder sb, ILoggingEvent event) {
431
432 }
433
434 protected void appenderMember(StringBuilder sb, String key, String value) {
435 sb.append(QUOTE).append(key).append(QUOTE_COL).append(QUOTE).append(value).append(QUOTE);
436 }
437
438 protected void appenderMemberWithIntValue(StringBuilder sb, String key, int value) {
439 sb.append(QUOTE).append(key).append(QUOTE_COL).append(value);
440 }
441
442 protected void appenderMemberWithLongValue(StringBuilder sb, String key, long value) {
443 sb.append(QUOTE).append(key).append(QUOTE_COL).append(value);
444 }
445
446 protected void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) {
447 List<KeyValuePair> kvpList = event.getKeyValuePairs();
448 if (kvpList == null || kvpList.isEmpty())
449 return;
450
451 sb.append(VALUE_SEPARATOR);
452 sb.append(QUOTE).append(KEY_VALUE_PAIRS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY);
453 final int len = kvpList.size();
454 for (int i = 0; i < len; i++) {
455 if (i != 0)
456 sb.append(VALUE_SEPARATOR);
457 KeyValuePair kvp = kvpList.get(i);
458 sb.append(OPEN_OBJ);
459 appenderMember(sb, jsonEscapedToString(kvp.key), jsonEscapedToString(kvp.value));
460 sb.append(CLOSE_OBJ);
461 }
462 sb.append(CLOSE_ARRAY);
463 }
464
465 protected void appendArgumentArray(StringBuilder sb, ILoggingEvent event) {
466 Object[] argumentArray = event.getArgumentArray();
467 if (argumentArray == null)
468 return;
469
470 sb.append(VALUE_SEPARATOR);
471 sb.append(QUOTE).append(ARGUMENT_ARRAY_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY);
472 final int len = argumentArray.length;
473 for (int i = 0; i < len; i++) {
474 if (i != 0)
475 sb.append(VALUE_SEPARATOR);
476 sb.append(QUOTE).append(jsonEscapedToString(argumentArray[i])).append(QUOTE);
477
478 }
479 sb.append(CLOSE_ARRAY);
480 }
481
482 protected void appendMarkers(StringBuilder sb, ILoggingEvent event) {
483 List<Marker> markerList = event.getMarkerList();
484 if (markerList == null)
485 return;
486
487 sb.append(VALUE_SEPARATOR);
488 sb.append(QUOTE).append(MARKERS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY);
489 final int len = markerList.size();
490 for (int i = 0; i < len; i++) {
491 if (i != 0)
492 sb.append(VALUE_SEPARATOR);
493 sb.append(QUOTE).append(jsonEscapedToString(markerList.get(i))).append(QUOTE);
494
495 }
496 sb.append(CLOSE_ARRAY);
497 }
498
499 private String jsonEscapedToString(Object o) {
500 if (o == null)
501 return NULL_STR;
502 return jsonEscapeString(o.toString());
503 }
504
505 private String nullSafeStr(String s) {
506 if (s == null)
507 return NULL_STR;
508 return s;
509 }
510
511 private String jsonEscape(String s) {
512 if (s == null)
513 return NULL_STR;
514 return jsonEscapeString(s);
515 }
516
517 protected void appendMDC(StringBuilder sb, ILoggingEvent event) {
518 Map<String, String> map = event.getMDCPropertyMap();
519 sb.append(VALUE_SEPARATOR);
520 sb.append(QUOTE).append(MDC_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_OBJ);
521 if (isNotEmptyMap(map)) {
522 Set<Map.Entry<String, String>> entrySet = map.entrySet();
523 int i = 0;
524 for (Map.Entry<String, String> entry : entrySet) {
525 if (i != 0)
526 sb.append(VALUE_SEPARATOR);
527 appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue()));
528 i++;
529 }
530
531 }
532 sb.append(CLOSE_OBJ);
533 }
534
535
536
537
538
539
540
541 boolean isNotEmptyMap(Map<?, ?> map) {
542 if (map == null)
543 return false;
544 return !map.isEmpty();
545 }
546
547 @Override
548 public byte[] footerBytes() {
549 return EMPTY_BYTES;
550 }
551
552
553
554
555
556
557 public void setWithSequenceNumber(boolean withSequenceNumber) {
558 this.withSequenceNumber = withSequenceNumber;
559 }
560
561
562
563
564
565
566 public void setWithTimestamp(boolean withTimestamp) {
567 this.withTimestamp = withTimestamp;
568 }
569
570
571
572
573
574
575 public void setWithNanoseconds(boolean withNanoseconds) {
576 this.withNanoseconds = withNanoseconds;
577 }
578
579
580
581
582
583
584 public void setWithLevel(boolean withLevel) {
585 this.withLevel = withLevel;
586 }
587
588
589
590
591
592
593 public void setWithThreadName(boolean withThreadName) {
594 this.withThreadName = withThreadName;
595 }
596
597
598
599
600
601
602 public void setWithLoggerName(boolean withLoggerName) {
603 this.withLoggerName = withLoggerName;
604 }
605
606
607
608
609
610
611 public void setWithContext(boolean withContext) {
612 this.withContext = withContext;
613 }
614
615
616
617
618
619
620 public void setWithMarkers(boolean withMarkers) {
621 this.withMarkers = withMarkers;
622 }
623
624
625
626
627
628
629 public void setWithMDC(boolean withMDC) {
630 this.withMDC = withMDC;
631 }
632
633
634
635
636
637
638 public void setWithKVPList(boolean withKVPList) {
639 this.withKVPList = withKVPList;
640 }
641
642
643
644
645
646
647 public void setWithMessage(boolean withMessage) {
648 this.withMessage = withMessage;
649 }
650
651
652
653
654
655
656 public void setWithArguments(boolean withArguments) {
657 this.withArguments = withArguments;
658 }
659
660
661
662
663
664
665 public void setWithThrowable(boolean withThrowable) {
666 this.withThrowable = withThrowable;
667 }
668
669
670
671
672
673
674 public void setWithFormattedMessage(boolean withFormattedMessage) {
675 this.withFormattedMessage = withFormattedMessage;
676 }
677
678 }