📝 ログユーティリティ
Nodeblocks SDKは、構造化ログのためのPino事前設定ログセットアップを提供します。これらのユーティリティは、Pretty フォーマッティングとHTTPリクエスト/レスポンスログを使用して、アプリケーション全体で一貫したログを提供します。
🎯 概要
ログユーティリティは、高性能なNode.jsログであるPinoを使用した標準化されたログセットアップを提供します。開発用のPretty フォーマッティングと本番環境対応のログをデプロイメント用に提供します。
主要機能
- 事前設定ログ: Pretty フォーマッティング付きのすぐに使えるPinoログ
- HTTPログ: 自動リクエスト/レスポンスログミドルウェア
- 構造化ログ: 簡単なパースのためのJSONベースログ
- パフォーマンス: 最小限のオーバーヘッドで高性能ログ
- TypeScriptサポート: ログ統合のための完全な型安全性
📝 基本ログ
nodeblocksLogger
Pretty フォーマッティングとカラー化出力付きの事前設定Pinoログ。
import { utils } from '@nodeblocks/backend-sdk';
const { nodeblocksLogger } = utils;
// 基本ログ
nodeblocksLogger.info('アプリケーションが開始されました');
nodeblocksLogger.warn('非推奨機能が使用されました');
nodeblocksLogger.error('エラーが発生しました', { error: '詳細' });
// 構造化ログ
nodeblocksLogger.info({
message: 'ユーザーが作成されました',
userId: 'user-123',
timestamp: new Date().toISOString()
});
ログレベル
import { utils } from '@nodeblocks/backend-sdk';
const { nodeblocksLogger } = utils;
// 利用可能なログレベル
nodeblocksLogger.trace('トレースレベル - 最も詳細');
nodeblocksLogger.debug('デバッグレベル - 開発情報');
nodeblocksLogger.info('情報レベル - 一般情報');
nodeblocksLogger.warn('警告レベル - 警告');
nodeblocksLogger.error('エラーレベル - エラー');
nodeblocksLogger.fatal('致命的レベル - 重大なエラー');
構造化ログの例
import { utils } from '@nodeblocks/backend-sdk';
const { nodeblocksLogger } = utils;
// ユーザーアクティビティのログ
nodeblocksLogger.info({
event: 'user_login',
userId: 'user-123',
ip: '192.168.1.1',
userAgent: 'Mozilla/5.0...',
timestamp: new Date().toISOString()
});
// API呼び出しのログ
nodeblocksLogger.debug({
event: 'api_call',
method: 'POST',
endpoint: '/api/users',
responseTime: 150,
statusCode: 201
});
// エラーのログ
nodeblocksLogger.error({
event: 'database_error',
error: error.message,
stack: error.stack,
collection: 'users',
operation: 'findOne'
});
🌐 HTTPログ
nodeblocksHTTPLogger
Expressリクエスト/レスポンス用の自動ログミドルウェア。
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: [] });
});
app.listen(3000);
HTTPログ出力
{
"level": "info",
"time": "2024-01-15T10:30:00.000Z",
"req": {
"id": "req-123",
"method": "GET",
"url": "/api/users",
"headers": {
"user-agent": "Mozilla/5.0...",
"accept": "application/json"
},
"remoteAddress": "192.168.1.1"
},
"res": {
"statusCode": 200,
"headers": {
"content-type": "application/json"
}
},
"responseTime": 145,
"msg": "GET /api/users 200 145ms"
}
カスタムHTTPログ設定
import { utils } from '@nodeblocks/backend-sdk';
const { nodeblocksHTTPLogger, nodeblocksLogger } = utils;
// カスタム設定でHTTPログを設定
const customHTTPLogger = nodeblocksHTTPLogger({
logger: nodeblocksLogger,
genReqId: () => `req-${Date.now()}`, // カスタムリクエストID生成
customLogLevel: (req, res) => {
// エラーレスポンスは 'error' レベル
if (res.statusCode >= 400) return 'error';
// 成功レスポンスは 'info' レベル
return 'info';
}
});
app.use(customHTTPLogger);
🔧 カスタムログ設定
開発環境設定
import { createLogger } from 'pino';
// 開発用のPretty ログ
const developmentLogger = createLogger({
level: 'debug',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'SYS:yyyy-mm-dd HH:MM:ss',
ignore: 'pid,hostname'
}
}
});
// 使用例
developmentLogger.info('開発モードでアプリケーションが開始されました');
本番環境設定
import { createLogger } from 'pino';
// 本番用の構造化ログ
const productionLogger = createLogger({
level: 'info',
formatters: {
time: (timestamp) => ({ time: new Date(timestamp).toISOString() }),
level: (label) => ({ level: label })
},
redact: {
paths: ['password', 'token', 'apiKey', 'secret'],
censor: '[REDACTED]'
}
});
// 使用例
productionLogger.info({
message: 'アプリケーションが開始されました',
port: process.env.PORT,
environment: process.env.NODE_ENV
});
🎯 ハンドラーでのログ使用
ハンドラー内でのログ
import { utils } from '@nodeblocks/backend-sdk';
import { ok, err } from 'neverthrow';
const { nodeblocksLogger } = utils;
export const createUserHandler = async (payload: RouteHandlerPayload) => {
const { params } = payload;
nodeblocksLogger.info({
event: 'user_creation_started',
requestId: payload.context.requestId,
email: params.requestBody.email
});
try {
const user = createBaseEntity(params.requestBody);
const result = await context.db.users.insertOne(user);
nodeblocksLogger.info({
event: 'user_created_successfully',
userId: user.id,
requestId: payload.context.requestId
});
return ok(mergeData(payload, { userId: user.id }));
} catch (error) {
nodeblocksLogger.error({
event: 'user_creation_failed',
error: error.message,
stack: error.stack,
requestId: payload.context.requestId
});
return err(new NodeblocksError(500, 'ユーザーの作成に失敗しました'));
}
};
条件付きログ
import { utils } from '@nodeblocks/backend-sdk';
const { nodeblocksLogger } = utils;
export const processDataHandler = async (payload: RouteHandlerPayload) => {
const isDebugMode = process.env.DEBUG === 'true';
if (isDebugMode) {
nodeblocksLogger.debug({
event: 'processing_data',
inputSize: payload.params.requestBody.data.length,
timestamp: new Date().toISOString()
});
}
// データ処理ロジック
const result = await processData(payload.params.requestBody.data);
nodeblocksLogger.info({
event: 'data_processed',
outputSize: result.length,
processingTime: Date.now() - startTime
});
return ok(mergeData(payload, { result }));
};
📊 ログ集約とモニタリング
ログ集約の設定
import { createLogger } from 'pino';
// ELKスタック用の設定
const elkLogger = createLogger({
level: 'info',
formatters: {
time: () => ({ '@timestamp': new Date().toISOString() }),
level: (label) => ({ log_level: label.toUpperCase() })
},
messageKey: 'message',
base: {
service: 'nodeblocks-api',
version: process.env.APP_VERSION,
environment: process.env.NODE_ENV
}
});
// Fluentd用の設定
const fluentdLogger = createLogger({
level: 'info',
transport: {
target: 'pino-socket',
options: {
address: process.env.FLUENTD_HOST,
port: process.env.FLUENTD_PORT,
mode: 'tcp'
}
}
});
アプリケーションメトリクス
import { utils } from '@nodeblocks/backend-sdk';
const { nodeblocksLogger } = utils;
// パフォーマンスメトリクス
const logPerformanceMetrics = (operation: string, duration: number) => {
nodeblocksLogger.info({
metric_type: 'performance',
operation,
duration_ms: duration,
timestamp: new Date().toISOString()
});
};
// ビジネスメトリクス
const logBusinessMetrics = (event: string, data: any) => {
nodeblocksLogger.info({
metric_type: 'business',
event,
data,
timestamp: new Date().toISOString()
});
};
// 使用例
const timedOperation = async (operationName: string, operation: () => Promise<any>) => {
const start = Date.now();
try {
const result = await operation();
logPerformanceMetrics(operationName, Date.now() - start);
return result;
} catch (error) {
logPerformanceMetrics(operationName, Date.now() - start);
throw error;
}
};
🔒 ログセキュリティ
機密データの編集
import { createLogger } from 'pino';
const secureLogger = createLogger({
level: 'info',
redact: {
paths: [
'password',
'token',
'apiKey',
'secret',
'creditCard',
'ssn',
'*.password',
'*.token',
'headers.authorization',
'body.password'
],
censor: '[REDACTED]'
}
});
// 使用例
secureLogger.info({
event: 'user_login',
user: {
email: 'user@example.com',
password: 'secret123' // 自動的に [REDACTED] になる
},
headers: {
authorization: 'Bearer token123' // 自動的に [REDACTED] になる
}
});
カスタム編集
const customRedactLogger = createLogger({
level: 'info',
serializers: {
req: (req) => ({
method: req.method,
url: req.url,
headers: {
...req.headers,
authorization: req.headers.authorization ? '[REDACTED]' : undefined,
cookie: req.headers.cookie ? '[REDACTED]' : undefined
}
}),
user: (user) => ({
id: user.id,
email: user.email,
// passwordフィールドを除外
})
}
});
🎯 ベストプラクティス
1. 構造化ログの使用
// 良い - 構造化ログ
nodeblocksLogger.info({
event: 'user_created',
userId: 'user-123',
email: 'user@example.com',
timestamp: new Date().toISOString()
});
// 悪い - 非構造化ログ
nodeblocksLogger.info('User user-123 with email user@example.com created');
2. 適切なログレベル
// 良い - 適切なレベル分け
nodeblocksLogger.debug('詳細なデバッグ情報'); // 開発時のみ
nodeblocksLogger.info('一般的な情報'); // 通常動作
nodeblocksLogger.warn('潜在的な問題'); // 注意が必要
nodeblocksLogger.error('エラーが発生'); // 修正が必要
// 悪い - 不適切なレベル
nodeblocksLogger.error('ユーザーが正常にログイン'); // エラーレベルで正常操作
3. 機密データの保護
// 良い - 機密データを編集
nodeblocksLogger.info({
event: 'authentication',
userId: user.id,
// パスワードやトークンは含めない
});
// 悪い - 機密データを含める
nodeblocksLogger.info({
event: 'authentication',
user: {
password: 'secret123', // ログに露出される危険
token: 'jwt-token'
}
});
➡️ 次へ
コンポジションユーティリティに戻って関数コンポジションパターンを復習するか、サービスについて学習してユーティリティがサービス全体でどのように使用されるかを理解しましょう!