-
Notifications
You must be signed in to change notification settings - Fork 1k
Add logstash structured arg support #14959
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
74d1a1e
feda427
25815fa
5d8517e
7fa4c3b
9e12b62
27fbe01
f2a4384
3c54b6e
bc58d37
23c2f6d
4ebc217
b69a47d
08a16d6
4b770a0
cda71d4
9495361
aad7ecb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | | ||
|
||
| `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. | | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | | ||
|
||
| `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. | | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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") | ||
|
@@ -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") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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")) | ||
|
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 { | ||
|
||
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> |
There was a problem hiding this comment.
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