semantic_logger icon indicating copy to clipboard operation
semantic_logger copied to clipboard

Support log format specifications directly from the configuration, without having to create a new formatter.

Open GarthJL1965 opened this issue 8 years ago • 4 comments

Currently using Log4R - which allows a Pattern Based Formatter - eg

class PatternFormatter < BasicFormatter

# Arguments to sprintf keyed to directive letters<br>
# %c - event short name<br>
# %C - event fullname<br>
# %d - date<br>
# %g - Global Diagnostic Context (GDC)<br>
# %t - trace<br>
# %m - message<br>
# %h - thread name<br>
# %p - process ID aka PID<br>
# %M - formatted message<br>
# %l - Level in string form<br>
# %x - Nested Diagnostic Context (NDC)<br>
# %X - Mapped Diagnostic Context (MDC), syntax is "%X{key}"<br>
# %% - Insert a %<br>
DirectiveTable = {
  "c" => 'event.name',
  "C" => 'event.fullname',
  "d" => 'format_date',
  "g" => 'Log4r::GDC.get()',
  "t" => '(event.tracer.nil? ? "no trace" : event.tracer[0])',
  "T" => '(event.tracer.nil? ? "no trace" : event.tracer[0].split(File::SEPARATOR)[-1])',
  "m" => 'event.data',
  "h" => '(Thread.current[:name] or Thread.current.to_s)',
  "p" => 'Process.pid.to_s',
  "M" => 'format_object(event.data)',
  "l" => 'LNAMES[event.level]',
  "x" => 'Log4r::NDC.get()',
  "X" => 'Log4r::MDC.get("DTR_REPLACE")',
  "%" => '"%"'
}

This is useful to me because I log to stdout (a simple message format for users) AND I log a timestamped message to a file as well (that may be at a different log level)

Is there a way to get this with Semantic Logger ? My first thought is to write a new Appender, but, maybe other appenders could use the 'custom formatter' as well

Cheers & Thanks

GarthJL1965 avatar Jun 08 '17 22:06 GarthJL1965

Sounds like a great idea. Inheriting from the default formatter, a Pattern formatter should not be difficult to implement. Essentially create a new formatter, for example: SemanticLogger::Formatter::Pattern, that derives from SemanticLogger::Formatter::Default and implement the call method to per similar rules as above. https://github.com/rocketjob/semantic_logger/blob/master/lib/semantic_logger/formatters/default.rb#L64

Then to use the formatter when creating a new appender:

formatter = SemanticLogger::Formatter::Pattern.new('%d %l %m')
SemanticLogger.add_appender(io: STDOUT, formatter: formatter)

Possibly add another option for the formatter to specify whether the output should be colorized.

formatter = SemanticLogger::Formatter::Pattern.new('%d %l %m', color: true)
SemanticLogger.add_appender(io: STDOUT, formatter: formatter)

Or if using Rails Semantic Logger:

config.rails_semantic_logger.format = SemanticLogger::Formatter::Pattern.new('%d %l %m', color: true)

I had not considered passing options to the formatter when adding the appender as above. Maybe it could be simplified, with code changes to add_appender, to support:

SemanticLogger.add_appender(io: STDOUT, formatter: {pattern: {pattern: '%d %l %m', color: true}} )

reidmorrison avatar Jun 13 '17 02:06 reidmorrison

cheers - flat out at work at the moment, and a beginner at Ruby, I'll see if I can nudge it along in spare time

GarthJL1965 avatar Jun 14 '17 03:06 GarthJL1965

# Arguments to sprintf keyed to directive letters<br>
# %c - event short name<br>
# %C - event fullname<br>
# %d - date<br>
# %g - Global Diagnostic Context (GDC)<br>
# %t - trace<br>
# %m - message<br>
# %h - thread name<br>
# %p - process ID aka PID<br>
# %M - formatted message<br>
# %l - Level in string form<br>
# %x - Nested Diagnostic Context (NDC)<br>
# %X - Mapped Diagnostic Context (MDC), syntax is "%X{key}"<br>
# %% - Insert a %<br>
DirectiveTable = {
  "c" => 'event.name',
  "C" => 'event.fullname',
  "d" => 'format_date',
  "g" => 'Log4r::GDC.get()',
  "t" => '(event.tracer.nil? ? "no trace" : event.tracer[0])',
  "T" => '(event.tracer.nil? ? "no trace" : event.tracer[0].split(File::SEPARATOR)[-1])',
  "m" => 'event.data',
  "h" => '(Thread.current[:name] or Thread.current.to_s)',
  "p" => 'Process.pid.to_s',
  "M" => 'format_object(event.data)',
  "l" => 'LNAMES[event.level]',
  "x" => 'Log4r::NDC.get()',
  "X" => 'Log4r::MDC.get("DTR_REPLACE")',
  "%" => '"%"'
}

I wouldn't mind taking a stab at this, but I'm not familiar with the log format you're referring to, so I'd need some test fixtures to show what sort of data you're trying to capture. As an example, what does NDC.get() look like?

You'd need a way to get the data from a context, tag, or proc, but otherwise it looks more or less like an sprintf string, which shouldn't be too hard to implement if one knows the shape of the data and the format you expect out of it.

todd-a-jacobs avatar Feb 21 '22 19:02 todd-a-jacobs

This is a great example of how to do string interpolations: https://github.com/reidmorrison/secret_config/blob/master/lib/secret_config/setting_interpolator.rb

We don't have to restrict ourselves to just single character replacements either. Especially since most command line interfaces have moved away from single character options like -r to --replace to make them clearer.

We would also need to copy the base class over: https://github.com/reidmorrison/secret_config/blob/master/lib/secret_config/string_interpolator.rb

This approach should also make it much easier to fit within the current formatters, since the methods could line up with the string interpolation names.

reidmorrison avatar Nov 06 '22 20:11 reidmorrison