logger

Android loggerからインスパイアされた美しいログを出力する小さく使いやすく拡張可能なDart/Flutterロギングパッケージ。trace、debug、info、warning、error、fatalレベルをサポート。デバッグモードでは設定レベル以上のログを表示、リリースモードでは全ログを省略。

ロギングライブラリRuby標準ライブラリファイル出力ローテーションカスタマイズ

ライブラリ

Logger (Standard Library)

概要

Logger (Standard Library)は「Ruby標準ライブラリのロギング機能」として、外部依存関係なしで基本的なロギング機能を提供するシンプルで信頼性の高いログソリューションです。ログレベル制御、ファイルローテーション、カスタムフォーマッターをサポートし、小規模なRubyアプリケーションやスクリプトでの日常的なロギング要件に対応。5つのログレベル(DEBUG、INFO、WARN、ERROR、FATAL)による詳細な出力制御と、プログラム名指定による識別機能を提供します。

詳細

Ruby標準Loggerは2025年でも小規模プロジェクトや学習用途での継続的な価値を持つ実用的な選択肢です。外部gemへの依存なしに即座に利用可能で、サイズベースとタイムベースの両方のログローテーション機能を内蔵。カスタムフォーマッター定義により柔軟な出力形式カスタマイズが可能で、日時フォーマット指定、プログラム名設定、複数の出力先指定など基本的なログ管理機能を網羅しています。シンプルな要件に十分対応できるものの、本格的なWebアプリケーションやRailsプロジェクトではより高機能なライブラリへの移行が推奨される現状です。

主な特徴

  • 標準ライブラリ: 外部依存なしで即座に利用可能
  • 5つのログレベル: DEBUG、INFO、WARN、ERROR、FATALによる詳細制御
  • ログローテーション: サイズベース・タイムベース両方をサポート
  • カスタムフォーマッター: Procによる柔軟な出力形式カスタマイズ
  • 複数出力先: ファイル、STDOUT、IOストリーム対応
  • プログラム名設定: ログエントリの識別機能

メリット・デメリット

メリット

  • 外部依存関係なしで即座に利用開始可能なゼロセットアップ環境
  • Ruby標準ライブラリとしての長期サポートと安定性保証
  • サイズベース・タイムベース両方のログローテーション機能内蔵
  • Procベースのカスタムフォーマッターによる柔軟な出力制御
  • 複数出力先(ファイル、STDOUT、ストリーム)への同時出力可能
  • 学習コストが低く、Rubyの基本概念のみで理解可能

デメリット

  • 構造化ログやJSON出力などのモダンなログ形式に対応不足
  • 高性能要件やマルチプロセス環境での機能制約
  • 本格的なWebアプリケーションでは機能不足
  • ログレベル別の出力先分散や高度なフィルタリング機能の欠如
  • サードパーティサービスとの統合機能なし
  • エンタープライズレベルの監査・セキュリティ要件への対応不足

参考ページ

書き方の例

インストールとセットアップ

# Loggerは標準ライブラリのため、requireのみで利用可能
require 'logger'

# 基本的なロガー作成(STDOUT出力)
logger = Logger.new(STDOUT)

# ファイル出力用ロガー
logger = Logger.new('application.log')

# IOストリーム指定
logger = Logger.new($stdout)

# プログラム名付きロガー
logger = Logger.new(STDOUT, progname: 'MyApp')

# バージョン確認
puts "Ruby Version: #{RUBY_VERSION}"
puts "Logger available: #{defined?(Logger) ? 'Yes' : 'No'}"

基本的なロギング(レベル別メッセージ出力)

require 'logger'

# 基本ロガー設定
logger = Logger.new(STDOUT)
logger.level = Logger::DEBUG

# 5つのログレベルでのメッセージ出力
logger.debug("デバッグ情報:詳細な実行トレース")
logger.info("情報:アプリケーション開始")
logger.warn("警告:非推奨メソッドの使用")
logger.error("エラー:処理中に問題が発生")
logger.fatal("致命的:システム停止レベルの問題")

