Module: Lumberjack::ContextLogger

Included in:
ForkedLogger, Logger
Defined in:
lib/lumberjack/context_logger.rb

Overview

ContextLogger provides a logging interface with support for contextual attributes, level management, and program name scoping. This module is included by Logger and ForkedLogger to provide a common API for structured logging.

Key features include:

  • Context-aware attribute management with tag/untag methods

  • Scoped logging levels and program names

  • Compatibility with Ruby’s standard Logger API

  • Support for forking isolated logger contexts

Constant Summary collapse

TRACE =

Constant used for setting trace log level.

Severity::TRACE
LEADING_OR_TRAILING_WHITESPACE =
/(?:\A\s)|(?:\s\z)/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



28
29
30
31
# File 'lib/lumberjack/context_logger.rb', line 28

def included(base)
  base.include(ContextLocals) unless base.include?(ContextLocals)
  base.include(IOCompatibility) unless base.include?(IOCompatibility)
end

Instance Method Details

#<<(msg) ⇒ void

This method returns an undefined value.

Add a message when the severity is not known.

Parameters:

  • msg (Object)

    The message to log.



302
303
304
# File 'lib/lumberjack/context_logger.rb', line 302

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

#add(severity, message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ true Also known as: log

::Logger compatible method to add a log entry.

Parameters:

  • severity (Integer, Symbol, String)

    The severity of the message.

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log, progname, or attributes.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program or attributes.

Returns:

  • (true)


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

