Skip to content

Commit b4534b1

Browse files
committed
Add log appender example
1 parent a45502b commit b4534b1

File tree

11 files changed

+282
-24
lines changed

11 files changed

+282
-24
lines changed

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ of them assume you have docker running on your local machine.
3333
- This module demonstrates using the OpenTelemetry Java Agent with a simple
3434
spring boot application. Traces, metrics, and logs are exported to a
3535
collector via OTLP.
36+
- [Configuring Log Appenders](log-appender)
37+
- This module demonstrates how to configure the Log4j and Logback appenders to
38+
bridge logs into the OpenTelemetry Log SDK.
3639
- [Configuring the Logging Exporters](logging)
3740
- This module contains a fully-functional example of configuring the
3841
OpenTelemetry SDK to use a logging exporter.
@@ -43,18 +46,21 @@ of them assume you have docker running on your local machine.
4346
with spring boot actuator) configured to bridge metrics to OpenTelemetry
4447
with the micrometer shim.
4548
- Note: the micrometer shim is still experimental at this time.
46-
- [Setting up the Prometheus exporter](prometheus)
47-
- The module shows how to configure the OpenTelemetry SDK to expose an
48-
endpoint that can be scraped by Prometheus.
49-
- Note: the prometheus metric reader is still experimental at this time.
5049
- [Setting up OTLP exporters](otlp)
5150
- OTLP is the OpenTelemetry Protocol. This module will demonstrate how to
5251
configure the OTLP exporters, and send data to the OpenTelemetry collector
5352
using them.
5453
- Note: this example requires having docker installed to run the example.
54+
- [Setting up the Prometheus exporter](prometheus)
55+
- The module shows how to configure the OpenTelemetry SDK to expose an
56+
endpoint that can be scraped by Prometheus.
57+
- Note: the prometheus metric reader is still experimental at this time.
5558
- [Manually Configuring the SDK](sdk-usage)
5659
- This module shows some concrete examples of manually configuring the Java
5760
OpenTelemetry SDK for Tracing.
61+
- [Telemetry Testing](sdk-usage)
62+
- This module demonstrates how to test OpenTelemetry instrumentation with a
63+
MockServer.
5864
- [Setting up the Zipkin exporter](zipkin)
5965
- This module contains a fully-functional example of configuring the
6066
OpenTelemetry SDK to use a Zipkin exporter, and send some spans to a

javaagent/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ services:
1212
depends_on:
1313
- collector
1414
collector:
15-
image: otel/opentelemetry-collector-contrib:0.51.0
15+
image: otel/opentelemetry-collector-contrib:0.72.0
1616
volumes:
1717
- ./otel-config.yaml:/otel-config.yaml
1818
command: ["--config=/otel-config.yaml"]

javaagent/otel-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ receivers:
44
grpc:
55
exporters:
66
logging:
7-
logLevel: DEBUG
7+
verbosity: detailed
88
service:
99
pipelines:
1010
metrics:

