異なる送信先へのログのルーティング
このガイドでは、カスタムプロセッサーを使用して、単一の ILogger から異なる OTLP エンドポイントにログをルーティングする方法を説明します。
これは、OpenTelemetry 仕様の補足ガイドラインに記載されているルーティングパターンに従っています。
なぜログをルーティングするのか
一部のシナリオでは、すべてのアプリケーションコードで同じ ILogger パイプラインを使用しつつ、特定のログを一方のバックエンドに送り、残りを別のバックエンドに送りたい場合があります。
たとえば、以下のようなケースです。
- 決済コンポーネントからのログは、専用の Collector エンドポイント(
OTLP2)に送信する。 - その他のすべてのログは、デフォルトのエンドポイント(
OTLP1)に送信する。
アプリケーションが複数の ILoggerFactory インスタンスを作成して、呼び出し元に適切なものを選ばせることができる場合は、かわりに専用パイプラインの使用を検討してください。
仕組み
ルーティングの判断は、各 LogRecord の CategoryName を検査することで、プロセッサーレベルで行われます。
カスタムプロセッサーは、カテゴリ名が設定された接頭辞で始まるかどうかを確認し、適切なエクスポートパイプラインにレコードを転送します。
ILogger (single pipeline)
|
v
LoggerProvider
|
v
RoutingProcessor (custom)
+-- CategoryName starts with prefix --> ExportProcessor -> OtlpLogExporter (OTLP2)
+-- otherwise --------------------------> ExportProcessor -> OtlpLogExporter (OTLP1)
- 2つの
OtlpLogExporterインスタンスが作成され、それぞれ異なるエンドポイントを指します。 - 各エクスポーターは
BatchLogRecordExportProcessorでラップされます。 - カスタムの
RoutingProcessorはBaseProcessor<LogRecord>を拡張し、OnEndをオーバーライドします。 ログレコードのCategoryNameが設定された接頭辞で始まるかどうかを確認し、どの内部プロセッサーにレコードを渡すかを決定します。 - ルーティングプロセッサーは
AddProcessorを介してLoggerProviderに登録されます。
実装
カスタムルーティングプロセッサー
RoutingProcessor は各ログレコードの CategoryName を検査し、2つの内部プロセッサーのいずれかに転送します。
また、ForceFlush、Shutdown、Dispose を委譲して、両方のエクスポートパイプラインが適切にドレインおよびクリーンアップされるようにします。
using OpenTelemetry;
using OpenTelemetry.Logs;
internal sealed class RoutingProcessor : BaseProcessor<LogRecord>
{
private readonly string categoryPrefix;
private readonly BaseProcessor<LogRecord> defaultProcessor;
private readonly BaseProcessor<LogRecord> paymentProcessor;
public RoutingProcessor(
string categoryPrefix,
BaseProcessor<LogRecord> defaultProcessor,
BaseProcessor<LogRecord> paymentProcessor)
{
this.categoryPrefix = categoryPrefix ?? throw new ArgumentNullException(nameof(categoryPrefix));
this.defaultProcessor = defaultProcessor ?? throw new ArgumentNullException(nameof(defaultProcessor));
this.paymentProcessor = paymentProcessor ?? throw new ArgumentNullException(nameof(paymentProcessor));
}
public override void OnEnd(LogRecord data)
{
if (data.CategoryName?.StartsWith(this.categoryPrefix, StringComparison.Ordinal) == true)
{
this.paymentProcessor.OnEnd(data);
}
else
{
this.defaultProcessor.OnEnd(data);
}
}
protected override bool OnForceFlush(int timeoutMilliseconds)
{
var result1 = this.defaultProcessor.ForceFlush(timeoutMilliseconds);
var result2 = this.paymentProcessor.ForceFlush(timeoutMilliseconds);
return result1 && result2;
}
protected override bool OnShutdown(int timeoutMilliseconds)
{
var result1 = this.defaultProcessor.Shutdown(timeoutMilliseconds);
var result2 = this.paymentProcessor.Shutdown(timeoutMilliseconds);
return result1 && result2;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.defaultProcessor.Dispose();
this.paymentProcessor.Dispose();
}
base.Dispose(disposing);
}
}
LoggerProvider へのルーティングプロセッサーの登録
LoggerProvider にルーティングプロセッサーを登録します。
以下の両方のロガーは同じ ILoggerFactory と LoggerProvider パイプラインを共有しますが、ログレコードはカテゴリ名に基づいて異なる OTLP 送信先にルーティングされます。
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
// 異なる送信先を指す2つの OTLP エクスポーターを作成する。
var otlpExporter1 = new OtlpLogExporter(new OtlpExporterOptions
{
Endpoint = new Uri("http://localhost:4317"), // OTLP 送信先 1
});
var otlpExporter2 = new OtlpLogExporter(new OtlpExporterOptions
{
Endpoint = new Uri("http://localhost:4318"), // OTLP 送信先 2
});
// 各エクスポーターを BatchLogRecordExportProcessor でラップする。
var defaultExportProcessor = new BatchLogRecordExportProcessor(otlpExporter1);
var paymentExportProcessor = new BatchLogRecordExportProcessor(otlpExporter2);
// ルーティングプロセッサーを構築する。カテゴリ名が
// "Payment." で始まるログは OTLP2 に送信され、それ以外は OTLP1 に送信される。
var routingProcessor = new RoutingProcessor(
categoryPrefix: "Payment.",
defaultProcessor: defaultExportProcessor,
paymentProcessor: paymentExportProcessor);
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.AddProcessor(routingProcessor);
// オプション: コンソールエクスポーターも追加して、すべてのログをローカルで確認できるようにする。
logging.AddConsoleExporter();
});
});
// 両方のロガーは同じ ILoggerFactory / LoggerProvider パイプラインを共有する。
var orderLogger = loggerFactory.CreateLogger("Order.Processing");
var paymentLogger = loggerFactory.CreateLogger("Payment.Processing");
orderLogger.LogInformation("Processing order {OrderId}.", "ORD-001"); // --> OTLP1
paymentLogger.LogInformation("Processing payment {PaymentId}.", "PAY-001"); // --> OTLP2
orderLogger.LogInformation("Order {OrderId} completed.", "ORD-001"); // --> OTLP1
// アプリケーション終了前にロガーファクトリーを破棄する。
// これにより、残りのログがフラッシュされ、ロギングパイプラインがシャットダウンされる。
loggerFactory.Dispose();
主な考慮事項
- ルーティング条件はログレコードごとに評価される。 ロジックは高速に保つこと——すべてのログ出力時に同期的に実行されます。
- ライフサイクル管理。
ルーティングプロセッサーが
ForceFlush、Shutdown、Disposeをすべての内部プロセッサーに委譲し、すべてのエクスポートパイプラインが適切にドレインおよびクリーンアップされるようにしてください。 - 単一パイプライン。
単一の
ILoggerFactoryとLoggerProviderがあるため、このアプローチはコンポーネントが複数のロガーを意識しない依存性注入と相性が良いです。
参考情報
- OpenTelemetry .NET リポジトリの完全な実行可能サンプル:
docs/logs/routing - OpenTelemetry 仕様の補足ガイドラインにおけるルーティング
- 専用パイプラインの設定——アプリケーションが複数の
ILoggerFactoryインスタンスを使用できる場合の代替手段
フィードバック
このページは役に立ちましたか?
Thank you. Your feedback is appreciated!
Please let us know how we can improve this page. Your feedback is appreciated!