メインコンテンツまでスキップ
バージョン: 0.14.0 (以前)

📝 ロギングユーティリティ

Nodeblocks SDK は、構造化ログのために Pino を事前構成したロギングセットアップを提供します。これらのユーティリティにより、見やすい整形と HTTP リクエスト/レスポンスロギングを伴う一貫したロギングをアプリケーション全体で実現できます。


🎯 概要​

ロギングユーティリティは Pino を使用した標準的なロギングセットアップを提供します。テストモード以外では、nodeblocksLogger は本番環境を含むすべての環境で、色付き出力のために pino-pretty を使用します。秘匿化を伴うハンドラーレベルのロギングについては、withLoggingを参照してください。

主な機能​

  • 事前構成済みロガー: pino-pretty で整形され、すぐに使える Pino ロガー(テスト以外)
  • HTTP ロギング: autoLogging: true を備えた pino-http による Express ミドルウェア
  • TypeScript サポート: エクスポートされる Logger 型エイリアス(pino.Logger)

このモジュールは nodeblocksLogger、nodeblocksHTTPLogger、Logger(型)の 3 つのシンボルをエクスポートします。


📝 基本ロギング​

nodeblocksLogger​

見やすい整形と色付き出力を備えた、事前構成済みの Pino ロガーです。

import { utils } from '@nodeblocks/backend-sdk';

const { nodeblocksLogger } = utils;

// 基本ロギング
nodeblocksLogger.info('Application started');
nodeblocksLogger.warn('Deprecated feature used');
nodeblocksLogger.error({ error: 'details' }, 'An error occurred');

// 構造化ログ
nodeblocksLogger.info({
message: 'User created',
userId: 'user-123',
timestamp: new Date().toISOString()
});

ログレベル​

import { utils } from '@nodeblocks/backend-sdk';

const { nodeblocksLogger } = utils;

// 利用できるログレベル
nodeblocksLogger.trace('Trace level - most detailed');
nodeblocksLogger.debug('Debug level - development info');
nodeblocksLogger.info('Info level - general information');
nodeblocksLogger.warn('Warn level - warnings');
nodeblocksLogger.error('Error level - errors');
nodeblocksLogger.fatal('Fatal level - critical errors');

🌐 HTTP ロギング​

nodeblocksHTTPLogger​

Express アプリケーション用の HTTP リクエスト/レスポンスロギングミドルウェアです。

import express from 'express';
import { utils } from '@nodeblocks/backend-sdk';

const { nodeblocksHTTPLogger } = utils;

const app = express();

// HTTP ロギングミドルウェアを追加
app.use(nodeblocksHTTPLogger);

// ルート
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});

HTTP ログ出力​

デフォルトの pino-http シリアライザーでは、HTTP ロガーは通常次を記録します。

  • リクエスト: メソッド、URL、リモートアドレス、選択されたヘッダー(例: user-agent)
  • レスポンス: ステータスコード、レスポンスタイム
  • メッセージ: 例: "request completed"

リクエスト本文はデフォルトでは記録されません。本文のロギングが必要な場合はカスタムシリアライザーを使用してください。

出力例(概算):

{
"req": {
"id": "req-1",
"method": "GET",
"url": "/api/users",
"headers": {
"user-agent": "Mozilla/5.0...",
"accept": "application/json"
}
},
"res": {
"statusCode": 200,
"responseTime": 45
},
"msg": "request completed"
}

🔧 高度な使用方法​

以下の例は SDK ユーティリティを基にした説明用のパターンであり、追加の SDK エクスポートではありません。自動秘匿化を伴うハンドラーログには、Handler Wrappers の withLogging を使用してください。

カスタムロガー構成​

import pino from 'pino';
import { utils } from '@nodeblocks/backend-sdk';

const { nodeblocksLogger } = utils;

// デフォルトロガーを拡張
const customLogger = nodeblocksLogger.child({
service: 'user-service',
version: '1.0.0'
});

// カスタムロガーを使用
customLogger.info({
port: 3000,
environment: process.env.NODE_ENV
}, 'Service started');

ハンドラー内のロガー​

ハンドラー例では他の SDK モジュールのシンボル(primitives の RouteHandlerPayload、neverthrow の ok など)を使用します。

import { ok } from 'neverthrow';
import { handlers, primitives, utils } from '@nodeblocks/backend-sdk';

const { nodeblocksLogger } = utils;
const { mergeData } = handlers;
type RouteHandlerPayload = primitives.RouteHandlerPayload;

const createUserHandler = async (payload: RouteHandlerPayload) => {
const { params, logger } = payload;

// ペイロードのロガーを使用。なければデフォルトへフォールバック
const log = logger || nodeblocksLogger;

log.info({
email: params.requestBody?.email,
timestamp: new Date().toISOString()
}, 'Creating user');

try {
// ハンドラーロジック
const user = await createUser(params.requestBody);

log.info({
userId: user.id,
email: user.email
}, 'User created successfully');

return ok(mergeData(payload, { user }));
} catch (error) {
log.error({
error: error instanceof Error ? error.message : String(error),
email: params.requestBody?.email
}, 'Failed to create user');

throw error;
}
};

