Pino

高性能TypeScript/Node.js向け構造化ロギングフレームワーク(1,150万週間ダウンロード)。JSON出力をデフォルトとし、非同期ログ処理により優れた応答性を実現。Winstonより5倍以上高速で、本番環境での使用に最適化。

GitHub概要

pinojs/pino

🌲 super fast, all natural json logger

ホームページ:http://getpino.io
スター15,768
ウォッチ83
フォーク909
作成日:2016年2月16日
言語:JavaScript
ライセンス:MIT License

トピックス

fastjsonloggernodejspino

スター履歴

pinojs/pino Star History
データ取得日時: 2025/7/17 10:01

pino

Pinoは、Node.jsアプリケーション向けに設計された超高速で低オーバーヘッドなロガーです。構造化されたJSONログを出力し、非同期処理による高パフォーマンスを実現します。大規模なプロダクション環境での使用に最適化されています。

主な特徴

  • 超高速パフォーマンス - 他のロガーの5倍以上の速度
  • 低オーバーヘッド - 最小限のリソース使用
  • 構造化ログ - NDJSON形式の機械可読ログ
  • 非同期処理 - トランスポートによる別スレッド処理
  • チャイルドロガー - 効率的なコンテキスト管理

インストール

npm install pino

# TypeScript型定義(Pinoに含まれている)
# 追加の型定義は不要

# 開発用のプリティプリント
npm install --save-dev pino-pretty

基本的な使い方

シンプルな例

import pino from 'pino';

// 基本的なロガーの作成
const logger = pino();

// ログ出力
logger.info('アプリケーションが起動しました');
logger.error('エラーが発生しました');
logger.warn('警告メッセージ');
logger.debug('デバッグ情報');

// オブジェクトを含むログ
logger.info({ userId: 123, action: 'login' }, 'ユーザーがログインしました');

設定オプション

import pino from 'pino';

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  timestamp: pino.stdTimeFunctions.isoTime,
  formatters: {
    level: (label) => {
      return { level: label.toUpperCase() };
    },
    bindings: (bindings) => {
      return { 
        pid: bindings.pid,
        host: bindings.hostname,
        node_version: process.version 
      };
    }
  },
  base: {
    env: process.env.NODE_ENV,
    revision: process.env.GIT_COMMIT
  }
});

チャイルドロガー

基本的な使用

import pino from 'pino';

const logger = pino();

// モジュール用のチャイルドロガー
const authLogger = logger.child({ module: 'auth' });
const dbLogger = logger.child({ module: 'database' });

authLogger.info('認証処理開始');
dbLogger.info('データベース接続確立');

// ネストされたチャイルドロガー
const userAuthLogger = authLogger.child({ 
  submodule: 'user-auth',
  version: '1.0.0' 
});

userAuthLogger.info({ userId: 123 }, 'ユーザー認証成功');

リクエストコンテキスト

import pino from 'pino';
import { randomUUID } from 'crypto';

const logger = pino();

// Express ミドルウェアの例
function requestLogger(req: any, res: any, next: any) {
  const requestId = randomUUID();
  const childLogger = logger.child({
    requestId,
    method: req.method,
    url: req.url,
    ip: req.ip
  });
  
  // リクエストオブジェクトにロガーを追加
  req.log = childLogger;
  
  childLogger.info('リクエスト受信');
  
  // レスポンス時のログ
  res.on('finish', () => {
    childLogger.info({
      statusCode: res.statusCode,
      duration: Date.now() - req.startTime
    }, 'リクエスト完了');
  });
  
  next();
}

トランスポート

開発環境用の設定

import pino from 'pino';

// 開発環境でのプリティプリント
const transport = pino.transport({
  target: 'pino-pretty',
  options: {
    colorize: true,
    translateTime: 'SYS:standard',
    ignore: 'pid,hostname',
    messageFormat: '{levelLabel} - {msg}'
  }
});

const logger = pino(transport);

複数のトランスポート

import pino from 'pino';

const transport = pino.transport({
  targets: [
    {
      // ファイルへの出力
      target: 'pino/file',
      level: 'info',
      options: { 
        destination: './logs/app.log',
        mkdir: true 
      }
    },
    {
      // エラーログ専用ファイル
      target: 'pino/file',
      level: 'error',
      options: { 
        destination: './logs/error.log',
        mkdir: true 
      }
    },
    {
      // 開発環境でのプリティプリント
      target: 'pino-pretty',
      level: 'debug',
      options: {
        colorize: true,
        levelFirst: true,
        translateTime: 'yyyy-mm-dd HH:MM:ss'
      }
    }
  ]
});

const logger = pino(transport);

カスタムトランスポート

// transport.ts
import { Transform } from 'stream';

export default (options: any) => {
  return new Transform({
    objectMode: true,
    transform(chunk: any, encoding: string, callback: Function) {
      // カスタム処理
      const log = JSON.parse(chunk);
      
      // 例: 特定のレベル以上をSlackに送信
      if (log.level >= 50) { // ERROR以上
        sendToSlack(log);
      }
      
      callback(null, chunk);
    }
  });
};