# ログレベル設定による出力制御
logger.level = Logger::WARN
logger.debug("このメッセージは出力されません")
logger.info("このメッセージも出力されません")
logger.warn("このメッセージは出力されます")

# プログラム名付きロギング
logger.progname = 'UserService'
logger.info("ユーザー認証処理開始")

# ブロック形式でのロギング(遅延評価)
logger.debug { "重い処理の結果: #{expensive_calculation}" }

# ログレベル確認
logger.debug? && logger.debug("デバッグレベルが有効です")

ログレベル制御とフィルタリング

require 'logger'

# 基本的なログレベル設定
logger = Logger.new('application.log')

# ログレベル設定の各種方法
logger.level = Logger::INFO
logger.level = Logger::DEBUG
logger.level = Logger::WARN
logger.level = Logger::ERROR
logger.level = Logger::FATAL

# 数値での設定も可能
logger.level = 2  # Logger::WARN相当

# 環境変数からのログレベル設定
log_level = ENV['RUBY_LOG_LEVEL'] || 'INFO'
logger.level = Logger.const_get(log_level)

# ログレベル判定メソッド
def log_with_level_check(logger)
  logger.debug?  && logger.debug("デバッグレベル有効")
  logger.info?   && logger.info("インフォレベル有効")
  logger.warn?   && logger.warn("警告レベル有効")
  logger.error?  && logger.error("エラーレベル有効")
  logger.fatal?  && logger.fatal("致命的レベル有効")
end

# 環境別ログレベル設定
case ENV['RAILS_ENV']
when 'development'
  logger.level = Logger::DEBUG
when 'test'
  logger.level = Logger::WARN
when 'production'
  logger.level = Logger::ERROR
else
  logger.level = Logger::INFO
end

# ログレベル表示
puts "現在のログレベル: #{logger.level}"
puts "ログレベル名: #{%w[DEBUG INFO WARN ERROR FATAL UNKNOWN][logger.level]}"

ログローテーションと出力先管理

require 'logger'

# サイズベースローテーション(3ファイル、10MB制限)
logger = Logger.new('application.log', 3, 10485760)

# タイムベースローテーション(日次ローテーション)
logger = Logger.new('daily.log', 'daily')
logger = Logger.new('weekly.log', 'weekly')
logger = Logger.new('monthly.log', 'monthly')

# 複数の出力先管理クラス
class MultiLogger
  def initialize(*loggers)
    @loggers = loggers
  end

  def info(message)
    @loggers.each { |logger| logger.info(message) }
  end

  def error(message)
    @loggers.each { |logger| logger.error(message) }
  end

  def warn(message)
    @loggers.each { |logger| logger.warn(message) }
  end
end

# 複数ロガーの使用例
file_logger = Logger.new('application.log')
console_logger = Logger.new(STDOUT)
error_logger = Logger.new('error.log')

multi_logger = MultiLogger.new(file_logger, console_logger)
multi_logger.info("全ロガーに出力されるメッセージ")

# 条件付きログローテーション
class ConditionalRotationLogger
  def initialize(filename, max_size = 1048576) # 1MB
    @filename = filename
    @max_size = max_size
    @logger = create_logger
  end

  def rotate_if_needed
    if File.exist?(@filename) && File.size(@filename) > @max_size
      backup_name = "#{@filename}.#{Time.now.strftime('%Y%m%d_%H%M%S')}"
      File.rename(@filename, backup_name)
      @logger = create_logger
    end
  end

  def info(message)
    rotate_if_needed
    @logger.info(message)
  end

  private

  def create_logger
    Logger.new(@filename)
  end
end

# 使用例
rotating_logger = ConditionalRotationLogger.new('app.log')
rotating_logger.info("ローテーション対応ログメッセージ")

カスタムフォーマッターとレイアウト設定

require 'logger'

# カスタムフォーマッター作成
logger = Logger.new(STDOUT)

# シンプルなカスタムフォーマッター
logger.formatter = proc do |severity, datetime, progname, msg|
  "#{datetime}: #{severity} - #{msg}\n"
end

