Module: SemanticLogger

Defined in:
lib/semantic_logger.rb,
lib/semantic_logger/log.rb,
lib/semantic_logger/base.rb,
lib/semantic_logger/utils.rb,
lib/semantic_logger/logger.rb,
lib/semantic_logger/version.rb,
lib/semantic_logger/appender.rb,
lib/semantic_logger/loggable.rb,
lib/semantic_logger/processor.rb,
lib/semantic_logger/formatters.rb,
lib/semantic_logger/subscriber.rb,
lib/semantic_logger/ansi_colors.rb,
lib/semantic_logger/appender/tcp.rb,
lib/semantic_logger/appender/udp.rb,
lib/semantic_logger/appender/file.rb,
lib/semantic_logger/appender/http.rb,
lib/semantic_logger/metric/statsd.rb,
lib/semantic_logger/appender/async.rb,
lib/semantic_logger/appender/kafka.rb,
lib/semantic_logger/formatters/raw.rb,
lib/semantic_logger/appender/sentry.rb,
lib/semantic_logger/appender/splunk.rb,
lib/semantic_logger/appender/syslog.rb,
lib/semantic_logger/formatters/base.rb,
lib/semantic_logger/formatters/json.rb,
lib/semantic_logger/metric/signalfx.rb,
lib/semantic_logger/semantic_logger.rb,
lib/semantic_logger/appender/bugsnag.rb,
lib/semantic_logger/appender/graylog.rb,
lib/semantic_logger/appender/mongodb.rb,
lib/semantic_logger/appender/wrapper.rb,
lib/semantic_logger/formatters/color.rb,
lib/semantic_logger/metric/new_relic.rb,
lib/semantic_logger/formatters/syslog.rb,
lib/semantic_logger/appender/new_relic.rb,
lib/semantic_logger/formatters/default.rb,
lib/semantic_logger/formatters/one_line.rb,
lib/semantic_logger/formatters/signalfx.rb,
lib/semantic_logger/appender/async_batch.rb,
lib/semantic_logger/appender/honeybadger.rb,
lib/semantic_logger/appender/splunk_http.rb,
lib/semantic_logger/debug_as_trace_logger.rb,
lib/semantic_logger/formatters/syslog_cee.rb,
lib/semantic_logger/appender/elasticsearch.rb,
lib/semantic_logger/concerns/compatibility.rb,
lib/semantic_logger/appender/elasticsearch_http.rb,
lib/semantic_logger/jruby/garbage_collection_logger.rb

Overview

)

Defined Under Namespace

Modules: AnsiColors, Appender, Concerns, Formatters, JRuby, Loggable, Metric, Utils Classes: Base, DebugAsTraceLogger, Log, Logger, Processor, Subscriber

Constant Summary collapse

VERSION =
'4.3.0'.freeze
LEVELS =

Logging levels in order of most detailed to most severe

%i[trace debug info warn error fatal].freeze

Class Method Summary collapse

Class Method Details

.[](klass) ⇒ Object

Return a logger for the supplied class or class_name



9
10
11
# File 'lib/semantic_logger/semantic_logger.rb', line 9

def self.[](klass)
  SemanticLogger::Logger.new(klass)
end

.add_appender(options, deprecated_level = nil, &block) ⇒ Object

Add a new logging appender as a new destination for all log messages emitted from Semantic Logger

Appenders will be written to in the order that they are added

If a block is supplied then it will be used to customize the format of the messages sent to that appender. See SemanticLogger::Logger.new for more information on custom formatters

Parameters

file_name: [String]
  File name to write log messages to.

Or,
io: [IO]
  An IO Stream to log to.
  For example STDOUT, STDERR, etc.

Or,
appender: [Symbol|SemanticLogger::Subscriber]
  A symbol identifying the appender to create.
  For example:
    :bugsnag, :elasticsearch, :graylog, :http, :mongodb, :new_relic, :splunk_http, :syslog, :wrapper
       Or,
  An instance of an appender derived from SemanticLogger::Subscriber
  For example:
    SemanticLogger::Appender::Http.new(url: 'http://localhost:8088/path')

