datadog plugin: fix error callback and general issues

also, made the handler a bit more functional style, which curbs some of the complexity
This commit is contained in:
HoneyryderChuck 2025-01-17 16:52:15 +00:00
parent 6ac3d346b9
commit aecb6f5ddd

View File

@ -30,56 +30,51 @@ module Datadog::Tracing
# receiving a response, and it is fed the start time stored inside the tracer object.
#
module Plugin
class RequestTracer
include Contrib::HttpAnnotationHelper
module RequestTracer
extend Contrib::HttpAnnotationHelper
module_function
SPAN_REQUEST = "httpx.request"
# initializes the tracer object on the +request+.
def initialize(request)
@request = request
@start_time = nil
# initializes tracing on the +request+.
def call(request)
start_time = nil
# request objects are reused, when already buffered requests get rerouted to a different
# connection due to connection issues, or when they already got a response, but need to
# be retried. In such situations, the original span needs to be extended for the former,
# while a new is required for the latter.
request.on(:idle) { reset }
request.on(:idle) do
next unless start_time
start_time = now
end
# the span is initialized when the request is buffered in the parser, which is the closest
# one gets to actually sending the request.
request.on(:headers) { call }
end
request.on(:headers) do
start_time ||= now
end
# sets up the span start time, while preparing the on response callback.
def call(*args)
return if @start_time
start(*args)
@request.once(:response, &method(:finish))
end
private
# just sets the span init time. It can be passed a +start_time+ in cases where
# this is collected outside the request transaction.
def start(start_time = now)
@start_time = start_time
end
# resets the start time for already finished request transactions.
def reset
return unless @start_time
start
request.on(:response) do |response|
finish(request, response, start_time)
end
end
# creates the span from the collected +@start_time+ to what the +response+ state
# contains. It also resets internal state to allow this object to be reused.
def finish(response)
return unless @start_time
def finish(request, response, start_time)
if !start_time && (response.is_a?(::HTTPX::ErrorResponse) && response.error.respond_to?(:connection))
# handles the case when the +error+ happened during name resolution, which means
# that the tracing start point hasn't been triggered yet; in such cases, the approximate
# initial resolving time is collected from the connection, and used as span start time,
# and the tracing object in inserted before the on response callback is called.
start_time = response.error.connection.init_time
end
span = initialize_span
return unless start_time
span = initialize_span(request, start_time)
return unless span
@ -92,22 +87,22 @@ module Datadog::Tracing
end
span.finish
ensure
@start_time = nil
end
# return a span initialized with the +@request+ state.
def initialize_span
verb = @request.verb
uri = @request.uri
def initialize_span(request, start_time)
verb = request.verb
uri = request.uri
span = create_span(@request)
config = configuration(request)
span = create_span(request, config, start_time)
span.resource = verb
# Add additional request specific tags to the span.
span.set_tag(TAG_URL, @request.path)
span.set_tag(TAG_URL, request.path)
span.set_tag(TAG_METHOD, verb)
span.set_tag(TAG_TARGET_HOST, uri.host)
@ -116,16 +111,16 @@ module Datadog::Tracing
# Tag as an external peer service
span.set_tag(TAG_PEER_SERVICE, span.service)
if configuration[:distributed_tracing]
if config[:distributed_tracing]
propagate_trace_http(
Datadog::Tracing.active_trace.to_digest,
@request.headers
request.headers
)
end
# Set analytics sample rate
if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
if Contrib::Analytics.enabled?(config[:analytics_enabled])
Contrib::Analytics.set_sample_rate(span, config[:analytics_sample_rate])
end
span
@ -138,8 +133,8 @@ module Datadog::Tracing
::Datadog::Core::Utils::Time.now.utc
end
def configuration
@configuration ||= Datadog.configuration.tracing[:httpx, @request.uri.host]
def configuration(request)
Datadog.configuration.tracing[:httpx, request.uri.host]
end
if Gem::Version.new(DATADOG_VERSION::STRING) >= Gem::Version.new("2.0.0")
@ -147,12 +142,12 @@ module Datadog::Tracing
Datadog::Tracing::Contrib::HTTP.inject(digest, headers)
end
def create_span(request)
def create_span(request, configuration, start_time)
Datadog::Tracing.trace(
SPAN_REQUEST,
service: service_name(request.uri.host, configuration, Datadog.configuration_for(self)),
service: service_name(request.uri.host, configuration),
type: TYPE_OUTBOUND,
start_time: @start_time
start_time: start_time
)
end
else
@ -160,12 +155,12 @@ module Datadog::Tracing
Datadog::Tracing::Propagation::HTTP.inject!(digest, headers)
end
def create_span(request)
def create_span(request, configuration, start_time)
Datadog::Tracing.trace(
SPAN_REQUEST,
service: service_name(request.uri.host, configuration, Datadog.configuration_for(self)),
service: service_name(request.uri.host, configuration),
span_type: TYPE_OUTBOUND,
start_time: @start_time
start_time: start_time
)
end
end
@ -178,7 +173,7 @@ module Datadog::Tracing
return unless Datadog::Tracing.enabled?
RequestTracer.new(self)
RequestTracer.call(self)
end
end
@ -190,26 +185,6 @@ module Datadog::Tracing
@init_time = ::Datadog::Core::Utils::Time.now.utc
end
# handles the case when the +error+ happened during name resolution, which meanns
# that the tracing logic hasn't been injected yet; in such cases, the approximate
# initial resolving time is collected from the connection, and used as span start time,
# and the tracing object in inserted before the on response callback is called.
def handle_error(error, request = nil)
return super unless Datadog::Tracing.enabled?
return super unless error.respond_to?(:connection)
@pending.each do |req|
next if request and request == req
RequestTracer.new(req).call(error.connection.init_time)
end
RequestTracer.new(request).call(error.connection.init_time) if request
super
end
end
end