# 詳細なカスタムフォーマッター
logger.formatter = proc do |severity, datetime, progname, msg|
  date_format = datetime.strftime("%Y-%m-%d %H:%M:%S")
  "[#{date_format}] #{severity.ljust(5)} #{progname}: #{msg}\n"
end

# JSON風フォーマッター
logger.formatter = proc do |severity, datetime, progname, msg|
  {
    timestamp: datetime.iso8601,
    level: severity,
    program: progname,
    message: msg
  }.to_json + "\n"
end

# カラー付きフォーマッター(ターミナル用)
class ColorFormatter
  COLORS = {
    'DEBUG' => "\033[36m",   # シアン
    'INFO'  => "\033[32m",   # グリーン
    'WARN'  => "\033[33m",   # イエロー
    'ERROR' => "\033[31m",   # レッド
    'FATAL' => "\033[35m"    # マゼンタ
  }.freeze
  
  RESET = "\033[0m".freeze

  def call(severity, datetime, progname, msg)
    color = COLORS[severity] || ''
    "#{color}[#{datetime.strftime('%H:%M:%S')}] #{severity}: #{msg}#{RESET}\n"
  end
end

logger.formatter = ColorFormatter.new

# 日時フォーマットカスタマイズ
logger.datetime_format = '%Y-%m-%d %H:%M:%S'

# プログラム名を含むフォーマッター
logger.progname = 'MyApp'
logger.formatter = proc do |severity, datetime, progname, msg|
  "[#{progname}] #{datetime.strftime('%H:%M:%S')} #{severity}: #{msg}\n"
end

# ログテスト
logger.debug("デバッグメッセージ")
logger.info("情報メッセージ")
logger.warn("警告メッセージ")
logger.error("エラーメッセージ")

エラーハンドリングとログ管理

require 'logger'

# 安全なロガー作成関数
def create_safe_logger(filename, level = Logger::INFO)
  begin
    logger = Logger.new(filename)
    logger.level = level
    logger.info("ログファイル初期化完了: #{filename}")
    logger
  rescue StandardError => e
    fallback_logger = Logger.new(STDERR)
    fallback_logger.error("ログファイル作成失敗 (#{filename}): #{e.message}")
    fallback_logger
  end
end

# 例外ログ出力の標準パターン
def log_exception(logger, exception, context = '')
  logger.error "#{context}例外が発生: #{exception.class}: #{exception.message}"
  logger.error "バックトレース:"
  exception.backtrace.each { |line| logger.error "  #{line}" }
end

# ログレベル別の例外処理
class ApplicationLogger
  def initialize(filename = 'application.log')
    @logger = create_safe_logger(filename)
  end

  def log_with_exception_handling(level, message)
    begin
      @logger.send(level, message)
    rescue StandardError => e
      STDERR.puts "ログ出力エラー: #{e.message}"
    end
  end

  def safe_info(message)
    log_with_exception_handling(:info, message)
  end

  def safe_error(message)
    log_with_exception_handling(:error, message)
  end

  def log_method_execution(method_name)
    start_time = Time.now
    @logger.debug("メソッド開始: #{method_name}")
    
    begin
      result = yield
      duration = Time.now - start_time
      @logger.info("メソッド完了: #{method_name} (#{duration.round(3)}秒)")
      result
    rescue StandardError => e
      duration = Time.now - start_time
      @logger.error("メソッド失敗: #{method_name} (#{duration.round(3)}秒)")
      log_exception(@logger, e, "#{method_name}で")
      raise
    end
  end
end

# 使用例
app_logger = ApplicationLogger.new

begin
  app_logger.log_method_execution('data_processing') do
    # 何らかの処理
    raise "テスト例外" if rand(2) == 0
    "処理完了"
  end
rescue StandardError => e
  app_logger.safe_error("アプリケーションレベルでの例外キャッチ: #{e.message}")
end

# ログファイルアクセス権限チェック
def check_log_permissions(filename)
  logger = Logger.new(STDERR)
  
  begin
    test_logger = Logger.new(filename)
    test_logger.info("権限テスト")
    test_logger.close
    logger.info("ログファイル権限OK: #{filename}")
    true
  rescue StandardError => e
    logger.error("ログファイル権限エラー (#{filename}): #{e.message}")
    false
  end