Or,
logger: [Logger|Log4r]
  An instance of a Logger or a Log4r logger.

level: [:trace | :debug | :info | :warn | :error | :fatal]
  Override the log level for this appender.
  Default: SemanticLogger.default_level

formatter: [Symbol|Object|Proc]
  Any of the following symbol values: :default, :color, :json
    Or,
  An instance of a class that implements #call
    Or,
  A Proc to be used to format the output from this appender
  Default: :default

filter: [Regexp|Proc]
  RegExp: Only include log messages where the class name matches the supplied.
  regular expression. All other messages will be ignored.
  Proc: Only include log messages where the supplied Proc returns true
        The Proc must return true or false.

Examples:

# Send all logging output to Standard Out (Screen)
SemanticLogger.add_appender(io: STDOUT)

# Send all logging output to a file
SemanticLogger.add_appender(file_name: 'logfile.log')

# Send all logging output to a file and only :info and above to standard output
SemanticLogger.add_appender(file_name: 'logfile.log')
SemanticLogger.add_appender(io: STDOUT, level: :info)

Log to log4r, Logger, etc.:

# Send Semantic logging output to an existing logger
require 'logger'
require 'semantic_logger'

# Built-in Ruby logger
log = Logger.new(STDOUT)
log.level = Logger::DEBUG

SemanticLogger.default_level = :debug
SemanticLogger.add_appender(logger: log)

logger = SemanticLogger['Example']
logger.info "Hello World"
logger.debug("Login time", user: 'Joe', duration: 100, ip_address: '127.0.0.1')


154
155
156
157
158
159
160
161
162
# File 'lib/semantic_logger/semantic_logger.rb', line 154

def self.add_appender(options, deprecated_level = nil, &block)
  options  = options.is_a?(Hash) ? options.dup : convert_old_appender_args(options, deprecated_level)
  appender = SemanticLogger::Appender.factory(options, &block)
  @appenders << appender

  # Start appender thread if it is not already running
  SemanticLogger::Processor.start
  appender
end

.add_signal_handler(log_level_signal = 'USR2', thread_dump_signal = 'TTIN', gc_log_microseconds = 100_000) ⇒ Object

Add signal handlers for Semantic Logger

Two signal handlers will be registered by default:

  1. Changing the log_level:

The log level can be changed without restarting the process by sending the
log_level_signal, which by default is 'USR2'

When the log_level_signal is raised on this process, the global default log level
rotates through the following log levels in the following order, starting
from the current global default level:
  :warn, :info, :debug, :trace

If the current level is :trace it wraps around back to :warn
  1. Logging a Ruby thread dump

When the signal is raised on this process, Semantic Logger will write the list
of threads to the log file, along with their back-traces when available

For JRuby users this thread dump differs form the standard QUIT triggered
Java thread dump which includes system threads and Java stack traces.

It is recommended to name any threads you create in the application, by
calling the following from within the thread itself:
   Thread.current.name = 'My Worker'

Also adds JRuby Garbage collection logging so that any garbage collections that exceed the time threshold will be logged. Default: 100 ms Currently only supported when running JRuby

Note:

To only register one of the signal handlers, set the other to nil
Set gc_log_microseconds to nil to not enable JRuby Garbage collections


264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/semantic_logger/semantic_logger.rb', line 264

def self.add_signal_handler(log_level_signal = 'USR2', thread_dump_signal = 'TTIN', gc_log_microseconds = 100_000)
  if log_level_signal
    Signal.trap(log_level_signal) do
      index     = default_level == :trace ? LEVELS.find_index(:error) : LEVELS.find_index(default_level)
      new_level = LEVELS[index - 1]
      self['SemanticLogger'].warn "Changed global default log level to #{new_level.inspect}"
      self.default_level = new_level
    end
  end

  if thread_dump_signal
    Signal.trap(thread_dump_signal) do
      logger = SemanticLogger['Thread Dump']
      Thread.list.each do |thread|
        # MRI re-uses the main thread for signals, JRuby uses `SIGTTIN handler` thread.
        next if defined?(JRuby) && (thread == Thread.current)
        logger.backtrace(thread: thread)
      end
    end
  end

  if gc_log_microseconds && defined?(JRuby)
    listener = SemanticLogger::JRuby::GarbageCollectionLogger.new(gc_log_microseconds)
    Java::JavaLangManagement::ManagementFactory.getGarbageCollectorMXBeans.each do |gcbean|
      gcbean.add_notification_listener(listener, nil, nil)
    end
  end

  true
