Class: Slog::Logger

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

Overview

A colorful structured logger.

Instance Method Summary collapse

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.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
# File 'lib/slog/logger.rb', line 19

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



99
100
101
102
103
104
105
106
# File 'lib/slog/logger.rb', line 99

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



110
111
112
113
# File 'lib/slog/logger.rb', line 110

def level= l
  l = ::Logger.const_get l.upcase if l.is_a? Symbol
  super l
end

#set_formatterObject

Set the formatter to work our magic



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/slog/logger.rb', line 67

def set_formatter
  self.formatter = proc do |severity, datetime, _, message|
    severity = 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



86
87
88
89
90
91
92
93
94
95
# File 'lib/slog/logger.rb', line 86

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