class Scrolls::Logger

Attributes

exceptions[RW]
logger[R]
timestamp[RW]

Public Class Methods

new(options={}) click to toggle source
# File lib/scrolls/logger.rb, line 39
def initialize(options={})
  @stream        = options.fetch(:stream, STDOUT)
  @log_facility  = options.fetch(:facility, LOG_FACILITY)
  @time_unit     = options.fetch(:time_unit, "seconds")
  @timestamp     = options.fetch(:timestamp, false)
  @exceptions    = options.fetch(:exceptions, "single")
  @global_ctx    = options.fetch(:global_context, {})
  @syslog_opts   = options.fetch(:syslog_options, SYSLOG_OPTIONS)
  @escape_keys   = options.fetch(:escape_keys, false)
  @strict_logfmt = options.fetch(:strict_logfmt, false)

  # Our main entry point to ensure our options are setup properly
  setup!
end

Public Instance Methods

context() click to toggle source
# File lib/scrolls/logger.rb, line 54
def context
  if Thread.current.thread_variables.include?(:scrolls_context)
    Thread.current.thread_variable_get(:scrolls_context)
  else
    Thread.current.thread_variable_set(:scrolls_context, {})
  end
end
context=(h) click to toggle source
# File lib/scrolls/logger.rb, line 62
def context=(h)
  Thread.current.thread_variable_set(:scrolls_context, h || {})
end
escape_keys?() click to toggle source
# File lib/scrolls/logger.rb, line 78
def escape_keys?
  @escape_keys
end
facility() click to toggle source
# File lib/scrolls/logger.rb, line 90
def facility
  @facility
end
facility=(f) click to toggle source
# File lib/scrolls/logger.rb, line 94
def facility=(f)
  if f
    setup_facility(f)
    # If we are using syslog, we need to setup our connection again
    if stream == "syslog"
      @logger = Scrolls::SyslogLogger.new(
                  progname,
                  syslog_options,
                  facility
                )
    end
  end
end
global_context() click to toggle source
# File lib/scrolls/logger.rb, line 117
def global_context
  @global_context.to_h
end
log(data) { || ... } click to toggle source
# File lib/scrolls/logger.rb, line 121
def log(data, &blk)
  # If we get a string lets bring it into our structure.
  if data.kind_of? String
    rawhash = { "log_message" => data }
  else
    rawhash = data
  end

  if gc = @global_context.to_h
    ctx = gc.merge(context)
    logdata = ctx.merge(rawhash)
  end

  # By merging the logdata into the timestamp, rather than vice-versa, we
  # ensure that the timestamp comes first in the Hash, and is placed first
  # on the output, which helps with readability.
  logdata = { :now => Time.now.utc }.merge(logdata) if prepend_timestamp?

  unless blk
    write(logdata)
  else
    start = Time.now
    res = nil
    log(logdata.merge(:at => "start"))
    begin
      res = yield
    rescue StandardError => e
      logdata.merge!({
        at:           "exception",
        reraise:      true,
        class:        e.class,
        message:      e.message,
        exception_id: e.object_id.abs,
        elapsed:      calculate_time(start, Time.now)
      })
      logdata.delete_if { |k,v| k if v == "" }
      log(logdata)
      raise e
    end
    log(logdata.merge(:at => "finish", :elapsed => calculate_time(start, Time.now)))
    res
  end
