Log4js

Java Log4jライブラリにインスパイアされたNode.js向けロギングライブラリ。Java開発者に馴染みのあるAPIを提供し、Node.jsへの移行を容易にする。ログレベル、アペンダー、レイアウト等の概念をJavaScriptに移植した設計。

ロギングライブラリJavaScriptNode.jsアペンダー設定ベース

GitHub概要

log4js-node/log4js-node

A port of log4js to node.js

スター5,826
ウォッチ109
フォーク768
作成日:2010年1月17日
言語:JavaScript
ライセンス:Other

トピックス

javascriptlog4js-node

スター履歴

log4js-node/log4js-node Star History
データ取得日時: 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');