log-appender/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# OpenTelemetry Log Appender Example
2+
3+
This example demonstrates an application configured to use the OpenTelemetry Log
4+
Appenders to bridge logs into the OpenTelemetry Log SDK, and export
5+
via [OTLP](https://opentelemetry.io/docs/reference/specification/protocol/otlp/).
6+
7+
Details about the example:
8+
9+
* The OpenTelemetry Log SDK is configured to export data to
10+
the [collector](https://opentelemetry.io/docs/collector/), which prints the
11+
logs to the console.
12+
* The application is configured with a variety of log solutions:
13+
* Log4j API [configured](./src/main/resources/log4j2.xml) to print logs to the
14+
console and
15+
the [OpenTelemetry Log4J Appender](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/log4j/log4j-appender-2.17/library/README.md).
16+
* SLF4J API [configured with Logback](./src/main/resources/logback.xml) to
17+
print logs to the console and
18+
the [OpenTelemetry Logback Appender](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/logback/logback-appender-1.0/library/README.md).
19+
* [JUL to SLF4J](./build.gradle), which bridges JUL logs to the SLF4J API, and
20+
ultimately to Logback.
21+
* Demonstrates how trace context is propagated to logs when recorded within a
22+
span.
23+
24+
## Prerequisites
25+
26+
* Java 1.8
27+
* Docker compose
28+
29+
# How to run
30+
31+
Run the collector via docker
32+
33+
```shell
34+
docker-compose up
35+
```
36+
37+
In a separate shell, run the application
38+
39+
```shell
40+
../gradlew run
41+
```
42+
43+
Watch the collector logs to see exported log records

log-appender/build.gradle

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
plugins {
2+
id 'java'
3+
id 'application'
4+
}
5+
6+
description = 'OpenTelemetry Log Appender Example'
7+
ext.moduleName = "io.opentelemetry.examples.log-appender"
8+
9+
dependencies {
10+
// Slf4J / logback
11+
implementation("org.slf4j:slf4j-api:2.0.6")
12+
implementation("ch.qos.logback:logback-core:1.4.5")
13+
implementation("ch.qos.logback:logback-classic:1.4.5")
14+
15+
// JUL to SLF4J bridge
16+
implementation("org.slf4j:jul-to-slf4j:2.0.6")
17+
18+
// Log4j
19+
implementation(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
20+
implementation("org.apache.logging.log4j:log4j-api")
21+
implementation("org.apache.logging.log4j:log4j-core")
22+
23+
// OpenTelemetry core
24+
implementation("io.opentelemetry:opentelemetry-sdk")
25+
implementation("io.opentelemetry:opentelemetry-sdk-logs")
26+
implementation("io.opentelemetry:opentelemetry-exporter-otlp-logs")
27+
implementation("io.opentelemetry:opentelemetry-semconv")
28+
29+
// OpenTelemetry log4j / logback appenders
30+
implementation 'io.opentelemetry.instrumentation:opentelemetry-log4j-appender-2.17'
31+
implementation 'io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0'
32+
}
33+
34+
application {
35+
mainClass = 'io.opentelemetry.example.logappender.Application'
36+
}

log-appender/docker-compose.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: '3'
2+
services:
3+
collector:
4+
image: otel/opentelemetry-collector-contrib:0.72.0
5+
volumes:
6+
- ./otel-config.yaml:/otel-config.yaml
7+
command: ["--config=/otel-config.yaml"]
8+
ports:
9+
- "4317:4317"

log-appender/otel-config.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
receivers:
2+
otlp:
3+
protocols:
4+
grpc:
5+
exporters:
6+
logging:
7+
verbosity: detailed
8+
service:
9+
pipelines:
10+
logs:
11+
receivers: [otlp]
12+
exporters: [logging]
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package io.opentelemetry.example.logappender;
2+
3+
import io.opentelemetry.api.GlobalOpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.logs.GlobalLoggerProvider;
6+
import io.opentelemetry.api.logs.Severity;
7+
import io.opentelemetry.api.trace.Span;
8+
import io.opentelemetry.context.Scope;
9+
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
10+
import io.opentelemetry.sdk.OpenTelemetrySdk;
11+
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
12+
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
13+
import io.opentelemetry.sdk.resources.Resource;
14+
import io.opentelemetry.sdk.trace.SdkTracerProvider;
15+
import io.opentelemetry.sdk.trace.samplers.Sampler;
16+
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
17+
import org.apache.logging.log4j.LogManager;
18+
import org.apache.logging.log4j.ThreadContext;
19+
import org.apache.logging.log4j.message.MapMessage;
20+
import org.slf4j.LoggerFactory;
21+
import org.slf4j.bridge.SLF4JBridgeHandler;
22+
23+
import java.util.HashMap;
24+
import java.util.Map;
25+
import java.util.logging.Logger;
26+
27+
public class Application {
28+
29+
private static final org.apache.logging.log4j.Logger log4jLogger = LogManager.getLogger("log4j-logger");
30+
private static final org.slf4j.Logger slf4jLogger = LoggerFactory.getLogger("slf4j-logger");
31+
private static final java.util.logging.Logger julLogger = Logger.getLogger("jul-logger");
32+
33+
public static void main(String[] args) {
34+
// Initialize OpenTelemetry as early as possible
35+
initializeOpenTelemetry();
36+
37+
// Route JUL logs to slf4j
38+
SLF4JBridgeHandler.removeHandlersForRootLogger();
39+
SLF4JBridgeHandler.install();
40+
41+
// Log using log4j API
42+
maybeRunWithSpan(() -> log4jLogger.info("A log4j log message without a span"), false);
43+
maybeRunWithSpan(() -> log4jLogger.info("A log4j log message with a span"), true);
44+
Map<String, Object> mapMessage = new HashMap<>();
45+
mapMessage.put("key", "value");
46+
mapMessage.put("message", "A log4j structured message");
47+
maybeRunWithSpan(() -> log4jLogger.info(new MapMessage<>(mapMessage)), false);
48+
ThreadContext.clearAll();
49+
50+
// Log using slf4j API w/ logback backend
51+
maybeRunWithSpan(() -> slf4jLogger.info("A slf4j log message without a span"), false);
52+
maybeRunWithSpan(() -> slf4jLogger.info("A slf4j log message with a span"), true);
53+
maybeRunWithSpan(() -> slf4jLogger.atInfo().setMessage("A slf4j structured message").addKeyValue("key", "value").log(), false);
54+
55+
// Log using JUL API, bridged to slf4j, w/ logback backend
56+
maybeRunWithSpan(() -> julLogger.info("A JUL log message without a span"), false);
57+
maybeRunWithSpan(() -> julLogger.info("A JUL log message with a span"), true);
58+
59+
// Log using OpenTelemetry Log Bridge API
60+
// WARNING: This illustrates how to write appenders which bridge logs from
61+
// existing frameworks into the OpenTelemetry Log Brdige API. These APIs
62+
// SHOULD NOT be used by end users in place of existing log APIs (i.e. Log4j, Slf4, JUL).
63+
io.opentelemetry.api.logs.Logger customAppenderLogger =
64+
GlobalLoggerProvider.get().get("custom-log-appender");
65+
maybeRunWithSpan(
66+
() ->
67+
customAppenderLogger
68+
.logRecordBuilder()
69+
.setSeverity(Severity.INFO)
70+
.setBody("A log message from a custom appender without a span")
71+
.setAttribute(AttributeKey.stringKey("key"), "value")
72+
.emit(),
73+
false);
74+
maybeRunWithSpan(
75+
() ->
76+
customAppenderLogger
77+
.logRecordBuilder()
78+
.setSeverity(Severity.INFO)
79+
.setBody("A log message from a custom appender with a span")
80+
.setAttribute(AttributeKey.stringKey("key"), "value")
81+
.emit(),
82+
true);
83+
}
84+
85+
private static void initializeOpenTelemetry() {
86+
OpenTelemetrySdk sdk =
87+
OpenTelemetrySdk.builder()
88+
.setTracerProvider(SdkTracerProvider.builder()
89+
.setSampler(Sampler.alwaysOn()).build())
90+
.setLoggerProvider(
91+
SdkLoggerProvider.builder()
92+
.setResource(
93+
Resource.getDefault().toBuilder()
94+
.put(ResourceAttributes.SERVICE_NAME, "log4j-example")
95+
.build())
96+
.addLogRecordProcessor(
97+
BatchLogRecordProcessor.builder(
98+
OtlpGrpcLogRecordExporter.builder()
99+
.setEndpoint("http://localhost:4317")
100+
.build())
101+
.build())
102+
.build())
103+
.build();
104+
GlobalOpenTelemetry.set(sdk);
105+
GlobalLoggerProvider.set(sdk.getSdkLoggerProvider());
106+
107+
// Add hook to close SDK, which flushes logs
108+
Runtime.getRuntime().addShutdownHook(new Thread(sdk::close));
109+
}
110+
111+
private static void maybeRunWithSpan(Runnable runnable, boolean withSpan) {
112+
if (!withSpan) {
113+
runnable.run();
114+
return;
115+
}
116+
Span span = GlobalOpenTelemetry.getTracer("my-tracer").spanBuilder("my-span").startSpan();
117+
try (Scope unused = span.makeCurrent()) {
118+
runnable.run();
119+
} finally {
120+
span.end();
121+
}
122+
}
123+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration status="WARN" packages="io.opentelemetry.instrumentation.log4j.appender.v2_17">
3+
<Appenders>
4+
<Console name="ConsoleAppender" target="SYSTEM_OUT" follow="true">
5+
<PatternLayout pattern="log4j2: %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
6+
</Console>
7+
<OpenTelemetry name="OpenTelemetryAppender" captureMapMessageAttributes="true"/>
8+
</Appenders>
9+
<Loggers>
10+
<Root level="info">
11+
<AppenderRef ref="OpenTelemetryAppender" />
12+
<AppenderRef ref="ConsoleAppender" />
13+
</Root>
14+
</Loggers>
15+
</Configuration>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
4+
<encoder>
5+
<pattern>
6+
logback: %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %kvp{DOUBLE}%n
7+
</pattern>
8+
</encoder>
9+
</appender>
10+
<appender name="OpenTelemetry"
11+
class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender"
12+
captureMdcAttributes="true" >
13+
</appender>
14+
<root level="INFO">
15+
<appender-ref ref="console"/>
16+
<appender-ref ref="OpenTelemetry"/>
17+
</root>
18+
</configuration>

settings.gradle

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,17 @@ pluginManagement {
77
}
88

99
rootProject.name = "opentelemetry-java-examples"
10-
include ":opentelemetry-examples-javaagent",
11-
":opentelemetry-examples-autoconfigure",
12-
":opentelemetry-examples-grpc",
13-
":opentelemetry-examples-http",
14-
":opentelemetry-examples-jaeger",
15-
":opentelemetry-examples-metrics",
16-
":opentelemetry-examples-micrometer-shim",
17-
":opentelemetry-examples-prometheus",
18-
":opentelemetry-examples-otlp",
19-
":opentelemetry-examples-sdk-usage",
20-
":opentelemetry-examples-zipkin",
21-
":opentelemetry-examples-logging",
22-
":opentelemetry-examples-telemetry-testing"
23-
24-
rootProject.children.each {
25-
it.projectDir = "$rootDir/" + it.name
26-
.replace("opentelemetry-examples-", "") as File
27-
}
10+
include ":autoconfigure",
11+
":grpc",
12+
":http",
13+
":jaeger",
14+
":javaagent",
15+
":log-appender",
16+
":logging",
17+
":metrics",
18+
":micrometer-shim",
19+
":otlp",
20+
":prometheus",
21+
":sdk-usage",
22+
":telemetry-testing",
23+
":zipkin"

0 commit comments

Comments
 (0)