Skip to content

Commit edbbd52

Browse files
committed
Add StructuredLogginJsonEncoder
Signed-off-by: Rubén Laguna <ruben.laguna@gmail.com>
1 parent a07cfd5 commit edbbd52

File tree

3 files changed

+319
-43
lines changed

3 files changed

+319
-43
lines changed

logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java

Lines changed: 56 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -89,37 +89,37 @@ public class JsonEncoder extends EncoderBase<ILoggingEvent> {
8989

9090
public static final String STEP_ARRAY_NAME_ATTRIBUTE = "stepArray";
9191

92-
private static final char OPEN_OBJ = '{';
93-
private static final char CLOSE_OBJ = '}';
94-
private static final char OPEN_ARRAY = '[';
95-
private static final char CLOSE_ARRAY = ']';
92+
protected static final char OPEN_OBJ = '{';
93+
protected static final char CLOSE_OBJ = '}';
94+
protected static final char OPEN_ARRAY = '[';
95+
protected static final char CLOSE_ARRAY = ']';
9696

97-
private static final char QUOTE = DOUBLE_QUOTE_CHAR;
98-
private static final char SP = ' ';
99-
private static final char ENTRY_SEPARATOR = COLON_CHAR;
97+
protected static final char QUOTE = DOUBLE_QUOTE_CHAR;
98+
protected static final char SP = ' ';
99+
protected static final char ENTRY_SEPARATOR = COLON_CHAR;
100100

101-
private static final String COL_SP = ": ";
101+
protected static final String COL_SP = ": ";
102102

103-
private static final String QUOTE_COL = "\":";
103+
protected static final String QUOTE_COL = "\":";
104104

105-
private static final char VALUE_SEPARATOR = COMMA_CHAR;
105+
protected static final char VALUE_SEPARATOR = COMMA_CHAR;
106106

107-
private boolean withSequenceNumber = true;
107+
protected boolean withSequenceNumber = true;
108108

109-
private boolean withTimestamp = true;
110-
private boolean withNanoseconds = true;
109+
protected boolean withTimestamp = true;
110+
protected boolean withNanoseconds = true;
111111

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;
112+
protected boolean withLevel = true;
113+
protected boolean withThreadName = true;
114+
protected boolean withLoggerName = true;
115+
protected boolean withContext = true;
116+
protected boolean withMarkers = true;
117+
protected boolean withMDC = true;
118+
protected boolean withKVPList = true;
119+
protected boolean withMessage = true;
120+
protected boolean withArguments = true;
121+
protected boolean withThrowable = true;
122+
protected boolean withFormattedMessage = false;
123123

124124
@Override
125125
public byte[] headerBytes() {
@@ -138,7 +138,7 @@ public byte[] encode(ILoggingEvent event) {
138138

139139
if (withTimestamp) {
140140
appendValueSeparator(sb, withSequenceNumber);
141-
appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp());
141+
appenderTimestamp(sb, event);
142142
}
143143

144144
if (withNanoseconds) {
@@ -179,7 +179,7 @@ public byte[] encode(ILoggingEvent event) {
179179

180180
if (withMessage) {
181181
sb.append(VALUE_SEPARATOR);
182-
appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage()));
182+
appenderMessage(sb, event);
183183
}
184184

185185
if (withFormattedMessage) {
@@ -194,12 +194,25 @@ public byte[] encode(ILoggingEvent event) {
194194
if (withThrowable)
195195
appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy());
196196

197+
appenderExtra(sb, event);
198+
197199
sb.append(CLOSE_OBJ);
198200
sb.append(CoreConstants.JSON_LINE_SEPARATOR);
199201
return sb.toString().getBytes(UTF_8_CHARSET);
200202
}
201203

202-
void appendValueSeparator(StringBuilder sb, boolean... subsequentConditionals) {
204+
protected void appenderMessage(StringBuilder sb, ILoggingEvent event) {
205+
appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage()));
206+
}
207+
208+
protected void appenderExtra(StringBuilder sb, ILoggingEvent event) {
209+
}
210+
211+
protected void appenderTimestamp(StringBuilder sb, ILoggingEvent event) {
212+
appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp());
213+
}
214+
215+
protected void appendValueSeparator(StringBuilder sb, boolean... subsequentConditionals) {
203216
boolean enabled = false;
204217
for (boolean subsequent : subsequentConditionals) {
205218
if (subsequent) {
@@ -212,7 +225,7 @@ void appendValueSeparator(StringBuilder sb, boolean... subsequentConditionals) {
212225
sb.append(VALUE_SEPARATOR);
213226
}
214227

215-
private void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) {
228+
protected void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) {
216229

217230
sb.append(QUOTE).append(CONTEXT_ATTR_NAME).append(QUOTE_COL);
218231
if (loggerContextVO == null) {
@@ -231,7 +244,7 @@ private void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContext
231244

232245
}
233246

234-
private void appendMap(StringBuilder sb, String attrName, Map<String, String> map) {
247+
protected void appendMap(StringBuilder sb, String attrName, Map<String, String> map) {
235248
sb.append(QUOTE).append(attrName).append(QUOTE_COL);
236249
if (map == null) {
237250
sb.append(NULL_STR);
@@ -253,11 +266,11 @@ private void appendMap(StringBuilder sb, String attrName, Map<String, String> ma
253266
sb.append(CLOSE_OBJ);
254267
}
255268

256-
private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) {
269+
protected void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) {
257270
appendThrowableProxy(sb, attributeName, itp, true);
258271
}
259272

260-
private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp, boolean appendValueSeparator) {
273+
protected void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp, boolean appendValueSeparator) {
261274

262275
if (appendValueSeparator)
263276
sb.append(VALUE_SEPARATOR);
@@ -316,7 +329,7 @@ private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrow
316329

317330
}
318331

319-
private void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArray, int commonFrames) {
332+
protected void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArray, int commonFrames) {
320333
sb.append(QUOTE).append(STEP_ARRAY_NAME_ATTRIBUTE).append(QUOTE_COL).append(OPEN_ARRAY);
321334

322335
int len = stepArray != null ? stepArray.length : 0;
@@ -351,19 +364,19 @@ private void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArra
351364
sb.append(CLOSE_ARRAY);
352365
}
353366

354-
private void appenderMember(StringBuilder sb, String key, String value) {
367+
protected void appenderMember(StringBuilder sb, String key, String value) {
355368
sb.append(QUOTE).append(key).append(QUOTE_COL).append(QUOTE).append(value).append(QUOTE);
356369
}
357370

358-
private void appenderMemberWithIntValue(StringBuilder sb, String key, int value) {
371+
protected void appenderMemberWithIntValue(StringBuilder sb, String key, int value) {
359372
sb.append(QUOTE).append(key).append(QUOTE_COL).append(value);
360373
}
361374

362-
private void appenderMemberWithLongValue(StringBuilder sb, String key, long value) {
375+
protected void appenderMemberWithLongValue(StringBuilder sb, String key, long value) {
363376
sb.append(QUOTE).append(key).append(QUOTE_COL).append(value);
364377
}
365378

366-
private void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) {
379+
protected void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) {
367380
List<KeyValuePair> kvpList = event.getKeyValuePairs();
368381
if (kvpList == null || kvpList.isEmpty())
369382
return;
@@ -382,7 +395,7 @@ private void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) {
382395
sb.append(CLOSE_ARRAY);
383396
}
384397

385-
private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) {
398+
protected void appendArgumentArray(StringBuilder sb, ILoggingEvent event) {
386399
Object[] argumentArray = event.getArgumentArray();
387400
if (argumentArray == null)
388401
return;
@@ -399,7 +412,7 @@ private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) {
399412
sb.append(CLOSE_ARRAY);
400413
}
401414

402-
private void appendMarkers(StringBuilder sb, ILoggingEvent event) {
415+
protected void appendMarkers(StringBuilder sb, ILoggingEvent event) {
403416
List<Marker> markerList = event.getMarkerList();
404417
if (markerList == null)
405418
return;
@@ -416,25 +429,25 @@ private void appendMarkers(StringBuilder sb, ILoggingEvent event) {
416429
sb.append(CLOSE_ARRAY);
417430
}
418431

419-
private String jsonEscapedToString(Object o) {
432+
protected String jsonEscapedToString(Object o) {
420433
if (o == null)
421434
return NULL_STR;
422435
return jsonEscapeString(o.toString());
423436
}
424437

425-
private String nullSafeStr(String s) {
438+
protected String nullSafeStr(String s) {
426439
if (s == null)
427440
return NULL_STR;
428441
return s;
429442
}
430443

431-
private String jsonEscape(String s) {
444+
protected String jsonEscape(String s) {
432445
if (s == null)
433446
return NULL_STR;
434447
return jsonEscapeString(s);
435448
}
436449

437-
private void appendMDC(StringBuilder sb, ILoggingEvent event) {
450+
protected void appendMDC(StringBuilder sb, ILoggingEvent event) {
438451
Map<String, String> map = event.getMDCPropertyMap();
439452
sb.append(VALUE_SEPARATOR);
440453
sb.append(QUOTE).append(MDC_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_OBJ);
@@ -452,7 +465,7 @@ private void appendMDC(StringBuilder sb, ILoggingEvent event) {
452465
sb.append(CLOSE_OBJ);
453466
}
454467

455-
boolean isNotEmptyMap(Map map) {
468+
protected boolean isNotEmptyMap(Map map) {
456469
if (map == null)
457470
return false;
458471
return !map.isEmpty();
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Logback: the reliable, generic, fast and flexible logging framework.
3+
* Copyright (C) 1999-2023, QOS.ch. All rights reserved.
4+
*
5+
* This program and the accompanying materials are dual-licensed under
6+
* either the terms of the Eclipse Public License v1.0 as published by
7+
* the Eclipse Foundation
8+
*
9+
* or (per the licensee's choosing)
10+
*
11+
* under the terms of the GNU Lesser General Public License version 2.1
12+
* as published by the Free Software Foundation.
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.time.Instant;
27+
import java.util.Arrays;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.Set;
31+
32+
import static ch.qos.logback.core.CoreConstants.COLON_CHAR;
33+
import static ch.qos.logback.core.CoreConstants.COMMA_CHAR;
34+
import static ch.qos.logback.core.CoreConstants.DOUBLE_QUOTE_CHAR;
35+
import static ch.qos.logback.core.CoreConstants.UTF_8_CHARSET;
36+
import static ch.qos.logback.core.encoder.JsonEscapeUtil.jsonEscapeString;
37+
import static ch.qos.logback.core.model.ModelConstants.NULL_STR;
38+
39+
/**
40+
*
41+
*
42+
* https://jsonlines.org/ https://datatracker.ietf.org/doc/html/rfc8259
43+
*/
44+
public class StructuredLoggingJsonEncoder extends JsonEncoder {
45+
46+
protected boolean withTimestampSeconds = false;
47+
protected boolean withTimestampNanos = false;
48+
protected boolean withTime = false;
49+
protected boolean withSeverity = true;
50+
51+
public StructuredLoggingJsonEncoder() {
52+
super();
53+
withArguments = false;
54+
withLevel = false;
55+
}
56+
57+
@Override
58+
protected void appenderTimestamp(StringBuilder sb, ILoggingEvent event) {
59+
sb.append(QUOTE).append("timestamp").append(QUOTE_COL);
60+
sb.append(OPEN_OBJ);
61+
Instant timestamp = event.getInstant();
62+
appenderMemberWithLongValue(sb, "seconds", timestamp.getEpochSecond());
63+
sb.append(VALUE_SEPARATOR);
64+
appenderMemberWithIntValue(sb, "nanos", timestamp.getNano());
65+
sb.append(CLOSE_OBJ);
66+
}
67+
68+
@Override
69+
protected void appenderExtra(StringBuilder sb, ILoggingEvent event) {
70+
Instant timestamp = event.getInstant();
71+
if (withTimestampSeconds) {
72+
sb.append(VALUE_SEPARATOR);
73+
appenderMemberWithLongValue(sb, "timestampSeconds", timestamp.getEpochSecond());
74+
}
75+
if (withTimestampNanos) {
76+
sb.append(VALUE_SEPARATOR);
77+
appenderMemberWithIntValue(sb, "timestampNanos", timestamp.getNano());
78+
}
79+
if (withTime) {
80+
sb.append(VALUE_SEPARATOR);
81+
appenderMember(sb, "time", java.time.format.DateTimeFormatter.ISO_INSTANT.format(timestamp));
82+
}
83+
if (withSeverity) {
84+
sb.append(VALUE_SEPARATOR);
85+
String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR;
86+
appenderMember(sb, "severity",levelStr);
87+
}
88+
}
89+
90+
@Override
91+
protected void appenderMessage(StringBuilder sb, ILoggingEvent event) {
92+
appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscapeString(event.getFormattedMessage()));
93+
}
94+
95+
public void setWithTimestampSeconds(boolean withTimestampSeconds) {
96+
this.withTimestampSeconds = withTimestampSeconds;
97+
}
98+
99+
public void setWithTimestampNanos(boolean withTimestampNanos) {
100+
this.withTimestampNanos = withTimestampNanos;
101+
}
102+
103+
public void setWithTime(boolean withTime) {
104+
this.withTime = withTime;
105+
}
106+
}

0 commit comments

Comments
 (0)