Add on additional fields to all log entries
Problem
https://github.com/roidrage/lograge/issues/255 and https://github.com/roidrage/lograge/issues/272 highlight the problem. To summarize, it's great that lograge by default tacks on fields like 'action', 'controller', 'format', etc onto the controller log entries. But it's unfortunate that other log entries triggered throughout the request do not have these fields tacked on.
Goal
The goal of this PR is to make it so that you can opt into having all log entries (even those triggered from third-party gems) within a request have the default fields that lograge typically adds on, plus any fields added on by the user's supplied config.lograge.custom_payload by using a special formatter.
Example Config
Below is an example of how the newly added formatter, Lograge::LogStashLoggerFormatters::MergedWithLogragePayload, can be used to add all the fields on all the log entries.
require 'lograge/logstash_logger_formatters/merged_with_lograge_payload'
Rails.application.configure do
config.lograge.enabled = true
config.lograge.formatter = Lograge::Formatters::Logstash.new
config.lograge.custom_payload do |controller|
request = controller.request
{
remote_ip: request.remote_ip,
request_id: request.request_id
# ...
}
end
config.lograge.logger = LogStashLogger.new(
type: :multi_delegator,
formatter: Lograge::LogStashLoggerFormatters::MergedWithLogragePayload,
outputs: [
{ type: :file, path: 'log/development.log' },
{ type: :tcp, host: 'listener.example.com', port: 5050 }
],
customize_event: ->(event) {
event['token'] = 'logstash_service_token'
}
)
Rails.logger = config.lograge.logger
end
Open Questions
I have left a couple of TODOs in the code that I think warrant more discussion; please feel free to comment and we can come to a consensus on the right approach.
Limitations
Logs triggered from middleware that is run before the Rails application is run will not get the additional fields tacked on because this change relies on the start_processing.action_controller event. As far as I can tell, there is no trivial fix for this, though this can be revisited later.
Callouts
- Provided this approach is on the right path, I will add to the specs and README.
- Using a formatter in this way with logstash-logger doesn't totally work out of the box, I have submitted a fix PR: https://github.com/dwbutler/logstash-logger/pull/155. If there is lag in getting that merged, I think I can outline a temporary workaround.
@bcharna I think I'm looking for something similar to you, at least conceptually, but I'm having a trouble applying your approach to my desired goal, which is simpler than yours. My goal is to trigger another line of log, similar to one that Lograge would trigger. Not looking for any third party loggers like LogStashLogger. I just want to trigger what Lograge would normally trigger - including my custom paylods and options.
config.lograge.enabled = true
config.lograge.custom_payload do |controller|
{ ... } # some custom stuff
end
config.lograge.custom_options = lambda do |event|
hash = {
params: event.payload[:params].except(:controller, :action, :format, :id),
}
if e = event.payload[:exception_object]
Exceptions.to_lograge e, hash, event
end
hash
end
Any ideas how I would do it? I don't have any Rails.configuration.lograge.logger because all I want is the standard Lograge output.
@bcharna Pardon my ignorance - I didn't spot it was a PR. Thought it was just an issue. ;) I set what you did there and will repurpose it for my use. Thank you for this contribution.
@bcharna Here's my feedback on your PR:
-
prepend_before_actionis problematic because mycustom_payloadmethod accesses data that my custombefore_actionset in Application Controller. For this reason,RequestStore.store[:lograge_custom_payload]does not have the same data Lograge has when generating my log line. What would be the right approach to executecustom_payloadon demand, when logger is actually called? -
unsure if MergedWithLogragePayload should care about the user's custom_options- it should!custom_optionsis no different fromcustom_payload. I have my custom stuff incustom_payloadbut I usecustom_optionssimultaneously for adding exception information if middleware sets it. -
it's not ideal that the user's custom_payload method would be called twice- not ideal but better than nothing! I wouldn't care about it, honestly.
hey @Nowaker, thank you for these notes. I'm a bit far removed from this PR and likely will not be able to dig deeper into it anytime soon. But I'd be happy if someone can pick it up and improve it or gain some traction on supporting something along these lines.