8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は デジタル創作サークル UniProject Advent Calendar 2025 および Node.js Advent Calendar 2025 の 11 日目の記事です。

はじめに

皆さん、ログをきちんと収集していますか?
(私は以前、十分に収集していませんでした。)

ログを適切に出力すると非常に有用です。ユースケースの例としては、

  • 監査
  • デバッグ時
  • 障害対応

などが挙げられます。

単にログを出すだけでも役立ちますが、特にデバッグや障害対応時には「どこの処理で発生したログか」が重要になります。また、「どの処理からどの関数が呼ばれたか」といった情報もログに含まれていると便利です。

本記事では Node.js において、処理ごとに一意の ID を付与してログに記録する方法をご紹介します。

AsyncLocalStorage とは

AsyncLocalStorage とは、ある一連の非同期処理の間でのみ参照可能な値を保持する仕組みです。今はイメージしづらいかもしれませんが、この記事を読み進めると理解できるようになります。

前提条件

本記事では以下の環境で動作確認を行っています。

  • Node.js 22
  • Pino v10

必要なパッケージ

サンプルとして Web サーバーを立ち上げるため、express をインストールします。

npm i express

サンプルコード

今回は、以下のコードにログを追加する形で説明を進めます。

処理の流れは次の通りです。

  1. /test にリクエストが届く
  2. サンプル関数 getTestData() が実行される
  3. サンプルデータが返却される

request_id を発行する

request_id とは、一連の処理で共有する一意の値のことです。

例えば、

  1. function GET() が呼ばれる
  2. request_id として hogehoge を生成する
  3. function GET() 内で sum() という関数を呼ぶ
  4. sum() 側でも request_id を参照できる

このようにすることで、一連の処理に対するログに共通の ID を付与することができます。

今回は UUID を使用します。request_id は処理が始まったタイミング(今回はリクエスト受信時、app.get() の冒頭)で生成します。

+ import crypt from "crypto";

app.get('/test', async (req: express.Request, res: express.Response) => {
  try {
+   const request_id = crypto.randomUUID()
    const result = await getTestData();
    res.json(result);
  } catch (error) {
    next(error);
  }
});

AsyncLocalStorage に保存する

前述の通り、今回は app.get() からも getTestData() からも request_id を参照したいため、AsyncLocalStorage に値を保存します。

import crypt from "crypto";
+ import { AsyncLocalStorage } from "async_hooks";

+ const ALStorage = new AsyncLocalStorage();

app.get('/test', async (req: express.Request, res: express.Response) => {
  try {
    const request_id = crypto.randomUUID()
+   ALStorage.run({request_id});
    const result = await getTestData();
    res.json(result);
  } catch (error) {
    next(error);
  }
});

ログを出力する

まず app.get() 内では、そのままログを出力できます。

import crypt from "crypto";
import { AsyncLocalStorage } from "async_hooks";

const ALStorage = new AsyncLocalStorage();

app.get('/test', async (req: express.Request, res: express.Response) => {
  try {
    const request_id = crypto.randomUUID()
    ALStorage.run({request_id});
+   console.log(`[${request_id}] /test called`);
    const result = await getTestData();
    res.json(result);
  } catch (error) {
    next(error);
  }
});

次に getTestData() 側で出力する際、request_id を引き継ぎたいので AsyncLocalStorage から取得します。

const getTestData = async () => {
+ const store = ALStorage.getStore();
+ console.log(`[${store.request_id}] getTestData called`);
  await sleep(20);
+ console.log(`[${store.request_id}] getTestData finished`);
  return {
    data: "Success"
  }
};

これで一連の処理に対して共通の request_id を付与したログが出力されます。
実際に実行して挙動をご確認ください。

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?