Skip to content

Commit

Permalink
ViewComponent base instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Hannah Ramadan committed Dec 18, 2023
1 parent b59d82a commit 543cd94
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ Metrics/AbcSize:
- lib/new_relic/cli/commands/deployments.rb
- test/**/*

# Offense count: 1
Metrics/CollectionLiteralLength:
Exclude:
- 'lib/new_relic/agent/configuration/default_source.rb'

# Offense count: 7
Minitest/AssertRaisesCompoundBody:
Exclude:
Expand Down
7 changes: 7 additions & 0 deletions lib/new_relic/agent/configuration/default_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,13 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of Stripe at startup. May be one of: `enabled`, `disabled`.'
},
:'instrumentation.view_component' => {
:default => 'enabled',
:public => true,
:type => String,
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of ViewComponent at startup. May be one of: `enabled`, `disabled`.'
},
:'stripe.user_data.include' => {
default: NewRelic::EMPTY_ARRAY,
public: true,
Expand Down
30 changes: 30 additions & 0 deletions lib/new_relic/agent/instrumentation/view_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require_relative 'view_component/instrumentation'
require_relative 'view_component/chain'
require_relative 'view_component/prepend'

DependencyDetection.defer do
named :'view_component'

depends_on do
NewRelic::Agent.config[:'instrumentation.view_component'] == 'enabled'
end

depends_on do
defined?(ViewComponent) &&
ViewComponent::Base.method_defined?(:render_in)
end

executes do
NewRelic::Agent.logger.info('Installing view_component instrumentation')

if use_prepend?
prepend_instrument ViewComponent::Base, NewRelic::Agent::Instrumentation::ViewComponent::Prepend
else
chain_instrument NewRelic::Agent::Instrumentation::ViewComponent::Chain
end
end
end
21 changes: 21 additions & 0 deletions lib/new_relic/agent/instrumentation/view_component/chain.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

module NewRelic::Agent::Instrumentation
module ViewComponent::Chain
def self.instrument!
::ViewComponent.class_eval do
include NewRelic::Agent::Instrumentation::ViewComponent

alias_method(:render_in_without_tracing, :render_in)

def render_in(*args)
render_in_with_tracing(*args) do
render_in_without_tracing(*args)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

module NewRelic::Agent::Instrumentation
module ViewComponent
INSTRUMENTATION_NAME = 'view_component'

def render_in_with_tracing(*args)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

component = self.class.name
identifier = self.class.identifier

segment = NewRelic::Agent::Tracer.start_segment(
name: metric_name(identifier, component)
)

begin
NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
ensure
NewRelic::Agent::Transaction::Segment.finish(segment)
end
end

def metric_name(identifier, component)
"View/#{metric_path(identifier)}/#{component}"
end

def metric_path(identifier)
if identifier.nil?
'component'
elsif identifier && (parts = identifier.split('/')).size > 1
parts[-2..-1].join('/') # Get filepath by assuming the Rails' structure: app/components/home/example_component.rb
else
NewRelic::Agent::UNKNOWN_METRIC
end
end
end
end
13 changes: 13 additions & 0 deletions lib/new_relic/agent/instrumentation/view_component/prepend.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

module NewRelic::Agent::Instrumentation
module ViewComponent::Prepend
include NewRelic::Agent::Instrumentation::ViewComponent

def render_in(*args)
render_in_with_tracing(*args) { super }
end
end
end
19 changes: 19 additions & 0 deletions test/multiverse/suites/view_component/config/newrelic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
development:
error_collector:
enabled: true
apdex_t: 0.5
monitor_mode: true
license_key: bootstrap_newrelic_admin_license_key_000
instrumentation:
view_component: <%= $render_in %>
app_name: test
log_level: debug
host: 127.0.0.1
api_host: 127.0.0.1
transaction_trace:
record_sql: obfuscated
enabled: true
stack_trace_threshold: 0.5
transaction_threshold: 1.0
capture_params: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

class ViewComponentInstrumentationTest < Minitest::Test
def setup
@stats_engine = NewRelic::Agent.instance.stats_engine
end

def teardown
NewRelic::Agent.instance.stats_engine.clear_stats
end

# Add tests here
end

0 comments on commit 543cd94

Please sign in to comment.