LogTape 0.7.0で導入される暗黙的コンテキストの紹介

2024/10/29に公開

LogTape 0.7.0のリリースをお知らせできることを嬉しく思います。この新バージョンでは、アプリケーション全体のログに文脈情報を簡単に追加できる、暗黙的コンテキストという強力な新機能を導入しています。

暗黙的コンテキスト(implict contexts)とは?

例えば、アプリケーションでHTTPリクエストを処理しているとき、リクエストの処理中に生成されるすべてのログメッセージにリクエストIDを含めたいとします。コードベースのどこでログが生成されるかに関係なく、これを実現したい場合です。

暗黙的コンテキスト以前は、以下のような方法が必要でした:

  1. すべての関数呼び出しでリクエストIDを引数として渡す
  2. 各モジュールでコンテキスト付きの新しいロガーを作成する
  3. またはグローバル変数を使用する(これには様々な問題があります)

暗黙的コンテキストを使用すると、リクエストハンドラの開始時点でコンテキストを設定するだけで、その実行コンテキスト内のすべてのログメッセージに自動的にその情報が含まれるようになります。以下が簡単な例です:

function handleRequest(requestId: string) {
  withContext({ requestId }, () => {
    // この関数内や、この関数が呼び出す任意の関数内のログメッセージに
    // 自動的にrequestIdが含まれます
    processRequest();
  });
}

function processRequest() {
  // requestIdを明示的に渡す必要はありません
  getLogger("processor").info(
    "リクエストを処理中: {requestId}"
  );
}

どのように動作するのか?

暗黙的コンテキストは、実行時環境のコンテキストローカルストレージメカニズム(Node.jsのAsyncLocalStorageなど)を使用して、コードの実行全体を通じて文脈情報を維持します。これにより、非同期操作をまたいでも適切にコンテキストが維持されます。

アプリケーションで暗黙的コンテキストを有効にするには、LogTapeをコンテキストローカルストレージで設定する必要があります:

import { AsyncLocalStorage } from "node:async_hooks";
import { configure } from "@logtape/logtape";

await configure({
  // ... その他の設定 ...
  contextLocalStorage: new AsyncLocalStorage(),
});

ネストされたコンテキストと優先順位

暗黙的コンテキストの強力な機能の1つは、ネストできることです。コンテキストをネストすると、内側のコンテキストは外側のコンテキストから値を継承し、必要に応じて上書きすることができます:

function handleRequest(requestId: string) {
  withContext({ requestId, stage: "request" }, () => {
    // ここでは stage は "request" です
    processUser(1234);
  });
}

function processUser(userId: number) {
  withContext({ userId, stage: "user" }, () => {
    // ここでは stage は "user" ですが、requestId は引き続き利用可能です
    getLogger("processor").info(
      "ユーザー {userId} を処理中、リクエストID: {requestId}"
    );
  });
}

コンテキスト値の解決には、以下の明確な優先順位があります:

  1. ログメッセージ内の明示的なプロパティが最も優先される
  2. Logger.with() で設定された明示的コンテキストが次に優先される
  3. withContext() で設定された暗黙的コンテキストが最も優先順位が低い

ランタイムのサポート

2024年10月現在、暗黙的コンテキストは以下の環境でサポートされています:

  • Node.js
  • Deno
  • Bun

Webブラウザは、TC39 Async Context提案の実装を待っているため、まだ暗黙的コンテキストをサポートしていません。

ユースケース

暗黙的コンテキストは、以下のような場面で特に価値を発揮します:

  1. リクエストの追跡: リクエストID、ユーザーID、セッションIDをリクエスト内のすべてのログに追加
  2. トランザクションの監視: 複数の操作にわたってトランザクションIDを追跡
  3. エラーコンテキスト: エラーログに常に関連する文脈情報が含まれることを保証
  4. パフォーマンスモニタリング: 複数の操作にわたるタイミング情報の追加
  5. テナントコンテキスト: マルチテナントアプリケーションでのテナント情報の追跡

ベストプラクティス

暗黙的コンテキストを使用する際は、以下のベストプラクティスを考慮してください:

  1. 実行コンテキスト全体に本当に属する情報に暗黙的コンテキストを使用する
  2. コンテキストデータは軽量に保つ - 実行全体を通じて保持されることを忘れずに
  3. アプリケーション全体で意味のある一貫したキー名を使用する
  4. TypeScriptを使用してコンテキスト構造の一貫性を確保することを検討する
  5. アプリケーションで期待されるコンテキスト構造をドキュメント化する

移行ガイド

すでにLogTapeを使用している場合、暗黙的コンテキストを使用するためのアップグレードは簡単です:

  1. LogTape 0.7.0にアップデート
  2. LogTapeの設定にコンテキストローカルストレージを追加
  3. コンテキストが手動で渡されている箇所を特定
  4. 適切な境界でwithContext()呼び出しに置き換え

結論

LogTape 0.7.0の暗黙的コンテキストは、コードを散らかすことなく、またコールスタックを通じてコンテキストを手動で渡すことなく、ログに文脈情報を追加する強力な方法を提供します。これは特に、Webサービス、API、その他の操作間でコンテキストを追跡することが重要なアプリケーションで価値を発揮します。

この機能を使ってアプリケーションのロギングと可観測性をどのように改善するのか、皆様のフィードバックをお待ちしています。ぜひ試してみてください!

より詳細な情報については、暗黙的コンテキストの完全なドキュメント(英語)をご覧ください。

Discussion