Skip to content

Commit

Permalink
Changed: ActionView instrumentation to use prepend instead of rewrite.
Browse files Browse the repository at this point in the history
  • Loading branch information
delner committed Aug 16, 2019
1 parent b094bd0 commit 3ec2bde
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 148 deletions.
182 changes: 181 additions & 1 deletion lib/ddtrace/contrib/action_view/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
151 changes: 5 additions & 146 deletions lib/ddtrace/contrib/action_view/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -24,164 +22,25 @@ 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

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
Expand Down
2 changes: 1 addition & 1 deletion lib/ddtrace/contrib/rails/framework.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 3ec2bde

Please sign in to comment.