-
Notifications
You must be signed in to change notification settings - Fork 375
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
Add Excon instrumentation #211
Conversation
Thanks for that @walterking ! will review it during the week! |
3f9d00c
to
8fe047c
Compare
I think I updated it to the new configuration style correctly: Datadog.configure do |c|
c.use :excon, distributed_tracing_enabled: true, split_by_domain: true
end |
8fe047c
to
c112129
Compare
@walterking great! we're going to merge the new Configuration API so that your PR can be merged too! Thank you! |
lib/ddtrace/contrib/excon/patcher.rb
Outdated
end | ||
|
||
def add_middleware | ||
::Excon.defaults[:middlewares].append(Middleware) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, I tracked the weirdness i was seeing in our excon instrumentation to this middleware being appended after another middleware that made some redis calls. Hence the order was messed up so it looked async. I think maybe this should be prepend, but not sure, or what other ordering issues may come up from this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the best place to do this is by injecting the trace middleware as soon as possible in the stack, but behind the ResponseParser middleware.
With some code changes, you can do either of these:
# Default middleware stack
# This is what the patcher now does
Excon.new(
'http://example.com',
middlewares: Datadog::Contrib::Excon::Middleware.with(middleware_options).around_default_stack
)
# Custom middleware stack
# If you want to specially configure an Excon connection
Excon.new(
'http://example.com',
middlewares: [
Excon::Middleware::ResponseParser,
Datadog::Contrib::Excon::Middleware.with(middleware_options),
Excon::Middleware::Mock
]
)
c112129
to
6326a3b
Compare
4ed8c81
to
93b9ec1
Compare
|
||
def service_name(datum) | ||
# TODO: Change this to implement more sensible multiplexing | ||
Datadog.configuration[:excon][:split_by_domain] ? datum[:host] : @options[:service_name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@palazzem Does this make sense to allow service_name
to derive from the host per request? This was a feature in Faraday but I think it could be problematic.
a648c5a
to
71ee091
Compare
Did some overhauling on this pull request:
Shown above: Three Excon requests using 1) default adapter, 2) using middleware with service name, 3) using middleware with
|
71ee091
to
f666303
Compare
| Key | Description | Default | | ||
| --- | --- | --- | | ||
| `service_name` | Service name for Excon instrumentation. When provided to middleware for a specific connection, it applies only to that connection object. | `'excon'` | | ||
| `split_by_domain` | Uses the request domain as the service name when set to `true`. | `false` | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| `error_handler` | A `Proc` that accepts a `response` parameter. If it evaluates to a *truthy* value, the trace span is marked as an error. By default only sets 5XX responses as errors. | `nil` | | ||
| `tracer` | A `Datadog::Tracer` instance used to instrument the application. Usually you don't need to set that. | `Datadog.tracer` | | ||
|
||
**Configuring connections to use different settings** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other integrations, we may do something like:
Datadog.configure(client, service_name: 'something_else')
I guess we can't do it for Excon
(and so the middleware must be added manually) because once initialized it cannot be changed? Consider it as a comment, if it introduces too much complexity we can keep it that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@palazzem Right. If you look at Faraday, which is almost identical to Excon, we can't do this either. Both of these integrations implement a middleware scheme, which require middleware to be injected when the connection/client object is created.
It might be possible to change the configuration to be derived from a Pin
(much like the Sequel integration), which would permit the Datadog.configure
strategy to work for a limited set of options (e.g. service_name
.) But that would also introduce monkey-patching into a library that already provides an API for instrumentation. Not to say that might not be worth it, but it would represent an larger overhaul that does run contrary to the gem author's intentions.
I'd recommend keeping this as is for now, but scheduling a task to refactor it to support that Datadog.configure
strategy in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Thanks!
module Excon | ||
# Middleware implements an excon-middleware for ddtrace instrumentation | ||
class Middleware < ::Excon::Middleware::Base | ||
SPAN_NAME = 'excon.request'.freeze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
end | ||
|
||
def service_name(datum) | ||
# TODO: Change this to implement more sensible multiplexing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this TODO
about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This split_by_domain?
function is generating new services for each host it connects to, named after the host. That seems like it could make a mess of your APM services page double quick. This comment was more to say "should we be splitting the resource by domain instead?"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is expected (and it's how it works for Python) is that if you enable that flag (disabled by default), you should see a new service for each host you're calling (so the name of the service is just the name of the host).
Splitting the resource by domain works too, but it doesn't show the time spent in different services in the sublayer graphs.
end | ||
|
||
def annotate!(span, datum) | ||
span.resource = datum[:method].to_s.upcase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The resource in that way is not great. We can keep it that way for now, but we should revisit all our HTTP clients at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. This was how it was before, not sure what it should be. There are a lot of things we could do with this resource.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
I took the faraday implementation and merged it with my excon instrumentation to generate this.
Configuration, not sure how thats supposed to work, but I know youre changing that.
tracer.trace('test') do |span| Excon.get("https://google.com") end
--->
Name: test
Span ID: 2407071452987440042
Parent ID: 0
Trace ID: 8046461497076297129
Type:
Service: deliver-default
Resource: test
Error: 0
Start: 1506929900532989952
End: 1506929900707412992
Duration: 174423000
Tags: [
system.pid => 3337]
Metrics: [ ],
Name: excon-request
Span ID: 6715890832042833974
Parent ID: 2407071452987440042
Trace ID: 8046461497076297129
Type: http
Service: excon-request
Resource: GET
Error: 0
Start: 1506929900533435904
End: 1506929900707217920
Duration: 173782000
Tags: [
http.url => /,
http.method => GET,
out.host => google.com,
out.port => 443,
http.status_code => 301]
Metrics: [ ]]