-
Notifications
You must be signed in to change notification settings - Fork 369
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 tests to verify active_span can be tagged from within controller methods #439
Add tests to verify active_span can be tagged from within controller methods #439
Conversation
…for setting custom tags
e28fb51
to
e6ac666
Compare
# ActionController patch for Ruby 2.0+ | ||
module ActionControllerPatch | ||
# compat for Ruby versions not supporting #prepend | ||
include CompatActionControllerPatch unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.0.0') |
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.
How does this line work? Won't doing an include
here only include it into the ActionControllerPatch
, and not into the ActionController::Instrumentation
module? Typically the only way to nest module inheritance is to implement the def self.included(base)
method.
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.
Its here to build proper ancestry chain for ActionControllerPatch to work when included in Ruby < 2.0.
alias_method :process_action, :process_action_with_datadog | ||
remove_method :process_action | ||
|
||
include ActionControllerPatch |
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 don't think you can just include
the patch here; this is something I spent a lot of time trying to figure out. I think you need prepend
in order to inject the patch at the top of the ancestor stack.
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.
prepend
is only required to override existing methods. In ruby < 2.0 we can still do that but it requires some additional steps.
This is to make sure we reuse the same class and instrumentation implementation for both versions. Ensuring there are no difference in actual instrumentation implementation when running ruby 1.9.3.
@@ -163,49 +163,36 @@ def patch_process_action | |||
else | |||
# Rewrite module that gets composed into the Rails controller base class | |||
::ActionController::Instrumentation.class_eval do | |||
def process_action_with_datadog(*args) |
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 it's great that we're adding tests to cover a scenario we didn't test previously. But I don't think it's a good time to make big changes to how this works. The implementation might be a bit ugly, but the added benefit of making it a little cleaner vs the risk of introducing bugs is not a good trade. In the longer term, we'd like to drop support for 1.9 altogether, so we'll just simply remove this code instead of refactoring it.
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 don't see it as big change. Code deduplication here is used to make sure there are no differences in behavior when running on ruby 1.9.3 or 2.00.
I touched this code, because before I found another way, I thought that I need to add some additional behavior to both methods. Instead of duplicating code I decided to ensure both implementations will allways be the same. But since that went away, I still left the change as in my opinion it improves the existing code.
AFAIK there is no timeline for deprecating 1.9.3 support so at this moment, I think its still worthwhile to treat 1.9.3 as first class citizen.
bd7cb21
to
6ea9366
Compare
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.
Just one suggestion to make the patch changes you've introduced a little more idiomatic, and some comments about documentation changes.
docs/GettingStarted.md
Outdated
def index | ||
return head :not_found unless authenticated? | ||
|
||
tracer = Datadog.configuration[:rails][: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.
Although this is a technically correct example, it applies very specifically to Rails. Would it be better to provide a more generic example? Don't want users to be hung up on how to get the tracer. Maybe something like:
current_span = Datadog.tracer.active_span
unless current_span.nil?
current_span.set_tag('my_tag', 'my_value')
end
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.
Yeah, I was quite curious which way should be cannonical. Since user can set different tracer for each integration.
Datadog.tracer.active_span
looks much nicer - lets use it.
docs/GettingStarted.md
Outdated
return head :not_found unless authenticated? | ||
|
||
tracer = Datadog.configuration[:rails][:tracer] | ||
tracer.active_span&.set_tag('authenticated', true) |
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 don't think we should use the &.
operator because we support 1.9.3/2.0.0/2.1.10/2.2.10 right now, and those versions don't have the safe operator. (Don't want to cause confusion with users who might copy-paste code.)
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.
Yeah since we support the old versions, I hoped I would be able to use this operator in the docs at least. But you're right, that less surprises for our users == :better
.
|
||
# ActionController patch | ||
module ActionControllerPatch | ||
def self.instrument(klass) |
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 strategy here is good, but it isn't quite as idiomatic as it could be. I might suggest restructuring this a bit to take advantage of the Ruby included
method to drive this. Something like:
module ActionControllerPatch
def self.included(base)
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.0.0')
base.send(:prepend, ProcessActionPatch)
else
base.class_eval do
alias_method :process_action_without_datadog, :process_action
include ProcessActionPatch
end
end
end
module ProcessActionCompatibilityPatch
def process_action(*args)
process_action_without_datadog(*args)
end
end
module ProcessActionPatch
# compat for Ruby versions not supporting #prepend
include ProcessActionCompatibilityPatch unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.0.0')
def process_action(*args)
# ...
end
end
end
This would enable you to simply do ::ActionController::Metal.send(:include, ActionControllerPatch)
to apply the patch.
Some benefits of doing so:
- Looks a bit more idiomatic
- You effectively prevent patching the controller twice, by using
include
instead of a generic module function. - You can compartmentalize all of the modules needed to patch
ActionController
within theActionControllerPatch
module.
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 | ||
|
||
# Compat module for Ruby versions not supporting #prepend | ||
module CompatActionControllerPatch |
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.
Compat
should be expanded into Compatible
or Compatibility
for readability.
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.
👍
- backported fix for status being Array into refactored patcher # Conflicts: # lib/ddtrace/contrib/rails/core_extensions.rb
ac2e71a
to
a4a084d
Compare
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'm happy with the code changes, although I still think we should change the documentation.
|
||
You can tag additional information to current active span from any method. Note however that if the method is called and there is no span currently active `active_span` will be nil. | ||
|
||
```ruby |
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 still think this example should be more generalized; don't want users to think this only applies to Rails controllers. See my comment from my previous review.
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.
Good point! Fixed
module Rails | ||
# Instrument ActiveController processing | ||
module ActionControllerPatch | ||
def self.included(base) |
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.
👍
@delner addressed the review comments. |
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 @pawelchcki !
While implementing possible alternative solution to #436
I've found that we already support tagging spans from within Controller methods.
This PR is meant to salvage some of that work
Todo: