Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
| `otel.instrumentation.logback-appender.experimental.capture-key-value-pair-attributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. |
| `otel.instrumentation.logback-appender.experimental.capture-logger-context-attributes` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. |
| `otel.instrumentation.logback-appender.experimental.capture-arguments` | Boolean | `false` | Enable the capture of Logback logger arguments. |
| `otel.instrumentation.logback-appender.experimental.capture-logstash-attributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. |
| `otel.instrumentation.logback-appender.experimental.capture-logstash-marker-attributes` | Boolean | `false` | Enable the capture of Logstash markers, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. |
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed capture-logstash-attributes to be more specific: capture-logstash-marker-attributes

to avoid confusion with the new capture-logstash-structured-arguments

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file needs a reformat

| `otel.instrumentation.logback-appender.experimental.capture-logstash-structured-arguments` | Boolean | `false` | Enable the capture of Logstash StructuredArguments as attributes (e.g., `StructuredArguments.v()` and `StructuredArguments.keyValue()`). |
| `otel.instrumentation.logback-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |
| `otel.instrumentation.logback-appender.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.opentelemetry.instrumentation.api.incubator.config.internal.InstrumentationConfig;
import io.opentelemetry.instrumentation.logback.appender.v1_0.internal.LoggingEventMapper;
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
import io.opentelemetry.javaagent.bootstrap.internal.DeprecatedConfigProperties;
import java.util.List;

