Class: Lumberjack::Logger

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

Overview

Logger is a thread safe logging object. It has a compatible API with the Ruby standard library Logger class, the Log4r gem, and ActiveSupport::BufferedLogger.

Example

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

Log entries are written to a logging Device if their severity meets or exceeds the log level.

Devices may use buffers internally and the log entries are not guaranteed to be written until you call the flush method. Sometimes this can result in problems when trying to track down extraordinarily long running sections of code since it is likely that none of the messages logged before the long running code will appear in the log until the entire process finishes. You can set the :flush_seconds option on the constructor to force the device to be flushed periodically. This will create a new monitoring thread, but its use is highly recommended.

Each log entry records the log message and severity along with the time it was logged, the program name, process id, and unit of work id. The message will be converted to a string, but otherwise, it is up to the device how these values are recorded. Messages are converted to strings using a Formatter associated with the logger.

Constant Summary

Constants included from Severity

Severity::DEBUG, Severity::ERROR, Severity::FATAL, Severity::INFO, Severity::SEVERITY_LABELS, Severity::UNKNOWN, Severity::WARN

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Severity

label_to_level, level_to_label

Constructor Details

#initialize(device = STDOUT, options = {}) ⇒ Logger

Create a new logger to log to a Device.

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

If it is a Device object, that object will be used. If it has a write method, it will be wrapped in a Device::Writer class. If it is :null, it will be a Null device that won’t record any output. Otherwise, it will be assumed to be file path and wrapped in a Device::LogFile class.

This method can take the following options:

  • :level - The logging level below which messages will be ignored.

  • :formatter - The formatter to use for outputting messages to the log.

  • :datetime_format - The format to use for log timestamps.

  • :tag_formatter - The TagFormatter to use for formatting tags.

  • :progname - The name of the program that will be recorded with each log entry.

  • :flush_seconds - The maximum number of seconds between flush calls.

  • :roll - If the log device is a file path, it will be a Device::DateRollingLogFile if this is set.

  • :max_size - If the log device is a file path, it will be a Device::SizeRollingLogFile if this is set.

All other options are passed to the device constuctor.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/lumberjack/logger.rb', line 66

def initialize(device = STDOUT, options = {})
  options = options.dup
  self.level = options.delete(:level) || INFO
  self.progname = options.delete(:progname)
  max_flush_seconds = options.delete(:flush_seconds).to_f

  @device = open_device(device, options) if device
  self.formatter = (options[:formatter] || Formatter.new)
  @tag_formatter = (options[:tag_formatter] || TagFormatter.new)
  time_format = (options[:datetime_format] || options[:time_format])
  self.datetime_format = time_format if time_format
  @last_flushed_at = Time.now
  @silencer = true
  @tags = {}
  @closed = false

  create_flusher_thread(max_flush_seconds) if max_flush_seconds > 0
end

Instance Attribute Details

#deviceObject

The device being written to



40
41
42
# File 'lib/lumberjack/logger.rb', line 40

def device
  @device
end

#last_flushed_atObject (readonly)

The time that the device was last flushed.



31
32
33
# File 'lib/lumberjack/logger.rb', line 31

def last_flushed_at
  @last_flushed_at
end

#prognameObject

Get the program name associated with log messages.



322
323
324
# File 'lib/lumberjack/logger.rb', line 322

def progname
  thread_local_value(:lumberjack_logger_progname) || @progname
end

#silencerObject

Set silencer to false to disable silencing the log.



34
35
36
# File 'lib/lumberjack/logger.rb', line 34

def silencer
  @silencer
end

#tag_formatterObject

The TagFormatter used for formatting tags for output



43
44
45
# File 'lib/lumberjack/logger.rb', line 43

def tag_formatter
  @tag_formatter
end

Instance Method Details

#<<(msg) ⇒ Object



287
288
289
# File 'lib/lumberjack/logger.rb', line 287

def <<(msg)
  add_entry(UNKNOWN, msg)
end

#add(severity, message = nil, progname = nil, &block) ⇒ Object Also known as: log

::Logger compatible method to add a log entry.



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/lumberjack/logger.rb', line 194

def add(severity, message = nil, progname = nil, &block)
  if message.nil?
    if block
      message = block
    else
      message = progname
      progname = nil
    end
  end
  add_entry(severity, message, progname)
end

#add_entry(severity, message, progname = nil, tags = nil) ⇒ Object

Add a message to the log with a given severity. The message can be either passed in the message argument or supplied with a block. This method is not normally called. Instead call one of the helper functions fatal, error, warn, info, or debug.

The severity can be passed in either as one of the Severity constants, or as a Severity label.

Example

