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/levels.rb,
lib/semantic_logger/logger.rb,
lib/semantic_logger/version.rb,
lib/semantic_logger/appender.rb,
lib/semantic_logger/loggable.rb,
lib/semantic_logger/appenders.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/io.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/sync_processor.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/appender/rabbitmq.rb,
lib/semantic_logger/formatters/logfmt.rb,
lib/semantic_logger/formatters/syslog.rb,
lib/semantic_logger/appender/new_relic.rb,
lib/semantic_logger/formatters/default.rb,
lib/semantic_logger/formatters/fluentd.rb,
lib/semantic_logger/reporters/minitest.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/sentry_ruby.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/test/capture_log_events.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, Levels, Loggable, Metric, Reporters, Test, Utils Classes: Appenders, Base, DebugAsTraceLogger, Log, Logger, Processor, Subscriber, SyncProcessor

Constant Summary collapse

VERSION =
"4.12.0".freeze
LEVELS =

Logging levels in order of most detailed to most severe

Levels::LEVELS

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)
  Logger.new(klass)
end

.add_appender(**args, &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, :logfmt, etc...
    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')


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

def self.add_appender(**args, &block)
  appender = appenders.add(**args, &block)
  # Start appender thread if it is not already running
  Logger.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:
  :fatal, :error, :warn, :info, :debug, :trace

If the current level is :trace it wraps around back to :fatal
  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


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/semantic_logger/semantic_logger.rb', line 279

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
      current_level_index = LEVELS.find_index(default_level)
      new_level_index = current_level_index == 0 ? LEVELS.size - 1 : current_level_index - 1
      new_level = LEVELS[new_level_index]
      self.default_level = new_level
      self["SemanticLogger"].warn "Changed global default log level to #{new_level.inspect}"
    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



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

def self.appenders
  Logger.processor.appenders
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 : Levels.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

.clear_appenders!Object

Clear out all previously registered appenders



183
184
185
# File 'lib/semantic_logger/semantic_logger.rb', line 183

def self.clear_appenders!
  Logger.processor.close
end

.closeObject

Close all appenders and flush any outstanding messages.



202
203
204
# File 'lib/semantic_logger/semantic_logger.rb', line 202

def self.close
  Logger.processor.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 = Levels.index(level)
end

.default_level_indexObject



497
498
499
# File 'lib/semantic_logger/semantic_logger.rb', line 497

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

.environmentObject

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



76
77
78
# File 'lib/semantic_logger/semantic_logger.rb', line 76

def self.environment
  @environment
end

.environment=(environment) ⇒ Object

Override the default environment



81
82
83
# File 'lib/semantic_logger/semantic_logger.rb', line 81

def self.environment=(environment)
  @environment = environment
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



314
315
316
317
318
319
320
321
322
323
324
# File 'lib/semantic_logger/semantic_logger.rb', line 314

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.


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

def self.flush
  Logger.processor.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

.lag_check_intervalObject

Returns the check_interval which is the number of messages between checks to determine if the appender thread is falling behind.



481
482
483
# File 'lib/semantic_logger/semantic_logger.rb', line 481

def self.lag_check_interval
  Logger.processor.lag_check_interval
end

.lag_check_interval=(lag_check_interval) ⇒ Object

Set the check_interval which is the number of messages between checks to determine if the appender thread is falling behind.



487
488
489
# File 'lib/semantic_logger/semantic_logger.rb', line 487

def self.lag_check_interval=(lag_check_interval)
  Logger.processor.lag_check_interval = lag_check_interval
end

.lag_threshold_sObject

Returns the amount of time in seconds to determine if the appender thread is falling behind.



493
494
495
# File 'lib/semantic_logger/semantic_logger.rb', line 493

def self.lag_threshold_s
  Logger.processor.lag_threshold_s
end

.named_tagged(hash) ⇒ Object

:nodoc

Raises:

  • (ArgumentError)


390
391
392
393
394
395
396
397
398
399
400
# File 'lib/semantic_logger/semantic_logger.rb', line 390

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.



403
404
405
406
407
408
409
410
411
412
413
# File 'lib/semantic_logger/semantic_logger.rb', line 403

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.



240
241
242
# File 'lib/semantic_logger/semantic_logger.rb', line 240

def self.on_log(object = nil, &block)
  Logger.subscribe(object, &block)
end

.pop_named_tags(quantity = 1) ⇒ Object



420
421
422
423
# File 'lib/semantic_logger/semantic_logger.rb', line 420

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



384
385
386
387
# File 'lib/semantic_logger/semantic_logger.rb', line 384

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

.push_named_tags(hash) ⇒ Object



415
416
417
418
# File 'lib/semantic_logger/semantic_logger.rb', line 415

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`
    


378
379
380
381
# File 'lib/semantic_logger/semantic_logger.rb', line 378

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

.queue_sizeObject

Returns [Integer] the number of log entries waiting to be written to the appenders.

When this number grows it is because the logging appender thread is not able to write to the appenders fast enough. Either reduce the amount of logging, increase the log level, reduce the number of appenders, or look into speeding up the appenders themselves



475
476
477
# File 'lib/semantic_logger/semantic_logger.rb', line 475

def self.queue_size
  Logger.processor.queue.size
end

.remove_appender(appender) ⇒ Object

Remove an existing appender Currently only supports appender instances



175
176
177
178
179
180
# File 'lib/semantic_logger/semantic_logger.rb', line 175

def self.remove_appender(appender)
  return unless appender

  appenders.delete(appender)
  appender.close
end

.reopenObject

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

Note:

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


212
213
214
# File 'lib/semantic_logger/semantic_logger.rb', line 212

def self.reopen
  Logger.processor.reopen
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


461
462
463
464
465
466
467
# File 'lib/semantic_logger/semantic_logger.rb', line 461

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

.sync!Object

Run Semantic Logger in Synchronous mode.

I.e. Instead of logging messages in a separate thread for better performance, log them using the current thread.



505
506
507
# File 'lib/semantic_logger/semantic_logger.rb', line 505

def self.sync!
  Logger.sync!
end

.sync?Boolean

Running in synchronous mode?

Returns:

  • (Boolean)


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

def self.sync?
  Logger.sync?
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')`
    


346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/semantic_logger/semantic_logger.rb', line 346

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



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

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