// 使用
const transport = pino.transport({
  target: './transport.js',
  options: { /* カスタムオプション */ }
});

高度な機能

カスタムレベル

import pino from 'pino';

const logger = pino({
  customLevels: {
    database: 35,
    network: 45,
    critical: 55
  },
  useOnlyCustomLevels: false,
  level: 'trace'
});

// TypeScript用の型拡張
declare module 'pino' {
  interface CustomLevels {
    database: 35;
    network: 45;
    critical: 55;
  }
}

// 使用
logger.database('データベースクエリ実行');
logger.network('API呼び出し開始');
logger.critical('重大なエラーが発生');

Mixin(共通データの追加)

import pino from 'pino';
import os from 'os';

const logger = pino({
  mixin(_context, level) {
    return {
      application: 'my-app',
      environment: process.env.NODE_ENV,
      level_label: logger.levels.labels[level],
      memory: process.memoryUsage().heapUsed,
      cpuUsage: os.loadavg()[0]
    };
  }
});

// すべてのログに共通データが含まれる
logger.info('メッセージ');

センシティブデータの除去

import pino from 'pino';

const logger = pino({
  redact: {
    paths: ['password', 'token', '*.secret', 'creditCard'],
    censor: '[REDACTED]'
  }
});

// センシティブデータは自動的に除去される
logger.info({
  user: 'john',
  password: '12345',
  token: 'abc123',
  data: {
    secret: 'sensitive-info'
  }
}, 'ユーザー情報');
// password, token, secretは[REDACTED]に置換される

エラーのシリアライズ

import pino from 'pino';

const logger = pino({
  serializers: {
    err: pino.stdSerializers.err,
    // カスタムシリアライザー
    user: (user) => ({
      id: user.id,
      email: user.email,
      // パスワードは含めない
    })
  }
});

try {
  throw new Error('何かエラーが発生');
} catch (err) {
  logger.error({ err, user: currentUser }, 'エラーが発生しました');
}

パフォーマンス最適化

非同期ロギング

import pino from 'pino';
import { pino as pinoHttp } from 'pino-http';

// sonic-boomを使用した高速書き込み
const dest = pino.destination({
  dest: './app.log',
  sync: false // 非同期書き込み
});

const logger = pino(dest);

// HTTPロギング(Express)
const httpLogger = pinoHttp({
  logger,
  autoLogging: {
    ignore: (req) => req.url === '/health'
  },
  customLogLevel: (req, res, err) => {
    if (res.statusCode >= 400 && res.statusCode < 500) {
      return 'warn';
    } else if (res.statusCode >= 500 || err) {
      return 'error';
    }
    return 'info';
  }
});

条件付きロギング

import pino from 'pino';

const logger = pino();

// ログレベルチェック
if (logger.isLevelEnabled('debug')) {
  const expensiveData = calculateExpensiveOperation();
  logger.debug({ data: expensiveData }, '詳細データ');
}

// より簡潔な方法
logger.debug(() => ({
  data: calculateExpensiveOperation()
}), '詳細データ');

ベストプラクティス

1. 構造化されたコンテキスト

class ServiceLogger {
  private logger: pino.Logger;
  
  constructor(serviceName: string) {
    this.logger = pino().child({ service: serviceName });
  }
  
  async processRequest(requestId: string, userId: string) {
    const requestLogger = this.logger.child({ requestId, userId });
    
    requestLogger.info('処理開始');
    
    try {
      // 処理
      requestLogger.info({ duration: 100 }, '処理完了');
    } catch (error) {
      requestLogger.error({ err: error }, '処理エラー');
      throw error;
    }
  }
}

2. 環境別設定

import pino from 'pino';

function createLogger() {
  const isDevelopment = process.env.NODE_ENV === 'development';
  
  if (isDevelopment) {
    return pino({
      level: 'debug',
      transport: {
        target: 'pino-pretty',
        options: {
          colorize: true,
          translateTime: 'HH:MM:ss',
          ignore: 'pid,hostname'
        }
      }
    });
  }
  
  // 本番環境
  return pino({
    level: process.env.LOG_LEVEL || 'info',
    formatters: {
      level: (label) => ({ severity: label })
    }
  });
}

export const logger = createLogger();

3. エラーハンドリング

import pino from 'pino';

const logger = pino();

// グローバルエラーハンドラー
process.on('uncaughtException', (error) => {
  logger.fatal({ err: error }, '未処理の例外');
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  logger.fatal({ 
    err: reason,
    promise: promise.toString()
  }, '未処理のPromise拒否');
});

// シャットダウン処理
async function gracefulShutdown(signal: string) {
  logger.info({ signal }, 'シャットダウン開始');
  
  // クリーンアップ処理
  await cleanup();
  
  logger.info('シャットダウン完了');
  process.exit(0);
}

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

まとめ

Pinoは、パフォーマンスを最優先に設計された強力なロギングライブラリです。構造化ログ、チャイルドロガー、トランスポートシステムなどの機能により、大規模なプロダクション環境でも効率的なログ管理が可能です。非同期処理と最小限のオーバーヘッドにより、アプリケーションのパフォーマンスへの影響を最小限に抑えながら、包括的なログ情報を収集できます。