logger.add_entry(Logger::ERROR, exception)
logger.add_entry(Logger::INFO, "Request completed")
logger.add_entry(:warn, "Request took a long time")
logger.add_entry(Logger::DEBUG){"Start processing with options #{options.inspect}"}


158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/lumberjack/logger.rb', line 158

def add_entry(severity, message, progname = nil, tags = nil)
  begin        
    severity = Severity.label_to_level(severity) unless severity.is_a?(Integer)
    return true unless device && severity && severity >= level
    
    return true if Thread.current[:lumberjack_logging]
    Thread.current[:lumberjack_logging] = true

    time = Time.now
    message = message.call if message.is_a?(Proc)
    message = formatter.format(message)
    progname ||= self.progname

    current_tags = self.tags
    tags = nil unless tags.is_a?(Hash)
    if current_tags.empty?
      tags = Tags.stringify_keys(tags) unless tags.nil?
    else
      if tags.nil?
        tags = current_tags.dup
      else
        tags = current_tags.merge(Tags.stringify_keys(tags))
      end
    end
    tags = Tags.expand_runtime_values(tags)
    tags = tag_formatter.format(tags) if tag_formatter

    entry = LogEntry.new(time, severity, message, progname, $$, tags)
    write_to_device(entry)
  ensure
    Thread.current[:lumberjack_logging] = nil
  end
  true
end

#closeObject

Close the logging device.



216
217
218
219
220
# File 'lib/lumberjack/logger.rb', line 216

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

#closed?Boolean

Returns:

  • (Boolean)


222
223
224
# File 'lib/lumberjack/logger.rb', line 222

def closed?
  @closed
end

#datetime_formatObject

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



86
87
88
# File 'lib/lumberjack/logger.rb', line 86

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

#datetime_format=(format) ⇒ Object

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



91
92
93
94
95
# File 'lib/lumberjack/logger.rb', line 91

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

#debug(message_or_progname_or_tags = nil, progname_or_tags = nil, &block) ⇒ Object

Log a DEBUG message. The message can be passed in either the message argument or in a block.



272
273
274
# File 'lib/lumberjack/logger.rb', line 272

