🌟

Cloud LoggingのログにOpenTelemetryのspan情報を埋め込む方法の確認

に公開

経緯

前回、TracerAgentによってCloud Loggingに送信していたログにSpan情報が付与されなくなった話をしました。せっかくなので、OpenTelemetryとCloud Loggingが連携する仕組みを詳しく確認してみましょう。

結論

OpenTelemetryでトレースを取得している場合、Cloud Loggingとの連携に特別な設定は必要ありません。

ログを書き込む際にSpanが開始されていれば、必要なトレース情報が自動的にメタデータに追加される仕組みになっています。

もうちょっと詳しく

Cloud Loggingの基本的な使い方

まず、Cloud Loggingでログを送信する基本的な方法を確認します。

import {Logging} from '@google-cloud/logging-min'

const logging = new Logging();
const log = logging.log('my-test');
const metadata = {
  resource: {type: 'global'},
  severity: 'INFO',
};
const entry = log.entry(metadata, 'message');
log.write(entry);

entry を用意して write するだけのシンプルな構造です。

トレース情報を手動で付与する方法

Cloud Loggingでトレース情報を認識させるには、以下のプロパティをメタデータに追加します:

  • trace: トレースID
  • spanId: スパンID
  • trace_sampled: サンプリングフラグ

これらはLogEntry型のプロパティとして定義されています。

具体的には以下のように書くことで、トレースが付与されていると認識されます。

const entry = log.entry({
  ...metadata,
  trace: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
  spanId: 'aaaaaaaaaaaaaaaa',
  trace_sampled: false,
}, 'message');

OpenTelemetryコンテキストからの自動取得

OpenTelemetryのSpanコンテキストからCloud Loggingのプロパティへの変換処理は、ライブラリ内部で実装されています。

この実装はgetContextFromOtelContext関数で確認できます。

https://github.com/googleapis/nodejs-logging/blob/main/src/utils/context.ts#L165-L181

メタデータ付与のタイミング

Cloud Loggingライブラリは、Entry#toStructuredJSONEntry#toSJONでログエントリをJSON形式に変換する際に、OpenTelemetryのコンテキストから自動的にトレース情報を取得して付与します。

Entry#extractTraceContext
https://github.com/googleapis/nodejs-logging/blob/9d1d480406c4d1526c8a7fafd9b18379c0c7fcea/src/entry.ts#L336-L346

Entry#toStructuredJSON
https://github.com/googleapis/nodejs-logging/blob/9d1d480406c4d1526c8a7fafd9b18379c0c7fcea/src/entry.ts#L255-L329

Entry#toJSON
https://github.com/googleapis/nodejs-logging/blob/9d1d480406c4d1526c8a7fafd9b18379c0c7fcea/src/entry.ts#L215-L249

これらのメソッドは以下のタイミングで呼び出されます:

logSync.writeではtoStructuredJSONが直接呼び出されます。
https://github.com/googleapis/nodejs-logging/blob/9d1d480406c4d1526c8a7fafd9b18379c0c7fcea/src/log-sync.ts#L423-L452

log.writeではdecorateEntries経由でよびだされます。
https://github.com/googleapis/nodejs-logging/blob/9d1d480406c4d1526c8a7fafd9b18379c0c7fcea/src/log.ts#L970-L982

https://github.com/googleapis/nodejs-logging/blob/9d1d480406c4d1526c8a7fafd9b18379c0c7fcea/src/log.ts#L1045-L1057

動作確認コード

以下は実際にOpenTelemetryとCloud Loggingを連携させて、自動的にトレース情報が付与されることを確認するコードです:

import { Logging } from '@google-cloud/logging-min'
import { NodeTracerProvider, SimpleSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node'
import { trace } from '@opentelemetry/api';

// OpenTelemetryの設定
const tracerProvider = new NodeTracerProvider({
  spanProcessors: [
    new SimpleSpanProcessor(new ConsoleSpanExporter()),
  ]
});
tracerProvider.register();

// Cloud Loggingの設定
const logging = new Logging({ projectId: 'my-dev'});
const log = logging.log('my-test'); // logging.logSyncとすると標準出力でも確認できます
const metadata = {
  resource: {type: 'global'},
  severity: 'INFO',
};

const tracer = trace.getTracer('sample-app');

// Spanを開始してログを出力
await tracer.startActiveSpan('first', async (span) => {
  const entry = log.entry(metadata, {
    message: 'entry',
  });
  
  // ログを出力(自動的にトレース情報が付与される)
  log.write(entry);
  
  // エントリの内容を確認(トレース情報が含まれているか確認)
  console.log('Entry JSON:', entry.toJSON({}, 'my-dev'));
  
  span.end();
});

await tracerProvider.shutdown();

このコードを実行すると、entry.toJSON()の出力にトレース情報が自動的に含まれていることが確認できます。

まとめ

OpenTelemetryとCloud Loggingは特別な設定なしに連携することが分かりました。

前回の記事の環境ではOpenTelemetryの設定は行われているため、トレースが付与されていなかった原因は、middlewareが削除されたことでスパンを計測していない範囲になったという仮説が立てられそうです。

株式会社Spir

Discussion