CrackedRuby logo

CrackedRuby

Syslog

Overview

Ruby's Syslog module provides access to the system logging facility found on Unix-like operating systems. The module acts as a wrapper around the system's syslog(3) library functions, enabling Ruby applications to send messages to system log files, remote logging servers, or other configured destinations.

The Syslog module operates as a singleton, maintaining a single connection to the syslog daemon throughout the application's lifecycle. Messages sent through Syslog include priority levels, facility codes, and formatting options that determine how the system processes and routes log entries.

Ruby's implementation supports all standard syslog facilities including mail, daemon, local0-7, and user facilities. Priority levels range from emergency through debug, following RFC 3164 specifications. The module handles message formatting, encoding, and transmission to the underlying system logging infrastructure.

require 'syslog'

# Open connection with program name
Syslog.open("myapp") do |s|
  s.info("Application started")
  s.warning("Configuration file missing, using defaults")
end
# Manual connection management
Syslog.open("worker", Syslog::LOG_PID, Syslog::LOG_DAEMON)
Syslog.notice("Worker process initialized with PID #{Process.pid}")
Syslog.close

The module integrates with system log rotation, filtering, and forwarding mechanisms configured by system administrators. Ruby applications using Syslog automatically inherit the logging infrastructure's reliability, persistence, and centralization features without requiring additional configuration or external dependencies.

Basic Usage

The Syslog module requires explicit opening before sending messages. The open method accepts a program identifier, logging options, and facility designation. Once opened, the connection remains active until explicitly closed or the process terminates.

require 'syslog'

# Basic connection with program name only
Syslog.open("myapp")
Syslog.info("User authentication successful")
Syslog.error("Database connection failed")
Syslog.close
# Block form automatically closes connection
Syslog.open("service", Syslog::LOG_PID | Syslog::LOG_CONS) do |syslog|
  syslog.debug("Processing request #{request_id}")
  syslog.warning("High memory usage detected: #{memory_usage}MB")
end

Priority levels determine message importance and filtering behavior. Each level corresponds to a specific method name and numeric value. System administrators configure filtering rules based on these priorities to control which messages reach specific destinations.

# Priority levels from most to least severe
Syslog.open("critical_app") do |s|
  s.emerg("System unusable, immediate attention required")
  s.alert("Action must be taken immediately") 
  s.crit("Critical conditions detected")
  s.err("Error conditions occurred")
  s.warning("Warning conditions present")
  s.notice("Normal but significant condition")
  s.info("Informational messages")
  s.debug("Debug-level messages")
end

Facility codes categorize message sources, enabling system administrators to route messages from different application types to appropriate destinations. Common facilities include daemon for background processes, local0-7 for custom applications, and mail for email-related services.

# Specify facility during connection
Syslog.open("webapp", Syslog::LOG_PID, Syslog::LOG_LOCAL0)
Syslog.info("Web server request processed")

# Different facility for background jobs  
Syslog.open("worker", Syslog::LOG_PID, Syslog::LOG_LOCAL1)
Syslog.notice("Background job completed")
Syslog.close

Message formatting follows printf-style conventions, accepting format strings and argument substitution. The module handles string encoding and character escaping automatically, ensuring messages remain readable across different system configurations.

Syslog.open("formatter_demo") do |s|
  user_id = 12345
  action = "login"
  ip_address = "192.168.1.100"
  
  # Printf-style formatting
  s.info("User %d performed %s from %s", user_id, action, ip_address)
  
  # String interpolation also works
  s.warning("Failed login attempt from #{ip_address} for user #{user_id}")
end

Error Handling & Debugging

Syslog operations can fail due to system resource constraints, permission restrictions, or network connectivity issues when logging to remote destinations. Ruby raises specific exceptions for different failure modes, requiring appropriate error handling strategies.

require 'syslog'

begin
  Syslog.open("test_app")
  Syslog.info("Application message")
