logger

Small, easy-to-use, and extensible Dart/Flutter logging package inspired by Android logger that prints beautiful logs. Supports trace, debug, info, warning, error, and fatal levels. Shows logs at or above configured level in debug mode, omits all logs in release mode.

Logging LibraryRubyStandard LibraryFile OutputRotationCustomization

Library

Logger (Standard Library)

Overview

Logger (Standard Library) is Ruby's built-in logging functionality that provides simple and reliable logging solutions without external dependencies. It supports log level control, file rotation, and custom formatters, catering to basic logging requirements for small-scale Ruby applications and scripts. The library offers detailed output control through five log levels (DEBUG, INFO, WARN, ERROR, FATAL) and identification functionality through program name specification, making it an accessible starting point for Ruby logging needs.

Details

Ruby's standard Logger maintains practical value for small-scale projects and learning purposes in 2025. Available immediately without external gem dependencies, it includes built-in log rotation functionality for both size-based and time-based rotation. Custom formatter definition enables flexible output format customization, covering basic log management features like date/time format specification, program name settings, and multiple output destinations. While sufficient for simple requirements, migration to more feature-rich libraries is recommended for serious web applications and Rails projects.

Key Features

  • Standard Library: Immediately available without external dependencies
  • Five Log Levels: Detailed control through DEBUG, INFO, WARN, ERROR, FATAL
  • Log Rotation: Support for both size-based and time-based rotation
  • Custom Formatters: Flexible output format customization via Proc
  • Multiple Outputs: Support for files, STDOUT, and IO streams
  • Program Name Setting: Log entry identification functionality

Pros and Cons

Pros

  • Zero-setup environment with immediate availability without external dependencies
  • Long-term support and stability guaranteed as Ruby standard library
  • Built-in log rotation functionality for both size-based and time-based rotation
  • Flexible output control through Proc-based custom formatters
  • Simultaneous output to multiple destinations (files, STDOUT, streams)
  • Low learning curve requiring only basic Ruby concepts for understanding

Cons

  • Insufficient support for modern log formats like structured logging and JSON output
  • Functional limitations in high-performance requirements and multi-process environments
  • Feature insufficiency for serious web applications
  • Lack of log level-specific output destination distribution and advanced filtering
  • No integration features with third-party services
  • Insufficient support for enterprise-level audit and security requirements

Reference Pages

Usage Examples

Installation and Setup

# Logger is part of standard library, available with require only
require 'logger'

# Basic logger creation (STDOUT output)
logger = Logger.new(STDOUT)

# File output logger
logger = Logger.new('application.log')

# IO stream specification
logger = Logger.new($stdout)

# Logger with program name
logger = Logger.new(STDOUT, progname: 'MyApp')

# Version check
puts "Ruby Version: #{RUBY_VERSION}"
puts "Logger available: #{defined?(Logger) ? 'Yes' : 'No'}"

Basic Logging with Different Levels

require 'logger'

# Basic logger setup
logger = Logger.new(STDOUT)
logger.level = Logger::DEBUG

# Message output with five log levels
logger.debug("Debug info: detailed execution trace")
logger.info("Info: application started")
logger.warn("Warning: deprecated method usage")
logger.error("Error: problem occurred during processing")
logger.fatal("Fatal: system-stopping level problem")

# Output control through log level setting
logger.level = Logger::WARN
logger.debug("This message will not be output")
logger.info("This message will also not be output")
logger.warn("This message will be output")

# Logging with program name
logger.progname = 'UserService'
logger.info("User authentication process started")

# Block-style logging (lazy evaluation)
logger.debug { "Heavy processing result: #{expensive_calculation}" }

# Log level checking
logger.debug? && logger.debug("Debug level is enabled")

Log Level Control and Filtering

require 'logger'

# Basic log level setting
logger = Logger.new('application.log')

# Various log level setting methods
logger.level = Logger::INFO
logger.level = Logger::DEBUG
logger.level = Logger::WARN
logger.level = Logger::ERROR
logger.level = Logger::FATAL

# Numeric setting also possible
logger.level = 2  # Equivalent to Logger::WARN

# Log level setting from environment variables
log_level = ENV['RUBY_LOG_LEVEL'] || 'INFO'
logger.level = Logger.const_get(log_level)

# Log level checking methods
def log_with_level_check(logger)
  logger.debug?  && logger.debug("Debug level enabled")
  logger.info?   && logger.info("Info level enabled")
  logger.warn?   && logger.warn("Warning level enabled")
  logger.error?  && logger.error("Error level enabled")
  logger.fatal?  && logger.fatal("Fatal level enabled")
end

# Environment-specific log level settings
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

# Display current log level
puts "Current log level: #{logger.level}"
puts "Log level name: #{%w[DEBUG INFO WARN ERROR FATAL UNKNOWN][logger.level]}"

Log Rotation and Output Destination Management

require 'logger'

# Size-based rotation (3 files, 10MB limit)
logger = Logger.new('application.log', 3, 10485760)

# Time-based rotation (daily rotation)
logger = Logger.new('daily.log', 'daily')
logger = Logger.new('weekly.log', 'weekly')
logger = Logger.new('monthly.log', 'monthly')