def debug(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
  call_add_entry(DEBUG, message_or_progname_or_tags, progname_or_tags, &block)
end

#debug?Boolean

Return true if DEBUG messages are being logged.

Returns:

  • (Boolean)


277
278
279
# File 'lib/lumberjack/logger.rb', line 277

def debug?
  level <= DEBUG
end

#error(message_or_progname_or_tags = nil, progname_or_tags = nil, &block) ⇒ Object

Log an ERROR message. The message can be passed in either the message argument or in a block.



242
243
244
# File 'lib/lumberjack/logger.rb', line 242

def error(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
  call_add_entry(ERROR, message_or_progname_or_tags, progname_or_tags, &block)
end

#error?Boolean

Return true if ERROR messages are being logged.

Returns:

  • (Boolean)


247
248
249
# File 'lib/lumberjack/logger.rb', line 247

def error?
  level <= ERROR
end

#fatal(message_or_progname_or_tags = nil, progname_or_tags = nil, &block) ⇒ Object

Log a FATAL message. The message can be passed in either the message argument or in a block.



232
233
234
# File 'lib/lumberjack/logger.rb', line 232

def fatal(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
  call_add_entry(FATAL, message_or_progname_or_tags, progname_or_tags, &block)
end

#fatal?Boolean

Return true if FATAL messages are being logged.

Returns:

  • (Boolean)


237
238
239
# File 'lib/lumberjack/logger.rb', line 237

def fatal?
  level <= FATAL
end

#flushObject

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



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

def flush
  device.flush
  @last_flushed_at = Time.now
  nil
end

#formatterObject

Get the Lumberjack::Formatter used to format objects for logging as messages.



123
124
125
126
127
128
129
130
# File 'lib/lumberjack/logger.rb', line 123

def formatter
  if respond_to?(:tagged)
    # Wrap in an object that supports ActiveSupport::TaggedLogger API
    TaggedLoggerSupport::Formatter.new(logger: self, formatter: @_formatter)
  else
    @_formatter
  end
end

#formatter=(value) ⇒ Object

Set the Lumberjack::Formatter used to format objects for logging as messages.



118
119
120
# File 'lib/lumberjack/logger.rb', line 118

def formatter=(value)
  @_formatter = (value.is_a?(TaggedLoggerSupport::Formatter) ? value.__formatter : value)
end

#info(message_or_progname_or_tags = nil, progname_or_tags = nil, &block) ⇒ Object

Log an INFO message. The message can be passed in either the message argument or in a block.



262
263
264
# File 'lib/lumberjack/logger.rb', line 262

def info(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
  call_add_entry(INFO, message_or_progname_or_tags, progname_or_tags, &block)
end

#info?Boolean

Return true if INFO messages are being logged.

Returns:

  • (Boolean)


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

def info?
  level <= INFO
end

#levelObject Also known as: sev_threshold

Get the level of severity of entries that are logged. Entries with a lower severity level will be ignored.



99
100
101
# File 'lib/lumberjack/logger.rb', line 99

def level
  thread_local_value(:lumberjack_logger_level) || @level
end

#level=(value) ⇒ Object Also known as: sev_threshold=

Set the log level using either an integer level like Logger::INFO or a label like :info or “info”



107
108
109
110
111
112
113
# File 'lib/lumberjack/logger.rb', line 107

def level=(value)
  if value.is_a?(Integer)
    @level = value
  else
    @level = Severity::label_to_level(value)
  end
end

#reopen(logdev = nil) ⇒ Object



226
227
228
229
# File 'lib/lumberjack/logger.rb', line 226

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

#set_progname(value, &block) ⇒ Object

Set the program name that is associated with log messages. If a block is given, the program name will be valid only within the block.



313
314
315
316
317
318
319
# File 'lib/lumberjack/logger.rb', line 313

def set_progname(value, &block)
  if block
    push_thread_local_value(:lumberjack_logger_progname, value, &block)
  else
    self.progname = value
  end
end

#silence(temporary_level = ERROR, &block) ⇒ Object

Silence the logger by setting a new log level inside a block. By default, only ERROR or FATAL messages will be logged.

Example

logger.level = Logger::INFO
logger.silence do
  do_something   # Log level inside the block is +ERROR+
end


300
301
302
303
304
305
306
307
308
309
# File 'lib/lumberjack/logger.rb', line 300

def silence(temporary_level = ERROR, &block)
  if silencer
    unless temporary_level.is_a?(Integer)
      temporary_level = Severity::label_to_level(temporary_level)
    end
    push_thread_local_value(:lumberjack_logger_level, temporary_level, &block)
  else
    yield
  end
end

#tag(tags, &block) ⇒ Object

Set a hash of tags on logger. If a block is given, the tags will only be set for the duration of the block.



328
329
330
331
332
333
334
335
336
337
# File 'lib/lumberjack/logger.rb', line 328

def tag(tags, &block)
  tags = Tags.stringify_keys(tags)
  if block
    thread_tags = thread_local_value(:lumberjack_logger_tags)
    value = (thread_tags ? thread_tags.merge(tags) : tags)
    push_thread_local_value(:lumberjack_logger_tags, value, &block)
  else
    @tags.merge!(tags)
  end
end

#tagged_logger!Object

Enable this logger to function like an ActiveSupport::TaggedLogger. This will make the logger API compatible with ActiveSupport::TaggedLogger and is provided as a means of compatibility with other libraries that assume they can call the ‘tagged` method on a logger to add tags.

The tags added with this method are just strings so they are stored in the logger tags in an array under the “tagged” tag. So calling ‘logger.tagged(“foo”, “bar”)` will result in tags `=> [“foo”, “bar”]`.



139
140
141
142
# File 'lib/lumberjack/logger.rb', line 139

def tagged_logger!
  self.extend(TaggedLoggerSupport)
  self
end

#tagsObject

Return all tags in scope on the logger including global tags set on the Lumberjack context, tags set on the logger, and tags set on the current block for the logger



341
342
343
344
345
346
347
348
349
# File 'lib/lumberjack/logger.rb', line 341

def tags
  tags = {}
  context_tags = Lumberjack.context_tags
  tags.merge!(context_tags) if context_tags && !context_tags.empty?
  tags.merge!(@tags) if !@tags.empty?
  scope_tags = thread_local_value(:lumberjack_logger_tags)
  tags.merge!(scope_tags) if scope_tags && !scope_tags.empty?
  tags
end

#unknown(message_or_progname_or_tags = nil, progname_or_tags = nil, &block) ⇒ Object

Log a message when the severity is not known. Unknown messages will always appear in the log. The message can be passed in either the message argument or in a block.



283
284
285
# File 'lib/lumberjack/logger.rb', line 283

def unknown(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
  call_add_entry(UNKNOWN, message_or_progname_or_tags, progname_or_tags, &block)
end

#warn(message_or_progname_or_tags = nil, progname_or_tags = nil, &block) ⇒ Object

Log a WARN message. The message can be passed in either the message argument or in a block.



252
253
254
# File 'lib/lumberjack/logger.rb', line 252

def warn(message_or_progname_or_tags = nil, progname_or_tags = nil, &block)
  call_add_entry(WARN, message_or_progname_or_tags, progname_or_tags, &block)
end

#warn?Boolean

Return true if WARN messages are being logged.

Returns:

  • (Boolean)


257
258
259
# File 'lib/lumberjack/logger.rb', line 257

def warn?
  level <= WARN
end