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 public class JsonEncoder extends EncoderBase<ILoggingEvent> {
43 static final boolean DO_NOT_ADD_QUOTE_KEY = false;
44 static final boolean ADD_QUOTE_KEY = true;
45 static int DEFAULT_SIZE = 1024;
46 static int DEFAULT_SIZE_WITH_THROWABLE = DEFAULT_SIZE * 8;
47
48 static byte[] EMPTY_BYTES = new byte[0];
49
50 public static final String CONTEXT_ATTR_NAME = "context";
51 public static final String NAME_ATTR_NAME = "name";
52 public static final String BIRTHDATE_ATTR_NAME = "birthdate";
53 public static final String CONTEXT_PROPERTIES_ATTR_NAME = "properties";
54
55 public static final String TIMESTAMP_ATTR_NAME = "timestamp";
56
57 public static final String NANOSECONDS_ATTR_NAME = "nanoseconds";
58
59 public static final String SEQUENCE_NUMBER_ATTR_NAME = "sequenceNumber";
60
61 public static final String LEVEL_ATTR_NAME = "level";
62 public static final String MARKERS_ATTR_NAME = "markers";
63 public static final String THREAD_NAME_ATTR_NAME = "threadName";
64 public static final String MDC_ATTR_NAME = "mdc";
65 public static final String LOGGER_ATTR_NAME = "loggerName";
66
67 public static final String MESSAGE_ATTR_NAME = "message";
68
69 public static final String FORMATTED_MESSAGE_ATTR_NAME = "formattedMessage";
70
71 public static final String ARGUMENT_ARRAY_ATTR_NAME = "arguments";
72 public static final String KEY_VALUE_PAIRS_ATTR_NAME = "kvpList";
73
74 public static final String THROWABLE_ATTR_NAME = "throwable";
75
76 private static final String CYCLIC_THROWABLE_ATTR_NAME = "cyclic";
77
78 public static final String CAUSE_ATTR_NAME = "cause";
79
80 public static final String SUPPRESSED_ATTR_NAME = "suppressed";
81
82 public static final String COMMON_FRAMES_COUNT_ATTR_NAME = "commonFramesCount";
83
84 public static final String CLASS_NAME_ATTR_NAME = "className";
85 public static final String METHOD_NAME_ATTR_NAME = "methodName";
86 private static final String FILE_NAME_ATTR_NAME = "fileName";
87 private static final String LINE_NUMBER_ATTR_NAME = "lineNumber";
88
89 public static final String STEP_ARRAY_NAME_ATTRIBUTE = "stepArray";
90
91 private static final char OPEN_OBJ = '{';
92 private static final char CLOSE_OBJ = '}';
93 private static final char OPEN_ARRAY = '[';
94 private static final char CLOSE_ARRAY = ']';
95
96 private static final char QUOTE = DOUBLE_QUOTE_CHAR;
97 private static final char SP = ' ';
98 private static final char ENTRY_SEPARATOR = COLON_CHAR;
99
100 private static final String COL_SP = ": ";
101
102 private static final String QUOTE_COL = "\":";
103
104 private static final char VALUE_SEPARATOR = COMMA_CHAR;
105
106 private boolean withSequenceNumber = true;
107
108 private boolean withTimestamp = true;
109 private boolean withNanoseconds = true;
110
111
112 private boolean withLevel = true;
113 private boolean withThreadName = true;
114 private boolean withLoggerName = true;
115 private boolean withContext = true;
116 private boolean withMarkers = true;
117 private boolean withMDC = true;
118 private boolean withKVPList = true;
119 private boolean withMessage = true;
120 private boolean withArguments = true;
121 private boolean withThrowable = true;
122 private boolean withFormattedMessage = false;
123
124
125 @Override
126 public byte[] headerBytes() {
127 return EMPTY_BYTES;
128 }
129
130 @Override
131 public byte[] encode(ILoggingEvent event) {
132 final int initialCapacity = event.getThrowableProxy() == null ? DEFAULT_SIZE : DEFAULT_SIZE_WITH_THROWABLE;
133 StringBuilder sb = new StringBuilder(initialCapacity);
134 sb.append(OPEN_OBJ);
135
136 if (withSequenceNumber) {
137 appenderMemberWithLongValue(sb, SEQUENCE_NUMBER_ATTR_NAME, event.getSequenceNumber());
138 sb.append(VALUE_SEPARATOR);
139 }
140
141 if (withTimestamp) {
142 appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp());
143 sb.append(VALUE_SEPARATOR);
144 }
145
146 if (withNanoseconds) {
147 appenderMemberWithLongValue(sb, NANOSECONDS_ATTR_NAME, event.getNanoseconds());
148 sb.append(VALUE_SEPARATOR);
149 }
150
151 if (withLevel) {
152 String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR;
153 appenderMember(sb, LEVEL_ATTR_NAME, levelStr);
154 sb.append(VALUE_SEPARATOR);
155 }
156
157 if (withThreadName) {
158 appenderMember(sb, THREAD_NAME_ATTR_NAME, jsonEscape(event.getThreadName()));
159 sb.append(VALUE_SEPARATOR);
160 }
161
162 if (withLoggerName) {
163 appenderMember(sb, LOGGER_ATTR_NAME, event.getLoggerName());
164 sb.append(VALUE_SEPARATOR);
165 }
166
167 if (withContext) {
168 appendLoggerContext(sb, event.getLoggerContextVO());
169 sb.append(VALUE_SEPARATOR);
170 }
171 if (withMarkers)
172 appendMarkers(sb, event);
173
174 if (withMDC)
175 appendMDC(sb, event);
176
177 if (withKVPList)
178 appendKeyValuePairs(sb, event);
179
180 if (withMessage) {
181 appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage()));
182 sb.append(VALUE_SEPARATOR);
183 }
184
185 if (withFormattedMessage) {
186 appenderMember(sb, FORMATTED_MESSAGE_ATTR_NAME, jsonEscape(event.getFormattedMessage()));
187 sb.append(VALUE_SEPARATOR);
188 }
189
190 if (withArguments)
191 appendArgumentArray(sb, event);
192
193 if (withThrowable)
194 appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy());
195
196 sb.append(CLOSE_OBJ);
197 sb.append(CoreConstants.JSON_LINE_SEPARATOR);
198 return sb.toString().getBytes(UTF_8_CHARSET);
199 }
200
201 private void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) {
202
203 sb.append(QUOTE).append(CONTEXT_ATTR_NAME).append(QUOTE_COL);
204 if (loggerContextVO == null) {
205 sb.append(NULL_STR);
206 return;
207 }
208
209 sb.append(OPEN_OBJ);
210 appenderMember(sb, NAME_ATTR_NAME, nullSafeStr(loggerContextVO.getName()));
211 sb.append(VALUE_SEPARATOR);
212 appenderMemberWithLongValue(sb, BIRTHDATE_ATTR_NAME, loggerContextVO.getBirthTime());
213 sb.append(VALUE_SEPARATOR);
214
215 appendMap(sb, CONTEXT_PROPERTIES_ATTR_NAME, loggerContextVO.getPropertyMap());
216 sb.append(CLOSE_OBJ);
217
218 }
219
220 private void appendMap(StringBuilder sb, String attrName, Map<String, String> map) {
221 sb.append(QUOTE).append(attrName).append(QUOTE_COL);
222 if (map == null) {
223 sb.append(NULL_STR);
224 return;
225 }
226
227 sb.append(OPEN_OBJ);
228
229 boolean addComma = false;
230 Set<Map.Entry<String, String>> entries = map.entrySet();
231 for (Map.Entry<String, String> entry : entries) {
232 if (addComma) {
233 sb.append(VALUE_SEPARATOR);
234 }
235 addComma = true;
236 appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue()));
237 }
238
239 sb.append(CLOSE_OBJ);
240 }
241
242 private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) {
243
244
245
246 if (attributeName != null) {
247 sb.append(QUOTE).append(attributeName).append(QUOTE_COL);
248 if (itp == null) {
249 sb.append(NULL_STR);
250 return;
251 }
252 }
253
254 sb.append(OPEN_OBJ);
255
256 appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(itp.getClassName()));
257
258 sb.append(VALUE_SEPARATOR);
259 appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(itp.getMessage()));
260
261 if (itp.isCyclic()) {
262 sb.append(VALUE_SEPARATOR);
263 appenderMember(sb, CYCLIC_THROWABLE_ATTR_NAME, jsonEscape("true"));
264 }
265
266 sb.append(VALUE_SEPARATOR);
267 appendSTEPArray(sb, itp.getStackTraceElementProxyArray(), itp.getCommonFrames());
268
269 if (itp.getCommonFrames() != 0) {
270 sb.append(VALUE_SEPARATOR);
271 appenderMemberWithIntValue(sb, COMMON_FRAMES_COUNT_ATTR_NAME, itp.getCommonFrames());
272 }
273
274 IThrowableProxy cause = itp.getCause();
275 if (cause != null) {
276 sb.append(VALUE_SEPARATOR);
277 appendThrowableProxy(sb, CAUSE_ATTR_NAME, cause);
278 }
279
280 IThrowableProxy[] suppressedArray = itp.getSuppressed();
281 if (suppressedArray != null && suppressedArray.length != 0) {
282 sb.append(VALUE_SEPARATOR);
283 sb.append(QUOTE).append(SUPPRESSED_ATTR_NAME).append(QUOTE_COL);
284 sb.append(OPEN_ARRAY);
285 boolean first = true;
286 for (IThrowableProxy suppressedITP : suppressedArray) {
287 if (first) {
288 first = false;
289 } else {
290 sb.append(VALUE_SEPARATOR);
291 }
292 appendThrowableProxy(sb, null, suppressedITP);
293 }
294 sb.append(CLOSE_ARRAY);
295 }
296
297 sb.append(CLOSE_OBJ);
298
299 }
300
301 private void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArray, int commonFrames) {
302 sb.append(QUOTE).append(STEP_ARRAY_NAME_ATTRIBUTE).append(QUOTE_COL).append(OPEN_ARRAY);
303
304 int len = stepArray != null ? stepArray.length : 0;
305
306 if (commonFrames >= len) {
307 commonFrames = 0;
308 }
309
310 for (int i = 0; i < len - commonFrames; i++) {
311 if (i != 0)
312 sb.append(VALUE_SEPARATOR);
313
314 StackTraceElementProxy step = stepArray[i];
315
316 sb.append(OPEN_OBJ);
317 StackTraceElement ste = step.getStackTraceElement();
318
319 appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(ste.getClassName()));
320 sb.append(VALUE_SEPARATOR);
321
322 appenderMember(sb, METHOD_NAME_ATTR_NAME, nullSafeStr(ste.getMethodName()));
323 sb.append(VALUE_SEPARATOR);
324
325 appenderMember(sb, FILE_NAME_ATTR_NAME, nullSafeStr(ste.getFileName()));
326 sb.append(VALUE_SEPARATOR);
327
328 appenderMemberWithIntValue(sb, LINE_NUMBER_ATTR_NAME, ste.getLineNumber());
329 sb.append(CLOSE_OBJ);
330
331 }
332
333 sb.append(CLOSE_ARRAY);
334 }
335
336 private void appenderMember(StringBuilder sb, String key, String value) {
337 sb.append(QUOTE).append(key).append(QUOTE_COL).append(QUOTE).append(value).append(QUOTE);
338 }
339
340 private void appenderMemberWithIntValue(StringBuilder sb, String key, int value) {
341 sb.append(QUOTE).append(key).append(QUOTE_COL).append(value);
342 }
343
344 private void appenderMemberWithLongValue(StringBuilder sb, String key, long value) {
345 sb.append(QUOTE).append(key).append(QUOTE_COL).append(value);
346 }
347
348 private void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) {
349 List<KeyValuePair> kvpList = event.getKeyValuePairs();
350 if (kvpList == null || kvpList.isEmpty())
351 return;
352
353 sb.append(QUOTE).append(KEY_VALUE_PAIRS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY);
354 final int len = kvpList.size();
355 for (int i = 0; i < len; i++) {
356 if (i != 0)
357 sb.append(VALUE_SEPARATOR);
358 KeyValuePair kvp = kvpList.get(i);
359 sb.append(OPEN_OBJ);
360 appenderMember(sb, jsonEscapedToString(kvp.key), jsonEscapedToString(kvp.value));
361 sb.append(CLOSE_OBJ);
362 }
363 sb.append(CLOSE_ARRAY);
364 sb.append(VALUE_SEPARATOR);
365 }
366
367 private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) {
368 Object[] argumentArray = event.getArgumentArray();
369 if (argumentArray == null)
370 return;
371
372 sb.append(QUOTE).append(ARGUMENT_ARRAY_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY);
373 final int len = argumentArray.length;
374 for (int i = 0; i < len; i++) {
375 if (i != 0)
376 sb.append(VALUE_SEPARATOR);
377 sb.append(QUOTE).append(jsonEscapedToString(argumentArray[i])).append(QUOTE);
378
379 }
380 sb.append(CLOSE_ARRAY);
381 sb.append(VALUE_SEPARATOR);
382 }
383
384 private void appendMarkers(StringBuilder sb, ILoggingEvent event) {
385 List<Marker> markerList = event.getMarkerList();
386 if (markerList == null)
387 return;
388
389 sb.append(QUOTE).append(MARKERS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY);
390 final int len = markerList.size();
391 for (int i = 0; i < len; i++) {
392 if (i != 0)
393 sb.append(VALUE_SEPARATOR);
394 sb.append(QUOTE).append(jsonEscapedToString(markerList.get(i))).append(QUOTE);
395
396 }
397 sb.append(CLOSE_ARRAY);
398 sb.append(VALUE_SEPARATOR);
399
400 }
401
402 private String jsonEscapedToString(Object o) {
403 if (o == null)
404 return NULL_STR;
405 return jsonEscapeString(o.toString());
406 }
407
408 private String nullSafeStr(String s) {
409 if (s == null)
410 return NULL_STR;
411 return s;
412 }
413
414 private String jsonEscape(String s) {
415 if (s == null)
416 return NULL_STR;
417 return jsonEscapeString(s);
418 }
419
420 private void appendMDC(StringBuilder sb, ILoggingEvent event) {
421 Map<String, String> map = event.getMDCPropertyMap();
422
423 sb.append(QUOTE).append(MDC_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_OBJ);
424 if (isNotEmptyMap(map)) {
425 Set<Map.Entry<String, String>> entrySet = map.entrySet();
426 int i = 0;
427 for (Map.Entry<String, String> entry : entrySet) {
428 if (i != 0)
429 sb.append(VALUE_SEPARATOR);
430 appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue()));
431 i++;
432 }
433
434 }
435 sb.append(CLOSE_OBJ);
436 sb.append(VALUE_SEPARATOR);
437
438 }
439
440 boolean isNotEmptyMap(Map map) {
441 if (map == null)
442 return false;
443 return !map.isEmpty();
444 }
445
446 @Override
447 public byte[] footerBytes() {
448 return EMPTY_BYTES;
449 }
450
451
452
453
454
455 public void setWithSequenceNumber(boolean withSequenceNumber) {
456 this.withSequenceNumber = withSequenceNumber;
457 }
458
459
460
461
462
463 public void setWithTimestamp(boolean withTimestamp) {
464 this.withTimestamp = withTimestamp;
465 }
466
467
468
469
470
471 public void setWithNanoseconds(boolean withNanoseconds) {
472 this.withNanoseconds = withNanoseconds;
473 }
474
475 public void setWithLevel(boolean withLevel) {
476 this.withLevel = withLevel;
477 }
478
479 public void setWithThreadName(boolean withThreadName) {
480 this.withThreadName = withThreadName;
481 }
482
483 public void setWithLoggerName(boolean withLoggerName) {
484 this.withLoggerName = withLoggerName;
485 }
486
487 public void setWithContext(boolean withContext) {
488 this.withContext = withContext;
489 }
490
491 public void setWithMarkers(boolean withMarkers) {
492 this.withMarkers = withMarkers;
493 }
494
495 public void setWithMDC(boolean withMDC) {
496 this.withMDC = withMDC;
497 }
498
499 public void setWithKVPList(boolean withKVPList) {
500 this.withKVPList = withKVPList;
501 }
502
503 public void setWithMessage(boolean withMessage) {
504 this.withMessage = withMessage;
505 }
506
507 public void setWithArguments(boolean withArguments) {
508 this.withArguments = withArguments;
509 }
510
511 public void setWithThrowable(boolean withThrowable) {
512 this.withThrowable = withThrowable;
513 }
514
515 public void setWithFormattedMessage(boolean withFormattedMessage) {
516 this.withFormattedMessage = withFormattedMessage;
517 }
518
519 }