> Source: https://www.ymotongpoo.com/works/oteps/logs/otep-0150/


# OTEP-0150: ロギングライブラリSDKプロトタイプ仕様

OpenTelemetryロギングライブラリSDKのプロトタイプを作成するための、初期ドラフト仕様です。

ステータス：プロトタイピング用。
プロトタイピングが完了するまでは、OpenTelemetry仕様のメインには統合しないでください。

## 動機 {#motivation}

これはOpenTelemetryロギングライブラリSDK仕様のドラフト提案です。
この提案の目的は、ロギングライブラリSDKがどのようなものになるかについての初期的な理解を示すことです。

私たちは、この提案に基づいて、いくつかの言語でプロトタイプを作成したいと考えています。
プロトタイプから得られた知見によって、この提案を洗練させ、最終的には提案されたアプローチをOpenTelemetry仕様の一部にすることができます。

このOTEPで提案されているアプローチは、OTEP自体が承認された後にOpenTelemetry仕様にマージされることを意図したものではありません。
私たちはまず、プロトタイピングを成功させたいのです。
このOTEPは何よりもまず、プロトタイプを作成する方法についての仕様とガイドラインです。

この仕様は、OpenTelemetryロギングライブラリSDKが、言語ごとのサードパーティ製ロギングライブラリの拡張機能の作成者や、[OpenTelemetryの方式](../../../specification/logs/README.md)でログを生成したいエンドユーザーに対して、どのようにその機能を公開するかを定義します。

この仕様は、ある程度までOpenTelemetryの[Trace SDK](../../../specification/trace)を反映するSDK要素を定義します。
これにより、トレースとログにわたるOpenTelemetry仕様および実装の均一性と一貫性が保証されます。
明確にするため、このドキュメントの定義では、該当する箇所でTraceにおける類似概念を参照しています。

このドキュメントにおけるインターフェースとメソッドの説明は、意図的に非常に簡潔にしてあります。
詳細で正確な説明は、このOTEPがログ仕様のPRとして提出される際に、Trace仕様から借用される予定です。
私はこのドキュメントの説明をできるだけ短くすることで、レビューを簡単かつ迅速にできるようにしました。

## 仕様 {#specification}

既存の多くのロギングライブラリには、ログレコードがどのようにエンコードされ、その宛先に届けられるかをカスタマイズできる、何らかの拡張メカニズムがあります（たとえばLog4jのAppenderやzapcoreのCore）。
OpenTelemetryロギングライブラリSDKは、そのような拡張機能によって、OpenTelemetryの形式でログを発行するために使用されることを意図しています。

注：このドキュメントで提案する機能は、SDKパッケージになります。
エンドユーザーが呼び出し可能なロギングAPIを持つことを決定した場合、将来的にロギング関連のAPIパッケージが追加される可能性があります。
それまでは、このSDKパッケージから呼び出し可能な関数やメソッドは、ロギングライブラリでのみ使用されることを意図しており、エンドユーザーが使用することは意図されて **おらず**、OpenTelemetry APIパッケージでは公開され **ません** 。

SDKには以下が含まれます。

### LogEmitterProvider {#logemitterprovider}

メソッド：

- LogEmitterの取得。
  計装ライブラリの名前とバージョンを受け取り、その計装ライブラリに関連付けられたLogEmitterを返します。
- Shutdown。
- ForceFlush。

LogEmitterProviderは、起動時に、Resourceと、LogProcessor / LogExporterパイプラインに関連付けて設定できます。

### LogEmitter {#logemitter}

メソッド：

