Skip to content

Commit

Permalink
Change the default logger to include govuk_request_id.
Browse files Browse the repository at this point in the history
We have added govuk_request_id to the logs. This is to increase traceability. Also, we changed the logs to log into json format and send them to stdout and show them in kibabna logs and doesn't send the logs into stderr.

Co-authored-by: Iris Lau <iris.lau@digital.cabinet-office.gov.uk>
Co-authored-by: Tuomas Nylund <tuomas.nylund@digital.cabinet-office.gov.uk>
  • Loading branch information
3 people committed Jul 5, 2023
1 parent 562c862 commit 5836a2e
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 6 deletions.
23 changes: 21 additions & 2 deletions lib/govuk_app_config/govuk_json_logging.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "json"
require "logstasher"
require "action_controller"
require_relative "rails_ext/action_dispatch/debug_exceptions"
Expand Down Expand Up @@ -28,8 +29,26 @@ def self.configure
Rails.logger = Logger.new(
$real_stdout, # rubocop:disable Style/GlobalVars
level: Rails.logger.level,
formatter: proc { |_severity, datetime, _progname, msg|
"#{msg.is_a?(JSON) ? { **msg, govuk_request_id: 'blah' }.to_json : JSON.dump(timestamp: datetime.to_s, message: msg)}\n"
formatter: proc { |severity, datetime, _progname, msg|

begin
message = JSON.parse(msg)
rescue JSON::ParserError, TypeError => _e
message = msg
end

hash = {
"@timestamp": datetime.utc.iso8601(3),
message: message,
level: severity,
tags: %w[rails],
}

if defined?(GdsApi::GovukHeaders) && !GdsApi::GovukHeaders.headers[:govuk_request_id].nil?
hash[:govuk_request_id] = GdsApi::GovukHeaders.headers[:govuk_request_id]
end

"#{hash.to_json}\n"
},
)

Expand Down
53 changes: 49 additions & 4 deletions spec/lib/govuk_json_logging_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
require "rack/test"

RSpec.describe GovukJsonLogging do
let(:govuk_headers_class) do
Class.new do
def self.headers
{ govuk_request_id: "some-value" }
end
end
end
before do
stub_const("DummyLoggingRailsApp", Class.new(Rails::Application) do
config.hosts.clear
Expand Down Expand Up @@ -74,22 +81,60 @@ def app
end

it "logs errors thrown by the application" do
stub_const("GdsApi::GovukHeaders", govuk_headers_class)
GovukJsonLogging.configure
get "/error"
fake_stdout.rewind
lines = fake_stdout.read.split("\n")
expect(lines).to include(/default exception/)
error_log_line = lines.find { |log| log.match?(/default exception/) }
expect(error_log_line).not_to be_empty

error_log_json = JSON.parse(error_log_line)
error_log_json_msg = JSON.parse(error_log_json["message"])
expect(error_log_json_msg).to match(hash_including(
"exception_class" => "StandardError",
"exception_message" => "default exception",
expect(error_log_json).to match(hash_including(
"govuk_request_id" => "some-value",
))

error_log_json_msg = error_log_json["message"]
expect(error_log_json_msg).to match(hash_including(
"exception_class" => "StandardError",
"exception_message" => "default exception",
))
expect(error_log_json_msg).to have_key("stacktrace")
expect(error_log_json_msg["stacktrace"]).to be_a(Array)
end

it "logs errors thrown by the application with no govuk_request_id" do
GovukJsonLogging.configure
get "/error"
fake_stdout.rewind
lines = fake_stdout.read.split("\n")
expect(lines).to include(/default exception/)
error_log_line = lines.find { |log| log.match?(/default exception/) }
expect(error_log_line).not_to be_empty
error_log_json = JSON.parse(error_log_line)
error_log_json_msg = error_log_json["message"]
expect(error_log_json_msg).to match(hash_including(
"exception_class" => "StandardError",
"exception_message" => "default exception",
))
expect(error_log_json_msg).to have_key("stacktrace")
expect(error_log_json_msg["stacktrace"]).to be_a(Array)
end

it "logs to stdout in JSON format with govuk_request_id" do
stub_const("GdsApi::GovukHeaders", govuk_headers_class)
GovukJsonLogging.configure
logger = Rails.logger
logger.info("test default log entry")
fake_stdout.rewind
log_line = fake_stdout.read
log_json = JSON.parse(log_line)

expect(log_json).to include("message" => "test default log entry")
expect(log_json).to include("govuk_request_id" => "some-value")

end
end
end
end

0 comments on commit 5836a2e

Please sign in to comment.