end

# システム情報ログ
def log_system_info(logger)
  logger.info("Ruby Version: #{RUBY_VERSION}")
  logger.info("Platform: #{RUBY_PLATFORM}")
  logger.info("Process ID: #{Process.pid}")
  logger.info("Logger Level: #{logger.level}")
end

# 実行例
system_logger = create_safe_logger('system.log')
log_system_info(system_logger)

実用的なアプリケーション統合

require 'logger'
require 'fileutils'

# アプリケーション用ロガークラス
class ApplicationLogger
  DEFAULT_LOG_DIR = 'log'.freeze
  DEFAULT_LOG_LEVEL = Logger::INFO

  def initialize(app_name, log_dir = DEFAULT_LOG_DIR)
    @app_name = app_name
    @log_dir = log_dir
    ensure_log_directory
    @logger = create_logger
    setup_formatter
    log_startup_info
  end

  def debug(message); @logger.debug(format_message(message)); end
  def info(message); @logger.info(format_message(message)); end
  def warn(message); @logger.warn(format_message(message)); end
  def error(message); @logger.error(format_message(message)); end
  def fatal(message); @logger.fatal(format_message(message)); end

  def log_performance(operation_name)
    start_time = Time.now
    info("開始: #{operation_name}")
    
    begin
      result = yield
      duration = Time.now - start_time
      info("完了: #{operation_name} (#{duration.round(3)}秒)")
      result
    rescue StandardError => e
      duration = Time.now - start_time
      error("失敗: #{operation_name} (#{duration.round(3)}秒): #{e.message}")
      raise
    end
  end

  def close
    info("ログシステム停止")
    @logger.close if @logger.respond_to?(:close)
  end

  private

  def ensure_log_directory
    FileUtils.mkdir_p(@log_dir) unless Dir.exist?(@log_dir)
  end

  def create_logger
    log_file = File.join(@log_dir, "#{@app_name}.log")
    Logger.new(log_file, 'daily')
  end

  def setup_formatter
    @logger.formatter = proc do |severity, datetime, progname, msg|
      "[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}] #{severity.ljust(5)} [#{@app_name}] #{msg}\n"
    end
  end

  def format_message(message)
    "[PID:#{Process.pid}] #{message}"
  end

  def log_startup_info
    info("=== #{@app_name} 開始 ===")
    info("Ruby Version: #{RUBY_VERSION}")
    info("Log Level: #{@logger.level}")
    info("Log File: #{@log_dir}/#{@app_name}.log")
  end
end

# 設定管理クラス
class LogConfig
  def self.setup_from_env
    {
      level: log_level_from_env,
      app_name: ENV['APP_NAME'] || 'MyApp',
      log_dir: ENV['LOG_DIR'] || 'log'
    }
  end

  def self.log_level_from_env
    level_name = ENV['LOG_LEVEL'] || 'INFO'
    Logger.const_get(level_name.upcase)
  rescue NameError
    Logger::INFO
  end
end

# 使用例
begin
  config = LogConfig.setup_from_env
  logger = ApplicationLogger.new(config[:app_name], config[:log_dir])
  logger.info("設定読み込み完了")

  # 実際のアプリケーション処理
  logger.log_performance('データベース初期化') do
    sleep(0.1) # 実際の初期化処理の代わり
    "DB接続完了"
  end

  logger.log_performance('ユーザーデータ処理') do
    # 何らかの処理
    100.times do |i|
      logger.debug("処理中: #{i+1}/100") if (i+1) % 10 == 0
    end
    "処理完了"
  end

ensure
  logger&.close
end

# シングルトンロガーパターン
class GlobalLogger
  @@instance = nil

  def self.instance
    @@instance ||= ApplicationLogger.new('global')
  end

  def self.method_missing(method, *args)
    instance.send(method, *args)
  end
end

# グローバルロガーの使用
GlobalLogger.info("グローバルロガーメッセージ")
GlobalLogger.warn("グローバル警告")