pino で次のように呼び出した場合、出力される JSON ログとして正しいものはどれか。 logger.info({ userId: 42, action: 'login' }, 'user logged in');
解説
pino のロガーメソッドは第1引数にオブジェクト、第2引数にメッセージ文字列を取るシグネチャを持っています。第1引数のオブジェクトの各プロパティはログJSONのトップレベルにそのまま展開(マージ)され、第2引数の文字列は msg フィールドに入ります。実際の出力はおおよそ次のようになります:{'level':30,'time':1700000000000,'pid':123,'hostname':'app','userId':42,'action':'login','msg':'user logged in'}選択肢Aの「オブジェクトが無視される」は誤り、選択肢Cのように文字列に埋め込まれることもありません。選択肢Dの「meta フィールドにネスト」も pino のデフォルト挙動ではありません。ネストさせたい場合は明示的に { meta: { userId: 42 } } のように書く必要があります。なぜ pino は構造化ログをトップレベルに展開するのかpino は構造化ログ(structured logging)を前提に設計されたロガーです。構造化ログとは、ログを単なる文字列ではなく機械可読な JSON として出力する手法で、Datadog、CloudWatch Logs Insights、Loki といったログ基盤でフィールド単位での検索・集計・アラート設定ができるようになります。トップレベルにフラットに展開されることで、たとえば「userId が 42 のログだけ抽出」といったクエリが単純な userId:42 で書けます。深くネストされていると検索クエリも複雑になり、インデックスも効きにくくなるためです。引数の順番を間違えるとどうなるか初学者がよくやる間違いが、引数を逆にしてしまうケースです:// NG: 文字列を第1引数に置いてオブジェクトを第2引数にすると… logger.info('user logged in', { userId: 42 });この書き方では msg に文字列補間用のフォーマット引数として扱われ、構造化フィールドとして取り出せなくなります。「オブジェクトが先、メッセージが後」と覚えておくのが安全です。console.log の感覚で書くとハマりやすいポイントです。child logger でリクエストごとに文脈を付与する実務では logger.child({ requestId, userId }) で子ロガーを作るパターンも頻出です。子ロガーから出力されるすべてのログに自動で requestId と userId が付与されるため、Web リクエストやジョブ単位でログを追跡しやすくなります。Express や Fastify のミドルウェアで child logger を request オブジェクトに紐付けておけば、ハンドラ内では req.log.info(...) と書くだけで文脈付きの構造化ログが残せます。これが pino が高速ロガーであると同時に「本番運用で使いやすい」と評価される理由のひとつです。