Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
GarthJL1965 opened this issue Jun 8, 2017 · 4 comments

Comments

@GarthJL1965
Copy link

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

@reidmorrison
Copy link
Owner

reidmorrison commented Jun 13, 2017

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}} )

@GarthJL1965
Copy link
Author

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

@todd-a-jacobs
Copy link
Contributor

# 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.

@reidmorrison
Copy link
Owner

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 reidmorrison changed the title Question about a 'Pattern based log message formatter' or A New Appender Support log format specifications directly from the configuration, without having to create a new formatter. Nov 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants