Skip to content
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

Conversation

pawelchcki
Copy link
Contributor

@pawelchcki pawelchcki commented May 30, 2018

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:

  • add documentation on how to access span from within nested method

@pawelchcki pawelchcki added do-not-merge/WIP Not ready for merge dev/refactor Involves refactoring existing components labels May 30, 2018
@pawelchcki pawelchcki force-pushed the feature/add_ability_to_set_access_current_span_from_within_controller branch from e28fb51 to e6ac666 Compare May 30, 2018 14:38
# 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')
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@pawelchcki pawelchcki force-pushed the feature/add_ability_to_set_access_current_span_from_within_controller branch from bd7cb21 to 6ea9366 Compare May 31, 2018 10:45
@pawelchcki pawelchcki added docs Involves documentation and removed do-not-merge/WIP Not ready for merge labels May 31, 2018
Copy link
Contributor

@delner delner left a 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.

def index
return head :not_found unless authenticated?

tracer = Datadog.configuration[:rails][:tracer]
Copy link
Contributor

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

Copy link
Contributor Author

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.

return head :not_found unless authenticated?

tracer = Datadog.configuration[:rails][:tracer]
tracer.active_span&.set_tag('authenticated', true)
Copy link
Contributor

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.)

Copy link
Contributor Author

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)
Copy link
Contributor

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 the ActionControllerPatch module.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@delner delner assigned pawelchcki and unassigned delner May 31, 2018
@pawelchcki pawelchcki force-pushed the feature/add_ability_to_set_access_current_span_from_within_controller branch from ac2e71a to a4a084d Compare June 4, 2018 11:59
@delner delner added the integrations Involves tracing integrations label Jun 4, 2018
Copy link
Contributor

@delner delner left a 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
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@pawelchcki pawelchcki assigned delner and unassigned pawelchcki Jun 5, 2018
@pawelchcki
Copy link
Contributor Author

@delner addressed the review comments.

Copy link
Contributor

@delner delner left a 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 !

@pawelchcki pawelchcki merged commit 0b2ca24 into master Jun 5, 2018
@pawelchcki pawelchcki deleted the feature/add_ability_to_set_access_current_span_from_within_controller branch June 5, 2018 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dev/refactor Involves refactoring existing components docs Involves documentation integrations Involves tracing integrations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants