この記事は デジタル創作サークル 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
サンプルコード
今回は、以下のコードにログを追加する形で説明を進めます。
処理の流れは次の通りです。
-
/testにリクエストが届く - サンプル関数
getTestData()が実行される - サンプルデータが返却される
request_id を発行する
request_id とは、一連の処理で共有する一意の値のことです。
例えば、
-
function GET()が呼ばれる -
request_idとしてhogehogeを生成する -
function GET()内でsum()という関数を呼ぶ -
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 を付与したログが出力されます。
実際に実行して挙動をご確認ください。