def add(severity, message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  # This convoluted logic is to have API compatibility with ::Logger#add.
  severity ||= Logger::UNKNOWN
  if message_or_progname_or_attributes.nil? && !progname_or_attributes.is_a?(Hash)
    message_or_progname_or_attributes = progname_or_attributes
    progname_or_attributes = nil
  end
  call_add_entry(severity, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#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. This method must be implemented by the class that includes this module.

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.

Raises:

  • (NotImplementedError)


522
523
524
# File 'lib/lumberjack/context_logger.rb', line 522

def add_entry(severity, message, progname = nil, attributes = nil)
  raise NotImplementedError
end

#append_to(attribute_name, *tags, &block) ⇒ Object, Lumberjack::Logger

Append a value to an attribute. This method can be used to add “tags” to a logger by appending values to the same attribute. The tag values will be appended to any value that is already in the attribute. If a block is passed, then a new context will be opened as well. If no block is passed, then the values will be appended to the attribute in the current context. If there is no current context, then nothing will happen.

Parameters:

  • attribute_name (String, Symbol)

    The name of the attribute to append values to.

  • tags (Array<String, Symbol, Hash>)

    The tags to add.

Returns:

  • (Object, Lumberjack::Logger)

    If a block is passed then returns the result of the block. Otherwise returns self so that calls can be chained.



388
389
390
391
392
393
394
395
396
# File 'lib/lumberjack/context_logger.rb', line 388

def append_to(attribute_name, *tags, &block)
  return self unless block || in_context?

  current_tags = attribute_value(attribute_name) || []
  current_tags = [current_tags] unless current_tags.is_a?(Array)
  new_tags = current_tags + tags.flatten

  tag(attribute_name => new_tags, &block)
end

#attribute_value(name) ⇒ Object?

Get the value of an attribute by name from the current context.

Parameters:

  • name (String, Symbol)

    The name of the attribute to get.

Returns:

  • (Object, nil)

    The value of the attribute or nil if the attribute does not exist.



487
488
489
490
# File 'lib/lumberjack/context_logger.rb', line 487

def attribute_value(name)
  name = name.join(".") if name.is_a?(Array)
  AttributesHelper.new(attributes)[name]
end

#attributesHash

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

Returns:

  • (Hash)


479
480
481
# File 'lib/lumberjack/context_logger.rb', line 479

def attributes
  merge_all_attributes || {}
end

#clear_attributes(&block) ⇒ void

This method returns an undefined value.

Remove all attributes on the current logger and logging context within a block. You can still set new block scoped attributes within the block and provide attributes on individual log methods.



497
498
499
500
501
502
503
504
505
# File 'lib/lumberjack/context_logger.rb', line 497

def clear_attributes(&block)
  new_context_locals do |locals|
    locals.cleared = true
    context do |ctx|
      ctx.clear_attributes
      block.call
    end
  end
end

#context(&block) {|Context| ... } ⇒ Object

Set up a context block for the logger. All attributes added within the block will be cleared when the block exits.

Parameters:

  • block (Proc)

    The block to execute with the context.

Yields:

Returns:

  • (Object)

    The result of the block.



404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/lumberjack/context_logger.rb', line 404

def context(&block)
  unless block_given?
    raise ArgumentError, "A block must be provided to the context method"
  end

  new_context = Context.new(current_context)
  new_context.parent = local_context
  new_context_locals do |locals|
    locals.context = new_context
    block.call(new_context)
  end
end

#debug(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ true

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

Parameters:

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log or progname if the message is passed in a block.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program that is logging the message or attributes if the message is passed in a block.

Returns:

  • (true)


241
242
243
# File 'lib/lumberjack/context_logger.rb', line 241

def debug(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  call_add_entry(Logger::DEBUG, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#debug!void

This method returns an undefined value.

Set the log level to debug.



255
256
257
# File 'lib/lumberjack/context_logger.rb', line 255

def debug!
  self.level = Logger::DEBUG
end

#debug?Boolean

Return true if DEBUG messages are being logged.

Returns:

  • (Boolean)


248
249
250
# File 'lib/lumberjack/context_logger.rb', line 248

def debug?
  level <= Logger::DEBUG
end

#default_severityInteger

Get the default severity used when writing log messages directly to a stream.

Returns:

  • (Integer)

    The default severity level.



102
103
104
# File 'lib/lumberjack/context_logger.rb', line 102

def default_severity
  current_context&.default_severity || default_context&.default_severity || Logger::UNKNOWN
end

#default_severity=(value) ⇒ void

This method returns an undefined value.

Set the default severity used when writing log messages directly to a stream for the current context.

Parameters:

  • value (Integer, Symbol, String)

    The default severity level.



111
112
113
114
# File 'lib/lumberjack/context_logger.rb', line 111

def default_severity=(value)
  ctx = current_context
  ctx.default_severity = value if ctx
end

#ensure_context(&block) ⇒ Object

Ensure that the block of code is wrapped by a context. If there is not already a context in scope for this logger, one will be created.

Returns:

  • (Object)

    The result of the block.



421
422
423
424
425
426
427
# File 'lib/lumberjack/context_logger.rb', line 421

def ensure_context(&block)
  if in_context?
    yield
  else
    context(&block)
  end
end

#error(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ true

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

Parameters:

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log or progname if the message is passed in a block.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program that is logging the message or attributes if the message is passed in a block.

Returns:

  • (true)


166
167
168
# File 'lib/lumberjack/context_logger.rb', line 166

def error(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  call_add_entry(Logger::ERROR, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#error!void

This method returns an undefined value.

Set the log level to error.



180
181
182
# File 'lib/lumberjack/context_logger.rb', line 180

def error!
  self.level = Logger::ERROR
end

#error?Boolean

Return true if ERROR messages are being logged.

Returns:

  • (Boolean)


173
174
175
# File 'lib/lumberjack/context_logger.rb', line 173

def error?
  level <= Logger::ERROR
end

#fatal(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ true

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

Parameters:

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log or progname if the message is passed in a block.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program that is logging the message or attributes if the message is passed in a block.

Returns:

  • (true)


141
142
143
# File 'lib/lumberjack/context_logger.rb', line 141

def fatal(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  call_add_entry(Logger::FATAL, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#fatal!void

This method returns an undefined value.

Set the log level to fatal.



155
156
157
# File 'lib/lumberjack/context_logger.rb', line 155

def fatal!
  self.level = Logger::FATAL
end

#fatal?Boolean

Return true if FATAL messages are being logged.

Returns:

  • (Boolean)


148
149
150
# File 'lib/lumberjack/context_logger.rb', line 148

def fatal?
  level <= Logger::FATAL
end

#fork(level: nil, progname: nil, attributes: nil) ⇒ ForkedLogger

Forks a new logger with a new context that will send output through this logger. The new logger will inherit the level, progname, and attributes of the current logger context. Any changes to those values, though, will be isolated to just the forked logger. Any calls to log messages will be forwarded to the parent logger for output to the logging device.

Examples:

Creating a forked logger

child_logger = logger.fork(level: :debug, progname: "Child")
child_logger.debug("This goes to the parent logger's device")

Parameters:

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

    The level to set on the new logger. If this is not specified, then the level on the parent logger will be used.

  • progname (String, nil) (defaults to: nil)

    The progname to set on the new logger. If this is not specified, then the progname on the parent logger will be used.

  • attributes (Hash, nil) (defaults to: nil)

    The attributes to set on the new logger. The forked logger will inherit all attributes from the current logging context.

Returns:



446
447
448
449
450
451
452
453
# File 'lib/lumberjack/context_logger.rb', line 446

def fork(level: nil, progname: nil, attributes: nil)
  logger = ForkedLogger.new(self)
  logger.level = level if level
  logger.progname = progname if progname
  logger.tag!(attributes) if attributes
  logger.isolation_level = isolation_level
  logger
end

#in_context?Boolean

Return true if the thread is currently in a context block with a local context.

Returns:

  • (Boolean)


510
511
512
# File 'lib/lumberjack/context_logger.rb', line 510

def in_context?
  !!local_context
end

#info(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ true

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

Parameters:

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log or progname if the message is passed in a block.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program that is logging the message or attributes if the message is passed in a block.

Returns:

  • (true)


216
217
218
# File 'lib/lumberjack/context_logger.rb', line 216

def info(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  call_add_entry(Logger::INFO, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#info!void

This method returns an undefined value.

Set the log level to info.



230
231
232
# File 'lib/lumberjack/context_logger.rb', line 230

def info!
  self.level = Logger::INFO
end

#info?Boolean

Return true if INFO messages are being logged.

Returns:

  • (Boolean)


223
224
225
# File 'lib/lumberjack/context_logger.rb', line 223

def info?
  level <= Logger::INFO
end

#levelInteger Also known as: sev_threshold

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

Returns:

  • (Integer)

    The severity level.



38
39
40
# File 'lib/lumberjack/context_logger.rb', line 38

def level
  current_context&.level || default_context&.level
end

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

This method returns an undefined value.

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

Parameters:

  • value (Integer, Symbol, String)

    The severity level.



49
50
51
52
53
54
# File 'lib/lumberjack/context_logger.rb', line 49

def level=(value)
  value = Severity.coerce(value) unless value.nil?

  ctx = current_context
  ctx.level = value if ctx
end

#prognameString?

Get the current progname.

Returns:

  • (String, nil)


83
84
85
# File 'lib/lumberjack/context_logger.rb', line 83

def progname
  current_context&.progname || default_context&.progname
end

#progname=(value) ⇒ void

This method returns an undefined value.

Set the logger progname for the current context. This is the name of the program that is logging.

Parameters:

  • value (String, nil)


73
74
75
76
77
78
# File 'lib/lumberjack/context_logger.rb', line 73

def progname=(value)
  value = value&.to_s&.freeze

  ctx = current_context
  ctx.progname = value if ctx
end

#tag(attributes, &block) ⇒ Object, Lumberjack::ContextLogger

Tag the logger with a set of attributes. If a block is given, the attributes will only be set for the duration of the block. Otherwise the attributes will be applied on the current logger context for the duration of the current context. If there is no current context, then a new logger object will be returned with those attributes set on it.

Examples:

# Only applies the attributes inside the block
logger.tag(foo: "bar") do
  logger.info("message")
end
# Only applies the attributes inside the context block
logger.context do
  logger.tag(foo: "bar")
  logger.info("message")
end

Parameters:

  • attributes (Hash)

    The attributes to set.

Returns:

  • (Object, Lumberjack::ContextLogger)

    If a block is given then the result of the block is returned. Otherwise it returns the logger itself so you can chain methods.



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

def tag(attributes, &block)
  if block
    context do |ctx|
      ctx.assign_attributes(attributes)
      block.call(ctx)
    end
  else
    local_context&.assign_attributes(attributes)
    self
  end
end

#tag!(attributes) ⇒ nil

Tags the logger with a set of persistent attributes. These attributes will be included on every log entry and are not tied to a context block. If the logger does not have a default context, then these will be ignored.

Examples:

logger.tag!(version: "1.2.3", environment: "production")
logger.info("Server started") # Will include version and environment attributes

Parameters:

  • attributes (Hash)

    The attributes to set persistently on the logger.

Returns:

  • (nil)


348
349
350
351
# File 'lib/lumberjack/context_logger.rb', line 348

def tag!(attributes)
  default_context&.assign_attributes(attributes)
  nil
end

#tag_all_contexts(attributes) ⇒ nil

Tags the outermost context with a set of attributes. If there is no outermost context, then nothing will happen. This method can be used to bubble attributes up to the top level context. It can be used in situations where you want to ensure a set of attributes are set for the rest of the request or operation defined by the outmermost context.

Examples:

logger.tag(request_id: "12345") do
  logger.tag(action: "login") do
    # Add the user_id attribute to the outermost context along with request_id so that
    # it doesn't fall out of scope after this tag block ends.
    logger.tag_all_contexts(user_id: "67890")
  end
end

Parameters:

  • attributes (Hash)

    The attributes to set on the outermost context.

Returns:

  • (nil)


369
370
371
372
373
374
375
376
# File 'lib/lumberjack/context_logger.rb', line 369

def tag_all_contexts(attributes)
  parent_context = local_context
  while parent_context
    parent_context.assign_attributes(attributes)
    parent_context = parent_context.parent
  end
  nil
end

#trace(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ true

Log a TRACE message. The message can be passed in either the message argument or in a block. Trace logs are a level lower than debug and are generally used to log code execution paths for low level debugging.

Parameters:

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log or progname if the message is passed in a block.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program that is logging the message or attributes if the message is passed in a block.

Returns:

  • (true)


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

def trace(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  call_add_entry(TRACE, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#trace!void

This method returns an undefined value.

Set the log level to trace.



282
283
284
# File 'lib/lumberjack/context_logger.rb', line 282

def trace!
  self.level = TRACE
end

#trace?Boolean

Return true if TRACE messages are being logged.

Returns:

  • (Boolean)


275
276
277
# File 'lib/lumberjack/context_logger.rb', line 275

def trace?
  level <= TRACE
end

#unknown(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ void

This method returns an undefined value.

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.

Parameters:

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log or progname if the message is passed in a block.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program that is logging the message or attributes if the message is passed in a block.



294
295
296
# File 'lib/lumberjack/context_logger.rb', line 294

def unknown(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  call_add_entry(Logger::UNKNOWN, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#untag(*attribute_names) ⇒ void

This method returns an undefined value.

Remove attributes from the current context block.

Parameters:

  • attribute_names (Array<String, Symbol>)

    The attributes to remove.



459
460
461
462
463
# File 'lib/lumberjack/context_logger.rb', line 459

def untag(*attribute_names)
  attributes = local_context&.attributes
  AttributesHelper.new(attributes).delete(*attribute_names) if attributes
  nil
end

#untag!(*attribute_names) ⇒ void

This method returns an undefined value.

Remove attributes from the default context for the logger.

Parameters:

  • attribute_names (Array<String, Symbol>)

    The attributes to remove.



469
470
471
472
473
# File 'lib/lumberjack/context_logger.rb', line 469

def untag!(*attribute_names)
  attributes = default_context&.attributes
  AttributesHelper.new(attributes).delete(*attribute_names) if attributes
  nil
end

#warn(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block) ⇒ true

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

Parameters:

  • message_or_progname_or_attributes (Object) (defaults to: nil)

    The message to log or progname if the message is passed in a block.

  • progname_or_attributes (String, Hash) (defaults to: nil)

    The name of the program that is logging the message or attributes if the message is passed in a block.

Returns:

  • (true)


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

def warn(message_or_progname_or_attributes = nil, progname_or_attributes = nil, &block)
  call_add_entry(Logger::WARN, message_or_progname_or_attributes, progname_or_attributes, &block)
end

#warn!void

This method returns an undefined value.

Set the log level to warn.



205
206
207
# File 'lib/lumberjack/context_logger.rb', line 205

def warn!
  self.level = Logger::WARN
end

#warn?Boolean

Return true if WARN messages are being logged.

Returns:

  • (Boolean)


198
199
200
# File 'lib/lumberjack/context_logger.rb', line 198

def warn?
  level <= Logger::WARN
end

#with_level(severity, &block) ⇒ Object

Adjust the log level during the block execution for the current Fiber only.

Parameters:

  • severity (Integer, Symbol, String)

    The severity level.

Returns:

  • (Object)

    The result of the block.



62
63
64
65
66
67
# File 'lib/lumberjack/context_logger.rb', line 62

def with_level(severity, &block)
  context do |ctx|
    ctx.level = severity
    block.call(ctx)
  end
end

#with_progname(value) {|Object| ... } ⇒ Object

Set the logger progname for the duration of the block.

Parameters:

  • value (String)

    The program name to use.

Yields:

  • (Object)

    The block to execute with the program name set.

Returns:

  • (Object)

    The result of the block.



92
93
94
95
96
97
# File 'lib/lumberjack/context_logger.rb', line 92

def with_progname(value, &block)
  context do |ctx|
    ctx.progname = value
    block.call(ctx)
  end
end