end
log_exception(e, data=nil) click to toggle source
# File lib/scrolls/logger.rb, line 165
def log_exception(e, data=nil)
  unless @defined
    @stream = STDERR
    setup_stream
  end

  # We check our arguments for type
  case data
  when String
    rawhash = { "log_message" => data }
  when Hash
    rawhash = data
  else
    rawhash = {}
  end

  if gc = @global_context.to_h
    logdata = gc.merge(rawhash)
  end

  excepdata = {
    at:           "exception",
    class:        e.class,
    message:      e.message,
    exception_id: e.object_id.abs
  }

  excepdata.delete_if { |k,v| k if v == "" }

  if e.backtrace
    if single_line_exceptions?
      lines = e.backtrace.map { |line| line.gsub(/[`'"]/, "") }

      if lines.length > 0
        excepdata[:site] = lines.join('\n')
        log(logdata.merge(excepdata))
      end
    else
      log(logdata.merge(excepdata))

      e.backtrace.each do |line|
        log(logdata.merge(excepdata).merge(
          :at           => "exception",
          :class        => e.class,
          :exception_id => e.object_id.abs,
          :site         => line.gsub(/[`'"]/, "")
        ))
      end
    end
  end
end
stream() click to toggle source
# File lib/scrolls/logger.rb, line 66
def stream
  @stream
end
stream=(s) click to toggle source
# File lib/scrolls/logger.rb, line 70
def stream=(s)
  # Return early to avoid setup
  return if s == @stream

  @stream = s
  setup_stream
end
strict_logfmt?() click to toggle source
# File lib/scrolls/logger.rb, line 82
def strict_logfmt?
  @strict_logfmt
end
syslog_options() click to toggle source
# File lib/scrolls/logger.rb, line 86
def syslog_options
  @syslog_opts
end
time_unit() click to toggle source
# File lib/scrolls/logger.rb, line 108
def time_unit
  @time_unit
end
time_unit=(u) click to toggle source
# File lib/scrolls/logger.rb, line 112
def time_unit=(u)
  @time_unit = u
  setup_time_unit
end
with_context(prefix) { || ... } click to toggle source
# File lib/scrolls/logger.rb, line 217
def with_context(prefix)
  return unless block_given?
  old = context
  self.context = old.merge(prefix)
  res = yield if block_given?
ensure
  self.context = old
  res
end

Private Instance Methods

calculate_time(start, finish) click to toggle source
# File lib/scrolls/logger.rb, line 301
def calculate_time(start, finish)
  translate_time_unit unless @t
  ((finish - start).to_f * @t)
end
log_level_ok?(level) click to toggle source
# File lib/scrolls/logger.rb, line 306
def log_level_ok?(level)
  if level
    raise LogLevelError, "Log level unknown" unless LOG_LEVEL_MAP.key?(level)
    LOG_LEVEL_MAP[level.to_s] <= LOG_LEVEL
  else
    true
  end
end
prepend_timestamp?() click to toggle source
# File lib/scrolls/logger.rb, line 247
def prepend_timestamp?
  @timestamp
end
progname() click to toggle source

We need this for our syslog setup

# File lib/scrolls/logger.rb, line 297
def progname
  File.basename($0)
end
setup!() click to toggle source
# File lib/scrolls/logger.rb, line 229
def setup!
  setup_global_context
  prepend_timestamp?
  setup_facility
  setup_stream
  single_line_exceptions?
  setup_time_unit
end
setup_facility(f=nil) click to toggle source
# File lib/scrolls/logger.rb, line 251
def setup_facility(f=nil)
  if f
    @facility = LOG_FACILITY_MAP.fetch(f, LOG_FACILITY)
  else
    @facility = LOG_FACILITY_MAP.fetch(@log_facility, LOG_FACILITY)
  end
end
setup_global_context() click to toggle source
# File lib/scrolls/logger.rb, line 238
def setup_global_context
  # Builds up an immutable object for our global_context
  # This is not backwards compatiable and was introduced after 0.3.7.
  # Removes ability to add to global context once we initialize our
  # logging object. This also deprecates #add_global_context.
  @global_context = GlobalContext.new(@global_ctx)
  @global_context.freeze
end
setup_stream() click to toggle source
# File lib/scrolls/logger.rb, line 259
def setup_stream
  unless @stream == STDOUT
    # Set this so we know we aren't using our default stream
    @defined = true
  end

  if @stream == "syslog"
    @logger = Scrolls::SyslogLogger.new(
                progname,
                syslog_options,
                facility
              )
  else
    @logger = IOLogger.new(@stream)
  end
end
setup_time_unit() click to toggle source
# File lib/scrolls/logger.rb, line 281
def setup_time_unit
  unless %w{s ms seconds milliseconds}.include? @time_unit
    raise TimeUnitError, "Specify the following: s, ms, seconds, milliseconds"
  end

  case @time_unit
  when %w{s seconds}
    @t = 1.0
  when %w{ms milliseconds}
    @t = 1000.0
  else
    @t = 1.0
  end
end
single_line_exceptions?() click to toggle source
# File lib/scrolls/logger.rb, line 276
def single_line_exceptions?
  return false if @exceptions == "multi"
  true
end
write(data) click to toggle source
# File lib/scrolls/logger.rb, line 315
def write(data)
  if log_level_ok?(data[:level])
    msg = Scrolls::Parser.unparse(data, escape_keys=escape_keys?, strict_logfmt=strict_logfmt?)
    @logger.log(msg)
  end
end