public final class LogbackSingletons {
Expand Down Expand Up @@ -39,9 +40,15 @@ public final class LogbackSingletons {
boolean captureArguments =
config.getBoolean(
"otel.instrumentation.logback-appender.experimental.capture-arguments", false);
boolean captureLogstashAttributes =
config.getBoolean(
boolean captureLogstashMarkerAttributes =
DeprecatedConfigProperties.getBoolean(
config,
"otel.instrumentation.logback-appender.experimental.capture-logstash-attributes",
"otel.instrumentation.logback-appender.experimental.capture-logstash-marker-attributes",
false);
boolean captureLogstashStructuredArguments =
config.getBoolean(
"otel.instrumentation.logback-appender.experimental.capture-logstash-structured-arguments",
false);
List<String> captureMdcAttributes =
config.getList(
Expand All @@ -60,7 +67,8 @@ public final class LogbackSingletons {
.setCaptureKeyValuePairAttributes(captureKeyValuePairAttributes)
.setCaptureLoggerContext(captureLoggerContext)
.setCaptureArguments(captureArguments)
.setCaptureLogstashAttributes(captureLogstashAttributes)
.setCaptureLogstashMarkerAttributes(captureLogstashMarkerAttributes)
.setCaptureLogstashStructuredArguments(captureLogstashStructuredArguments)
.setCaptureEventName(captureEventName)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ The available settings are:
| `captureKeyValuePairAttributes` | Boolean | `false` | Enable the capture of Logback key value pairs as attributes. |
| `captureLoggerContext` | Boolean | `false` | Enable the capture of Logback logger context properties as attributes. |
| `captureArguments` | Boolean | `false` | Enable the capture of Logback logger arguments. |
| `captureLogstashAttributes` | Boolean | `false` | Enable the capture of Logstash attributes, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. |
| `captureLogstashMarkerAttributes` | Boolean | `false` | Enable the capture of Logstash markers, supported are those added to logs via `Markers.append()`, `Markers.appendEntries()`, `Markers.appendArray()` and `Markers.appendRaw()` methods. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one also needs a reformat

| `captureLogstashStructuredArguments` | Boolean | `false` | Enable the capture of Logstash StructuredArguments as attributes (e.g., `StructuredArguments.v()` and `StructuredArguments.keyValue()`). |
| `captureMdcAttributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |
| `captureEventName` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. |
| `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Logback appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. thread.id attribute is not captured. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,30 @@ val latestDepTest = findProperty("testLatestDeps") as Boolean
testing {
suites {
val slf4j2ApiTest by registering(JvmTestSuite::class) {
dependencies {
implementation(project(":instrumentation:logback:logback-appender-1.0:library"))
implementation("io.opentelemetry:opentelemetry-sdk-testing")
implementation(project(":testing-common"))

if (latestDepTest) {
implementation("ch.qos.logback:logback-classic:latest.release")
implementation("org.slf4j:slf4j-api:latest.release")
} else {
implementation("ch.qos.logback:logback-classic") {
version {
strictly("1.3.0")
}
}
implementation("org.slf4j:slf4j-api") {
version {
strictly("2.0.0")
}
}
}
}
}

val logstashMarkerTest by registering(JvmTestSuite::class) {
dependencies {
implementation(project(":instrumentation:logback:logback-appender-1.0:library"))
implementation("io.opentelemetry:opentelemetry-sdk-testing")
Expand Down Expand Up @@ -103,6 +127,36 @@ testing {
}
}

val logstashStructuredArgsTest by registering(JvmTestSuite::class) {
dependencies {
implementation(project(":instrumentation:logback:logback-appender-1.0:library"))
implementation("io.opentelemetry:opentelemetry-sdk-testing")
implementation(project(":testing-common"))

if (latestDepTest) {
implementation("ch.qos.logback:logback-classic:latest.release")
implementation("org.slf4j:slf4j-api:latest.release")
implementation("net.logstash.logback:logstash-logback-encoder:latest.release")
} else {
implementation("ch.qos.logback:logback-classic") {
version {
strictly("1.3.0")
}
}
implementation("org.slf4j:slf4j-api") {
version {
strictly("2.0.0")
}
}
implementation("net.logstash.logback:logstash-logback-encoder") {
version {
strictly("6.6")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StructuredArguments was introduced after LogstashMarker which is why separate test suite here

}
}
}
}
}

val asyncAppenderTest by registering(JvmTestSuite::class) {
dependencies {
implementation(project(":instrumentation:logback:logback-appender-1.0:library"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.logback.appender.v1_0;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.resources.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import net.logstash.logback.marker.Markers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogstashMarkerTest {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these tests are just move here from Slf4j2Test.java

private static final Logger logger = LoggerFactory.getLogger("TestLogger");

@RegisterExtension
private static final LibraryInstrumentationExtension testing =
LibraryInstrumentationExtension.create();

private static Resource resource;
private static InstrumentationScopeInfo instrumentationScopeInfo;

@BeforeAll
static void setupAll() {
resource = Resource.getDefault();
instrumentationScopeInfo = InstrumentationScopeInfo.create("TestLogger");

OpenTelemetryAppender.install(testing.getOpenTelemetry());
}

@Test
void logstash() {
Map<String, Object> entries = new HashMap<>();
entries.put("field2", 2);
entries.put("field3", "value3");

logger
.atInfo()
.setMessage("log message 1")
.addMarker(Markers.append("field1", "value1"))
.addMarker(Markers.append("event.name", "MyEventName"))
.addMarker(Markers.appendEntries(entries))
.log();

testing.waitAndAssertLogRecords(
logRecord ->
logRecord
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("log message 1")
.hasTotalAttributeCount(3) // 3 markers (event.name handled separately)
.hasEventName("MyEventName")
.hasAttributesSatisfying(
equalTo(AttributeKey.stringKey("field1"), "value1"),
equalTo(AttributeKey.longKey("field2"), 2L),
equalTo(AttributeKey.stringKey("field3"), "value3")));
}

@Test
void logstashVariousValues() {
Map<String, Object> entries = new HashMap<>();
entries.put("map1", 1);
entries.put("map2", 2.0);
entries.put("map3", "text-5");
entries.put("map4", null);

logger
.atInfo()
.setMessage("log message 1")
.addMarker(Markers.append("field1", 1))
.addMarker(Markers.append("field2", 2.0))
.addMarker(Markers.append("field3", "text-1"))
.addMarker(Markers.append("field4", true))
.addMarker(Markers.append("field5", new Integer[] {1, null, 2, 3}))
.addMarker(Markers.append("field6", new double[] {1.0, 2.0, 3.0}))
.addMarker(Markers.append("field7", new String[] {"text-2", "text-3", "text-4", null}))
.addMarker(Markers.append("field8", new Boolean[] {true, false, true}))
.addMarker(Markers.appendArray("field9", 1, 2.0, true, "text"))
.addMarker(Markers.appendRaw("field10", "raw value"))
.addMarker(Markers.append("field11", Arrays.asList(1, 2, 3)))
.addMarker(Markers.appendEntries(entries))
.log();

testing.waitAndAssertLogRecords(
logRecord ->
logRecord
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("log message 1")
// 14 fields (including map keys)
.hasTotalAttributeCount(14)
.hasAttributesSatisfying(
equalTo(AttributeKey.longKey("field1"), 1L),
equalTo(AttributeKey.doubleKey("field2"), 2.0),
equalTo(AttributeKey.stringKey("field3"), "text-1"),
equalTo(AttributeKey.booleanKey("field4"), true),
equalTo(AttributeKey.longArrayKey("field5"), Arrays.asList(1L, 2L, 3L)),
equalTo(AttributeKey.doubleArrayKey("field6"), Arrays.asList(1.0, 2.0, 3.0)),
equalTo(
AttributeKey.stringArrayKey("field7"),
Arrays.asList("text-2", "text-3", "text-4")),
equalTo(
AttributeKey.booleanArrayKey("field8"), Arrays.asList(true, false, true)),
equalTo(
AttributeKey.stringArrayKey("field9"),
Arrays.asList("1", "2.0", "true", "text")),
equalTo(AttributeKey.stringKey("field10"), "raw value"),
equalTo(AttributeKey.stringArrayKey("field11"), Arrays.asList("1", "2", "3")),
equalTo(AttributeKey.longKey("map1"), 1L),
equalTo(AttributeKey.doubleKey("map2"), 2.0),
equalTo(AttributeKey.stringKey("map3"), "text-5")));
}

@Test
void logstashEmptyAndNullValues() {
Map<String, Object> noEntries = new HashMap<>();

logger
.atInfo()
.setMessage("log message 1")
.addMarker(Markers.appendEntries(noEntries))
.addMarker(Markers.append("field2", null))
.addMarker(Markers.append("field3", new int[0]))
.addMarker(Markers.append("field4", new String[0]))
.addMarker(Markers.appendArray("field5"))
.addMarker(Markers.appendArray("field6", (Object) null))
.addMarker(Markers.appendArray("field7", null, null, null))
.log();

testing.waitAndAssertLogRecords(
logRecord ->
logRecord
.hasResource(resource)
.hasInstrumentationScope(instrumentationScopeInfo)
.hasBody("log message 1")
.hasTotalAttributeCount(0));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>

<appender name="OpenTelemetry"
class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
<captureLogstashMarkerAttributes>true</captureLogstashMarkerAttributes>
<captureEventName>true</captureEventName>
</appender>

<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="OpenTelemetry"/>
</root>

</configuration>
Loading
Loading