Skip to content

Commit 75f2ca3

Browse files
authored
Unify Log4j2 appender tests (#14859)
1 parent 5457391 commit 75f2ca3

File tree

11 files changed

+366
-504
lines changed

11 files changed

+366
-504
lines changed

instrumentation/log4j/log4j-appender-2.17/javaagent/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies {
2020

2121
implementation(project(":instrumentation:log4j:log4j-appender-2.17:library"))
2222

23+
testImplementation(project(":instrumentation:log4j:log4j-appender-2.17:testing"))
2324
testImplementation("org.awaitility:awaitility")
2425

2526
if (testLatestDeps) {
@@ -64,6 +65,7 @@ tasks.withType<Test>().configureEach {
6465
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-map-message-attributes=true")
6566
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-mdc-attributes=*")
6667
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-marker-attribute=true")
68+
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-event-name=true")
6769
}
6870

6971
configurations {

instrumentation/log4j/log4j-appender-2.17/javaagent/src/test/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/Log4j2Test.java

Lines changed: 4 additions & 268 deletions
Original file line numberDiff line numberDiff line change
@@ -5,281 +5,17 @@
55

66
package io.opentelemetry.instrumentation.log4j.appender.v2_17;
77

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;
248
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
259
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;
4310
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;
4711

48-
class Log4j2Test {
12+
class Log4j2Test extends AbstractLog4j2Test {
4913

5014
@RegisterExtension
5115
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
5216

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;
28420
}
28521
}

instrumentation/log4j/log4j-appender-2.17/library/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dependencies {
88

99
implementation(project(":instrumentation:log4j:log4j-context-data:log4j-context-data-2.17:library-autoconfigure"))
1010

11+
testImplementation(project(":instrumentation:log4j:log4j-appender-2.17:testing"))
1112
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
1213
testLibrary("com.lmax:disruptor:3.3.4")
1314

instrumentation/log4j/log4j-appender-2.17/library/src/main/java/io/opentelemetry/instrumentation/log4j/appender/v2_17/internal/LogEventMapper.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.semconv.ExceptionAttributes;
1717
import java.io.PrintWriter;
1818
import java.io.StringWriter;
19+
import java.util.ArrayList;
1920
import java.util.List;
2021
import java.util.function.Supplier;
2122
import javax.annotation.Nullable;
@@ -76,9 +77,16 @@ public LogEventMapper(
7677
this.captureExperimentalAttributes = captureExperimentalAttributes;
7778
this.captureMapMessageAttributes = captureMapMessageAttributes;
7879
this.captureMarkerAttribute = captureMarkerAttribute;
79-
this.captureContextDataAttributes = captureContextDataAttributes;
8080
this.captureAllContextDataAttributes =
8181
captureContextDataAttributes.size() == 1 && captureContextDataAttributes.get(0).equals("*");
82+
// If captureEventName is enabled, ensure "event.name" is in the list that we loop over
83+
if (captureEventName
84+
&& !captureAllContextDataAttributes
85+
&& !captureContextDataAttributes.contains("event.name")) {
86+
captureContextDataAttributes = new ArrayList<>(captureContextDataAttributes);
87+
captureContextDataAttributes.add("event.name");
88+
}
89+
this.captureContextDataAttributes = captureContextDataAttributes;
8290
this.captureEventName = captureEventName;
8391
}
8492

0 commit comments

Comments
 (0)