end

.appendersObject

Returns [SemanticLogger::Subscriber] a copy of the list of active appenders for debugging etc. Use SemanticLogger.add_appender and SemanticLogger.remove_appender to manipulate the active appenders list



174
175
176
# File 'lib/semantic_logger/semantic_logger.rb', line 174

def self.appenders
  @appenders.clone
end

.applicationObject

Returns [String] name of this application for logging purposes Note: Not all appenders use ‘application`



65
66
67
# File 'lib/semantic_logger/semantic_logger.rb', line 65

def self.application
  @application
end

.application=(application) ⇒ Object

Override the default application



70
71
72
# File 'lib/semantic_logger/semantic_logger.rb', line 70

def self.application=(application)
  @application = application
end

.backtrace_levelObject

Returns the current backtrace level



42
43
44
# File 'lib/semantic_logger/semantic_logger.rb', line 42

def self.backtrace_level
  @backtrace_level
end

.backtrace_level=(level) ⇒ Object

Sets the level at which backtraces should be captured for every log message.

By enabling backtrace capture the filename and line number of where message was logged can be written to the log file. Additionally, the backtrace can be forwarded to error management services such as Bugsnag.

Warning:

Capturing backtraces is very expensive and should not be done all
the time. It is recommended to run it at :error level in production.


35
36
37
38
39
# File 'lib/semantic_logger/semantic_logger.rb', line 35

def self.backtrace_level=(level)
  @backtrace_level = level
  # For performance reasons pre-calculate the level index
  @backtrace_level_index = level.nil? ? 65_535 : level_to_index(level)
end

.backtrace_level_indexObject

Returns the current backtrace level index For internal use only



48
49
50
# File 'lib/semantic_logger/semantic_logger.rb', line 48

def self.backtrace_level_index
  @backtrace_level_index
end

.closeObject

Close all appenders and flush any outstanding messages.



185
186
187
# File 'lib/semantic_logger/semantic_logger.rb', line 185

def self.close
  SemanticLogger::Processor.instance.close
end

.default_levelObject

Returns the global default log level



21
22
23
# File 'lib/semantic_logger/semantic_logger.rb', line 21

def self.default_level
  @default_level
end

.default_level=(level) ⇒ Object

Sets the global default log level



14
15
16
17
18
# File 'lib/semantic_logger/semantic_logger.rb', line 14

def self.default_level=(level)
  @default_level = level
  # For performance reasons pre-calculate the level index
  @default_level_index = level_to_index(level)
end

.default_level_indexObject



452
453
454
# File 'lib/semantic_logger/semantic_logger.rb', line 452

def self.default_level_index
  Thread.current[:semantic_logger_silence] || @default_level_index
end

.fast_tag(tag) ⇒ Object

If the tag being supplied is definitely a string then this fast tag api can be used for short lived tags



297
298
299
300
301
302
303
304
305
306
307
# File 'lib/semantic_logger/semantic_logger.rb', line 297

def self.fast_tag(tag)
  return yield if tag.nil? || tag == ''

  t = Thread.current[:semantic_logger_tags] ||= []
  begin
    t << tag
    yield
  ensure
    t.pop
  end
end

.flushObject

Flush all queued log entries disk, database, etc.

All queued log messages are written and then each appender is flushed in turn.


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

def self.flush
  SemanticLogger::Processor.instance.flush
end

.hostObject

Returns [String] name of this host for logging purposes Note: Not all appenders use ‘host`