# Multiple output destination management class
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

# Multiple logger usage example
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("Message output to all loggers")

# Conditional log rotation
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

# Usage example
rotating_logger = ConditionalRotationLogger.new('app.log')
rotating_logger.info("Rotation-aware log message")

Custom Formatters and Layout Configuration

require 'logger'

# Create custom formatter
logger = Logger.new(STDOUT)

# Simple custom formatter
logger.formatter = proc do |severity, datetime, progname, msg|
  "#{datetime}: #{severity} - #{msg}\n"
end

# Detailed custom formatter
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-style formatter
logger.formatter = proc do |severity, datetime, progname, msg|
  {
    timestamp: datetime.iso8601,
    level: severity,
    program: progname,
    message: msg
  }.to_json + "\n"
end

# Colored formatter (for terminal)
class ColorFormatter
  COLORS = {
    'DEBUG' => "\033[36m",   # Cyan
    'INFO'  => "\033[32m",   # Green
    'WARN'  => "\033[33m",   # Yellow
    'ERROR' => "\033[31m",   # Red
    'FATAL' => "\033[35m"    # Magenta
  }.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

# Custom datetime format
logger.datetime_format = '%Y-%m-%d %H:%M:%S'

# Formatter with program name
logger.progname = 'MyApp'
logger.formatter = proc do |severity, datetime, progname, msg|
  "[#{progname}] #{datetime.strftime('%H:%M:%S')} #{severity}: #{msg}\n"
end

# Log testing
logger.debug("Debug message")
logger.info("Info message")
logger.warn("Warning message")
logger.error("Error message")

Error Handling and Log Management

require 'logger'

# Safe logger creation function
def create_safe_logger(filename, level = Logger::INFO)
  begin
    logger = Logger.new(filename)
    logger.level = level
    logger.info("Log file initialization completed: #{filename}")
    logger
  rescue StandardError => e
    fallback_logger = Logger.new(STDERR)
    fallback_logger.error("Log file creation failed (#{filename}): #{e.message}")
    fallback_logger
  end
end

# Standard pattern for exception log output
def log_exception(logger, exception, context = '')
  logger.error "#{context}Exception occurred: #{exception.class}: #{exception.message}"
  logger.error "Backtrace:"
  exception.backtrace.each { |line| logger.error "  #{line}" }
end

# Exception handling by log level
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 "Log output error: #{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 started: #{method_name}")
    
    begin
      result = yield
      duration = Time.now - start_time
      @logger.info("Method completed: #{method_name} (#{duration.round(3)} seconds)")
      result
    rescue StandardError => e
      duration = Time.now - start_time
      @logger.error("Method failed: #{method_name} (#{duration.round(3)} seconds)")
      log_exception(@logger, e, "In #{method_name}: ")
      raise
    end
  end
end

# Usage example
app_logger = ApplicationLogger.new

begin
  app_logger.log_method_execution('data_processing') do
    # Some processing
    raise "Test exception" if rand(2) == 0
    "Processing completed"
  end
rescue StandardError => e
  app_logger.safe_error("Application-level exception catch: #{e.message}")
end

# Log file permission check
def check_log_permissions(filename)
  logger = Logger.new(STDERR)
  
  begin
    test_logger = Logger.new(filename)
    test_logger.info("Permission test")
    test_logger.close
    logger.info("Log file permissions OK: #{filename}")
    true
  rescue StandardError => e
    logger.error("Log file permission error (#{filename}): #{e.message}")
    false
  end
end

# System information logging
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

# Execution example
system_logger = create_safe_logger('system.log')
log_system_info(system_logger)

Practical Application Integration

require 'logger'
require 'fileutils'

# Application logger class
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("Started: #{operation_name}")
    
    begin
      result = yield
      duration = Time.now - start_time
      info("Completed: #{operation_name} (#{duration.round(3)} seconds)")
      result
    rescue StandardError => e
      duration = Time.now - start_time
      error("Failed: #{operation_name} (#{duration.round(3)} seconds): #{e.message}")
      raise
    end
  end

  def close
    info("Logging system shutdown")
    @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} Started ===")
    info("Ruby Version: #{RUBY_VERSION}")
    info("Log Level: #{@logger.level}")
    info("Log File: #{@log_dir}/#{@app_name}.log")
  end
end

# Configuration management class
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

# Usage example
begin
  config = LogConfig.setup_from_env
  logger = ApplicationLogger.new(config[:app_name], config[:log_dir])
  logger.info("Configuration loaded successfully")

  # Actual application processing
  logger.log_performance('database_initialization') do
    sleep(0.1) # Instead of actual initialization process
    "DB connection completed"
  end

  logger.log_performance('user_data_processing') do
    # Some processing
    100.times do |i|
      logger.debug("Processing: #{i+1}/100") if (i+1) % 10 == 0
    end
    "Processing completed"
  end

ensure
  logger&.close
end

# Singleton logger pattern
class GlobalLogger
  @@instance = nil

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

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

# Global logger usage
GlobalLogger.info("Global logger message")
GlobalLogger.warn("Global warning")