Class: SemanticLogger::Formatters::Pattern

Inherits:
Default
  • Object
show all
Defined in:
lib/semantic_logger/formatters/pattern.rb

Overview

Formats log messages using a configurable pattern string, so a custom log line layout can be specified directly in the configuration without having to write a new formatter class.

Pattern placeholders use the form %{directive}, where directive is the name of any of the formatting methods (inherited from Default, or defined below). Named tags support a parameterized form: %{named_tags:request_id} returns the value of a single named tag. Use %%{...} to emit a literal %{...} without interpolation.

Example:

SemanticLogger.add_appender(
io:        $stdout,
formatter: {
  pattern: { pattern: "%{time} %{level} %{name} -- %{message}" }
}
)

Available directives:

time                 Formatted timestamp. Optionally accepts a strftime
                   format, e.g. time:%Y-%m-%dT%H:%M:%S.%6N.
level                Full level name, e.g. "debug".
level_short          Single character level, e.g. "D".
name                 Logger / class name.
message              Log message.
payload              Payload rendered as a string.
exception_class      Class of the logged exception, e.g. "RuntimeError".
exception_message    Message of the logged exception.
backtrace            Backtrace of the logged exception.
duration             Human readable duration, e.g. "1.2ms".
duration_ms          Duration in milliseconds (numeric).
thread_name          Name of the thread that logged the message.
pid                  Process id.
file_name            Ruby file name that logged the message, e.g. "app.rb".
line                 Line number within the Ruby file, e.g. 42.
tags                 Tags, comma separated.
named_tags           All named tags, or one tag with named_tags:key.
host                 Host name.
application          Application name.
environment          Environment name.

Constant Summary collapse

DEFAULT_PATTERN =

Approximates the Default formatter's output.

"%{time} %{level} [%{pid}:%{thread_name}] %{name} -- %{message}".freeze
DIRECTIVES =

The directives that may appear in a pattern. The value is whether the directive accepts a parameter, e.g. %named_tags:request_id.

{
  time:              true,
  level:             false,
  level_short:       false,
  name:              false,
  message:           false,
  payload:           false,
  exception_class:   false,
  exception_message: false,
  backtrace:         false,
  duration:          false,
  duration_ms:       false,
  thread_name:       false,
  pid:               false,
  file_name:         false,
  line:              false,
  tags:              false,
  named_tags:        true,
  host:              false,
  application:       false,
  environment:       false
}.freeze

Constants inherited from Base

Base::CONTROL_CHARS, Base::CONTROL_CHAR_ESCAPES, Base::PRECISION

Instance Attribute Summary collapse

Attributes inherited from Base

#escape_control_chars, #log, #log_application, #log_environment, #log_host, #logger, #precision, #time_format

Instance Method Summary collapse

Methods inherited from Default

#exception, #file_name_and_line, #name, #process_info, #thread_name

Methods inherited from Base

build_time_format, #pid

Constructor Details

#initialize(pattern: DEFAULT_PATTERN, **args) ⇒ Pattern

Parameters:

pattern: [String]
The pattern string used to format every log entry.
Default: DEFAULT_PATTERN

Plus all the options supported by SemanticLogger::Formatters::Base.



86
87
88
89
90
91
92
93
# File 'lib/semantic_logger/formatters/pattern.rb', line 86

def initialize(pattern: DEFAULT_PATTERN, **args)
  @pattern = pattern
  super(**args)
  # Parse the pattern once, up front, so that formatting every log entry
  # is just a walk over the pre-compiled tokens (no regex on the hot path).
  # Unknown directives raise here, at configuration time, not per log.
  @tokens = compile(pattern)
end

Instance Attribute Details

#patternObject (readonly)

Returns the value of attribute pattern.



46
47
48
# File 'lib/semantic_logger/formatters/pattern.rb', line 46

def pattern
  @pattern
end

Instance Method Details

#applicationObject

Application name.



185
186
187
# File 'lib/semantic_logger/formatters/pattern.rb', line 185

def application
  logger&.application if log_application
end

#backtraceObject

Backtrace of the logged exception.



135
136
137
# File 'lib/semantic_logger/formatters/pattern.rb', line 135

def backtrace
  log.backtrace_to_s if log.exception
