Class: Slog::Logger

Inherits:
Logger show all
Defined in:
lib/slog/logger.rb

Overview

A colorful structured logger.

Constant Summary

Constants inherited from Logger

Logger::TRACE

Instance Method Summary collapse

Methods inherited from Logger

#trace

Constructor Details

#initialize(conf = {}) ⇒ Logger

Create a new Logger.

A little different than the canonical Logger. Add options to set the log level and to disable both colorization and pretty JSON output. In this form, it’s perfect for use with either Logstash or Franz.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/slog/logger.rb', line 37

def initialize conf={}
  defaults = {
    out: $stdout,                     # Output handle
    shift_age: 7,                     # Max seven logs
    shift_size: 1_048_576,            # 1024 KiB
    colorize: nil,                    # nil to guess
    prettify: nil,                    # nil to guess
    level: :info,                     # INFO
    level_transform: 'downcase',      # Any String method
    level_field: 'level',             # nil to disable
    message_field: 'message',         # nil to disable
    timestamp_field: '@timestamp',    # nil to disable
    timestamp_format: '%FT%T.%3N%:z', # ISO-8601
    color_map: {
      'debug' => [ :blue,    :default ],
      'info'  => [ :green,   :default ],
      'warn'  => [ :yellow,  :default ],
      'error' => [ :red,     :default ],
      'fatal' => [ :red,     :black   ],
      'trace' => [ :magenta, :default ]
    }
  }

  conf = defaults.merge(conf)

  begin
    conf[:colorize] = true if conf[:out].tty? && conf[:colorize].nil?
    conf[:prettify] = true if conf[:out].tty? && conf[:prettify].nil?
  rescue NoMethodError
  end

  @prettify = conf[:prettify]
  @colorize = conf[:colorize]
  @color_map = conf[:color_map]
  @level_field = conf[:level_field]
  @level_transform = conf[:level_transform]
  @message_field = conf[:message_field]
  @timestamp_field = conf[:timestamp_field]
  @timestamp_format = conf[:timestamp_format]

  super conf[:out], conf[:shift_age], conf[:shift_size]
  self.level = conf[:level]

  set_formatter
end

Instance Method Details

#format_json(event, severity) ⇒ Object

Convert the structured event into it’s JSON representation



117
118
119
120
121
122
123
124
# File 'lib/slog/logger.rb', line 117

def format_json event, severity
  generator = @prettify ? :pretty_generate : :generate
  event = JSON.send(generator, event) + "\n"
  return event unless @colorize
  event.colorize \
    color: @color_map[severity][0],
    background: @color_map[severity][1]
end

#level=(l) ⇒ Object

Override the level setter to allow symbols and TRACE



128
129
130
131
132
133
# File 'lib/slog/logger.rb', line 128

def level= l
  l = ::Logger.const_get l.upcase if l.is_a? Symbol
  @trace = l == ::Logger::TRACE # TRACE is really a fancy DEBUG
  l = ::Logger::DEBUG if @trace # Here's the proof.
  super l
end

#set_formatterObject

Set the formatter to work our magic



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/slog/logger.rb', line 85

def set_formatter
  self.formatter = proc do |severity, datetime, _, message|
    severity.downcase!

    # If it ain't a structured log, it is now.
    event = structure_event severity, datetime, message

    # When debugging, mark the location in Hoss's source
    unless level == ::Logger::INFO
      event.merge! marker: File.basename(caller[4])
    end

    # Apply colorization and prettification as required
    format_json event, severity
  end
end

#structure_event(severity, datetime, message) ⇒ Object

Turn a call to the formatter into a Hash structure



104
105
106
107
108
109
110
111
112
113
# File 'lib/slog/logger.rb', line 104

def structure_event severity, datetime, message
  message = { @message_field => message } unless message.is_a? Hash
  event = {
    @level_field => severity.send(@level_transform),
    @timestamp_field => datetime.strftime(@timestamp_format)
  }
  event.merge! message
  event.delete nil # ignore @field if @field.nil?
  event
end