class Logtail::Logger
The Logtail
Logger
behaves exactly like the standard Ruby `::Logger`, except that it supports a transparent API for logging structured data and events.
@example Basic logging
logger.info "Payment rejected for customer #{customer_id}"
@example Logging an event
logger.info "Payment rejected", payment_rejected: {customer_id: customer_id, amount: 100}
Public Class Methods
Creates a new Logtail::Logger
instance where the passed argument is an IO device. That is, anything that responds to `#write` and `#close`.
Note, this method does not accept the same arguments as the standard Ruby `::Logger`. The Ruby `::Logger` accepts additional options controlling file rotation if the first argument is a file name. This is a design flaw that Logtail
does not assume. Logging to a file, or multiple IO devices is demonstrated in the examples below.
@example Logging to STDOUT
logger = Logtail::Logger.new(STDOUT)
@example Logging to the Logtail
HTTP device
http_device = Logtail::LogDevices::HTTP.new("my-logtail-source-token") logger = Logtail::Logger.new(http_device)
@example Logging to a file (with rotation)
file_device = Logger::LogDevice.new("path/to/file.log") logger = Logtail::Logger.new(file_device)
@example Logging to a file and the Logtail
HTTP device (multiple log devices)
http_device = Logtail::LogDevices::HTTP.new("my-logtail-source-token") file_logger = ::Logger.new("path/to/file.log") logger = Logtail::Logger.new(http_device, file_logger)
# File lib/logtail/logger.rb, line 149 def initialize(*io_devices_and_loggers) if io_devices_and_loggers.size == 0 raise ArgumentError.new("At least one IO device or Logger must be provided when " + "instantiating a Logtail::Logger. Ex: Logtail::Logger.new(STDOUT).") end @extra_loggers = io_devices_and_loggers[1..-1].collect do |obj| if is_a_logger?(obj) obj else self.class.new(obj) end end io_device = io_devices_and_loggers[0] super(io_device) # Ensure we sync STDOUT to avoid buffering if io_device.respond_to?(:"sync=") io_device.sync = true end # Set the default formatter. The formatter cannot be set during # initialization, and can be changed with #formatter=. if io_device.is_a?(LogDevices::HTTP) self.formatter = PassThroughFormatter.new elsif Config.instance.development? || Config.instance.test? self.formatter = MessageOnlyFormatter.new else self.formatter = JSONFormatter.new end self.level = environment_level after_initialize if respond_to?(:after_initialize) Logtail::Config.instance.debug { "Logtail::Logger instantiated, level: #{level}, formatter: #{formatter.class}" } @initialized = true end
Public Instance Methods
Patch to ensure that the {#level} method is used instead of `@level`. This is required because of Rails' monkey patching on Logger
via `::LoggerSilence`.
# File lib/logtail/logger.rb, line 218 def add(severity, message = nil, progname = nil, &block) return true if @logdev.nil? || (severity || UNKNOWN) < level @extra_loggers.each do |logger| logger.add(severity, message, progname, &block) end super end
Sets a new formatted on the logger.
@note The formatter cannot be changed if you are using the HTTP logger backend.
# File lib/logtail/logger.rb, line 194 def formatter=(value) if @initialized && @logdev && @logdev.dev.is_a?(Logtail::LogDevices::HTTP) && !value.is_a?(PassThroughFormatter) raise ArgumentError.new("The formatter cannot be changed when using the " + "Logtail::LogDevices::HTTP log device. The PassThroughFormatter must be used for proper " + "delivery.") end super end
# File lib/logtail/logger.rb, line 204 def level=(value) if value.is_a?(Symbol) value = level_from_symbol(value) end super end
@private
# File lib/logtail/logger.rb, line 212 def with_context(context, &block) Logtail::CurrentContext.with(context, &block) end
Private Instance Methods
# File lib/logtail/logger.rb, line 249 def environment_level level = ([ENV['LOG_LEVEL'].to_s.upcase, "DEBUG"] & %w[DEBUG INFO WARN ERROR FATAL UNKNOWN]).compact.first self.class.const_get(level) end
# File lib/logtail/logger.rb, line 266 def is_a_logger?(obj) obj.respond_to?(:debug) && obj.respond_to?(:info) && obj.respond_to?(:warn) end
# File lib/logtail/logger.rb, line 254 def level_from_symbol(value) case value when :debug; DEBUG when :info; INFO when :warn; WARN when :error; ERROR when :fatal; FATAL when :unknown; UNKNOWN else; raise ArgumentError.new("level #{value.inspect} is not a valid logger level") end end