|
5 | 5 |
|
6 | 6 | package io.opentelemetry.instrumentation.log4j.appender.v2_17;
|
7 | 7 |
|
8 |
| -import static io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil.codeFileAndLineAssertions; |
9 |
| -import static io.opentelemetry.instrumentation.testing.junit.code.SemconvCodeStabilityUtil.codeFunctionAssertions; |
10 |
| -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; |
11 |
| -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; |
12 |
| -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; |
13 |
| -import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; |
14 |
| -import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; |
15 |
| -import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; |
16 |
| -import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_ID; |
17 |
| -import static io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes.THREAD_NAME; |
18 |
| -import static java.util.concurrent.TimeUnit.MILLISECONDS; |
19 |
| - |
20 |
| -import io.opentelemetry.api.common.AttributeKey; |
21 |
| -import io.opentelemetry.api.common.Value; |
22 |
| -import io.opentelemetry.api.logs.Severity; |
23 |
| -import io.opentelemetry.api.trace.SpanContext; |
24 | 8 | import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
25 | 9 | import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
26 |
| -import io.opentelemetry.sdk.common.InstrumentationScopeInfo; |
27 |
| -import io.opentelemetry.sdk.logs.data.LogRecordData; |
28 |
| -import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; |
29 |
| -import java.time.Instant; |
30 |
| -import java.util.ArrayList; |
31 |
| -import java.util.Arrays; |
32 |
| -import java.util.List; |
33 |
| -import java.util.stream.Stream; |
34 |
| -import org.apache.logging.log4j.LogManager; |
35 |
| -import org.apache.logging.log4j.Logger; |
36 |
| -import org.apache.logging.log4j.Marker; |
37 |
| -import org.apache.logging.log4j.MarkerManager; |
38 |
| -import org.apache.logging.log4j.ThreadContext; |
39 |
| -import org.apache.logging.log4j.message.StringMapMessage; |
40 |
| -import org.apache.logging.log4j.message.StructuredDataMessage; |
41 |
| -import org.assertj.core.api.AssertAccess; |
42 |
| -import org.junit.jupiter.api.Test; |
43 | 10 | import org.junit.jupiter.api.extension.RegisterExtension;
|
44 |
| -import org.junit.jupiter.params.ParameterizedTest; |
45 |
| -import org.junit.jupiter.params.provider.Arguments; |
46 |
| -import org.junit.jupiter.params.provider.MethodSource; |
47 | 11 |
|
48 |
| -class Log4j2Test { |
| 12 | +class Log4j2Test extends AbstractLog4j2Test { |
49 | 13 |
|
50 | 14 | @RegisterExtension
|
51 | 15 | static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
52 | 16 |
|
53 |
| - private static final Logger logger = LogManager.getLogger("abc"); |
54 |
| - |
55 |
| - private static Stream<Arguments> provideParameters() { |
56 |
| - return Stream.of( |
57 |
| - Arguments.of(false, false), |
58 |
| - Arguments.of(false, true), |
59 |
| - Arguments.of(true, false), |
60 |
| - Arguments.of(true, true)); |
61 |
| - } |
62 |
| - |
63 |
| - private static List<AttributeAssertion> threadAttributesAssertions() { |
64 |
| - return Arrays.asList( |
65 |
| - equalTo(THREAD_NAME, Thread.currentThread().getName()), |
66 |
| - equalTo(THREAD_ID, Thread.currentThread().getId())); |
67 |
| - } |
68 |
| - |
69 |
| - @ParameterizedTest |
70 |
| - @MethodSource("provideParameters") |
71 |
| - public void test(boolean logException, boolean withParent) throws InterruptedException { |
72 |
| - test(Logger::debug, Logger::debug, logException, withParent, null, null, null); |
73 |
| - testing.clearData(); |
74 |
| - test(Logger::info, Logger::info, logException, withParent, "abc", Severity.INFO, "INFO"); |
75 |
| - testing.clearData(); |
76 |
| - test(Logger::warn, Logger::warn, logException, withParent, "abc", Severity.WARN, "WARN"); |
77 |
| - testing.clearData(); |
78 |
| - test(Logger::error, Logger::error, logException, withParent, "abc", Severity.ERROR, "ERROR"); |
79 |
| - testing.clearData(); |
80 |
| - } |
81 |
| - |
82 |
| - private static void test( |
83 |
| - OneArgLoggerMethod oneArgLoggerMethod, |
84 |
| - TwoArgLoggerMethod twoArgLoggerMethod, |
85 |
| - boolean logException, |
86 |
| - boolean withParent, |
87 |
| - String expectedLoggerName, |
88 |
| - Severity expectedSeverity, |
89 |
| - String expectedSeverityText) |
90 |
| - throws InterruptedException { |
91 |
| - |
92 |
| - Instant start = Instant.now(); |
93 |
| - |
94 |
| - // when |
95 |
| - if (withParent) { |
96 |
| - testing.runWithSpan( |
97 |
| - "parent", () -> performLogging(oneArgLoggerMethod, twoArgLoggerMethod, logException)); |
98 |
| - } else { |
99 |
| - performLogging(oneArgLoggerMethod, twoArgLoggerMethod, logException); |
100 |
| - } |
101 |
| - |
102 |
| - // then |
103 |
| - if (withParent) { |
104 |
| - testing.waitForTraces(1); |
105 |
| - } |
106 |
| - |
107 |
| - if (expectedSeverity != null) { |
108 |
| - testing.waitAndAssertLogRecords( |
109 |
| - logRecord -> { |
110 |
| - logRecord |
111 |
| - .hasBody("xyz: 123") |
112 |
| - .hasInstrumentationScope( |
113 |
| - InstrumentationScopeInfo.builder(expectedLoggerName).build()) |
114 |
| - .hasSeverity(expectedSeverity) |
115 |
| - .hasSeverityText(expectedSeverityText) |
116 |
| - .hasSpanContext( |
117 |
| - withParent |
118 |
| - ? testing.spans().get(0).getSpanContext() |
119 |
| - : SpanContext.getInvalid()); |
120 |
| - |
121 |
| - List<AttributeAssertion> attributeAsserts = |
122 |
| - new ArrayList<>( |
123 |
| - Arrays.asList( |
124 |
| - equalTo(THREAD_NAME, Thread.currentThread().getName()), |
125 |
| - equalTo(THREAD_ID, Thread.currentThread().getId()))); |
126 |
| - |
127 |
| - attributeAsserts.addAll(codeFunctionAssertions(Log4j2Test.class, "performLogging")); |
128 |
| - attributeAsserts.addAll(codeFileAndLineAssertions("Log4j2Test.java")); |
129 |
| - |
130 |
| - if (logException) { |
131 |
| - attributeAsserts.addAll( |
132 |
| - Arrays.asList( |
133 |
| - equalTo(EXCEPTION_TYPE, IllegalStateException.class.getName()), |
134 |
| - equalTo(EXCEPTION_MESSAGE, "hello"), |
135 |
| - satisfies( |
136 |
| - EXCEPTION_STACKTRACE, v -> v.contains(Log4j2Test.class.getName())))); |
137 |
| - } |
138 |
| - logRecord.hasAttributesSatisfyingExactly(attributeAsserts); |
139 |
| - |
140 |
| - LogRecordData logRecordData = AssertAccess.getActual(logRecord); |
141 |
| - assertThat(logRecordData.getTimestampEpochNanos()) |
142 |
| - .isGreaterThanOrEqualTo(MILLISECONDS.toNanos(start.toEpochMilli())) |
143 |
| - .isLessThanOrEqualTo(MILLISECONDS.toNanos(Instant.now().toEpochMilli())); |
144 |
| - }); |
145 |
| - } else { |
146 |
| - Thread.sleep(500); // sleep a bit just to make sure no log is captured |
147 |
| - assertThat(testing.logRecords()).isEmpty(); |
148 |
| - } |
149 |
| - } |
150 |
| - |
151 |
| - @Test |
152 |
| - void testContextData() { |
153 |
| - ThreadContext.put("key1", "val1"); |
154 |
| - ThreadContext.put("key2", "val2"); |
155 |
| - try { |
156 |
| - logger.info("xyz: {}", 123); |
157 |
| - } finally { |
158 |
| - ThreadContext.clearMap(); |
159 |
| - } |
160 |
| - |
161 |
| - List<AttributeAssertion> assertions = |
162 |
| - codeFunctionAssertions(Log4j2Test.class, "testContextData"); |
163 |
| - assertions.addAll(codeFileAndLineAssertions("Log4j2Test.java")); |
164 |
| - assertions.addAll(threadAttributesAssertions()); |
165 |
| - assertions.add(equalTo(AttributeKey.stringKey("key1"), "val1")); |
166 |
| - assertions.add(equalTo(AttributeKey.stringKey("key2"), "val2")); |
167 |
| - |
168 |
| - testing.waitAndAssertLogRecords( |
169 |
| - logRecord -> |
170 |
| - logRecord |
171 |
| - .hasBody("xyz: 123") |
172 |
| - .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) |
173 |
| - .hasSeverity(Severity.INFO) |
174 |
| - .hasSeverityText("INFO") |
175 |
| - .hasAttributesSatisfyingExactly(assertions)); |
176 |
| - } |
177 |
| - |
178 |
| - @Test |
179 |
| - void testStringMapMessage() { |
180 |
| - StringMapMessage message = new StringMapMessage(); |
181 |
| - message.put("key1", "val1"); |
182 |
| - message.put("key2", "val2"); |
183 |
| - logger.info(message); |
184 |
| - |
185 |
| - List<AttributeAssertion> assertions = |
186 |
| - codeFunctionAssertions(Log4j2Test.class, "testStringMapMessage"); |
187 |
| - assertions.addAll(codeFileAndLineAssertions("Log4j2Test.java")); |
188 |
| - assertions.addAll(threadAttributesAssertions()); |
189 |
| - assertions.add(equalTo(AttributeKey.stringKey("log4j.map_message.key1"), "val1")); |
190 |
| - assertions.add(equalTo(AttributeKey.stringKey("log4j.map_message.key2"), "val2")); |
191 |
| - |
192 |
| - testing.waitAndAssertLogRecords( |
193 |
| - logRecord -> |
194 |
| - logRecord |
195 |
| - .hasBody((Value<?>) null) |
196 |
| - .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) |
197 |
| - .hasSeverity(Severity.INFO) |
198 |
| - .hasSeverityText("INFO") |
199 |
| - .hasAttributesSatisfyingExactly(assertions)); |
200 |
| - } |
201 |
| - |
202 |
| - @Test |
203 |
| - void testStringMapMessageWithSpecialAttribute() { |
204 |
| - StringMapMessage message = new StringMapMessage(); |
205 |
| - message.put("key1", "val1"); |
206 |
| - message.put("message", "val2"); |
207 |
| - logger.info(message); |
208 |
| - |
209 |
| - List<AttributeAssertion> assertions = |
210 |
| - codeFunctionAssertions(Log4j2Test.class, "testStringMapMessageWithSpecialAttribute"); |
211 |
| - assertions.addAll(codeFileAndLineAssertions("Log4j2Test.java")); |
212 |
| - assertions.addAll(threadAttributesAssertions()); |
213 |
| - assertions.add(equalTo(AttributeKey.stringKey("log4j.map_message.key1"), "val1")); |
214 |
| - |
215 |
| - testing.waitAndAssertLogRecords( |
216 |
| - logRecord -> |
217 |
| - logRecord |
218 |
| - .hasBody("val2") |
219 |
| - .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) |
220 |
| - .hasSeverity(Severity.INFO) |
221 |
| - .hasSeverityText("INFO") |
222 |
| - .hasAttributesSatisfyingExactly(assertions)); |
223 |
| - } |
224 |
| - |
225 |
| - @Test |
226 |
| - void testStructuredDataMapMessage() { |
227 |
| - StructuredDataMessage message = new StructuredDataMessage("an id", "a message", "a type"); |
228 |
| - message.put("key1", "val1"); |
229 |
| - message.put("key2", "val2"); |
230 |
| - logger.info(message); |
231 |
| - |
232 |
| - List<AttributeAssertion> assertions = |
233 |
| - codeFunctionAssertions(Log4j2Test.class, "testStructuredDataMapMessage"); |
234 |
| - assertions.addAll(codeFileAndLineAssertions("Log4j2Test.java")); |
235 |
| - assertions.addAll(threadAttributesAssertions()); |
236 |
| - assertions.add(equalTo(AttributeKey.stringKey("log4j.map_message.key1"), "val1")); |
237 |
| - assertions.add(equalTo(AttributeKey.stringKey("log4j.map_message.key2"), "val2")); |
238 |
| - |
239 |
| - testing.waitAndAssertLogRecords( |
240 |
| - logRecord -> |
241 |
| - logRecord |
242 |
| - .hasBody("a message") |
243 |
| - .hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build()) |
244 |
| - .hasSeverity(Severity.INFO) |
245 |
| - .hasSeverityText("INFO") |
246 |
| - .hasAttributesSatisfyingExactly(assertions)); |
247 |
| - } |
248 |
| - |
249 |
| - @Test |
250 |
| - public void testMarker() { |
251 |
| - String markerName = "aMarker"; |
252 |
| - Marker marker = MarkerManager.getMarker(markerName); |
253 |
| - |
254 |
| - logger.info(marker, "Message"); |
255 |
| - |
256 |
| - List<AttributeAssertion> assertions = codeFunctionAssertions(Log4j2Test.class, "testMarker"); |
257 |
| - assertions.addAll(codeFileAndLineAssertions("Log4j2Test.java")); |
258 |
| - assertions.addAll(threadAttributesAssertions()); |
259 |
| - assertions.add(equalTo(AttributeKey.stringKey("log4j.marker"), markerName)); |
260 |
| - |
261 |
| - testing.waitAndAssertLogRecords( |
262 |
| - logRecord -> logRecord.hasAttributesSatisfyingExactly(assertions)); |
263 |
| - } |
264 |
| - |
265 |
| - private static void performLogging( |
266 |
| - OneArgLoggerMethod oneArgLoggerMethod, |
267 |
| - TwoArgLoggerMethod twoArgLoggerMethod, |
268 |
| - boolean logException) { |
269 |
| - if (logException) { |
270 |
| - twoArgLoggerMethod.call(logger, "xyz: {}", 123, new IllegalStateException("hello")); |
271 |
| - } else { |
272 |
| - oneArgLoggerMethod.call(logger, "xyz: {}", 123); |
273 |
| - } |
274 |
| - } |
275 |
| - |
276 |
| - @FunctionalInterface |
277 |
| - interface OneArgLoggerMethod { |
278 |
| - void call(Logger logger, String msg, Object arg); |
279 |
| - } |
280 |
| - |
281 |
| - @FunctionalInterface |
282 |
| - interface TwoArgLoggerMethod { |
283 |
| - void call(Logger logger, String msg, Object arg1, Object arg2); |
| 17 | + @Override |
| 18 | + protected InstrumentationExtension testing() { |
| 19 | + return testing; |
284 | 20 | }
|
285 | 21 | }
|
0 commit comments