- Emit(LogRecord)。
  ログレコードを発行します。
  LogRecordと、LogEmitterに関連付けられたResourceおよび計装ライブラリは、読み取り可能なLogDataに変換され、SDKおよび設定済みのLogProcessorとLogExporterを通じてプッシュされます。
  呼び出し元は、該当する場合、呼び出しを行う前にトレースコンテキストに関連するフィールド（TraceId、SpanId、TraceFlags）を設定しておくことが期待されます。
  未解決の課題：ログプロセッサーやエクスポーターが必要に応じて使用できるように、Baggageも渡す必要があるでしょうか。

  注：言語によっては、LogRecordというデータ型を持つことを避け、代わりにより慣用的なビルダーパターンを使ってログレコードを準備し発行することを選ぶ場合があります（たとえば[Javaでの議論](https://github.com/open-telemetry/opentelemetry-java/pull/3759#discussion_r738019425)を参照）。

- Flush。

### LogRecord {#logrecord}

フィールドの一覧については、LogRecordの[データモデル](../../../specification/logs/data-model.md)を参照してください。

未解決の課題：ロギングライブラリがそれを供給できるように、LoggerNameフィールドをデータモデルに追加すべきでしょうか。
[未解決のPR](https://github.com/open-telemetry/opentelemetry-specification/pull/1236)があります。

### LogProcessor {#logprocessor}

プラグインインターフェースです。
SpanProcessorの類似物です。
ログレコードの発行アクションにフックするためのインターフェースです。

メソッド：

- Emit(LogData)。
  ログレコードが処理・エクスポートされる準備ができたときに呼び出されます。
- Shutdown。
- ForceFlush。

組み込み実装：SimpleLogProcessor、BatchLogProcessor。

### LogData {#logdata}

読み取り可能なLogRecordのデータに、関連付けられたResourceおよびInstrumentationLibraryを加えたものです。
SpanDataの類似物です。

### LogExporter {#logexporter}

プラグインインターフェースです。
SpanExporterの類似物です。
プロトコル固有のエクスポーターを実装できるようにし、OpenTelemetry SDKにプラグインしてログデータの送信をサポートできるようにします。

メソッド：

- Export(batch)。
  LogDataのバッチをエクスポートします。
- Shutdown。

## 使い方 {#usage}

### Log4Jスタイルのアペンダーの作り方 {#how-to-create-log4j-style-appender}

Appender実装を使うことで、OpenTelemetryロギングライブラリのエクスポーターを介してログレコードを発行できるようになります。
このアプローチは、ログの転送方式を変更しても問題ないアプリケーションで一般的に使われ、[サポートされているログ収集アプローチの1つ](../../../specification/logs/README.md#direct-to-collector)です。

Appender実装は通常、起動時にグローバルなLogEmitterProviderからLogEmitterを取得し、その後、アプリケーションから受け取ったログレコードに対してLogEmitter.Emitを呼び出します。

暗黙的なContextを持つ言語では、Appenderは発行前に、Context APIを呼び出して現在の[アクティブなSpan](../../../specification/trace/api.md#context-interaction)を取得し、LogRecordのTraceId、SpanId、TraceFlagsの各フィールドを設定できます。
ロギングライブラリには、コンテキストをログレコードに注入する別の方法（たとえばLog4jにおけるMDC）が用意されている場合もあります。

![Appender](appender.png)

同じアプローチは、たとえば以下にも使用できます。

- Handlerを作成することによるPythonロギングライブラリ。
- Coreインターフェースを実装することによるGoのzapロギングライブラリ。
  なお、Goには暗黙的なContextが存在しないため、アクティブなSpanを取得して使用することはできません。

Appenderは、OpenTelemetryのメンテナーによってOpenTelemetryの言語ライブラリの中に作成することも、同様の拡張メカニズムをサポートする任意のロギングライブラリのためにサードパーティによって作成することもできます。
本仕様は、各OpenTelemetry言語ライブラリが、少なくとも1つの最も人気のあるロギングライブラリについて、そのまま使えるAppender実装を含めることを推奨します。

### ファイルへのロギング {#logging-to-file}

OpenTelemetryがサポートする、ログを発行・収集するための可能なアプローチの1つは、中間ファイルを介する方法です。
LogEmitterProviderを設定する際、OTLP Fileエクスポーターを使用して、OTLP JSONまたはOTLP protobufバイナリ形式でファイルまたは標準出力にログを書き込むべきです。

![Logging to File](otlp-file.png)

### OTLPネットワーク宛先への直接のロギング {#logging-directly-to-otlp-network-destination}

このアプローチは、ファイルへのロギングと同じですが、OTLP/gRPCまたはOTLP/HTTPのエクスポーター実装が使用される点が異なります。

### 暗黙的なコンテキストの注入 {#implicit-context-injection}

Contextが暗黙的に利用可能な場合（たとえばJava）、ログライブラリの拡張機能は、各ログレコードについて同期的にOpenTelemetry Context APIを呼び出し、発行前にスパンコンテキストのフィールドをLogRecordに注入することで、それを取得できます。

一部のロギングライブラリには、コンテキスト情報をログレコードに注入するために特別に調整されたメカニズムがあります。
そのようなメカニズムの一例が、Log4jのMDCです。
利用可能な場合、そのようなメカニズムは、スパンコンテキストを取得してログレコードに注入するための好ましい場所となることがあります。
なぜなら、通常、ログレコードが非同期的に発行される場合でも、コンテキストの取得が正しく機能するようにできるからです（そうでない場合、誤った暗黙的なコンテキストが取得される結果になることがあります）。

### 明示的なコンテキスト {#explicit-context}

Contextを明示的に渡す必要がある言語（たとえばGo）では、トレースコンテキストがLogレコードに記録されるようにするために、エンドユーザーがコンテキストを取得し、明示的にロギングサブシステムに渡す必要があります。

これらの言語におけるロギングライブラリのOpenTelemetryサポートは、通常、ロガーラッパーという形で実装できます。
このラッパーは、スパンが作成されたときに一度だけコンテキストを取得し、その後、ラップされたロガーを使って通常の方法でログステートメントを実行します。
このラッパーは、取得したコンテキストをログレコードに注入する責任を負います。

この仕様は、実際のメカニズムが言語や使用される特定のロギングライブラリに依存するため、それがどのように達成されるかを正確には定義しません。
いずれにせよ、ラッパーはTrace Context APIを使用して現在のアクティブなスパンを取得することが期待されます。

Goのzapロギングライブラリについて、これがどのように行えるかの[例](https://docs.google.com/document/d/15vR7D1x2tKd7u3zaTF0yH1WaHkUr2T4hhr7OyiZgmBg/edit#heading=h.4xuru5ljcups)を参照してください。

### カスタムLogExporter {#custom-logexporter}

LogExporter実装は、カスタムプロトコルでログを送信するために、OpenTelemetryロギングライブラリにプラグインできます。

OTLP/gRPC、OTLP/HTTP、OTLP/FileのログエクスポーターはOpenTelemetryロギングライブラリにそのまま使える状態で提供されます。

![Custom Exporter](custom-exporter.png)

### カスタムLogProcessor {#custom-logprocessor}

LogProcessor実装は、エクスポートされる前にログをカスタム処理させるために、OpenTelemetryロギングライブラリにプラグインできます。

SimpleおよびBatchのプロセッサーは、OpenTelemetryロギングライブラリによってそのまま使える状態で提供されるべきです。

![Custom Processor](custom-processor.png)

## 先行技術と代替技術 {#prior-art-and-alternatives}

この仕様は、可能な限りOpenTelemetryのTraceおよびAPI仕様に依拠しています。
これは、既存のロギングライブラリを調査した上で書かれています（参考文献を参照）。

代替アプローチとして、OpenTelemetryのTraceライブラリにより近い形で、APIパッケージとSDKパッケージへの伝統的な分離を行う案も検討されました。
しかし、この代替アプローチは、人為的に均一性を強制しようとするものであり、その結果、ログの観点からはより劣った、一貫性の低いソリューションになってしまい、また、将来のエンドユーザー向けロギングAPIの導入をより複雑にしてしまうことがわかりました。

## 未解決の課題 {#open-questions}

- BaggageをLogEmitterのEmitメソッドに渡すかどうかを決定する。
  たとえば、Spring Cloud Sleuthは現在、slf4jのMDCを介してSleuthのBaggageをログレコードに追加できるようにしています。
  この機能についての[Springのドキュメント](https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/htmlsingle/#features-baggage)を参照してください。

- [LoggerNameの追加](https://github.com/open-telemetry/opentelemetry-specification/pull/1236)について最終決定を行う。

## 将来の可能性 {#future-possibilities}

将来的には、エンドユーザーが呼び出し可能なOpenTelemetryロギングAPI仕様を追加することもあり得ます。

## 参考文献 {#references}

- [David Poncelow](https://docs.google.com/document/d/15vR7D1x2tKd7u3zaTF0yH1WaHkUr2T4hhr7OyiZgmBg/edit#)によるロギングライブラリ構造の概要
- Log4Jの[拡張ガイド](https://logging.apache.org/log4j/2.x/manual/extending.html)。
- Pythonの[ロギングハンドラー](https://docs.python.org/3/library/logging.handlers.html)。
- Zapの[Core](https://pkg.go.dev/go.uber.org/zap@v1.16.0/zapcore#Core)。
</content>

