-
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
Implement Sinatra Env and Headers extensions #441
Conversation
2018809
to
d4175f2
Compare
def initialize(app) | ||
@app = app | ||
end | ||
|
||
def call(env) | ||
span = RequestSpan.span!(env) | ||
# Extend the Env with Sinatra tracing functions | ||
env.extend(Sinatra::Env) |
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.
Interesting approach. I'm worried however that this might introduce somewhat unstable API to rack envs.
Imagine another rack middleware used in the same application. That does use env.datadog_span
accessor methods. It all works well as long as the order of MWs is TracerMiddleware -> OtherMiddleware
.
When the order gets reversed then calling env.datadog_span
will throw NoMethodError
.
As such I think it would be much safer to use module functions to share the code operating on env
.
If avoiding object allocations wasn't one of our goals, I would suggest using Composition over Inheritance. Because env.extend
is Ruby specific runtime inheritance.
In general also extending objects this way should be avoided due to reasons mentioned above, where order of execution change would mean the code could suddenly throw NoMethodError
.
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.
Fair concern about datadog_span
, regarding the change in middleware order. However, I don't think whether you extend
or call the module will make a difference anyways.
My reason for saying so is imagine this second middleware that was dependent on env.datadog_span
. Instead, it will call to a function like Sinatra::Env.datadog_span(env)
which won't raise a NoMethodError
now, but will still fail because the span will be missing from the env
because TracerMiddleware
didn't run yet. In this case, OtherMiddleware
still depends on TracerMiddleware
no matter how you articulate it. And if there is such a dependency, it's reasonable that the dependent be responsible for placing itself in the right position (i.e. the developer should be responsible for configuring their middleware stack correctly.)
However, I will yield that extend
is a bit unorthodox, and by using it, you might have inconsistency between objects of the same type; something in the general sense that might not sit well with many developers. As such, I think it is reasonable to remove the extend
implementation in favor of something simpler, albeit not as elegant.
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.
Thanks @delner for creating this PR making the code more reusable.
However overall obj.extend
can cause problems down the line by introducing API's that change based on order of execution. Using that in production code should be carefully weighed.
@pawelchcki Thanks for the feedback! I've altered the modules to implement their functions as 'static', instead of being extended by the Rack objects directly. Will this resolve your concerns? Feel free to take another look! |
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.
Looks good, thanks for making this PR @delner!.
I've left a few minor remarks about TODOs and code comments describing what code below is doing.
One suggestion, would you mind rebasing this on 0.13-dev? So that this change would not block merging of
#427
I'm still happy to merge this once the remarks are addressed I only think we could do it after #427 is merged since this change is quite self contained.
lib/ddtrace/contrib/sinatra/env.rb
Outdated
module Contrib | ||
module Sinatra | ||
# Gets and sets trace information from a Rack Env | ||
# TODO: Extract me? |
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.
Could we handle this TODO i.e. implement the suggestion or create Trello card to track 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 can remove this comment for now. We should extract this in a future PR, ideally as a part of the Rack/Sinatra header refactor PR you had opened. (#432)
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'll add that there as a TODO so we won't lose this idea
module Contrib | ||
module Sinatra | ||
# Gets and sets trace information from a Rack headers Hash | ||
# TODO: Extract me? |
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.
Could we handle this TODO as well ?
private | ||
|
||
def tracer | ||
configuration[:tracer] |
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.
👍
service: configuration[:service_name], | ||
span_type: Datadog::Ext::HTTP::TYPE | ||
) do |span| | ||
# Set the span on the Env |
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.
Is this comment necessary? It seems to duplicate what the line below says.
Maybe we could thing about renaming the method below so that this comment is necessary ?
ensure | ||
span.finish | ||
end | ||
# Tag request headers |
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.
Is this comment necessary? It seems to duplicate what the line below says.
Maybe we could thing about renaming the method below so that this comment is necessary ?
if env.key?(rack_header) | ||
result[Datadog::Ext::HTTP::RequestHeaders.to_tag(header)] = env[rack_header] | ||
end | ||
# Tag response headers |
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.
Is this comment necessary? It seems to duplicate what the line below says.
Maybe we could thing about renaming the method below so that this comment is necessary ?
end | ||
|
||
# Return response |
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.
Since ruby has implicit returns and and I think we enforce them in Rubocop, maybe we should consider removing that restriction so you could explicitly state return [status, headers, response_body]
if you feel it improves readablity
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 suppose you're right. I'll remove all of these extraneous comments if you don't think it helps.
@pawelchcki I think that's reasonable. We can merge this one after. |
de49de7
to
7905dd6
Compare
@pawelchcki Fixed the comments and rebased on |
Thanks for making these changes @delner. Looks really good now! Lets merge it. |
As a suggestion for #427, I felt inspired to try a spin on @pawelchcki 's idea.
This pull request implements a
Sinatra::Env
andSinatra::Headers
extension, which decorates theenv
passed into the middleware with some Sinatra-tracing specific features. The benefit of doing this means our utility features become available, and context specific, across all the components of our Sinatra tracing, whereverenv
is available.Another potentially cool benefit is the
Env
andHeaders
modules aren't very Sinatra specific: they could be extracted and used for any Rack apps, so this might aid our efforts to undertake that refactor as well.This PR would be intended to be merged into #427 . @pawelchcki , let me know what you think!