Class: Lumberjack::Logger

Inherits:
Logger
  • Object
show all
Includes:
ContextLogger
Defined in:
lib/lumberjack/logger.rb

Overview

Lumberjack::Logger is a thread-safe, feature-rich logging implementation that extends Ruby's standard library Logger class with advanced capabilities for structured logging.

Key features include:

  • Structured logging with attributes (key-value pairs) attached to log entries
  • Context isolation for scoping logging behavior to specific code blocks
  • Flexible output devices supporting files, streams, and custom destinations
  • Customizable formatters for messages and attributes

The Logger maintains full API compatibility with Ruby's standard Logger while adding powerful extensions for modern logging needs.

Log entries are written to a logging Device if their severity meets or exceeds the log level. Each log entry records the log message and severity along with the time it was logged, the program name, process id, and an optional hash of attributes. Messages are converted to strings using a Formatter associated with the logger.

Examples:

Basic usage

logger = Lumberjack::Logger.new(STDOUT)
logger.info("Starting processing")
logger.debug("Processing options #{options.inspect}")
logger.fatal("OMG the application is on fire!")

Structured logging with attributes

logger = Lumberjack::Logger.new("/var/log/app.log")
logger.tag(request_id: "abc123") do
  logger.info("User logged in", user_id: 123, ip: "192.168.1.1")
  logger.info("Processing request")  # Will include request_id: "abc123"
end

Log rotation

# Keep 10 files, rotate when each reaches 10MB
logger = Lumberjack::Logger.new("/var/log/app.log", 10, 10 * 1024 * 1024)

Using different devices

logger = Lumberjack::Logger.new("logs/application.log")  # Log to file
logger = Lumberjack::Logger.new(STDOUT, template: "{{severity}} - {{message}}")  # Log to a stream with a template
logger = Lumberjack::Logger.new(:test)  # Log to an in memory buffer for testing
logger = Lumberjack::Logger.new(another_logger) # Proxy logs to another logger
logger = Lumberjack::Logger.new(MyDevice.new)  # Log to a custom Lumberjack::Device

Logging to multiple devices with an array

logger = Lumberjack::Logger.new(["/var/log/app.log", [:stdout, {template: "{{message}}"}]])

See Also:

Direct Known Subclasses

ForkedLogger

Constant Summary

Constants included from ContextLogger

ContextLogger::LEADING_OR_TRAILING_WHITESPACE, ContextLogger::TRACE

Instance Method Summary collapse

Methods included from ContextLogger

#<<, #add, #append_to, #attribute_value, #attributes, #clear_attributes, #context, #debug, #debug!, #debug?, #default_severity, #default_severity=, #ensure_context, #error, #error!, #error?, #fatal, #fatal!, #fatal?, #fork, #in_context?, included, #info, #info!, #info?, #level, #level=, #progname, #progname=, #tag, #tag!, #tag_all_contexts, #trace, #trace!, #trace?, #unknown, #untag, #untag!, #warn, #warn!, #warn?, #with_level, #with_progname

Constructor Details

#initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG, progname: nil, formatter: nil, datetime_format: nil, binmode: false, shift_period_suffix: "%Y%m%d", **kwargs) ⇒ Lumberjack::Logger

Create a new logger to log to a Device.

The device argument can be in any one of several formats:

  • A symbol for a device name (e.g. :null, :test). You can call Lumberjack::DeviceRegistry.registered_devices for a list.
  • A stream
  • A file path string or Pathname
  • A Lumberjack::Device object
  • An object with a write method will be wrapped in a Device::Writer
  • An array of any of the above will open a Multi device that will send output to all devices.

