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/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/appender/rabbitmq.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/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, Levels, Loggable, Metric, Reporters, Utils Classes: Appenders, Base, DebugAsTraceLogger, Log, Logger, Processor, Subscriber

Constant Summary collapse

VERSION =
'4.5.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(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
# File 'lib/semantic_logger/semantic_logger.rb', line 154

def self.add_appender(options, deprecated_level = nil, &block)
  appender = Logger.processor.appenders.add(options, deprecated_level, &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:
  :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


259
260
261
262
263
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
# File 'lib/semantic_logger/semantic_logger.rb', line 259

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



171
172
173
# File 'lib/semantic_logger/semantic_logger.rb', line 171

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

.closeObject

Close all appenders and flush any outstanding messages.



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

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



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

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



292
293
294
295
296
297
298
299
300
301
302
# File 'lib/semantic_logger/semantic_logger.rb', line 292

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.


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

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.



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

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.



465
466
467
# File 'lib/semantic_logger/semantic_logger.rb', line 465

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.



471
472
473
# File 'lib/semantic_logger/semantic_logger.rb', line 471

def self.lag_threshold_s
  Logger.processor.lag_threshold_s
end

.named_tagged(hash) ⇒ Object

:nodoc

Raises:

  • (ArgumentError)


368
369
370
371
372
373
374
375
376
377
378
# File 'lib/semantic_logger/semantic_logger.rb', line 368

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.



381
382
383
384
385
386
387
388
389
390
391
# File 'lib/semantic_logger/semantic_logger.rb', line 381

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.



220
221
222
# File 'lib/semantic_logger/semantic_logger.rb', line 220

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

.pop_named_tags(quantity = 1) ⇒ Object



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

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



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

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

.push_named_tags(hash) ⇒ Object



393
394
395
396
# File 'lib/semantic_logger/semantic_logger.rb', line 393

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`
    


356
357
358
359
# File 'lib/semantic_logger/semantic_logger.rb', line 356

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



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

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

.remove_appender(appender) ⇒ Object

Remove an existing appender Currently only supports appender instances



163
164
165
# File 'lib/semantic_logger/semantic_logger.rb', line 163

def self.remove_appender(appender)
  Logger.processor.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 appender's implement reopen.
Check the code for each appender you are using before relying on this behavior.


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

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


439
440
441
442
443
444
445
# File 'lib/semantic_logger/semantic_logger.rb', line 439

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

.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')`
    


324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/semantic_logger/semantic_logger.rb', line 324

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



343
344
345
346
347
# File 'lib/semantic_logger/semantic_logger.rb', line 343

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