Skip to content

Commit

Permalink
Fix OpenTelemetry span processing when using logstasher
Browse files Browse the repository at this point in the history
The logstasher gem monkey patches the `process_action.action_controller`
instrumentation. However the gem hasn't been updated for Rails 7, which
assumes that the payload has the 'request' object available. This
request object is used by the OpenTelemetry SDK to process spans. This
generates lots of error logs from the SDK, and prevents us from
recording some spans.

This re-monkey patches the instrumentation to use a raw_payload that is
expected in Rails 7.  Ideally, this would be fixed in the logstasher gem
however there has been little active development over the last year.
Longer term fix might be to move to a maintained logging library.
  • Loading branch information
theseanything committed Jun 4, 2024
1 parent f2d22cd commit ae4603e
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Unreleased

* Fix OpenTelemetry errors when using with Logstasher gem ([#372](https://github.com/alphagov/govuk_app_config/pull/372))

# 9.11.0

* Add GDS::SSO::PermissionDeniedError to excluded exceptions list ([#366](https://github.com/alphagov/govuk_app_config/pull/366))
Expand Down
6 changes: 6 additions & 0 deletions lib/govuk_app_config/govuk_json_logging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ def add_custom_fields(&block)
end

def self.configure(&block)
# Fixes the monkey patch from the logstasher gem to support Rails 7
config = Rails.application.config.logstasher
if (!config.controller_monkey_patch && config.controller_monkey_patch != false) || config.controller_monkey_patch == true
require_relative "./govuk_json_logging/rails_ext/action_controller/metal/instrumentation"
end

configuration = Configuration.new

configuration.instance_eval(&block) if block_given?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# This is copied from
# https://github.com/shadabahmed/logstasher/blob/main/lib/logstasher/rails_ext/action_controller/metal/instrumentation.rb
# Changes have been highlight in comments, otherwise the code is the same.

module ActionController
module Instrumentation
alias_method "orig_process_action", "process_action"

def process_action(*args)
# The raw payload has been updated to reflect the payload structure used
# in Rails 7.1, primarily the addition of the `headers`, `request` keys
# and using `request.filtered_path` instead of `request.fullpath`.
# https://github.com/rails/rails/blame/d39db5d1891f7509cde2efc425c9d69bbb77e670/actionpack/lib/action_controller/metal/instrumentation.rb#L60
raw_payload = {
controller: self.class.name,
action: action_name,
request:,
params: request.filtered_parameters,
headers: request.headers,
format: request.format.ref,
method: request.request_method,
path: begin
request.filtered_path
rescue StandardError
"unknown"
end,
}

LogStasher.add_default_fields_to_payload(raw_payload, request)

LogStasher.clear_request_context
LogStasher.add_default_fields_to_request_context(request)

ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)

ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
if respond_to?(:logstasher_add_custom_fields_to_request_context)
logstasher_add_custom_fields_to_request_context(LogStasher.request_context)
end

if respond_to?(:logstasher_add_custom_fields_to_payload)
before_keys = raw_payload.keys.clone
logstasher_add_custom_fields_to_payload(raw_payload)
after_keys = raw_payload.keys
# Store all extra keys added to payload hash in payload itself. This is a thread safe way
LogStasher::CustomFields.add(*(after_keys - before_keys))
end

result = super

payload[:status] = response.status
append_info_to_payload(payload)
LogStasher.store.each do |key, value|
payload[key] = value
end

LogStasher.request_context.each do |key, value|
payload[key] = value
end
result
end
end
alias_method "logstasher_process_action", "process_action"
end
end
1 change: 1 addition & 0 deletions spec/lib/govuk_json_logging_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def self.headers
end
end
end

before do
stub_const("DummyLoggingRailsApp", Class.new(Rails::Application) do
config.hosts.clear
Expand Down

0 comments on commit ae4603e

Please sign in to comment.