Parameters:

  • logdev (Lumberjack::Device, IO, Symbol, String, Pathname)

    The device to log to. If this is a symbol, the device will be looked up from the DeviceRegistry. If it is a string or a Pathname, the logs will be sent to the corresponding file path.

  • shift_age (Integer, String, Symbol) (defaults to: 0)

    If this is an integer greater than zero, then log files will be rolled when they get to the size specified in shift_size and the number of files to keep will be determined by this value. Otherwise it will be interpreted as a date rolling value and must be one of "daily", "weekly", or "monthly". This parameter has no effect unless the device parameter is a file path or file stream.

  • shift_size (Integer) (defaults to: 1048576)

    The size in bytes of the log files before rolling them. This can be passed as a string with a unit suffix of K, M, or G (e.g. "10M" for 10 megabytes).

  • level (Integer, Symbol, String) (defaults to: DEBUG)

    The logging level below which messages will be ignored.

  • progname (String) (defaults to: nil)

    The name of the program that will be recorded with each log entry.

  • formatter (Lumberjack::EntryFormatter, Lumberjack::Formatter, ::Logger::Formatter, :default, #call) (defaults to: nil)

    The formatter to use for outputting messages to the log. If this is a Lumberjack::EntryFormatter or a Lumberjack::Formatter, it will be used to format structured log entries. You can also pass the value :default to use the default message formatter which formats non-primitive objects with inspect and includes the backtrace in exceptions.

    For compatibility with the standard library Logger when writing to a stream, you can also pass in a ::Logger::Formatter object or a callable object that takes exactly 4 arguments (severity, time, progname, msg).

  • datetime_format (String) (defaults to: nil)

    The format to use for log timestamps.

  • binmode (Boolean) (defaults to: false)

    Whether to open the log file in binary mode.

  • shift_period_suffix (String) (defaults to: "%Y%m%d")

    The suffix to use for the shifted log file names.

  • kwargs (Hash)

    Additional device-specific options. These will be passed through when creating a device from the logdev argument.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/lumberjack/logger.rb', line 92

def initialize(logdev, shift_age = 0, shift_size = 1048576,
  level: DEBUG, progname: nil, formatter: nil, datetime_format: nil,
  binmode: false, shift_period_suffix: "%Y%m%d", **kwargs)
  init_context_locals!
  @recursion_guard_key = :"lumberjack_logging_#{object_id}"

  self.isolation_level = kwargs.delete(:isolation_level) || Lumberjack.isolation_level

  # Include standard args that affect devices with the optional kwargs which may
  # contain device specific options.
  device_options = kwargs.merge(shift_age: shift_age, shift_size: size_with_units(shift_size), binmode: binmode, shift_period_suffix: shift_period_suffix)
  device_options[:standard_logger_formatter] = formatter if standard_logger_formatter?(formatter)

  @logdev = Device.open_device(logdev, device_options)

  @context = Context.new
  self.level = level || DEBUG
  self.progname = progname

  self.formatter = build_entry_formatter(formatter)
  self.datetime_format = datetime_format if datetime_format

  @closed = false
end

Instance Method Details

#add_entry(severity, message, progname = nil, attributes = nil) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Add an entry to the log.

Parameters:

  • severity (Integer, Symbol, String)

    The severity of the message.

  • message (Object)

    The message to log.

  • progname (String) (defaults to: nil)

    The name of the program that is logging the message.

  • attributes (Hash) (defaults to: nil)

    The attributes to add to the log entry.



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/lumberjack/logger.rb', line 232

def add_entry(severity, message, progname = nil, attributes = nil)
  return false unless device

  # Prevent infinite recursion if logging is attempted from within a logging call.
  # The guard is stored in fiber-local storage since recursion is a property of the
  # current call stack, which belongs to exactly one fiber.
  if Thread.current[@recursion_guard_key]
    log_to_stderr(severity, message)
    return false
  end

  severity = Severity.label_to_level(severity) unless severity.is_a?(Integer)

  begin
    Thread.current[@recursion_guard_key] = true

    locals = current_context_locals
    time = Time.now
    progname ||= locals&.context&.progname || default_context&.progname
    attributes = nil unless attributes.is_a?(Hash)
    attributes = merge_all_attributes(locals, attributes)
    message, attributes = formatter.format(message, attributes) if formatter

    entry = Lumberjack::LogEntry.new(time, severity, message, progname, Process.pid, attributes)

    write_to_device(entry)
  ensure
    Thread.current[@recursion_guard_key] = nil
  end

  true
end

#attribute_formatterLumberjack::AttributeFormatter

Get the attribute formatter used to format log entry attributes.

Returns:



176
177
178
# File 'lib/lumberjack/logger.rb', line 176

def attribute_formatter
  formatter.attribute_formatter
end

#attribute_formatter=(value) ⇒ void

This method returns an undefined value.

Set the attribute formatter used to format log entry attributes.

Parameters:



184
185
186
# File 'lib/lumberjack/logger.rb', line 184

def attribute_formatter=(value)
  formatter.attribute_formatter = value
end

#closevoid

This method returns an undefined value.

Close the logging device.



199
200
201
202
203
# File 'lib/lumberjack/logger.rb', line 199

def close
  flush
  device.close if device.respond_to?(:close)
  @closed = true
end

#closed?Boolean

Returns true if the logging device is closed.

Returns:

  • (Boolean)

    true if the logging device is closed.



208
209
210
211
212
# File 'lib/lumberjack/logger.rb', line 208

def closed?
  return true if @closed

  device.respond_to?(:closed?) && device.closed?
end

#datetime_formatString?

Get the timestamp format on the device if it has one.

Returns:

  • (String, nil)

    The timestamp format or nil if the device doesn't support it.



144
145
146
# File 'lib/lumberjack/logger.rb', line 144

def datetime_format
  device.datetime_format if device.respond_to?(:datetime_format)
end

#datetime_format=(format) ⇒ void

This method returns an undefined value.

Set the timestamp format on the device if it is supported.

Parameters:

  • format (String)

    The timestamp format.



152
153
154
155
156
# File 'lib/lumberjack/logger.rb', line 152

def datetime_format=(format)
  if device.respond_to?(:datetime_format=)
    device.datetime_format = format
  end
end

#deviceLumberjack::Device

Get the logging device that is used to write log entries.

Returns:



120
121
122
# File 'lib/lumberjack/logger.rb', line 120

def device
  @logdev
end

#device=(device) ⇒ void

This method returns an undefined value.

Set the logging device to a new device.

Parameters:



128
129
130
# File 'lib/lumberjack/logger.rb', line 128

def device=(device)
  @logdev = Device.open_device(device, {})
end

#flushvoid

This method returns an undefined value.

Flush the logging device. Messages are not guaranteed to be written until this method is called.



191
192
193
194
# File 'lib/lumberjack/logger.rb', line 191

def flush
  device.flush
  nil
end

#formatter=(value) ⇒ void

This method returns an undefined value.

Set the formatter used for log entries. This can be an EntryFormatter, a standard Logger::Formatter, or any callable object that formats log entries.

Parameters:



137
138
139
# File 'lib/lumberjack/logger.rb', line 137

def formatter=(value)
  @formatter = build_entry_formatter(value)
end

#inspectString

Return a human-readable representation of the logger showing its key configuration.

Returns:

  • (String)

    A string representation of the logger.



268
269
270
271
# File 'lib/lumberjack/logger.rb', line 268

def inspect
  formatted_object_id = object_id.to_s(16).rjust(16, "0")
  "#<Lumberjack::Logger:0x#{formatted_object_id} level:#{Severity.level_to_label(level)} device:#{device.class.name} progname:#{progname.inspect} attributes:#{attributes.inspect}>"
end

#message_formatterLumberjack::Formatter

Get the message formatter used to format log messages.

Returns:



161
162
163
# File 'lib/lumberjack/logger.rb', line 161

def message_formatter
  formatter.message_formatter
end

#message_formatter=(value) ⇒ void

This method returns an undefined value.

Set the message formatter used to format log messages.

Parameters:



169
170
171
# File 'lib/lumberjack/logger.rb', line 169

def message_formatter=(value)
  formatter.message_formatter = value
end

#reopen(logdev = nil) ⇒ Lumberjack::Logger

Reopen the logging device.

Parameters:

  • logdev (Object) (defaults to: nil)

    passed through to the logging device.

Returns:



218
219
220
221
222
# File 'lib/lumberjack/logger.rb', line 218

def reopen(logdev = nil)
  @closed = false
  device.reopen(logdev) if device.respond_to?(:reopen)
  self
end