rescue SystemCallError => e
  # Handle system-level errors (permissions, resources)
  STDERR.puts "Syslog unavailable: #{e.message}"
  # Fallback to file logging or stderr
rescue RuntimeError => e
  # Handle Ruby-level errors (already open, invalid arguments)
  STDERR.puts "Syslog configuration error: #{e.message}"
ensure
  Syslog.close if Syslog.opened?
end

Multiple connection attempts without proper cleanup result in runtime errors. The module maintains singleton state, preventing concurrent connections from the same process. Proper resource management requires explicit connection closure or block-form usage.

# Detect and handle existing connections
def safe_syslog_operation(program_name)
  if Syslog.opened?
    current_identity = Syslog.ident
    if current_identity == program_name
      yield Syslog
    else
      raise "Syslog already open with different identity: #{current_identity}"
    end
  else
    Syslog.open(program_name) do |s|
      yield s
    end
  end
end

# Usage with error recovery
safe_syslog_operation("myapp") do |syslog|
  syslog.info("Operation completed successfully")
end

Message truncation occurs when log entries exceed system-defined limits, typically 1024 or 2048 bytes. Applications generating large messages require chunking or summarization strategies to prevent data loss.

def safe_syslog_message(syslog, priority, message, max_length: 1000)
  if message.length <= max_length
    case priority
    when :info then syslog.info(message)
    when :warning then syslog.warning(message)
    when :error then syslog.err(message)
    end
  else
    # Split long messages into chunks
    chunks = message.scan(/.{1,#{max_length - 20}}/)
    chunks.each_with_index do |chunk, index|
      formatted_message = "[#{index + 1}/#{chunks.length}] #{chunk}"
      case priority
      when :info then syslog.info(formatted_message)
      when :warning then syslog.warning(formatted_message)
      when :error then syslog.err(formatted_message)
      end
    end
  end
end

# Handle potentially large error messages
Syslog.open("error_handler") do |s|
  begin
    # Some operation that might fail
    complex_operation
  rescue => e
    error_details = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
    safe_syslog_message(s, :error, error_details)
  end
end

Debugging syslog integration requires understanding both Ruby-level behavior and system configuration. Message delivery failures might not raise exceptions, making verification through system log inspection necessary.

# Debugging helper for syslog integration
class SyslogDebugger
  def self.test_connection(program_name, facility = Syslog::LOG_USER)
    puts "Testing syslog connection for '#{program_name}'"
    
    begin
      Syslog.open(program_name, Syslog::LOG_PID, facility) do |s|
        puts "  Connection successful"
        puts "  Identity: #{Syslog.ident}"
        puts "  Facility: #{Syslog.facility}"
        puts "  Options: #{Syslog.options}"
        
        # Send test messages at different priorities
        s.debug("Debug test message")
        s.info("Info test message") 
        s.warning("Warning test message")
        
        puts "  Test messages sent successfully"
      end
    rescue => e
      puts "  Connection failed: #{e.message}"
      puts "  Error class: #{e.class}"
    end
    
    puts "Check system logs (usually /var/log/messages or /var/log/syslog) for test messages"
  end
end

# Run debugging test
SyslogDebugger.test_connection("debug_app", Syslog::LOG_LOCAL0)

Thread Safety & Concurrency

The Syslog module maintains process-global state, creating thread safety concerns in concurrent Ruby applications. Multiple threads sharing a single syslog connection can interfere with each other's operations, leading to message corruption or connection errors.

Concurrent access to syslog methods requires synchronization mechanisms to prevent race conditions. Ruby's Thread class and Mutex objects provide coordination tools for safe multi-threaded logging operations.

require 'syslog'
require 'thread'

class ThreadSafeSyslog
  def initialize(program_name, facility = Syslog::LOG_USER)
    @mutex = Mutex.new
    @program_name = program_name
    @facility = facility
    @connection_active = false
  end
  
  def log(priority, message)
    @mutex.synchronize do
      ensure_connection
      case priority
      when :debug then Syslog.debug(message)
      when :info then Syslog.info(message)
      when :warning then Syslog.warning(message)  
      when :error then Syslog.err(message)
      end
    end
  end
  
  def close
    @mutex.synchronize do
      if @connection_active
        Syslog.close
        @connection_active = false
      end
    end
  end
  
  private
  
  def ensure_connection
    unless @connection_active
      Syslog.open(@program_name, Syslog::LOG_PID, @facility)
      @connection_active = true
    end
  end
end

# Usage with multiple threads
logger = ThreadSafeSyslog.new("concurrent_app")

threads = 10.times.map do |i|
  Thread.new do
    5.times do |j|
      logger.log(:info, "Thread #{i} message #{j}")
      sleep(rand * 0.1) # Simulate work
    end
  end
end

threads.each(&:join)
logger.close

Process forking creates additional complexity because child processes inherit the parent's syslog connection state. Forked processes require connection re-establishment to prevent message routing issues and resource conflicts.

def fork_safe_syslog_operation
  parent_pid = Process.pid
  
  if Syslog.opened?
    # Close parent connection before forking
    parent_ident = Syslog.ident
    parent_facility = Syslog.facility
    Syslog.close
    should_reopen = true
  end
  
  pid = fork do
    # Child process: establish new connection
    if should_reopen
      Syslog.open("#{parent_ident}-child-#{Process.pid}", 
                   Syslog::LOG_PID, parent_facility)
    end
    
    Syslog.info("Child process #{Process.pid} started")
    # Child work here
    sleep(1)
    Syslog.info("Child process #{Process.pid} completed")
    Syslog.close if Syslog.opened?
  end
  
  # Parent process: reopen connection if needed
  if should_reopen
    Syslog.open(parent_ident, Syslog::LOG_PID, parent_facility)
    Syslog.info("Parent process #{parent_pid} resumed after fork")
  end
  
  Process.wait(pid)
end

Fiber-based concurrency requires careful consideration of syslog state across fiber switches. The global connection state persists across fiber boundaries, but message ordering might become unpredictable under heavy concurrent load.

require 'fiber'

class FiberAwareSyslog
  def initialize(program_name)
    @program_name = program_name
    @fiber_mutex = Mutex.new
  end
  
  def with_logging
    @fiber_mutex.synchronize do
      unless Syslog.opened?
        Syslog.open(@program_name, Syslog::LOG_PID)
      end
      
      fiber_id = Fiber.current.object_id
      Syslog.info("Fiber #{fiber_id} started logging")
      
      begin
        yield
      ensure
        Syslog.info("Fiber #{fiber_id} finished logging")
      end
    end
  end
end

# Fiber coordination example
syslog_manager = FiberAwareSyslog.new("fiber_app")

fibers = 3.times.map do |i|
  Fiber.new do
    syslog_manager.with_logging do
      5.times do |j|
        Syslog.debug("Fiber #{i} iteration #{j}")
        Fiber.yield # Allow other fibers to run
      end
    end
  end
end

# Round-robin fiber scheduling
until fibers.all? { |f| !f.alive? }
  fibers.select(&:alive?).each do |fiber|
    fiber.resume rescue nil
  end
end

Syslog.close if Syslog.opened?

Production Patterns

Production Ruby applications require robust syslog integration patterns that handle service restarts, log rotation, monitoring integration, and operational visibility. Syslog connections must survive application lifecycle events while maintaining message delivery guarantees.

class ProductionSyslogManager
  def self.configure(app_name:, facility: Syslog::LOG_LOCAL0, 
                     min_level: :info, include_pid: true)
    @app_name = app_name
    @facility = facility
    @min_level = min_level
    @include_pid = include_pid
    @mutex = Mutex.new
    
    establish_connection
    setup_signal_handlers
    register_exit_handler
  end
  
  def self.log(level, message, context: {})
    return if skip_level?(level)
    
    @mutex.synchronize do
      ensure_connection_health
      formatted_message = format_message(message, context)
      send_message(level, formatted_message)
    end
  rescue => e
    # Fallback to stderr if syslog fails
    STDERR.puts "[SYSLOG_ERROR] #{e.message}: #{message}"
  end
  
  private
  
  def self.establish_connection
    options = Syslog::LOG_CONS | Syslog::LOG_NDELAY
    options |= Syslog::LOG_PID if @include_pid
    
    Syslog.open(@app_name, options, @facility)
    Syslog.info("Application #{@app_name} logging initialized")
  end
  
  def self.ensure_connection_health
    unless Syslog.opened?
      establish_connection
      Syslog.warning("Syslog connection restored after interruption")
    end
  end
  
  def self.format_message(message, context)
    if context.any?
      context_str = context.map { |k, v| "#{k}=#{v}" }.join(" ")
      "#{message} [#{context_str}]"
    else
      message.to_s
    end
  end
  
  def self.send_message(level, message)
    case level
    when :debug then Syslog.debug(message)
    when :info then Syslog.info(message)
    when :warning then Syslog.warning(message)
    when :error then Syslog.err(message)
    when :critical then Syslog.crit(message)
    end
  end
  
  def self.skip_level?(level)
    level_priorities = {
      debug: 7, info: 6, warning: 4, error: 3, critical: 2
    }
    min_priority = level_priorities[@min_level]
    current_priority = level_priorities[level]
    
    current_priority > min_priority
  end
  
  def self.setup_signal_handlers
    Signal.trap('USR1') do
      Syslog.info("Received USR1 signal - logging statistics")
      # Implementation specific statistics logging
    end
    
    Signal.trap('HUP') do
      @mutex.synchronize do
        Syslog.info("Received HUP signal - reconnecting to syslog")
        Syslog.close if Syslog.opened?
        establish_connection
      end
    end
  end
  
  def self.register_exit_handler
    at_exit do
      if Syslog.opened?
        Syslog.info("Application #{@app_name} shutting down")
        Syslog.close
      end
    end
  end
end

# Production initialization
ProductionSyslogManager.configure(
  app_name: "webapp", 
  facility: Syslog::LOG_LOCAL0,
  min_level: :warning,
  include_pid: true
)

# Usage in application code
ProductionSyslogManager.log(:info, "User session created", 
                           context: { user_id: 12345, ip: "10.0.0.1" })
ProductionSyslogManager.log(:error, "Payment processing failed",
                           context: { order_id: 67890, amount: 99.99 })

Structured logging with syslog requires consistent message formatting that parsing tools can process reliably. JSON formatting within syslog messages enables automated log analysis while preserving syslog delivery benefits.

require 'json'

class StructuredSyslogLogger
  def initialize(service_name, version:, environment:)
    @service_name = service_name
    @version = version
    @environment = environment
    @base_context = {
      service: service_name,
      version: version,
      environment: environment,
      hostname: Socket.gethostname,
      pid: Process.pid
    }
    
    Syslog.open(service_name, Syslog::LOG_PID, Syslog::LOG_LOCAL0)
  end
  
  def log_event(level, event_type, message, **context)
    log_data = @base_context.merge(
      timestamp: Time.now.utc.iso8601,
      level: level.to_s,
      event_type: event_type.to_s,
      message: message,
      **context
    )
    
    json_message = JSON.generate(log_data)
    
    case level
    when :info then Syslog.info(json_message)
    when :warning then Syslog.warning(json_message)  
    when :error then Syslog.err(json_message)
    when :critical then Syslog.crit(json_message)
    end
  rescue JSON::GeneratorError => e
    # Fallback for non-serializable data
    fallback_message = "JSON_ERROR: #{e.message} - #{message}"
    Syslog.err(fallback_message)
  end
  
  def log_request(method, path, status, duration, **context)
    log_event(:info, :http_request, "#{method} #{path}",
              http_status: status,
              duration_ms: duration,
              **context)
  end
  
  def log_error(exception, **context)
    log_event(:error, :exception, exception.message,
              exception_class: exception.class.name,
              backtrace: exception.backtrace&.first(10),
              **context)
  end
end

# Usage in web application
logger = StructuredSyslogLogger.new("api-service", 
                                    version: "1.2.3", 
                                    environment: "production")

# HTTP request logging
logger.log_request("POST", "/api/users", 201, 45.2, user_id: 123)

# Error logging with context
begin
  raise StandardError, "Database connection timeout"
rescue => e
  logger.log_error(e, operation: "user_lookup", database: "primary")
end

High-throughput applications require syslog message batching and buffering strategies to prevent logging operations from impacting application performance. Asynchronous message delivery with background thread processing maintains throughput while preserving message ordering.

require 'thread'
require 'json'

class AsyncSyslogBuffer
  def initialize(app_name, buffer_size: 100, flush_interval: 5)
    @app_name = app_name
    @buffer = Queue.new
    @buffer_size = buffer_size
    @flush_interval = flush_interval
    @running = false
    @mutex = Mutex.new
    
    initialize_syslog
    start_background_thread
  end
  
  def log(level, message, **context)
    return unless @running
    
    log_entry = {
      level: level,
      message: message,
      context: context,
      timestamp: Time.now.utc.iso8601
    }
    
    @buffer.push(log_entry)
    force_flush if @buffer.size >= @buffer_size
  end
  
  def shutdown
    @running = false
    @flush_thread&.join(5) # Wait up to 5 seconds for flush
    flush_remaining_messages
    Syslog.close if Syslog.opened?
  end
  
  private
  
  def initialize_syslog
    Syslog.open(@app_name, Syslog::LOG_PID | Syslog::LOG_NDELAY, 
                Syslog::LOG_LOCAL0)
  end
  
  def start_background_thread
    @running = true
    @flush_thread = Thread.new do
      while @running
        sleep(@flush_interval)
        flush_buffer
      end
    end
  end
  
  def flush_buffer
    return if @buffer.empty?
    
    @mutex.synchronize do
      messages_to_flush = []
      messages_to_flush << @buffer.pop until @buffer.empty?
      
      messages_to_flush.each do |entry|
        send_to_syslog(entry)
      end
    end
  rescue => e
    # Log flush errors to stderr to avoid recursion
    STDERR.puts "Syslog flush error: #{e.message}"
  end
  
  def force_flush
    Thread.new { flush_buffer }
  end
  
  def flush_remaining_messages
    flush_buffer until @buffer.empty?
  end
  
  def send_to_syslog(entry)
    formatted_message = format_entry(entry)
    
    case entry[:level]
    when :debug then Syslog.debug(formatted_message)
    when :info then Syslog.info(formatted_message)
    when :warning then Syslog.warning(formatted_message)
    when :error then Syslog.err(formatted_message)
    end
  end
  
  def format_entry(entry)
    if entry[:context].any?
      context_json = JSON.generate(entry[:context])
      "#{entry[:message]} #{context_json}"
    else
      entry[:message]
    end
  rescue JSON::GeneratorError
    entry[:message]
  end
end

# Production usage
async_logger = AsyncSyslogBuffer.new("high_volume_app", 
                                     buffer_size: 50,
                                     flush_interval: 2)

# High frequency logging won't block application
1000.times do |i|
  async_logger.log(:info, "Processing item #{i}", 
                   batch_id: "batch_001", item_count: i)
end

# Ensure clean shutdown
at_exit { async_logger.shutdown }

Common Pitfalls

Message formatting inconsistencies create parsing difficulties and information loss in production logging systems. Applications mixing formatted and unformatted messages, or using inconsistent field separators, complicate automated log analysis and monitoring setup.

# PROBLEMATIC: Inconsistent message formats
Syslog.open("inconsistent_app") do |s|
  s.info("User login successful")                    # Plain text
  s.info("user_id=123 action=login status=success") # Key-value pairs
  s.info('{"user_id": 123, "action": "login"}')     # JSON
  s.info("LOGIN: user 123 from 192.168.1.1")       # Custom format
end

# BETTER: Consistent structured format
class ConsistentLogger
  def initialize(app_name)
    @app_name = app_name
    Syslog.open(app_name, Syslog::LOG_PID)
  end
  
  def log_structured(level, event, **fields)
    # Always use consistent key=value format
    message_parts = ["event=#{event}"]
    message_parts += fields.map { |k, v| "#{k}=#{v.inspect}" }
    formatted_message = message_parts.join(" ")
    
    case level
    when :info then Syslog.info(formatted_message)
    when :warning then Syslog.warning(formatted_message)
    when :error then Syslog.err(formatted_message)
    end
  end
end

logger = ConsistentLogger.new("consistent_app")
logger.log_structured(:info, "user_login", user_id: 123, ip: "192.168.1.1")
logger.log_structured(:error, "db_error", table: "users", error: "timeout")

Facility misuse leads to message routing problems and operational confusion. Applications incorrectly using system facilities like kern or mail, or failing to coordinate facility usage across related services, create log management complications.

# PROBLEMATIC: Using inappropriate facilities
Syslog.open("webapp", Syslog::LOG_PID, Syslog::LOG_KERN)    # Kernel facility!
Syslog.open("emailer", Syslog::LOG_PID, Syslog::LOG_CRON)  # Cron facility!

# CORRECT: Use appropriate facilities for application types
class FacilityManager
  # Define facility mappings for different application types
  FACILITY_MAP = {
    web_application: Syslog::LOG_LOCAL0,
    background_worker: Syslog::LOG_LOCAL1,
    database_service: Syslog::LOG_LOCAL2,
    cache_service: Syslog::LOG_LOCAL3,
    monitoring_agent: Syslog::LOG_LOCAL4,
    custom_daemon: Syslog::LOG_DAEMON,
    user_script: Syslog::LOG_USER
  }.freeze
  
  def self.open_for_type(app_name, app_type)
    facility = FACILITY_MAP[app_type] || Syslog::LOG_USER
    
    unless FACILITY_MAP.key?(app_type)
      warn "Unknown app_type #{app_type}, defaulting to LOG_USER"
    end
    
    Syslog.open(app_name, Syslog::LOG_PID, facility)
    Syslog.info("Opened syslog with facility #{facility} for #{app_type}")
  end
end

# Proper facility usage
FacilityManager.open_for_type("web_server", :web_application)
FacilityManager.open_for_type("job_processor", :background_worker)

Connection state assumptions cause runtime errors and message loss. Applications assuming syslog connections persist across process boundaries, signal handling, or exception recovery create unreliable logging behavior.

# PROBLEMATIC: Assuming connection persistence
def unsafe_logging_pattern
  # This fails if connection was closed elsewhere
  Syslog.info("This might fail silently")
  
  # This fails after process fork
  fork do
    Syslog.info("Child process message") # Uses parent's connection!
  end
end

# ROBUST: Always verify connection state
class SafeSyslogWrapper
  def initialize(app_name, facility = Syslog::LOG_USER)
    @app_name = app_name
    @facility = facility
    @options = Syslog::LOG_PID | Syslog::LOG_CONS
    @mutex = Mutex.new
  end
  
  def with_syslog
    @mutex.synchronize do
      was_open = ensure_connection
      
      begin
        yield Syslog
      rescue => e
        # Log the error and attempt reconnection
        attempt_error_recovery(e)
        raise e
      ensure
        # Only close if we opened it in this call
        Syslog.close if !was_open && Syslog.opened?
      end
    end
  end
  
  private
  
  def ensure_connection
    if Syslog.opened?
      # Verify connection is for correct identity
      if Syslog.ident != @app_name
        Syslog.close
        Syslog.open(@app_name, @options, @facility)
        return false
      end
      return true
    else
      Syslog.open(@app_name, @options, @facility)
      return false
    end
  end
  
  def attempt_error_recovery(error)
    case error
    when SystemCallError
      # System-level error, try reconnecting
      Syslog.close if Syslog.opened?
      sleep(0.1) # Brief delay before retry
      Syslog.open(@app_name, @options, @facility)
    when RuntimeError
      # Ruby-level error, might be connection state issue
      if error.message.include?("syslog") 
        Syslog.close if Syslog.opened?
        Syslog.open(@app_name, @options, @facility)
      end
    end
  rescue
    # If recovery fails, fall back to stderr
    STDERR.puts "Syslog recovery failed: #{error.message}"
  end
end

# Usage with automatic connection management
wrapper = SafeSyslogWrapper.new("reliable_app")

wrapper.with_syslog do |s|
  s.info("This message will be delivered reliably")
end

# Safe across process boundaries
fork do
  wrapper.with_syslog do |s|
    s.info("Child process message with proper connection")
  end
end

Priority level confusion leads to inappropriate message filtering and alerting behavior. Applications misusing emergency or critical levels for routine errors, or failing to distinguish between warning and error conditions, create operational noise and missed alerts.

# PROBLEMATIC: Incorrect priority usage
def bad_priority_usage
  Syslog.open("confused_app")
  
  # These are wrong priority levels for these situations
  Syslog.emerg("User entered invalid email format")     # Not an emergency!
  Syslog.alert("Database query took 2 seconds")         # Not urgent!
  Syslog.debug("Payment processed successfully")        # Should be info!
  Syslog.info("Database server unreachable")           # Should be error!
  
  Syslog.close
end

# CORRECT: Appropriate priority selection
class PriorityGuideLogger
  def initialize(app_name)
    @app_name = app_name
    Syslog.open(app_name, Syslog::LOG_PID)
  end
  
  def log_user_error(message, **context)
    # User errors are informational for debugging
    Syslog.info(format_message("USER_ERROR", message, context))
  end
  
  def log_validation_warning(message, **context)
    # Validation issues worth noting but not errors
    Syslog.notice(format_message("VALIDATION", message, context))
  end
  
  def log_performance_warning(message, duration:, threshold:, **context)
    if duration > threshold * 3
      # Severe performance degradation
      Syslog.warning(format_message("PERFORMANCE_SEVERE", message, 
                                   context.merge(duration: duration)))
    else
      # Minor performance issue
      Syslog.notice(format_message("PERFORMANCE_MINOR", message,
                                  context.merge(duration: duration)))
    end
  end
  
  def log_system_error(message, recoverable: true, **context)
    if recoverable
      # Recoverable system error
      Syslog.err(format_message("RECOVERABLE_ERROR", message, context))
    else
      # Non-recoverable system error requiring intervention
      Syslog.crit(format_message("CRITICAL_ERROR", message, context))
    end
  end
  
  def log_emergency(message, **context)
    # Only for system unusable conditions
    # Requires immediate human intervention
    Syslog.emerg(format_message("EMERGENCY", message, context))
  end
  
  private
  
  def format_message(category, message, context)
    context_str = context.map { |k, v| "#{k}=#{v}" }.join(" ")
    "[#{category}] #{message} #{context_str}".strip
  end
end

# Proper usage examples
logger = PriorityGuideLogger.new("priority_aware_app")

# Appropriate priority selections
logger.log_user_error("Invalid email format", user_id: 123, email: "bad-email")
logger.log_performance_warning("Slow query detected", 
                               duration: 5.2, threshold: 2.0, query: "SELECT * FROM users")
logger.log_system_error("Database connection failed", 
                        recoverable: true, database: "primary", retry_count: 2)
logger.log_system_error("Disk space critically low", 
                        recoverable: false, filesystem: "/var", available: "1%")

Reference

Core Methods

Method Parameters Returns Description
Syslog.open(...) ident (String), options (Integer), facility (Integer) Syslog Opens connection with program identity, logging options, and facility
Syslog.open(...) { } ident (String), block Block result Opens connection and yields to block, automatically closes
Syslog.close None nil Closes active syslog connection
Syslog.opened? None Boolean Returns true if connection is currently open
Syslog.reopen None Syslog Reopens connection with same parameters

Message Priority Methods

Method Priority Level Numeric Value Usage Guidelines
#emerg(message, ...) Emergency 0 System unusable, requires immediate action
#alert(message, ...) Alert 1 Action must be taken immediately
#crit(message, ...) Critical 2 Critical conditions requiring urgent attention
#err(message, ...) Error 3 Error conditions that affect functionality
#warning(message, ...) Warning 4 Warning conditions worth monitoring
#notice(message, ...) Notice 5 Normal but significant conditions
#info(message, ...) Informational 6 General informational messages
#debug(message, ...) Debug 7 Debug-level messages for development

Connection State Methods

Method Parameters Returns Description
Syslog.ident None String Returns current program identity string
Syslog.options None Integer Returns current logging options bitmask
Syslog.facility None Integer Returns current facility code

Logging Options Constants

Constant Value Description
LOG_PID Process ID inclusion Includes process ID in each message
LOG_CONS Console fallback Writes to console if syslog unavailable
LOG_ODELAY Delayed open Delays connection until first message
LOG_NDELAY Immediate open Opens connection immediately
LOG_NOWAIT No wait Does not wait for child processes
LOG_PERROR Standard error Also logs to standard error

Facility Constants

Constant Purpose Typical Usage
LOG_KERN Kernel messages System kernel (restricted)
LOG_USER User-level messages Default for user applications
LOG_MAIL Mail system Email server applications
LOG_DAEMON System daemons Background service processes
LOG_AUTH Security/authorization Authentication systems
LOG_SYSLOG Syslog daemon Internal syslog messages
LOG_LPR Line printer Print system messages
LOG_NEWS Network news USENET news systems
LOG_UUCP UUCP system Unix-to-Unix copy protocol
LOG_CRON Clock daemon Scheduled task systems
LOG_AUTHPRIV Security/authorization Private authentication
LOG_FTP FTP daemon File transfer systems
LOG_LOCAL0 through LOG_LOCAL7 Local use Custom application facilities

Priority Level Bitmasks

Priority Bitmask Value Filtering Usage
LOG_EMERG 0 Most critical messages only
LOG_ALERT 1 Alert level and above
LOG_CRIT 2 Critical level and above
LOG_ERR 3 Error level and above
LOG_WARNING 4 Warning level and above
LOG_NOTICE 5 Notice level and above
LOG_INFO 6 Info level and above
LOG_DEBUG 7 All messages including debug

Common Exception Classes

Exception Trigger Conditions Recovery Strategy
RuntimeError Connection already open, invalid arguments Check opened? state, verify parameters
SystemCallError System resource limits, permissions Fallback logging, retry with delay
Errno::ENOENT Syslog daemon unavailable Alternative logging destination
Errno::EACCES Permission denied Check process privileges, facility permissions
Errno::EMFILE File descriptor limit reached Close unused connections, system tuning

Format String Guidelines

Format Type Example Best Practice
Printf-style "User %d logged in from %s" Use for type safety
String interpolation "User #{id} from #{ip}" Acceptable for simple cases
Structured "event=login user_id=#{id} ip=#{ip}" Preferred for parsing
JSON JSON.generate(event: "login", user: id) Best for complex data