debug (TypeScript)
軽量デバッグ専用ライブラリのTypeScript対応版。型定義によりTypeScriptプロジェクトでの開発体験が向上。環境変数による柔軟な制御と、開発時のデバッグ情報表示に特化。多くのTypeScriptライブラリで内部使用。
debug
debugは、Node.jsとブラウザの両方で動作する軽量なデバッグユーティリティです。名前空間ベースのフィルタリング、自動的な色分け、環境変数による制御など、開発時のデバッグを効率化する機能を提供します。
主な特徴
- 名前空間ベースのデバッグ - モジュール単位でのログ制御
- 自動色分け - 名前空間ごとに自動的に色を割り当て
- 環境変数制御 - DEBUG環境変数で出力を制御
- 軽量 - 最小限の依存関係
- ユニバーサル - Node.jsとブラウザの両方で動作
インストール
npm install debug
# TypeScript型定義
npm install --save-dev @types/debug
基本的な使い方
シンプルな例
import debug from 'debug';
// デバッガーの作成
const log = debug('myapp:server');
const error = debug('myapp:error');
// メッセージの出力
log('サーバーが起動しました');
error('エラーが発生しました');
// 環境変数でデバッグを有効化
// DEBUG=myapp:* node app.js
名前空間の階層
import debug from 'debug';
// 階層的な名前空間
const dbDebug = debug('myapp:database');
const dbQuery = debug('myapp:database:query');
const dbConnection = debug('myapp:database:connection');
dbDebug('データベース初期化');
dbQuery('SELECT * FROM users');
dbConnection('接続プール作成');
// 環境変数での制御例
// DEBUG=myapp:database:* - データベース関連のみ
// DEBUG=myapp:* - アプリ全体
// DEBUG=* - すべて
名前空間の拡張
import debug from 'debug';
const log = debug('auth');
// extendを使用した名前空間の拡張
const logSign = log.extend('sign');
const logLogin = log.extend('login');
const logLogout = log.extend('logout');
log('認証モジュール初期化'); // auth 認証モジュール初期化
logSign('サインアップ処理開始'); // auth:sign サインアップ処理開始
logLogin('ログイン試行'); // auth:login ログイン試行
logLogout('ログアウト完了'); // auth:logout ログアウト完了
高度な使い方
フォーマッター
import debug from 'debug';
const log = debug('myapp:formatter');
// printf スタイルのフォーマット
log('ユーザー %s がログインしました', 'user123');
log('処理時間: %d ms', 150);
// オブジェクトの整形
const user = { id: 1, name: '田中', role: 'admin' };
log('ユーザー情報: %O', user); // 1行で表示
log('ユーザー詳細: %o', user); // 複数行で表示
// JSONフォーマット
log('JSON: %j', { status: 'ok', count: 42 });
カスタムフォーマッター
import debug from 'debug';
// カスタムフォーマッターの追加
debug.formatters.h = (v: any) => {
return v.toString('hex');
};
const log = debug('myapp:custom');
const buffer = Buffer.from('hello');
log('Buffer内容: %h', buffer); // 16進数で表示
条件付きデバッグ
import debug from 'debug';
const log = debug('myapp:conditional');
// デバッグが有効か確認
if (log.enabled) {
// 重い処理(デバッグが有効な場合のみ実行)
const expensiveData = calculateExpensiveOperation();
log('計算結果:', expensiveData);
}
// より簡潔な方法
log.enabled && log('デバッグモードで実行中');
出力先のカスタマイズ
import debug from 'debug';
import fs from 'fs';
const log = debug('myapp:custom-output');
// ファイルに出力
const logStream = fs.createWriteStream('debug.log', { flags: 'a' });
log.log = (...args: any[]) => {
logStream.write(args.join(' ') + '\n');
};
// または console.log を使用(デフォルトは console.error)
log.log = console.log.bind(console);
log('このメッセージはファイルに保存されます');
TypeScriptでの型安全な実装
型定義付きラッパー
import createDebug, { Debugger } from 'debug';
interface Logger {
debug: Debugger;
info: Debugger;
warn: Debugger;
error: Debugger;
}
function createLogger(namespace: string): Logger {
const base = createDebug(namespace);
return {
debug: base.extend('debug'),
info: base.extend('info'),
warn: base.extend('warn'),
error: base.extend('error')
};
}
// 使用例
const logger = createLogger('myapp:service');
logger.info('サービス開始');
logger.error('エラーが発生しました');
構造化ログ
import debug from 'debug';
class StructuredDebugger {
private debug: debug.Debugger;
constructor(namespace: string) {
this.debug = debug(namespace);
}
log(level: string, message: string, meta?: Record<string, any>): void {
if (!this.debug.enabled) return;
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
level,
message,
...meta
};
this.debug('%j', logEntry);
}
info(message: string, meta?: Record<string, any>): void {
this.log('INFO', message, meta);
}
error(message: string, error?: Error, meta?: Record<string, any>): void {
this.log('ERROR', message, {
...meta,
error: error ? {
name: error.name,
message: error.message,
stack: error.stack
} : undefined
});
}
}
// 使用例
const structuredLog = new StructuredDebugger('myapp:structured');
structuredLog.info('ユーザーログイン', { userId: 123, ip: '192.168.1.1' });
環境変数
DEBUG環境変数
# 特定の名前空間のみ有効化
DEBUG=myapp:server node app.js
# ワイルドカードの使用
DEBUG=myapp:* node app.js
# 複数の名前空間
DEBUG=myapp:server,myapp:database node app.js
# 除外パターン(-を使用)
DEBUG=myapp:*,-myapp:verbose node app.js
# すべて有効化
DEBUG=* node app.js
その他の環境変数
// 色を無効化
process.env.DEBUG_COLORS = 'false';
// 日付を非表示(非TTY環境)
process.env.DEBUG_HIDE_DATE = 'true';
// オブジェクトの検査深度
process.env.DEBUG_DEPTH = '10';
// 隠しプロパティを表示
process.env.DEBUG_SHOW_HIDDEN = 'true';
ブラウザでの使用
基本設定
// ブラウザ環境での使用
import debug from 'debug';
const log = debug('myapp:browser');
// localStorage でデバッグを有効化
localStorage.debug = 'myapp:*';
// または特定の名前空間のみ
localStorage.debug = 'myapp:browser';
log('ブラウザでのデバッグメッセージ');
Web Workerでの使用
// Web Worker内
import debug from 'debug';
const log = debug('myapp:worker');
// メインスレッドから設定を受け取る
self.addEventListener('message', (event) => {
if (event.data.debug) {
localStorage.debug = event.data.debug;
}
});
log('Worker内でのデバッグ');
パフォーマンスの最適化
遅延評価
import debug from 'debug';
const log = debug('myapp:performance');
// 悪い例: 常に文字列が構築される
function badExample(data: any[]) {
log(`配列サイズ: ${data.length}, 内容: ${JSON.stringify(data)}`);
}
// 良い例: デバッグが有効な場合のみ実行
function goodExample(data: any[]) {
if (log.enabled) {
log(`配列サイズ: ${data.length}, 内容: ${JSON.stringify(data)}`);
}
}
// より良い例: 関数を使用
function betterExample(data: any[]) {
log(() => `配列サイズ: ${data.length}, 内容: ${JSON.stringify(data)}`);
}
バッチ処理
import debug from 'debug';
class BatchDebugger {
private debug: debug.Debugger;
private buffer: string[] = [];
private flushInterval: number = 1000;
private timer?: NodeJS.Timeout;
constructor(namespace: string) {
this.debug = debug(namespace);
}
log(message: string): void {
if (!this.debug.enabled) return;
this.buffer.push(message);
if (!this.timer) {
this.timer = setTimeout(() => this.flush(), this.flushInterval);
}
}
flush(): void {
if (this.buffer.length > 0) {
this.debug('バッチログ:\n%s', this.buffer.join('\n'));
this.buffer = [];
}
this.timer = undefined;
}
}
ベストプラクティス
1. 意味のある名前空間
// 良い例
const log = debug('myapp:http:request');
const dbLog = debug('myapp:db:query');
const authLog = debug('myapp:auth:jwt');
// 悪い例
const log1 = debug('log1');
const debugger = debug('debug');
2. モジュール単位での設定
// logger.ts
import debug from 'debug';
export function createModuleLogger(moduleName: string) {
return {
trace: debug(`myapp:${moduleName}:trace`),
debug: debug(`myapp:${moduleName}:debug`),
info: debug(`myapp:${moduleName}:info`),
warn: debug(`myapp:${moduleName}:warn`),
error: debug(`myapp:${moduleName}:error`)
};
}
// userService.ts
import { createModuleLogger } from './logger';
const logger = createModuleLogger('userService');
export class UserService {
async createUser(data: any) {
logger.debug('ユーザー作成開始', data);
try {
// 処理
logger.info('ユーザー作成成功');
} catch (error) {
logger.error('ユーザー作成失敗', error);
throw error;
}
}
}
3. 開発/本番環境の切り替え
import debug from 'debug';
const isDevelopment = process.env.NODE_ENV === 'development';
// 開発環境では詳細なログを有効化
if (isDevelopment) {
debug.enable('myapp:*');
} else {
// 本番環境ではエラーのみ
debug.enable('myapp:*:error');
}
まとめ
debugライブラリは、シンプルながら強力なデバッグツールです。名前空間ベースのフィルタリング、環境変数による制御、自動的な色分けなど、開発効率を向上させる機能を提供します。特に大規模なアプリケーションでは、モジュール単位でのログ制御が可能なため、必要な情報だけを効率的に取得できます。TypeScriptとの組み合わせにより、型安全なロギングシステムを構築することも可能です。