54
55
56
# File 'lib/semantic_logger/semantic_logger.rb', line 54

def self.host
  @host ||= Socket.gethostname.force_encoding('UTF-8')
end

.host=(host) ⇒ Object

Override the default host name



59
60
61
# File 'lib/semantic_logger/semantic_logger.rb', line 59

def self.host=(host)
  @host = host
end

.index_to_level(level_index) ⇒ Object

Returns the symbolic level for the supplied level index



459
460
461
# File 'lib/semantic_logger/semantic_logger.rb', line 459

def self.index_to_level(level_index)
  LEVELS[level_index]
end

.level_to_index(level) ⇒ Object

Internal method to return the log level as an internal index Also supports mapping the ::Logger levels to SemanticLogger levels



467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/semantic_logger/semantic_logger.rb', line 467

def self.level_to_index(level)
  return if level.nil?

  index =
    if level.is_a?(Symbol)
      LEVELS.index(level)
    elsif level.is_a?(String)
      level = level.downcase.to_sym
      LEVELS.index(level)
    elsif level.is_a?(Integer) && defined?(::Logger::Severity)
      # Mapping of Rails and Ruby Logger levels to SemanticLogger levels
      @map_levels ||= begin
        levels = []
        ::Logger::Severity.constants.each do |constant|
          levels[::Logger::Severity.const_get(constant)] =
            LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
        end
        levels
      end
      @map_levels[level]
    end
  raise "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
  index
end

.named_tagged(hash) ⇒ Object

:nodoc

Raises:

  • (ArgumentError)


373
374
375
376
377
378
379
380
381
382
383
# File 'lib/semantic_logger/semantic_logger.rb', line 373

def self.named_tagged(hash)
  return yield if hash.nil? || hash.empty?
  raise(ArgumentError, '#named_tagged only accepts named parameters (Hash)') unless hash.is_a?(Hash)

  begin
    push_named_tags(hash)
    yield
  ensure
    pop_named_tags
  end
end

.named_tagsObject

Returns [Hash] a copy of the named tags currently active for this thread.



386
387
388
389
390
391
392
393
394
395
396
# File 'lib/semantic_logger/semantic_logger.rb', line 386

def self.named_tags
  if (list = Thread.current[:semantic_logger_named_tags]) && !list.empty?
    if list.size > 1
      list.reduce({}) { |sum, h| sum.merge(h) }
    else
      list.first.clone
    end
  else
    {}
  end
end

.on_log(object = nil, &block) ⇒ Object

Supply a callback to be called whenever a log entry is created. Useful for capturing appender specific context information.

Parameters
  object: [Object | Proc]
    [Proc] the block to call.
    [Object] any object on which to call #call.

Example:

SemanticLogger.on_log do |log|
  log.set_context(:honeybadger, Honeybadger.get_context)
end

Example:

module CaptureContext
  def call(log)
    log.set_context(:honeybadger, Honeybadger.get_context)
  end
end
SemanticLogger.on_log(CaptureContext)

Note:

  • This callback is called within the thread of the application making the logging call.

  • If these callbacks are slow they will slow down the application.



225
226
227
# File 'lib/semantic_logger/semantic_logger.rb', line 225

def self.on_log(object = nil, &block)
  Processor.instance.appender.on_log(object, &block)
end

.pop_named_tags(quantity = 1) ⇒ Object



403
404
405
406
# File 'lib/semantic_logger/semantic_logger.rb', line 403

def self.pop_named_tags(quantity = 1)
  t = Thread.current[:semantic_logger_named_tags]
  t&.pop(quantity)
end

.pop_tags(quantity = 1) ⇒ Object

Remove specified number of tags from the current tag list



367
368
369
370
# File 'lib/semantic_logger/semantic_logger.rb', line 367

def self.pop_tags(quantity = 1)
  t = Thread.current[:semantic_logger_tags]
  t&.pop(quantity)
end

.push_named_tags(hash) ⇒ Object



398
399
400
401
# File 'lib/semantic_logger/semantic_logger.rb', line 398