条件付きロギング​

import { utils } from '@nodeblocks/backend-sdk';

const { nodeblocksLogger } = utils;

const getConditionalLogger = (isDevelopment: boolean) => {
// 子ロガーはバインディングを追加するだけで、ログレベルは変更しない。
return nodeblocksLogger.child({
environment: isDevelopment ? 'development' : 'production',
});
};

// 使用方法
const logger = getConditionalLogger(process.env.NODE_ENV === 'development');
logger.debug('Debug info, if enabled by the logger level');

パフォーマンスロギング​

import { utils } from '@nodeblocks/backend-sdk';

const { nodeblocksLogger } = utils;

const performanceHandler = async (payload: RouteHandlerPayload) => {
const startTime = Date.now();
const log = payload.logger || nodeblocksLogger;

log.info('Starting performance-critical operation');

try {
// コストの高い操作
const result = await expensiveOperation();

const duration = Date.now() - startTime;
log.info({
duration,
resultSize: result.length
}, 'Operation completed');

return ok(mergeData(payload, { result }));
} catch (error) {
const duration = Date.now() - startTime;
log.error({
duration,
error: error instanceof Error ? error.message : String(error)
}, 'Operation failed');

throw error;
}
};

📊 ロガー構成​

デフォルト構成(テスト以外)​

Vitest または NODE_ENV === 'test' のもとで実行していない場合、nodeblocksLogger は次のように構成されます。

{
enabled: !process.env.DISABLE_LOGGING,
level: 'trace',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
singleLine: false,
translateTime: 'SYS:standard',
},
},
}

本番環境用に別の分岐はありません。テスト以外の環境は常に pino-pretty トランスポートワーカーを使用します。

テスト環境​

process.env.VITEST が設定されているか、NODE_ENV === 'test' の場合、ロガーはテスト向けセットアップに切り替わります。

pino(
{
base: null,
enabled: !process.env.DISABLE_LOGGING,
level: 'error',
},
pretty({
colorize: true,
singleLine: false,
translateTime: 'SYS:standard',
})
)

これはトランスポートワーカースレッドを使用する代わりに、見やすい出力を標準出力へ直接書き込みます(Vitest で表示されます)。

ロギングを無効化する​

どちらの分岐でも、DISABLE_LOGGING 環境変数を設定するとロガー出力を無効化できます(enabled: false)。

カスタムロガー(パターン例)​

SDK は本番用に NODE_ENV で構成を切り替えません。本番環境で構造化 JSON ロギングを使用するには、独自の pino インスタンスを作成してください。

import pino from 'pino';

const customLogger = pino({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
// 本番: 構造化 JSON を標準出力へ(pino-pretty なし)
});

🔗 ロガー型​

Logger 型​

pino.Logger の TypeScript 型エイリアスです。実行時の値ではなく型エクスポートなので、utils から分割代入しないでください。

import { utils } from '@nodeblocks/backend-sdk';

type Logger = utils.Logger;

// または SDK 内部と同様に pino から直接インポート:
import type { Logger } from 'pino';

const useLogger = (logger: Logger) => {
logger.info('Using typed logger');
logger.error({ error: 'details' }, 'Error with logger');
};

// 使用方法
useLogger(utils.nodeblocksLogger);

ペイロード内のロガー​

ハンドラーはペイロードでロガーを受け取ることがあります。withLogging が注入し、defService は注入しません。

interface RouteHandlerPayload {
// ...その他のプロパティ
logger?: Logger;
}

📐 ベストプラクティス​

1. 構造化ログ​

// ✅ 良い例: コンテキストを伴う構造化ログ
nodeblocksLogger.info({
message: 'User action performed',
userId: 'user-123',
action: 'login',
timestamp: new Date().toISOString(),
metadata: { ip: '192.168.1.1' }
});

// ❌ 避ける: 単純な文字列ロギング
nodeblocksLogger.info('User logged in'); // コンテキストがない

2. エラーロギング​

// ✅ 良い例: 包括的なエラーロギング
try {
await riskyOperation();
} catch (error) {
nodeblocksLogger.error({
message: 'Operation failed',
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
context: { userId: 'user-123' }
});
throw error;
}

// ❌ 避ける: 最小限のエラーロギング
try {
await riskyOperation();
} catch (error) {
nodeblocksLogger.error('Error'); // 詳細がない
}

3. パフォーマンスロギング​

// ✅ 良い例: パフォーマンス監視
const startTime = Date.now();
const result = await expensiveOperation();
const duration = Date.now() - startTime;

nodeblocksLogger.info({
message: 'Operation completed',
duration,
resultSize: result.length,
operation: 'expensiveOperation'
});

4. 条件付きロギング​

// ✅ 良い例: 環境に応じたロギング
import pino from 'pino';

const logLevel = process.env.NODE_ENV === 'development' ? 'debug' : 'info';
// ロガーインスタンスでレベルを構成する。`child({ level })` はバインディングを追加するだけ。
const logger = pino({ level: logLevel });

logger.debug('Debug info only in development');
logger.info('Info in all environments');

🔗 関連項目​