これは、なにをしたくて書いたもの?
久しぶりのOpenTelemetryまわりを見てみようかなということで、今まで触ってこなかったログを見てみることにしました。
今回はAppender Instrumentation for Logbackを使ってOpenTelemetry Collectorにログを送信してみます。
OpenTelemetryとログ
OpenTelemetryのログに関するコンセプトはこちら。
ログはタイムスタンプ付きのテキストレコードで、構造化(推奨)または非構造化されています。オプションとしてメタデータを
持ちます。
A log is a timestamped text record, either structured (recommended) or unstructured, with optional metadata.
OpenTelemetryはログを作成するためのAPIやSDKを定義しません。OpenTelemetryログはロギングフレームワークや
インフラストラクチャーコンポーネントが持つ、すでに存在するログになります。
OpenTelemetry does not define a bespoke API or SDK to create logs. Instead, OpenTelemetry logs are the existing logs you already have from a logging framework or infrastructure component.
OpenTelemetryはログを作成するためのAPIやSDKは提供しませんが、ログとトレースを自動的に関連付けるためのいくつかの
コンポーネントを使い、OpenTelemetry SDKや自動instrumentationを提供します。
OpenTelemetry SDKs and autoinstrumentation utilize several components to automatically correlate logs with traces.
OpenTelemetryのログサポートは、すでに使用しているであろう既存のものと完全に互換性があるように設計されており、
それらの追加のコンテキストでラップする機能と、様々なソース間でログをパース、操作して共通の形式とするための
ツールキットを提供するものです。
OpenTelemetry’s support for logs is designed to be fully compatible with what you already have, providing capabilities to wrap those logs with additional context and a common toolkit to parse and manipulate logs into a common format across many different sources.
つまり、OpenTelemetryのログに関するAPIはアプリケーションが直接利用するものではなさそうですね。そしてトレースと
紐付けることが重要視されているようです。
OpenTelmetryのログに関するコンポーネントはこちら。
- Log Record
- イベントのデータを表すもの
- 特定の型と意味を持つ名前付きのトップレベルフィールドと、任意の値と型のリソースと属性フィールドの2つで構成される
- データモデル定義はこちら
- Log Record Exporter
- Log Recordをコンシューマーに送信する
- コンシューマーには標準出力、OpenTelemetry Collector、任意のオープンソースやベンダーのバックエンドを利用可能
- Logger
- Log Recordを作成するもの
- LoggerはLogger Providerにより作成される
- Logger Provider
- Loggerのファクトリー
- 通常は1度初期化され、ライフサイクルはアプリケーションと一致する
- Logger Providerの初期化にはResourceやExporterの初期化も含まれる
- Log Appender/Bridge
Logs / OpenTelemetry logging components
ここで出てくるログのコンポーネントは、ロギングフレームワークを開発する人向けであることがわかります。
つまり、通常のロギングフレームワークを使うと、その裏でこのようなコンポーネントを使ってコンシューマーにLog Recordを
送るということですね。
OpenTelemetry Javaとログ
ここでJavaに関するOpenTelemetry APIやSDKのドキュメントを見てみます。
Instrumentation ecosystem / Log instrumentation
OpenTelemetryのログ用のコンポーネントはアプリケーション開発者が直接利用するものではなく、
既存のロギングフレームワークを使用して出力するログをOpenTelemetryにブリッジするための機能を作成するためのものです。
While the LoggerProvider / Logger APIs are structurally similar to the equivalent trace and metric APIs, they serve a different use case. As of now, LoggerProvider / Logger and associated classes represent the Log Bridge API, which exists to write log appenders to bridge logs recorded through other log APIs / frameworks into OpenTelemetry. They are not intended for end user use as a replacement for Log4j / SLF4J / Logback / etc.
ここで、OpenTelemetryのログinstrumentationを使うアプローチは次の2つがあるとされています。
- Collectorにログを直接送信する
- ファイルまたは標準出力経由でログを送信する
この2つのアプローチの特徴は、こんな感じです。
- Collector(コンシューマー)にログを直接送信する(Instrumentation ecosystem / Log instrumentation/ Direct to collector)
- ファイルまたは標準出力経由でログを送信するInstrumentation ecosystem / Log instrumentation/ Via file or stdout
今回はLogbackのAppenderからOpenTelemetry Collectorにログを送信するので、「Bridge Logback into OpenTelemetry」を
使うことになります。
Bridge Logback into OpenTelemetry(Appender Instrumentation for Logback)
Bridge Logback into OpenTelemetry(Appender Instrumentation for Logback)はこちらです。
LogbackのAppenderとして使うことで、コンシューマーへログを送信できます。
<appender name="OpenTelemetry" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender"> </appender>
OpenTelemetry SDKのセットアップが必要になりますが、
OpenTelemetrySdk openTelemetrySdk = // Configure OpenTelemetrySdk // Find OpenTelemetryAppender in logback configuration and install openTelemetrySdk OpenTelemetryAppender.install(openTelemetrySdk);
手動で構成する場合はこちらを参照します。
Manage Telemetry with SDK | OpenTelemetry
自動構成に任せる場合はこちらですね。基本的には自動構成することになるでしょう。
Configure the SDK | OpenTelemetry
というわけで、使っていってみましょう。
準備
今回の環境はこちら。
$ java --version openjdk 21.0.7 2025-04-15 OpenJDK Runtime Environment (build 21.0.7+6-Ubuntu-0ubuntu124.04) OpenJDK 64-Bit Server VM (build 21.0.7+6-Ubuntu-0ubuntu124.04, mixed mode, sharing) $ mvn --version Apache Maven 3.9.10 (5f519b97e944483d878815739f519b2eade0a91d) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.7, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "6.8.0-64-generic", arch: "amd64", family: "unix"
OpenTelemetry Collectorをインストールする
アプリケーションを作成する前に、OpenTelemetry Collectorをインストールしましょう。
$ curl -LO https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.130.0/otelcol-contrib_0.130.0_linux_amd64.tar.gz $ tar xf otelcol-contrib_0.130.0_linux_amd64.tar.gz
Install the Collector | OpenTelemetry
バージョン。
$ ./otelcol-contrib --version otelcol-contrib version 0.130.0
設定はこちらを参考にしておきます。
Exporterとしてはdebugのみ、パイプラインはLogのみとしました。今回はこれくらいしか使わないので。
onfig.yaml
receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: exporters: debug: verbosity: detailed #verbosity: normal #verbosity: basic extensions: health_check: pprof: zpages: service: extensions: [health_check, pprof, zpages] pipelines: logs: receivers: [otlp] processors: [batch] exporters: [debug]
起動。
$ ./otelcol-contrib --config=config.yaml
これでOpenTelemetry Collectorの準備はできました。
アプリケーション側の準備
Maven依存関係などはこちら。
<properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-bom</artifactId> <version>1.52.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-instrumentation-bom</artifactId> <version>2.18.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-instrumentation-bom-alpha</artifactId> <version>2.18.0-alpha</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-logging</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <exclusions> <exclusion> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-okhttp</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-jdk</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.17</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.5.18</version> </dependency> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-logback-appender-1.0</artifactId> </dependency>
BOMはこちらを参照して設定しています。
Intro to OpenTelemetry Java | OpenTelemetry
<dependencyManagement> <dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-bom</artifactId> <version>1.52.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-instrumentation-bom</artifactId> <version>2.18.0</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-instrumentation-bom-alpha</artifactId> <version>2.18.0-alpha</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Bridge Logback into OpenTelemetry(Appender Instrumentation for Logback)はこちらです。BOMとしては
opentelemetry-instrumentation-bom-alphaに含まれています。
<dependency> <groupId>io.opentelemetry.instrumentation</groupId> <artifactId>opentelemetry-logback-appender-1.0</artifactId> </dependency>
こちらはOpenTelemetry SDKを自動構成するモジュールです。
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId> </dependency>
opentelemetry-sdk-extension-autoconfigureがあればopentelemetry-apiとopentelemetry-sdkは明示的に含めなくてもいいのですが、
なんとなくということで。
こちらはログのExporterと、送信するプロトコルをgRPCからhttp/protobufに切り替えるために使っています。
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-logging</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <exclusions> <exclusion> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-okhttp</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-jdk</artifactId> </dependency>
通常、こういうことをせずにgRPCを使えばいいと思うのですが、今回はここでハマったのでこういう処置になりました。
Bridge Logback into OpenTelemetry(Appender Instrumentation for Logback)を使って、OpenTelemetry Collectorにログを送信する
それでは、Bridge Logback into OpenTelemetry(Appender Instrumentation for Logback)を使ってOpenTelmetry Collectorに
ログを送信してみます。
アプリケーションの雛形はこんな感じにしておきます。
src/main/java/org/littlewings/logback/opentelemetry/App.java
package org.littlewings.logback.opentelemetry; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ServiceAttributes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class App { public static void main(String... args) { // あとで Logger logger = LoggerFactory.getLogger(App.class); logger.info("Hello SLF4J with OpenTelemetry!"); logger.warn("Hello Logback with OpenTelemetry!"); } } }
手動構成する
最初はOpenTelemetry SDKを手動構成する場合から。こんな感じになります。
public static void main(String... args) { Resource resource = Resource.getDefault().toBuilder() .put(ServiceAttributes.SERVICE_NAME, "app") .build(); try (SdkLoggerProvider sdkLoggerProvider = SdkLoggerProvider.builder() .setResource(resource) .addLogRecordProcessor(SimpleLogRecordProcessor.create( OtlpHttpLogRecordExporter.builder() .setEndpoint("http://localhost:4318/v1/logs") .build())) .build(); OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() .setLoggerProvider(sdkLoggerProvider) .build()) { OpenTelemetryAppender.install(openTelemetrySdk); Logger logger = LoggerFactory.getLogger(App.class); logger.info("Hello SLF4J with OpenTelemetry!"); logger.warn("Hello Logback with OpenTelemetry!"); } }
参考にしているのはこちら。
Manage Telemetry with SDK | OpenTelemetry
https://github.com/open-telemetry/opentelemetry-java-examples/tree/main/http
https://github.com/open-telemetry/opentelemetry-java-examples/tree/main/log-appender
今回のプログラムの場合、起動してすぐに終了するのでSdkLoggerProviderやOpenTelemetrySdkをクローズしないと
ログがOpenTelemetry Collectorまで送信されないので(溜め込まれて送信待ちになります)注意しましょう。
OpenTelemetry SDKを設定したら、Bridge Logback into OpenTelemetry(Appender Instrumentation for Logback)に
インストールします。
OpenTelemetryAppender.install(openTelemetrySdk);
Lobackの設定はこちら。
src/main/resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern> %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n </pattern> </encoder> </appender> <appender name="opentelemetry" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender"> </appender> <root level="INFO"> <appender-ref ref="console"/> <appender-ref ref="opentelemetry"/> </root> </configuration>
OpenTelemetryAppenderを設定してはいますが、OpenTelemetryAppender#install(OpenTelemetrySdk)を行わないと
ログがコンシューマーに送信されません。
これでアプリケーションを実行してみます。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.logback.opentelemetry.App
すると、OpenTelemetry Collector側ではこんな出力が得られます。
2025-07-20T16:06:34.063+0900 info Logs {"resource": {"service.instance.id": "60289890-88eb-4554-b01b-82b6b2b193b1", "service.name": "otelcol-contrib", "service.version": "0.130.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs", "resource logs": 2, "log records": 2}
2025-07-20T16:06:34.064+0900 info ResourceLog #0
Resource SchemaURL:
Resource attributes:
-> service.name: Str(app)
-> telemetry.sdk.language: Str(java)
-> telemetry.sdk.name: Str(opentelemetry)
-> telemetry.sdk.version: Str(1.52.0)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope org.littlewings.logback.opentelemetry.App
LogRecord #0
ObservedTimestamp: 2025-07-20 07:06:33.941853461 +0000 UTC
Timestamp: 2025-07-20 07:06:33.941695759 +0000 UTC
SeverityText: WARN
SeverityNumber: Warn(13)
Body: Str(Hello Logback with OpenTelemetry!)
Trace ID:
Span ID:
Flags: 0
ResourceLog #1
Resource SchemaURL:
Resource attributes:
-> service.name: Str(app)
-> telemetry.sdk.language: Str(java)
-> telemetry.sdk.name: Str(opentelemetry)
-> telemetry.sdk.version: Str(1.52.0)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope org.littlewings.logback.opentelemetry.App
LogRecord #0
ObservedTimestamp: 2025-07-20 07:06:33.91585833 +0000 UTC
Timestamp: 2025-07-20 07:06:33.911750168 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Hello SLF4J with OpenTelemetry!)
Trace ID:
Span ID:
Flags: 0
{"resource": {"service.instance.id": "60289890-88eb-4554-b01b-82b6b2b193b1", "service.name": "otelcol-contrib", "service.version": "0.130.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs"}
このあたりがResourceにしていた設定ですね。
Resource attributes:
-> service.name: Str(app)
-> telemetry.sdk.language: Str(java)
-> telemetry.sdk.name: Str(opentelemetry)
-> telemetry.sdk.version: Str(1.52.0)
この部分。
Resource resource = Resource.getDefault().toBuilder()
.put(ServiceAttributes.SERVICE_NAME, "app")
.build();
ログメッセージやレベルなど。
ObservedTimestamp: 2025-07-20 07:06:33.941853461 +0000 UTC Timestamp: 2025-07-20 07:06:33.941695759 +0000 UTC SeverityText: WARN SeverityNumber: Warn(13) Body: Str(Hello Logback with OpenTelemetry!) ObservedTimestamp: 2025-07-20 07:06:33.91585833 +0000 UTC Timestamp: 2025-07-20 07:06:33.911750168 +0000 UTC SeverityText: INFO SeverityNumber: Info(9) Body: Str(Hello SLF4J with OpenTelemetry!)
よく見ると出力順は入れ替わっているのですが、Timestamp(イベント発生時刻)やObservedTimestamp(イベント観測時刻)は
順番どおりなので大丈夫でしょう。Timestampが守られていればいいのだと思いますが。
自動構成する
次はOpenTelemetry SDKを自動構成して実行します。
自動構成する場合は、こんな感じでだいぶシンプルになります。
public static void main(String... args) { try (OpenTelemetrySdk openTelemetrySdk = AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk()) { OpenTelemetryAppender.install(openTelemetrySdk); Logger logger = LoggerFactory.getLogger(App.class); logger.info("Hello SLF4J with OpenTelemetry!"); logger.warn("Hello Logback with OpenTelemetry!"); } }
OpenTelemetryAppender#installは必要です。
参考にしたのはこちら。
Configure the SDK | OpenTelemetry
logback.xmlは手動構成の時と同じものを使います。
また、実行前に以下のように環境変数を設定します。
$ export OTEL_TRACES_EXPORTER=none $ export OTEL_METRICS_EXPORTER=none $ export OTEL_LOGS_EXPORTER=otlp $ export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf $ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 $ export OTEL_SERVICE_NAME=app
トレースとメトリクスは使わないので無効にします。というか無効にしないとgRPCでOpenTelemetry Collectorに接続しようと
します。
また今回の構成の場合、ログ送信に使うプロトコルをhttp/protobufにする必要があります。
$ export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf
OTEL_LOGS_EXPORTERはデフォルト値を明示的に指定して、OTEL_EXPORTER_OTLP_ENDPOINTは
プロトコルにhttp/protobufを使う場合は4318(gRPCの場合は4317)なのですがこちらも指定しておきます。
このあたりも以下に載っています。
Configure the SDK | OpenTelemetry
実行。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.logback.opentelemetry.App
結果。
2025-07-20T16:32:11.893+0900 info Logs {"resource": {"service.instance.id": "60289890-88eb-4554-b01b-82b6b2b193b1", "service.name": "otelcol-contrib", "service.version": "0.130.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs", "resource logs": 1, "log records": 2}
2025-07-20T16:32:11.893+0900 info ResourceLog #0
Resource SchemaURL:
Resource attributes:
-> service.name: Str(app)
-> telemetry.sdk.language: Str(java)
-> telemetry.sdk.name: Str(opentelemetry)
-> telemetry.sdk.version: Str(1.52.0)
ScopeLogs #0
ScopeLogs SchemaURL:
InstrumentationScope org.littlewings.logback.opentelemetry.App
LogRecord #0
ObservedTimestamp: 2025-07-20 07:32:11.773433885 +0000 UTC
Timestamp: 2025-07-20 07:32:11.769007298 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Hello SLF4J with OpenTelemetry!)
Trace ID:
Span ID:
Flags: 0
LogRecord #1
ObservedTimestamp: 2025-07-20 07:32:11.777225417 +0000 UTC
Timestamp: 2025-07-20 07:32:11.777105834 +0000 UTC
SeverityText: WARN
SeverityNumber: Warn(13)
Body: Str(Hello Logback with OpenTelemetry!)
Trace ID:
Span ID:
Flags: 0
{"resource": {"service.instance.id": "60289890-88eb-4554-b01b-82b6b2b193b1", "service.name": "otelcol-contrib", "service.version": "0.130.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs"}
結果はほとんど変わらないので説明を省略します。
こんなところでしょうか。
オマケ: gRPCを使っていない理由
ところで今回はプロトコルをhttp/protobufにしていますが、デフォルトはgRPCです。どうしてこういう構成になったかと
いうと、OkHttp3まわりの扱いが微妙だったからです…。
自動構成の場合で実行してみます。
依存関係は最終的にこうなっていますが
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <exclusions> <exclusion> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-okhttp</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-jdk</artifactId> </dependency>
ひとまずこれにしてみます。
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <!-- <exclusions> <exclusion> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-okhttp</artifactId> </exclusion> </exclusions> --> </dependency> <!-- <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-jdk</artifactId> </dependency> -->
これで以下の最低限の環境変数を設定して
$ export OTEL_TRACES_EXPORTER=none $ export OTEL_METRICS_EXPORTER=none $ export OTEL_LOGS_EXPORTER=otlp $ export OTEL_SERVICE_NAME=app
実行します。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.logback.opentelemetry.App
すると、okhttp3.RequestBodyが見つからないという例外に悩まされることになります…。
[WARNING]
java.lang.NoClassDefFoundError: okhttp3/RequestBody
at io.opentelemetry.exporter.sender.okhttp.internal.OkHttpGrpcSenderProvider.createSender (OkHttpGrpcSenderProvider.java:23)
at io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder.build (GrpcExporterBuilder.java:236)
at io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder.build (OtlpGrpcLogRecordExporterBuilder.java:313)
at io.opentelemetry.exporter.otlp.internal.OtlpLogRecordExporterProvider.createExporter (OtlpLogRecordExporterProvider.java:77)
at io.opentelemetry.sdk.autoconfigure.internal.SpiHelper.lambda$loadConfigurable$0 (SpiHelper.java:76)
at io.opentelemetry.sdk.autoconfigure.internal.NamedSpiManager.tryLoadImplementationForName (NamedSpiManager.java:51)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent (ConcurrentHashMap.java:1708)
at io.opentelemetry.sdk.autoconfigure.internal.NamedSpiManager.getByName (NamedSpiManager.java:41)
at io.opentelemetry.sdk.autoconfigure.LogRecordExporterConfiguration.configureExporter (LogRecordExporterConfiguration.java:87)
at io.opentelemetry.sdk.autoconfigure.LogRecordExporterConfiguration.configureLogRecordExporters (LogRecordExporterConfiguration.java:62)
at io.opentelemetry.sdk.autoconfigure.LoggerProviderConfiguration.configureLoggerProvider (LoggerProviderConfiguration.java:49)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.configureSdk (AutoConfiguredOpenTelemetrySdkBuilder.java:553)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.buildImpl (AutoConfiguredOpenTelemetrySdkBuilder.java:488)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.lambda$build$12 (AutoConfiguredOpenTelemetrySdkBuilder.java:435)
at io.opentelemetry.api.GlobalOpenTelemetry.set (GlobalOpenTelemetry.java:130)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.build (AutoConfiguredOpenTelemetrySdkBuilder.java:433)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.initialize (AutoConfiguredOpenTelemetrySdk.java:37)
at org.littlewings.logback.opentelemetry.App.main (App.java:34)
at org.codehaus.mojo.exec.ExecJavaMojo.doMain (ExecJavaMojo.java:371)
at org.codehaus.mojo.exec.ExecJavaMojo.doExec (ExecJavaMojo.java:360)
at org.codehaus.mojo.exec.ExecJavaMojo.lambda$execute$0 (ExecJavaMojo.java:280)
at java.lang.Thread.run (Thread.java:1583)
Caused by: java.lang.ClassNotFoundException: okhttp3.RequestBody
at org.codehaus.mojo.exec.URLClassLoaderBuilder$ExecJavaClassLoader.loadClass (URLClassLoaderBuilder.java:211)
at java.lang.ClassLoader.loadClass (ClassLoader.java:526)
at io.opentelemetry.exporter.sender.okhttp.internal.OkHttpGrpcSenderProvider.createSender (OkHttpGrpcSenderProvider.java:23)
at io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder.build (GrpcExporterBuilder.java:236)
at io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporterBuilder.build (OtlpGrpcLogRecordExporterBuilder.java:313)
at io.opentelemetry.exporter.otlp.internal.OtlpLogRecordExporterProvider.createExporter (OtlpLogRecordExporterProvider.java:77)
at io.opentelemetry.sdk.autoconfigure.internal.SpiHelper.lambda$loadConfigurable$0 (SpiHelper.java:76)
at io.opentelemetry.sdk.autoconfigure.internal.NamedSpiManager.tryLoadImplementationForName (NamedSpiManager.java:51)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent (ConcurrentHashMap.java:1708)
at io.opentelemetry.sdk.autoconfigure.internal.NamedSpiManager.getByName (NamedSpiManager.java:41)
at io.opentelemetry.sdk.autoconfigure.LogRecordExporterConfiguration.configureExporter (LogRecordExporterConfiguration.java:87)
at io.opentelemetry.sdk.autoconfigure.LogRecordExporterConfiguration.configureLogRecordExporters (LogRecordExporterConfiguration.java:62)
at io.opentelemetry.sdk.autoconfigure.LoggerProviderConfiguration.configureLoggerProvider (LoggerProviderConfiguration.java:49)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.configureSdk (AutoConfiguredOpenTelemetrySdkBuilder.java:553)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.buildImpl (AutoConfiguredOpenTelemetrySdkBuilder.java:488)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.lambda$build$12 (AutoConfiguredOpenTelemetrySdkBuilder.java:435)
at io.opentelemetry.api.GlobalOpenTelemetry.set (GlobalOpenTelemetry.java:130)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder.build (AutoConfiguredOpenTelemetrySdkBuilder.java:433)
at io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.initialize (AutoConfiguredOpenTelemetrySdk.java:37)
at org.littlewings.logback.opentelemetry.App.main (App.java:34)
at org.codehaus.mojo.exec.ExecJavaMojo.doMain (ExecJavaMojo.java:371)
at org.codehaus.mojo.exec.ExecJavaMojo.doExec (ExecJavaMojo.java:360)
at org.codehaus.mojo.exec.ExecJavaMojo.lambda$execute$0 (ExecJavaMojo.java:280)
at java.lang.Thread.run (Thread.java:1583)
OkHttp3自体はopentelemetry-exporter-otlpによる依存関係に含まれるのですが、これではどうも動かないようです…。
[INFO] +- io.opentelemetry:opentelemetry-exporter-otlp:jar:1.52.0:compile [INFO] | +- io.opentelemetry:opentelemetry-exporter-otlp-common:jar:1.52.0:runtime [INFO] | | \- io.opentelemetry:opentelemetry-exporter-common:jar:1.52.0:runtime [INFO] | \- io.opentelemetry:opentelemetry-exporter-sender-okhttp:jar:1.52.0:runtime [INFO] | \- com.squareup.okhttp3:okhttp:jar:5.1.0:runtime [INFO] | +- org.jetbrains.kotlin:kotlin-stdlib:jar:2.2.0:runtime [INFO] | | \- org.jetbrains:annotations:jar:13.0:runtime [INFO] | \- com.squareup.okio:okio:jar:3.15.0:runtime [INFO] | \- com.squareup.okio:okio-jvm:jar:3.15.0:runtime
で、これをどうにかするのがめんどうになったので、デフォルトのhttp/protobufからhttp/protobufに切り替えることにしました。
依存関係はこのように整理したわけですね。
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <exclusions> <exclusion> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-okhttp</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-sender-jdk</artifactId> </dependency>
ちなみにトレースとメトリクスを切らない場合は、ここでもgRPCを使おうとして阻まれます…。
オマケでした。
おわりに
Bridge Logback into OpenTelemetry(Appender Instrumentation for Logback)を使って、OpenTelemetry Collectorにログを
送信してみました。
最初はいきなりLobackのinstrumentationから見始めたのでかなりてこずりましたが、OpenTelemetryのログの位置づけや
APIの立ち位置を見ていてだいぶ整理できました。
ドキュメントを見るって大事ですね(笑)。
今までOpenTelemetryといえばトレースやメトリクスに寄っていたので、ここにログも絡められるようになるとよいかなと
思います。