def self.push_named_tags(hash)
  (Thread.current[:semantic_logger_named_tags] ||= []) << hash
  hash
end

.push_tags(*tags) ⇒ Object

Add tags to the current scope

Note:

  • This method does not flatten the array or remove any empty elements, or duplicates since the performance penalty is excessive.

  • To get the flattening behavior use the slower api:

    `logger.push_tags`
    


361
362
363
364
# File 'lib/semantic_logger/semantic_logger.rb', line 361

def self.push_tags(*tags)
  (Thread.current[:semantic_logger_tags] ||= []).concat(tags)
  tags
end

.remove_appender(appender) ⇒ Object

Remove an existing appender Currently only supports appender instances



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

def self.remove_appender(appender)
  @appenders.delete(appender)
end

.reopenObject

After forking an active process call SemanticLogger.reopen to re-open any open file handles etc to resources.

Note:

Not all appenders implement reopen.
Check the code for each appender you are using before relying on this behavior.


195
196
197
198
199
# File 'lib/semantic_logger/semantic_logger.rb', line 195

def self.reopen
  @appenders.each { |appender| appender.reopen if appender.respond_to?(:reopen) }
  # After a fork the appender thread is not running, start it if it is not running.
  SemanticLogger::Processor.start
end

.silence(new_level = :error) ⇒ Object

Silence noisy log levels by changing the default_level within the block

This setting is thread-safe and only applies to the current thread

Any threads spawned within the block will not be affected by this setting

#silence can be used to both raise and lower the log level within the supplied block.

Example:

# Perform trace level logging within the block when the default is higher
SemanticLogger.default_level = :info

logger.debug 'this will _not_ be logged'

SemanticLogger.silence(:trace) do
  logger.debug "this will be logged"
end

Parameters

new_level
  The new log level to apply within the block
  Default: :error

Example:

# Silence all logging for this thread below :error level
SemanticLogger.silence do
  logger.info "this will _not_ be logged"
  logger.warn "this neither"
  logger.error "but errors will be logged"
end

Note:

#silence does not affect any loggers which have had their log level set
explicitly. I.e. That do not rely on the global default level


444
445
446
447
448
449
450
# File 'lib/semantic_logger/semantic_logger.rb', line 444

def self.silence(new_level = :error)
  current_index                            = Thread.current[:semantic_logger_silence]
  Thread.current[:semantic_logger_silence] = SemanticLogger.level_to_index(new_level)
  yield
ensure
  Thread.current[:semantic_logger_silence] = current_index
end

.tagged(*tags, &block) ⇒ Object

Add the tags or named tags to the list of tags to log for this thread whilst the supplied block is active.

Returns result of block.

Tagged example:

SemanticLogger.tagged(12345, 'jack') do
  logger.debug('Hello World')
end

Named Tags (Hash) example:

SemanticLogger.tagged(tracking_number: 12345) do
  logger.debug('Hello World')
end

Notes:

  • Tags should be a list without any empty values, or contain any array.

    • ‘logger.tagged` is a slower api that will flatten the example below: `logger.tagged([[’first’, nil], nil, [‘more’], ‘other’])‘

    to the equivalent of:

    `logger.tagged('first', 'more', 'other')`
    


329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/semantic_logger/semantic_logger.rb', line 329

def self.tagged(*tags, &block)
  return yield if tags.empty?

  # Allow named tags to be passed into the logger
  if tags.size == 1
    tag = tags[0]
    return tag.is_a?(Hash) ? named_tagged(tag, &block) : fast_tag(tag, &block)
  end

  begin
    push_tags(*tags)
    yield
  ensure
    pop_tags(tags.size)
  end
end

.tagsObject

Returns a copy of the [Array] of [String] tags currently active for this thread Returns nil if no tags are set



348
349
350
351
352
# File 'lib/semantic_logger/semantic_logger.rb', line 348

def self.tags
  # Since tags are stored on a per thread basis this list is thread-safe
  t = Thread.current[:semantic_logger_tags]
  t.nil? ? [] : t.clone
end