Skip to content

[NO-TICKET] Improve tracing Rails route computation #4688

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,23 @@ module ActionPack
module ActionDispatch
# Instrumentation for ActionDispatch components
module Instrumentation
SCRIPT_NAME_KEY = 'SCRIPT_NAME'
FORMAT_SUFFIX = '(.:format)'

module_function

def set_http_route_tags(route_spec, script_name)
def set_http_route_tags(route_spec, route_path)
return unless Tracing.enabled?

return unless route_spec

request_trace = Tracing.active_trace
return unless request_trace

request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, route_spec.to_s.gsub(/\(.:format\)\z/, ''))
request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, route_spec)

if script_name && !script_name.empty?
request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH, script_name)
if route_path && !route_path.empty?
request_trace.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH, route_path)
end
end

Expand All @@ -40,16 +43,17 @@ module Journey
# Instrumentation for ActionDispatch::Journey::Router for Rails versions older than 7.1
module Router
def find_routes(req)
# result is an array of [match, parameters, route] tuples
result = super
result.each do |_, _, route|
next unless Instrumentation.dispatcher_route?(route)

# result is an array of [match, parameters, route] tuples
routes = result.map(&:last)
http_route = route.path.spec.to_s
http_route.delete_suffix!(FORMAT_SUFFIX)

routes.each do |route|
if Instrumentation.dispatcher_route?(route)
Instrumentation.set_http_route_tags(route.path.spec, req.env['SCRIPT_NAME'])
break
end
Instrumentation.set_http_route_tags(http_route, req.env[SCRIPT_NAME_KEY])

break
end

result
Expand All @@ -62,7 +66,10 @@ module LazyRouter
def find_routes(req)
super do |match, parameters, route|
if Instrumentation.dispatcher_route?(route)
Instrumentation.set_http_route_tags(route.path.spec, req.env['SCRIPT_NAME'])
http_route = route.path.spec.to_s
http_route.delete_suffix!(FORMAT_SUFFIX)

Instrumentation.set_http_route_tags(http_route, req.env[SCRIPT_NAME_KEY])
end

yield [match, parameters, route]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@
require 'datadog/tracing/contrib/action_pack/action_dispatch/instrumentation'

RSpec.describe Datadog::Tracing::Contrib::ActionPack::ActionDispatch::Instrumentation do
describe '::set_http_route_tags' do
let(:tracing_enabled) { true }

before do
expect(Datadog::Tracing).to receive(:enabled?).and_return(tracing_enabled)
end

describe '.set_http_route_tags' do
context 'when tracing is disabled' do
let(:tracing_enabled) { false }
before { expect(Datadog::Tracing).to receive(:enabled?).and_return(false) }

it 'sets no tags' do
Datadog::Tracing.trace('rack.request') do |_span, trace|
Expand All @@ -24,37 +18,41 @@
end
end

it 'sets http.route and http.route.path tags on existing trace' do
Datadog::Tracing.trace('rack.request') do |_span, trace|
described_class.set_http_route_tags('/users/:id(.:format)', '/auth')
context 'when tracing is enabled' do
before { expect(Datadog::Tracing).to receive(:enabled?).and_return(true) }

expect(trace.send(:meta).fetch('http.route')).to eq('/users/:id')
expect(trace.send(:meta).fetch('http.route.path')).to eq('/auth')
it 'sets http.route and http.route.path tags on existing trace' do
Datadog::Tracing.trace('rack.request') do |_span, trace|
described_class.set_http_route_tags('/users/:id', '/auth')

expect(trace.send(:meta).fetch('http.route')).to eq('/users/:id')
expect(trace.send(:meta).fetch('http.route.path')).to eq('/auth')
end
end
end

it 'sets no http.route.path when script name is nil' do
Datadog::Tracing.trace('rack.request') do |_span, trace|
described_class.set_http_route_tags('/users/:id(.:format)', nil)
it 'sets no http.route.path when script name is nil' do
Datadog::Tracing.trace('rack.request') do |_span, trace|
described_class.set_http_route_tags('/users/:id', nil)

expect(trace.send(:meta).fetch('http.route')).to eq('/users/:id')
expect(trace.send(:meta)).not_to have_key('http.route.path')
expect(trace.send(:meta).fetch('http.route')).to eq('/users/:id')
expect(trace.send(:meta)).not_to have_key('http.route.path')
end
end
end

it 'sets no tags when route spec is nil' do
Datadog::Tracing.trace('rack.request') do |_span, trace|
described_class.set_http_route_tags(nil, '/auth')
it 'sets no tags when route spec is nil' do
Datadog::Tracing.trace('rack.request') do |_span, trace|
described_class.set_http_route_tags(nil, '/auth')

expect(trace.send(:meta)).not_to have_key('http.route')
expect(trace.send(:meta)).not_to have_key('http.route.path')
expect(trace.send(:meta)).not_to have_key('http.route')
expect(trace.send(:meta)).not_to have_key('http.route.path')
end
end
end

it 'does not create new traces when no active trace is present' do
described_class.set_http_route_tags('/users/:id', '/auth')
it 'does not create new traces when no active trace is present' do
described_class.set_http_route_tags('/users/:id', '/auth')

expect(traces).to be_empty
expect(traces).to be_empty
end
end
end
end
Loading