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.ClassicTestConstants;
18 import ch.qos.logback.classic.Level;
19 import ch.qos.logback.classic.Logger;
20 import ch.qos.logback.classic.LoggerContext;
21 import ch.qos.logback.classic.joran.JoranConfigurator;
22 import ch.qos.logback.classic.jsonTest.JsonLoggingEvent;
23 import ch.qos.logback.classic.jsonTest.JsonStringToLoggingEventMapper;
24 import ch.qos.logback.classic.jsonTest.ThrowableProxyComparator;
25 import ch.qos.logback.classic.spi.ILoggingEvent;
26 import ch.qos.logback.classic.spi.LoggingEvent;
27 import ch.qos.logback.classic.util.LogbackMDCAdapter;
28 import ch.qos.logback.core.joran.spi.JoranException;
29 import ch.qos.logback.core.read.ListAppender;
30 import ch.qos.logback.core.status.testUtil.StatusChecker;
31 import ch.qos.logback.core.testUtil.RandomUtil;
32 import ch.qos.logback.core.util.StatusPrinter;
33 import com.fasterxml.jackson.core.JsonProcessingException;
34 import org.junit.jupiter.api.AfterEach;
35 import org.junit.jupiter.api.BeforeEach;
36 import org.junit.jupiter.api.Test;
37 import org.slf4j.Marker;
38 import org.slf4j.event.KeyValuePair;
39 import org.slf4j.helpers.BasicMarkerFactory;
40
41 import java.io.IOException;
42 import java.nio.charset.StandardCharsets;
43 import java.nio.file.Files;
44 import java.nio.file.Path;
45 import java.util.Arrays;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Objects;
50
51 import static org.junit.jupiter.api.Assertions.assertEquals;
52 import static org.junit.jupiter.api.Assertions.assertTrue;
53
54
55
56
57
58 class JsonEncoderTest {
59
60 int diff = RandomUtil.getPositiveInt();
61
62 LoggerContext loggerContext = new LoggerContext();
63 StatusChecker statusChecker = new StatusChecker(loggerContext);
64 Logger logger = loggerContext.getLogger(JsonEncoderTest.class);
65
66 JsonEncoder jsonEncoder = new JsonEncoder();
67
68 BasicMarkerFactory markerFactory = new BasicMarkerFactory();
69
70 Marker markerA = markerFactory.getMarker("A");
71
72 Marker markerB = markerFactory.getMarker("B");
73
74 ListAppender<ILoggingEvent> listAppender = new ListAppender();
75 JsonStringToLoggingEventMapper stringToLoggingEventMapper = new JsonStringToLoggingEventMapper(markerFactory);
76
77 LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter();
78
79 @BeforeEach
80 void setUp() {
81 loggerContext.setName("test_" + diff);
82 loggerContext.setMDCAdapter(logbackMDCAdapter);
83
84 jsonEncoder.setContext(loggerContext);
85 jsonEncoder.start();
86
87 listAppender.setContext(loggerContext);
88 listAppender.start();
89 }
90
91 @AfterEach
92 void tearDown() {
93 }
94
95 @Test
96 void smoke() throws JsonProcessingException {
97 LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null);
98
99 byte[] resultBytes = jsonEncoder.encode(event);
100 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
101
102
103 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
104 compareEvents(event, resultEvent);
105
106 }
107
108 @Test
109 void contextWithProperties() throws JsonProcessingException {
110 loggerContext.putProperty("k", "v");
111 loggerContext.putProperty("k" + diff, "v" + diff);
112
113 LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null);
114
115 byte[] resultBytes = jsonEncoder.encode(event);
116 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
117
118
119 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
120 compareEvents(event, resultEvent);
121
122 }
123
124 private static void compareEvents(LoggingEvent event, JsonLoggingEvent resultEvent) {
125 assertEquals(event.getSequenceNumber(), resultEvent.getSequenceNumber());
126 assertEquals(event.getTimeStamp(), resultEvent.getTimeStamp());
127 assertEquals(event.getLevel(), resultEvent.getLevel());
128 assertEquals(event.getLoggerName(), resultEvent.getLoggerName());
129 assertEquals(event.getThreadName(), resultEvent.getThreadName());
130 assertEquals(event.getMarkerList(), resultEvent.getMarkerList());
131 assertEquals(event.getMDCPropertyMap(), resultEvent.getMDCPropertyMap());
132 assertTrue(compareKeyValuePairLists(event.getKeyValuePairs(), resultEvent.getKeyValuePairs()));
133
134 assertEquals(event.getLoggerContextVO(), resultEvent.getLoggerContextVO());
135 assertTrue(ThrowableProxyComparator.areEqual(event.getThrowableProxy(), resultEvent.getThrowableProxy()));
136
137 assertEquals(event.getMessage(), resultEvent.getMessage());
138 assertEquals(event.getFormattedMessage(), resultEvent.getFormattedMessage());
139
140 assertTrue(Arrays.equals(event.getArgumentArray(), resultEvent.getArgumentArray()));
141
142 }
143
144 private static boolean compareKeyValuePairLists(List<KeyValuePair> leftList, List<KeyValuePair> rightList) {
145 if (leftList == rightList)
146 return true;
147
148 if (leftList == null || rightList == null)
149 return false;
150
151 int length = leftList.size();
152 if (rightList.size() != length) {
153 System.out.println("length discrepancy");
154 return false;
155 }
156
157
158
159 for (int i = 0; i < length; i++) {
160 KeyValuePair leftKVP = leftList.get(i);
161 KeyValuePair rightKVP = rightList.get(i);
162
163 boolean result = Objects.equals(leftKVP.key, rightKVP.key) && Objects.equals(leftKVP.value, rightKVP.value);
164
165 if (!result) {
166 System.out.println("mismatch oin kvp " + leftKVP + " and " + rightKVP);
167 return false;
168 }
169 }
170 return true;
171
172 }
173
174 @Test
175 void withMarkers() throws JsonProcessingException {
176 LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null);
177 event.addMarker(markerA);
178 event.addMarker(markerB);
179
180 byte[] resultBytes = jsonEncoder.encode(event);
181 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
182
183
184 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
185 compareEvents(event, resultEvent);
186 }
187
188 @Test
189 void withArguments() throws JsonProcessingException {
190 LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, new Object[] { "arg1", "arg2" });
191
192 byte[] resultBytes = jsonEncoder.encode(event);
193 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
194
195
196 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
197 compareEvents(event, resultEvent);
198 }
199
200 @Test
201 void withKeyValuePairs() throws JsonProcessingException {
202 LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello kvp", null,
203 new Object[] { "arg1", "arg2" });
204 event.addKeyValuePair(new KeyValuePair("k1", "v1"));
205 event.addKeyValuePair(new KeyValuePair("k2", "v2"));
206
207 byte[] resultBytes = jsonEncoder.encode(event);
208 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
209
210 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
211 compareEvents(event, resultEvent);
212 }
213
214 @Test
215 void withFormattedMessage() throws JsonProcessingException {
216 LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello {} {}", null,
217 new Object[] { "arg1", "arg2" });
218 jsonEncoder.setWithFormattedMessage(true);
219
220 byte[] resultBytes = jsonEncoder.encode(event);
221 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
222 System.out.println(resultString);
223
224 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
225 compareEvents(event, resultEvent);
226 }
227
228 @Test
229 void withMDC() throws JsonProcessingException {
230 Map<String, String> map = new HashMap<>();
231 map.put("key", "value");
232 map.put("a", "b");
233
234 LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello kvp", null,
235 new Object[] { "arg1", "arg2" });
236 Map<String, String> mdcMap = new HashMap<>();
237 mdcMap.put("mdcK1", "v1");
238 mdcMap.put("mdcK2", "v2");
239
240 event.setMDCPropertyMap(mdcMap);
241
242 byte[] resultBytes = jsonEncoder.encode(event);
243 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
244
245 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
246 compareEvents(event, resultEvent);
247 }
248
249 @Test
250 void withThrowable() throws JsonProcessingException {
251 Throwable t = new RuntimeException("test");
252 LoggingEvent event = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", t, null);
253
254 byte[] resultBytes = jsonEncoder.encode(event);
255 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
256 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
257 compareEvents(event, resultEvent);
258 }
259
260 @Test
261 void withThrowableHavingCause() throws JsonProcessingException {
262 Throwable cause = new IllegalStateException("test cause");
263
264 Throwable t = new RuntimeException("test", cause);
265
266 LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t,
267 null);
268
269 byte[] resultBytes = jsonEncoder.encode(event);
270 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
271
272 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
273 compareEvents(event, resultEvent);
274 }
275
276 @Test
277 void withThrowableHavingCyclicCause() throws JsonProcessingException {
278 Throwable cause = new IllegalStateException("test cause");
279
280 Throwable t = new RuntimeException("test", cause);
281 cause.initCause(t);
282
283 LoggingEvent event = new LoggingEvent("in withThrowableHavingCyclicCause test", logger, Level.WARN, "hello kvp",
284 t, null);
285
286 byte[] resultBytes = jsonEncoder.encode(event);
287 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
288
289 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
290 compareEvents(event, resultEvent);
291 }
292
293 @Test
294 void withThrowableHavingSuppressed() throws JsonProcessingException {
295 Throwable suppressed = new IllegalStateException("test suppressed");
296
297 Throwable t = new RuntimeException("test");
298 t.addSuppressed(suppressed);
299
300 LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t,
301 null);
302
303 byte[] resultBytes = jsonEncoder.encode(event);
304 String resultString = new String(resultBytes, StandardCharsets.UTF_8);
305
306 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString);
307 compareEvents(event, resultEvent);
308 }
309
310 void configure(String file) throws JoranException {
311 JoranConfigurator jc = new JoranConfigurator();
312 jc.setContext(loggerContext);
313 loggerContext.putProperty("diff", "" + diff);
314 jc.doConfigure(file);
315 }
316
317 @Test
318 void withJoran() throws JoranException, IOException {
319 String configFilePathStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "json/jsonEncoder.xml";
320
321 configure(configFilePathStr);
322 Logger logger = loggerContext.getLogger(this.getClass().getName());
323 logger.addAppender(listAppender);
324
325 logger.debug("hello");
326 logbackMDCAdapter.put("a1", "v1" + diff);
327 logger.atInfo().addKeyValue("ik" + diff, "iv" + diff).addKeyValue("a", "b").log("bla bla \"x\" foobar");
328 logbackMDCAdapter.put("a2", "v2" + diff);
329 logger.atWarn().addMarker(markerA).setMessage("some warning message").log();
330 logbackMDCAdapter.remove("a2");
331 logger.atError().addKeyValue("ek" + diff, "v" + diff).setCause(new RuntimeException("an error"))
332 .log("some error occurred");
333
334
335
336 Path outputFilePath = Path.of(ClassicTestConstants.OUTPUT_DIR_PREFIX + "json/test-" + diff + ".json");
337 List<String> lines = Files.readAllLines(outputFilePath);
338 int count = 4;
339 assertEquals(count, lines.size());
340
341 for (int i = 0; i < count; i++) {
342
343 LoggingEvent withnessEvent = (LoggingEvent) listAppender.list.get(i);
344 JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(lines.get(i));
345 compareEvents(withnessEvent, resultEvent);
346 }
347 }
348
349 @Test
350 void withJoranAndEnabledFormattedMessage() throws JoranException, IOException {
351 String configFilePathStr =
352 ClassicTestConstants.JORAN_INPUT_PREFIX + "json/jsonEncoderAndEnabledFormattedMessage.xml";
353
354 configure(configFilePathStr);
355 Logger logger = loggerContext.getLogger(this.getClass().getName());
356
357
358 statusChecker.isWarningOrErrorFree(0);
359
360 logger.atError().addKeyValue("ek1", "v1").addArgument("arg1").log("this is {}");
361
362 Path outputFilePath = Path.of(ClassicTestConstants.OUTPUT_DIR_PREFIX + "json/test-" + diff + ".json");
363 List<String> lines = Files.readAllLines(outputFilePath);
364
365 int count = 1;
366 assertEquals(count, lines.size());
367
368 String withness = "{\"sequenceNumber\":0,\"level\":\"ERROR\",\"threadName\":\"main\","
369 + "\"loggerName\":\"ch.qos.logback.classic.encoder.JsonEncoderTest\",\"mdc\": {},"
370 + "\"kvpList\": [{\"ek1\":\"v1\"}],\"formattedMessage\":\"this is arg1\",\"throwable\":null}";
371
372 assertEquals(withness, lines.get(0));
373 }
374 }