diff --git a/lib/ddtrace/contrib/action_view/instrumentation.rb b/lib/ddtrace/contrib/action_view/instrumentation.rb index 008e71a3629..25d9849d808 100644 --- a/lib/ddtrace/contrib/action_view/instrumentation.rb +++ b/lib/ddtrace/contrib/action_view/instrumentation.rb @@ -5,7 +5,187 @@ module Contrib module ActionView # Defines instrumentation for ActionView module Instrumentation - # TODO: Add instrumentation here. + # Instrumentation for template rendering + module TemplateRenderer + # Rails < 3.1 template rendering + module Rails30 + # rubocop:disable Metrics/MethodLength + def self.prepended(base) + # rubocop:disable Metrics/BlockLength + base.class_eval do + def render_with_datadog(*args, &block) + # NOTE: This check exists purely for Rails 3.0 compatibility. + # The 'if' part can be removed when support for Rails 3.0 is removed. + if active_datadog_span + render_without_datadog(*args, &block) + else + datadog_tracer.trace( + Ext::SPAN_RENDER_TEMPLATE, + span_type: Datadog::Ext::HTTP::TEMPLATE + ) do |span| + with_datadog_span(span) { render_without_datadog(*args, &block) } + end + end + end + + def render_template_with_datadog(*args) + begin + template = args[0] + layout_name = args[1] + + # update the tracing context with computed values before the rendering + template_name = template.try('identifier') + template_name = Utils.normalize_template_name(template_name) + + if template_name + active_datadog_span.set_tag( + Ext::TAG_TEMPLATE_NAME, + template_name + ) + end + + if layout_name + active_datadog_span.set_tag( + Ext::TAG_LAYOUT, + layout_name + ) + end + rescue StandardError => e + Datadog::Tracer.log.debug(e.message) + end + + # execute the original function anyway + render_template_without_datadog(*args) + end + + private + + attr_accessor :active_datadog_span + + def datadog_tracer + Datadog.configuration[:action_view][:tracer] + end + + def with_datadog_span(span) + self.active_datadog_span = span + yield + ensure + self.active_datadog_span = nil + end + + # method aliasing to patch the class + alias_method :render_without_datadog, :render + alias_method :render, :render_with_datadog + + alias_method :render_template_without_datadog, :_render_template + alias_method :_render_template, :render_template_with_datadog + end + end + end + + # Rails >= 3.1 template rendering + module Rails31Plus + def render(*args, &block) + datadog_tracer.trace( + Ext::SPAN_RENDER_TEMPLATE, + span_type: Datadog::Ext::HTTP::TEMPLATE + ) do |span| + with_datadog_span(span) { super(*args, &block) } + end + end + + def render_template(*args) + begin + # arguments based on render_template signature (stable since Rails 3.2) + template = args[0] + layout_name = args[1] + + # update the tracing context with computed values before the rendering + template_name = template.try('identifier') + template_name = Utils.normalize_template_name(template_name) + layout = layout_name.try(:[], 'virtual_path') + + if template_name + active_datadog_span.set_tag( + Ext::TAG_TEMPLATE_NAME, + template_name + ) + end + + if layout + active_datadog_span.set_tag( + Ext::TAG_LAYOUT, + layout + ) + end + rescue StandardError => e + Datadog::Tracer.log.debug(e.message) + end + + # execute the original function anyway + super(*args) + end + + private + + attr_accessor :active_datadog_span + + def datadog_tracer + Datadog.configuration[:action_view][:tracer] + end + + def with_datadog_span(span) + self.active_datadog_span = span + yield + ensure + self.active_datadog_span = nil + end + end + end + + # Instrumentation for partial rendering + module PartialRenderer + def render(*args, &block) + datadog_tracer.trace( + Ext::SPAN_RENDER_PARTIAL, + span_type: Datadog::Ext::HTTP::TEMPLATE + ) do |span| + with_datadog_span(span) { super(*args) } + end + end + + def render_partial(*args) + begin + template_name = Utils.normalize_template_name(@template.try('identifier')) + if template_name + active_datadog_span.set_tag( + Ext::TAG_TEMPLATE_NAME, + template_name + ) + end + rescue StandardError => e + Datadog::Tracer.log.debug(e.message) + end + + # execute the original function anyway + super(*args) + end + + private + + attr_accessor :active_datadog_span + + def datadog_tracer + Datadog.configuration[:action_view][:tracer] + end + + def with_datadog_span(span) + self.active_datadog_span = span + yield + ensure + self.active_datadog_span = nil + end + end end end end diff --git a/lib/ddtrace/contrib/action_view/patcher.rb b/lib/ddtrace/contrib/action_view/patcher.rb index d7d857f080e..7aa20414b28 100644 --- a/lib/ddtrace/contrib/action_view/patcher.rb +++ b/lib/ddtrace/contrib/action_view/patcher.rb @@ -8,8 +8,6 @@ module Datadog module Contrib module ActionView # Patcher enables patching of ActionView module. - # rubocop:disable Metrics/ModuleLength - # rubocop:disable Metrics/MethodLength module Patcher include Contrib::Patcher @@ -24,7 +22,7 @@ def patch begin patch_renderer rescue StandardError => e - Datadog::Tracer.log.error("Unable to apply Action View integration: #{e}") + Datadog::Tracer.log.error("Unable to apply Action View integration: #{e} Location: #{e.backtrace.first}") end end end @@ -32,156 +30,17 @@ def patch def patch_renderer do_once(:patch_renderer) do if defined?(::ActionView::TemplateRenderer) && defined?(::ActionView::PartialRenderer) - patch_template_renderer(::ActionView::TemplateRenderer) - patch_partial_renderer(::ActionView::PartialRenderer) + ::ActionView::TemplateRenderer.send(:prepend, Instrumentation::TemplateRenderer::Rails31Plus) + ::ActionView::PartialRenderer.send(:prepend, Instrumentation::PartialRenderer) elsif defined?(::ActionView::Rendering) && defined?(::ActionView::Partials::PartialRenderer) # NOTE: Rails < 3.1 compatibility: different classes are used - patch_template_renderer(::ActionView::Rendering) - patch_partial_renderer(::ActionView::Partials::PartialRenderer) + ::ActionView::Rendering.send(:prepend, Instrumentation::TemplateRenderer::Rails30) + ::ActionView::Partials::PartialRenderer.send(:prepend, Instrumentation::PartialRenderer) else Datadog::Tracer.log.debug('Expected Template/Partial classes not found; template rendering disabled') end end end - - def patch_template_renderer(klass) - # rubocop:disable Metrics/BlockLength - do_once(:patch_template_renderer) do - klass.class_eval do - def render_with_datadog(*args, &block) - # NOTE: This check exists purely for Rails 3.0 compatibility. - # The 'if' part can be removed when support for Rails 3.0 is removed. - if active_datadog_span - render_without_datadog(*args, &block) - else - datadog_tracer.trace( - Ext::SPAN_RENDER_TEMPLATE, - span_type: Datadog::Ext::HTTP::TEMPLATE - ) do |span| - with_datadog_span(span) { render_without_datadog(*args, &block) } - end - end - end - - def render_template_with_datadog(*args) - begin - # arguments based on render_template signature (stable since Rails 3.2) - template = args[0] - layout_name = args[1] - - # update the tracing context with computed values before the rendering - template_name = template.try('identifier') - template_name = Utils.normalize_template_name(template_name) - layout = if layout_name.is_a?(String) - # NOTE: Rails < 3.1 compatibility: the second argument is the layout name - layout_name - else - layout_name.try(:[], 'virtual_path') - end - if template_name - active_datadog_span.set_tag( - Ext::TAG_TEMPLATE_NAME, - template_name - ) - end - - if layout - active_datadog_span.set_tag( - Ext::TAG_LAYOUT, - layout - ) - end - rescue StandardError => e - Datadog::Tracer.log.debug(e.message) - end - - # execute the original function anyway - render_template_without_datadog(*args) - end - - private - - attr_accessor :active_datadog_span - - def datadog_tracer - Datadog.configuration[:action_view][:tracer] - end - - def with_datadog_span(span) - self.active_datadog_span = span - yield - ensure - self.active_datadog_span = nil - end - - # method aliasing to patch the class - alias_method :render_without_datadog, :render - alias_method :render, :render_with_datadog - - if klass.private_method_defined?(:render_template) || klass.method_defined?(:render_template) - alias_method :render_template_without_datadog, :render_template - alias_method :render_template, :render_template_with_datadog - else - # NOTE: Rails < 3.1 compatibility: the method name is different - alias_method :render_template_without_datadog, :_render_template - alias_method :_render_template, :render_template_with_datadog - end - end - end - end - - def patch_partial_renderer(klass) - do_once(:patch_partial_renderer) do - klass.class_eval do - def render_with_datadog(*args, &block) - datadog_tracer.trace( - Ext::SPAN_RENDER_PARTIAL, - span_type: Datadog::Ext::HTTP::TEMPLATE - ) do |span| - with_datadog_span(span) { render_without_datadog(*args) } - end - end - - def render_partial_with_datadog(*args) - begin - template_name = Utils.normalize_template_name(@template.try('identifier')) - if template_name - active_datadog_span.set_tag( - Ext::TAG_TEMPLATE_NAME, - template_name - ) - end - rescue StandardError => e - Datadog::Tracer.log.debug(e.message) - end - - # execute the original function anyway - render_partial_without_datadog(*args) - end - - private - - attr_accessor :active_datadog_span - - def datadog_tracer - Datadog.configuration[:action_view][:tracer] - end - - def with_datadog_span(span) - self.active_datadog_span = span - yield - ensure - self.active_datadog_span = nil - end - - # method aliasing to patch the class - alias_method :render_without_datadog, :render - alias_method :render, :render_with_datadog - alias_method :render_partial_without_datadog, :render_partial - alias_method :render_partial, :render_partial_with_datadog - end - end - end end end end diff --git a/lib/ddtrace/contrib/rails/framework.rb b/lib/ddtrace/contrib/rails/framework.rb index eef03b8ebba..78bea2b4a64 100644 --- a/lib/ddtrace/contrib/rails/framework.rb +++ b/lib/ddtrace/contrib/rails/framework.rb @@ -67,7 +67,7 @@ def self.activate_active_support!(config) end def self.activate_action_view!(config) - return unless defined?(::ActiveSupport) + return unless defined?(::ActionView) Datadog.configuration.use( :action_view,