Log4js
Java Log4jライブラリにインスパイアされたNode.js向けロギングライブラリ。Java開発者に馴染みのあるAPIを提供し、Node.jsへの移行を容易にする。ログレベル、アペンダー、レイアウト等の概念をJavaScriptに移植した設計。
GitHub概要
log4js-node/log4js-node
A port of log4js to node.js
スター5,826
ウォッチ109
フォーク768
作成日:2010年1月17日
言語:JavaScript
ライセンス:Other
トピックス
javascriptlog4js-node
スター履歴
データ取得日時: 2025/7/17 10:01
ライブラリ
Log4js
概要
Log4jsはJavaScriptのためのLog4jスタイルロギングライブラリです。Java開発者にとって馴染み深いLog4jの設計哲学をJavaScriptエコシステムに移植し、階層的ロガー、複数のアペンダー、設定ベースの管理、ログレベル制御など企業グレードの機能を提供します。Node.jsとブラウザの両方で動作し、設定ファイルによる詳細な制御と豊富なアペンダーにより、複雑なログ要件への対応に優れています。
詳細
Log4js 6.9.1は2025年現在、Java背景を持つ開発チームや企業環境でのJavaScript開発において重要な選択肢として位置づけられています。Log4jの成熟したロギングパターンをJavaScriptに適用し、XML/JSON設定ファイルによる宣言的設定、カテゴリベースのロガー階層、多様なアペンダー(File、Console、Database、Network等)、レイアウトパターンによるフォーマット制御を実現。Node.jsサーバーサイドアプリケーションから複雑な企業システムまで、スケーラブルなログ管理を提供します。
主な特徴
- 階層的ロガーシステム: カテゴリベースのロガー継承と設定の階層管理
- 豊富なアペンダー: File、Console、Date File、SMTP、Database等の多様な出力先
- 設定ファイル主導: JSON/XMLによる宣言的な設定とホットリロード対応
- レイアウトパターン: Log4jライクなパターン文字列による柔軟なフォーマット制御
- クラスター対応: Node.js clusterモジュールとの統合によるマルチプロセス対応
- リアルタイム設定変更: ファイル監視による設定の動的更新
メリット・デメリット
メリット
- Java開発者にとって学習コストが極めて低い(Log4jの知識を直接活用可能)
- 設定ファイルによる運用時のログレベル変更とアペンダー追加が容易
- 企業環境で求められる詳細なログ制御と監査要件への対応力
- 複数のアペンダーによる同時出力(ファイル、データベース、メール等)の柔軟性
- Node.js clusterでのマルチプロセス環境における安定したログ管理
- 既存のLog4jベース監視ツールとの親和性
デメリット
- モダンなJavaScript開発者には設定が冗長に感じられる場合がある
- JSON/XML設定ファイルの理解と管理コストが必要
- Winstonやpinoと比較してパフォーマンスがやや劣る場合がある
- 構造化ログ(JSON出力)のサポートが他のライブラリより限定的
- 軽量なアプリケーションには機能がオーバースペックになる可能性
- ブラウザ環境では一部の機能(ファイル出力等)が制限される
参考ページ
書き方の例
インストールと基本セットアップ
# Log4jsのインストール
npm install log4js
# TypeScript用の型定義(最新版では自動的に含まれる)
npm install @types/log4js # TypeScript使用時のみ
// 最もシンプルな使用例
const log4js = require('log4js');
const logger = log4js.getLogger();
logger.level = 'debug';
logger.debug('Some debug messages');
logger.info('Information message');
logger.warn('Warning message');
logger.error('Error message');
logger.fatal('Fatal error message');
基本的なロギング操作(レベル、フォーマット)
const log4js = require('log4js');
// 基本設定
log4js.configure({
appenders: {
console: { type: 'console' },
file: { type: 'file', filename: 'app.log' }
},
categories: {
default: { appenders: ['console', 'file'], level: 'info' }
}
});
const logger = log4js.getLogger('application');
// 各レベルでのログ出力
logger.trace('Entering function');
logger.debug('Debug information');
logger.info('Application started');
logger.warn('This is a warning');
logger.error('Error occurred', new Error('Sample error'));
logger.fatal('Fatal system error');
// フォーマット付きログ
logger.info('User %s logged in with ID %d', 'john_doe', 12345);
logger.error('Database connection failed: %s', err.message);
// オブジェクトログ
logger.info('User data:', { id: 123, name: 'John', role: 'admin' });
// カテゴリ別ロガー
const dbLogger = log4js.getLogger('database');
const apiLogger = log4js.getLogger('api');
dbLogger.info('Database query executed');
apiLogger.warn('API rate limit approached');
高度な設定とカスタマイズ(アペンダー、レイアウト等)
const log4js = require('log4js');
const path = require('path');
// 複雑な設定例
log4js.configure({
appenders: {
// コンソール出力(カラー付き)
console: {
type: 'console',
layout: {
type: 'pattern',
pattern: '%[[%d{yyyy-MM-dd hh:mm:ss}] [%p] %c%] - %m'
}
},
// 一般ログファイル
fileAppender: {
type: 'file',
filename: 'logs/application.log',
maxLogSize: 10485760, // 10MB
backups: 5,
compress: true,
layout: {
type: 'pattern',
pattern: '[%d{ISO8601}] [%p] [%c] - %m'
}
},
// 日付ベースローテーション
dateFile: {
type: 'dateFile',
filename: 'logs/daily.log',
pattern: '.yyyy-MM-dd',
compress: true,
keepFileExt: true,
layout: {
type: 'pattern',
pattern: '[%d] [%p] [%c{2}] %m'
}
},
// エラー専用ログ
errorFile: {
type: 'file',
filename: 'logs/errors.log',
layout: {
type: 'pattern',
pattern: '[%d{ISO8601}] [%p] [%c] [%f{1}:%l] - %m%n%s'
}
},
// JSON形式ログ
jsonFile: {
type: 'file',
filename: 'logs/json.log',
layout: { type: 'json' }
},
// ネットワークアペンダー(Logstash等向け)
tcp: {
type: '@log4js-node/tcp',
host: 'localhost',
port: 5000,
layout: { type: 'json' }
}
},
categories: {
// デフォルトカテゴリ
default: {
appenders: ['console', 'fileAppender'],
level: 'info'
},
// データベース操作ログ
database: {
appenders: ['console', 'dateFile'],
level: 'debug'
},
// エラーログ
error: {
appenders: ['console', 'errorFile'],
level: 'error'
},
// API関連ログ
api: {
appenders: ['console', 'jsonFile', 'tcp'],
level: 'info'
},
// 本番環境用(コンソール出力なし)
production: {
appenders: ['fileAppender', 'errorFile'],
level: 'warn'
}
}
});
// カテゴリ別ロガーの取得
const defaultLogger = log4js.getLogger();
const dbLogger = log4js.getLogger('database');
const apiLogger = log4js.getLogger('api');
const errorLogger = log4js.getLogger('error');
// 使用例
defaultLogger.info('Application initialized');
dbLogger.debug('SQL query: SELECT * FROM users WHERE id = ?', [123]);
apiLogger.info('API request', { method: 'GET', url: '/users', status: 200 });
errorLogger.error('Uncaught exception', new Error('Sample error'));
構造化ログと現代的な可観測性対応
const log4js = require('log4js');
// 構造化ログ設定
log4js.configure({
appenders: {
// ELK Stack用JSON出力
elk: {
type: 'file',
filename: 'logs/elk.log',
layout: {
type: 'json',
separator: ',',
include: ['timestamp', 'level', 'category', 'data']
}
},
// Prometheus metrics用
metrics: {
type: 'file',
filename: 'logs/metrics.log',
layout: {
type: 'pattern',
pattern: 'timestamp=%d level=%p category=%c method=%X{method} duration=%X{duration}ms %m'
}
},
// カスタムJSONレイアウト
customJson: {
type: 'file',
filename: 'logs/custom.json',
layout: {
type: 'json',
static: {
service: 'my-service',
version: '1.0.0'
},
include: ['timestamp', 'level', 'category', 'data', 'static']
}
}
},
categories: {
default: { appenders: ['elk'], level: 'info' },
metrics: { appenders: ['metrics'], level: 'info' },
structured: { appenders: ['customJson'], level: 'debug' }
}
});
const logger = log4js.getLogger('structured');
const metricsLogger = log4js.getLogger('metrics');
// 構造化データのログ
logger.info('User operation', {
userId: 'user_123',
operation: 'login',
timestamp: new Date().toISOString(),
metadata: {
ip: '192.168.1.100',
userAgent: 'Mozilla/5.0...',
sessionId: 'sess_456'
}
});
// メトリクス情報の記録
logger.addContext('method', 'POST');
logger.addContext('duration', 245);
metricsLogger.info('API call completed');
// トレース情報付きログ
logger.addContext('traceId', 'trace_789');
logger.addContext('spanId', 'span_012');
logger.debug('Processing request step', { step: 'validation', result: 'success' });
// カスタムフィールド付きログ
const contextLogger = log4js.getLogger('structured');
contextLogger.addContext('environment', 'production');
contextLogger.addContext('region', 'us-east-1');
contextLogger.info('Service health check', {
status: 'healthy',
checks: ['database', 'redis', 'external-api']
});
エラーハンドリングとパフォーマンス最適化
const log4js = require('log4js');
const cluster = require('cluster');
// クラスター対応設定
if (cluster.isMaster) {
// マスタープロセス用設定
log4js.configure({
appenders: {
master: {
type: 'file',
filename: 'logs/master.log',
layout: {
type: 'pattern',
pattern: '[%d] [MASTER] [%p] %m'
}
}
},
categories: {
default: { appenders: ['master'], level: 'info' }
}
});
} else {
// ワーカープロセス用設定
log4js.configure({
appenders: {
worker: {
type: 'file',
filename: `logs/worker-${cluster.worker.id}.log`,
layout: {
type: 'pattern',
pattern: '[%d] [WORKER-' + cluster.worker.id + '] [%p] %m'
}
},
// 高性能ログファイル
highPerformance: {
type: 'file',
filename: 'logs/performance.log',
writeMode: 'append',
flags: 'a',
encoding: 'utf8',
maxLogSize: 52428800, // 50MB
backups: 10
}
},
categories: {
default: { appenders: ['worker'], level: 'info' },
performance: { appenders: ['highPerformance'], level: 'debug' }
}
});
}
const logger = log4js.getLogger();
const perfLogger = log4js.getLogger('performance');
// エラーハンドリング例
function handleDatabaseOperation(query, params) {
const startTime = Date.now();
logger.debug('Database operation started', { query, params });
try {
// データベース操作の実行
const result = executeQuery(query, params);
const duration = Date.now() - startTime;
perfLogger.info('Query executed', {
query,
duration,
rowCount: result.length,
success: true
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
logger.error('Database operation failed', {
query,
params,
duration,
error: error.message,
stack: error.stack
});
// エラーの再スロー
throw error;
}
}
// 非同期エラーハンドリング
async function asyncOperationWithLogging(operation) {
const operationId = Math.random().toString(36).substr(2, 9);
logger.addContext('operationId', operationId);
try {
logger.info('Async operation started');
const result = await operation();
logger.info('Async operation completed successfully');
return result;
} catch (error) {
logger.error('Async operation failed', {
error: error.message,
stack: error.stack,
operationId
});
throw error;
} finally {
logger.removeContext('operationId');
}
}
// パフォーマンス監視
function createPerformanceMonitor() {
const startTimes = new Map();
return {
start(label) {
startTimes.set(label, Date.now());
perfLogger.debug(`Performance monitor started: ${label}`);
},
end(label) {
const startTime = startTimes.get(label);
if (startTime) {
const duration = Date.now() - startTime;
perfLogger.info(`Performance monitor: ${label}`, { duration });
startTimes.delete(label);
return duration;
}
}
};
}
const monitor = createPerformanceMonitor();
// 使用例
monitor.start('user-authentication');
// 認証処理...
monitor.end('user-authentication');
フレームワーク統合と実用例
// Express.js統合例
const express = require('express');
const log4js = require('log4js');
// Express用ログ設定
log4js.configure({
appenders: {
access: {
type: 'dateFile',
filename: 'logs/access.log',
pattern: '.yyyy-MM-dd',
layout: {
type: 'pattern',
pattern: '%h %l %u %d{ISO8601} "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
}
},
app: {
type: 'file',
filename: 'logs/app.log',
layout: {
type: 'pattern',
pattern: '[%d] [%p] [%c] %m'
}
},
error: {
type: 'file',
filename: 'logs/error.log',
layout: {
type: 'pattern',
pattern: '[%d] [%p] %m%n%s'
}
}
},
categories: {
default: { appenders: ['app'], level: 'info' },
access: { appenders: ['access'], level: 'info' },
error: { appenders: ['error'], level: 'error' }
}
});
const app = express();
const logger = log4js.getLogger();
const errorLogger = log4js.getLogger('error');
// アクセスログミドルウェア
app.use(log4js.connectLogger(log4js.getLogger('access'), { level: 'auto' }));
// リクエストロギングミドルウェア
app.use((req, res, next) => {
req.logger = log4js.getLogger('api');
req.logger.addContext('requestId', Math.random().toString(36).substr(2, 9));
req.logger.info('Request received', {
method: req.method,
url: req.url,
ip: req.ip,
userAgent: req.get('User-Agent')
});
next();
});
// エラーハンドリングミドルウェア
app.use((error, req, res, next) => {
errorLogger.error('Unhandled error', {
error: error.message,
stack: error.stack,
url: req.url,
method: req.method,
ip: req.ip
});
res.status(500).json({ error: 'Internal Server Error' });
});
// Socket.IO統合例
const http = require('http');
const socketIo = require('socket.io');
const server = http.createServer(app);
const io = socketIo(server);
const socketLogger = log4js.getLogger('socket');
io.on('connection', (socket) => {
socketLogger.info('Socket connected', { socketId: socket.id });
socket.on('message', (data) => {
socketLogger.debug('Message received', { socketId: socket.id, data });
});
socket.on('disconnect', () => {
socketLogger.info('Socket disconnected', { socketId: socket.id });
});
socket.on('error', (error) => {
socketLogger.error('Socket error', { socketId: socket.id, error: error.message });
});
});
// 設定ファイルの動的リロード
const fs = require('fs');
function setupConfigWatcher() {
const configFile = 'log4js.json';
fs.watchFile(configFile, (curr, prev) => {
logger.info('Log configuration file changed, reloading...');
try {
const newConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
log4js.configure(newConfig);
logger.info('Log configuration reloaded successfully');
} catch (error) {
logger.error('Failed to reload log configuration', error);
}
});
}
// アプリケーション起動
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
logger.info(`Server started on port ${PORT}`);
setupConfigWatcher();
});
// グレースフルシャットダウン
process.on('SIGTERM', () => {
logger.info('SIGTERM received, shutting down gracefully');
log4js.shutdown(() => {
process.exit(0);
});
});
process.on('SIGINT', () => {
logger.info('SIGINT received, shutting down gracefully');
log4js.shutdown(() => {
process.exit(0);
});
});
外部設定ファイルとアドバンス機能
// log4js.json 設定ファイル例
{
"appenders": {
"out": {
"type": "stdout",
"layout": {
"type": "colored"
}
},
"app": {
"type": "dateFile",
"filename": "logs/app.log",
"pattern": ".yyyy-MM-dd-hh",
"compress": true,
"layout": {
"type": "pattern",
"pattern": "[%d{ISO8601}] [%p] [%c{2}] %m"
}
},
"errorFile": {
"type": "file",
"filename": "logs/errors.log",
"layout": {
"type": "pattern",
"pattern": "[%d{ISO8601}] [%p] [%c] %m%n%s"
}
},
"errors": {
"type": "logLevelFilter",
"level": "error",
"appender": "errorFile"
}
},
"categories": {
"default": {
"appenders": ["out", "app", "errors"],
"level": "info"
},
"development": {
"appenders": ["out"],
"level": "debug"
},
"production": {
"appenders": ["app", "errors"],
"level": "warn"
}
}
}
// 設定ファイルを使用した初期化
const log4js = require('log4js');
const path = require('path');
// 環境に応じた設定ファイルの読み込み
const env = process.env.NODE_ENV || 'development';
const configFile = path.join(__dirname, `config/log4js-${env}.json`);
try {
log4js.configure(configFile);
} catch (error) {
console.error('Failed to load log4js configuration:', error);
process.exit(1);
}
// カスタムアペンダーの作成
function createDatabaseAppender(config) {
const { connectionString, tableName } = config;
return (loggingEvent) => {
// データベースへの書き込み処理
const logData = {
timestamp: loggingEvent.startTime,
level: loggingEvent.level.levelStr,
category: loggingEvent.categoryName,
message: loggingEvent.data.join(' ')
};
// 実際のDB保存処理
saveToDatabase(connectionString, tableName, logData);
};
}
// カスタムアペンダーの登録
log4js.addAppender(createDatabaseAppender({
connectionString: 'mongodb://localhost:27017/logs',
tableName: 'application_logs'
}), 'database');
const logger = log4js.getLogger();
logger.info('Custom appender example');