end

#call(log, logger) ⇒ Object



194
195
196
197
198
199
200
201
# File 'lib/semantic_logger/formatters/pattern.rb', line 194

def call(log, logger)
  self.log    = log
  self.logger = logger

  @tokens.each_with_object(+"") do |token, out|
    out << (token.is_a?(Token) ? public_send(token.method_name, *token.arguments).to_s : token)
  end
end

#durationObject

Human readable duration (without the Default formatter's surrounding parentheses).



140
141
142
# File 'lib/semantic_logger/formatters/pattern.rb', line 140

def duration
  log.duration_human
end

#duration_msObject

Duration in milliseconds.



145
146
147
# File 'lib/semantic_logger/formatters/pattern.rb', line 145

def duration_ms
  log.duration
end

#environmentObject

Environment name.



190
191
192
# File 'lib/semantic_logger/formatters/pattern.rb', line 190

def environment
  logger&.environment if log_environment
end

#exception_classObject

Class of the logged exception, e.g. "RuntimeError".



125
126
127
# File 'lib/semantic_logger/formatters/pattern.rb', line 125

def exception_class
  log.exception&.class
end

#exception_messageObject

Message of the logged exception.



130
131
132
# File 'lib/semantic_logger/formatters/pattern.rb', line 130

def exception_message
  escape_control_characters(log.exception&.message)
end

#file_nameObject

Name of the Ruby file that logged the message, e.g. "app.rb".



150
151
152
# File 'lib/semantic_logger/formatters/pattern.rb', line 150

def file_name
  log.file_name_and_line(true)&.first
end

#hostObject

Host name.



180
181
182
# File 'lib/semantic_logger/formatters/pattern.rb', line 180

def host
  logger&.host if log_host
end

#levelObject

Full level name, e.g. "debug" (Default formatter uses the short "D").



105
106
107
# File 'lib/semantic_logger/formatters/pattern.rb', line 105

def level
  log.level.to_s
end

#level_shortObject

Single character level, e.g. "D".



110
111
112
# File 'lib/semantic_logger/formatters/pattern.rb', line 110

def level_short
  log.level_to_s
end

#lineObject

Line number within the Ruby file that logged the message.



155
156
157
# File 'lib/semantic_logger/formatters/pattern.rb', line 155

def line
  log.file_name_and_line(true)&.last
end

#messageObject

Log message (without the "-- " prefix the Default formatter adds).



115
116
117
# File 'lib/semantic_logger/formatters/pattern.rb', line 115

def message
  escape_control_characters(log.message)
end

#named_tags(key = nil) ⇒ Object

With a key: the value of a single named tag, e.g. %named_tags:request_id. Without a key: all named tags rendered as "key: value, ...".



168
169
170
171
172
173
174
175
176
177
# File 'lib/semantic_logger/formatters/pattern.rb', line 168

def named_tags(key = nil)
  named = log.named_tags
  return if named.nil? || named.empty?

  if key
    escape_control_characters(named[key.to_sym] || named[key.to_s])
  else
    named.map { |name, value| "#{escape_control_characters(name)}: #{escape_control_characters(value)}" }.join(", ")
  end
end

#payloadObject

Raw payload rendered as a string.



120
121
122
# File 'lib/semantic_logger/formatters/pattern.rb', line 120

def payload
  log.payload_to_s
end

#tagsObject

Tags joined by a comma (without the Default formatter's surrounding brackets).



160
161
162
163
164
# File 'lib/semantic_logger/formatters/pattern.rb', line 160

def tags
  return if log.tags.nil? || log.tags.empty?

  log.tags.map { |tag| escape_control_characters(tag) }.join(", ")
end

#time(format = nil) ⇒ Object

Formatted timestamp. With a strftime format argument, e.g. %SemanticLogger::Formatters::Pattern.time:%Y-%m-%dT%H:%M:%Stime:%Y-%m-%dT%H:%M:%S.%6N, the time is formatted with that string. Without an argument it uses the formatter's configured time_format.



98
99
100
101
102
# File 'lib/semantic_logger/formatters/pattern.rb', line 98

def time(format = nil)
  return super() if format.nil?

  log.time.strftime(format)
end