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 Roda instrumentation #2144

Merged
merged 32 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
283279b
Initial commit Roda instrumentation
hannahramadan Jul 25, 2023
64b6ac9
disable Lint/DuplicateMethods
hannahramadan Jul 25, 2023
31a1ee2
Address feedback
hannahramadan Jul 26, 2023
4612fe8
Remove uncessary alias
hannahramadan Jul 26, 2023
6ee5ef9
Refactor naming method
hannahramadan Aug 1, 2023
05cc30f
Roda test files
hannahramadan Aug 1, 2023
67cacf6
Add some tests
hannahramadan Aug 2, 2023
1852fb9
Add test
hannahramadan Aug 4, 2023
0926775
test: remove minitest from env
hannahramadan Aug 4, 2023
db7734d
debug txn
hannahramadan Aug 4, 2023
8fbbe4e
Ruby 2.4 fix
hannahramadan Aug 4, 2023
ca48d74
test: and another one
hannahramadan Aug 4, 2023
cc43194
test: middleware disabling
hannahramadan Aug 7, 2023
13ca306
test: middleware fix
hannahramadan Aug 7, 2023
b91579d
Merge branch 'dev' into roda_instrumentation
hannahramadan Aug 7, 2023
10d8b0e
Add CHANGELOG
hannahramadan Aug 7, 2023
6ec648c
Add tests
hannahramadan Aug 8, 2023
160a64b
Refactor
hannahramadan Aug 8, 2023
72010af
Update CHANGELOG.md
hannahramadan Aug 8, 2023
8a57394
Apply suggestions from code review
hannahramadan Aug 8, 2023
2834638
Update test/multiverse/suites/roda/roda_instrumentation_test.rb
hannahramadan Aug 8, 2023
545c126
Updates
hannahramadan Aug 8, 2023
57ca227
Apply suggestions from code review
hannahramadan Aug 8, 2023
c79d978
Code feedback
hannahramadan Aug 9, 2023
e4ca1e0
Update lib/new_relic/agent/instrumentation/roda/instrumentation.rb
hannahramadan Aug 9, 2023
8123d11
Don't stub any_instance
hannahramadan Aug 9, 2023
c08cf6b
Move require to top of file
hannahramadan Aug 9, 2023
9b89d72
Relocate rack/browser requires
hannahramadan Aug 9, 2023
ed60437
Refactor
hannahramadan Aug 9, 2023
8e90f4f
put requires back
hannahramadan Aug 9, 2023
6b1fcc1
remove from instrum class
hannahramadan Aug 9, 2023
138176b
Update lib/new_relic/agent/instrumentation/roda/roda_transaction_name…
hannahramadan Aug 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

## dev

Version <dev> of the agent introduces improved error tracking functionality by associating a transaction id with each error, and uses more reliable network timeout logic.
Version <dev> of the agent adds [Roda](https://roda.jeremyevans.net/) instrumentation, introduces improved error tracking functionality by associating a transaction id with each error, and uses more reliable network timeout logic.

- **Feature: Add Roda instrumentation**

[Roda](https://roda.jeremyevans.net/) is a now an instrumented framework. The agent currently supports Roda versions 3.19.0+. [PR#2144](https://github.com/newrelic/newrelic-ruby-agent/pull/2144)

- **Feature: Improved error tracking transaction linking**

Expand Down
16 changes: 16 additions & 0 deletions lib/new_relic/agent/configuration/default_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def self.framework
:rails_notifications
end
when defined?(::Sinatra) && defined?(::Sinatra::Base) then :sinatra
when defined?(::Roda) then :roda
when defined?(::NewRelic::IA) then :external
else :ruby
end
Expand Down Expand Up @@ -1227,6 +1228,13 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:allowed_from_server => false,
:description => 'If `true`, disables [Sidekiq instrumentation](/docs/agents/ruby-agent/background-jobs/sidekiq-instrumentation).'
},
:disable_roda_auto_middleware => {
:default => false,
:public => true,
:type => Boolean,
:allowed_from_server => false,
:description => 'If `true`, disables agent middleware for Roda. This middleware is responsible for advanced feature support such as [page load timing](/docs/browser/new-relic-browser/getting-started/new-relic-browser) and [error collection](/docs/apm/applications-menu/events/view-apm-error-analytics).'
},
kaylareopelle marked this conversation as resolved.
Show resolved Hide resolved
:disable_sinatra_auto_middleware => {
:default => false,
:public => true,
Expand Down Expand Up @@ -1564,6 +1572,14 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of resque at start up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
},
:'instrumentation.roda' => {
:default => 'auto',
:public => true,
:type => String,
:dynamic_name => true,
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of Roda at start up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
fallwith marked this conversation as resolved.
Show resolved Hide resolved
},
:'instrumentation.sinatra' => {
:default => 'auto',
:public => true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ def self.prefix_for_category(txn, category = nil)
when :background then ::NewRelic::Agent::Transaction::TASK_PREFIX
when :rack then ::NewRelic::Agent::Transaction::RACK_PREFIX
when :uri then ::NewRelic::Agent::Transaction::CONTROLLER_PREFIX
when :roda then ::NewRelic::Agent::Transaction::RODA_PREFIX
when :sinatra then ::NewRelic::Agent::Transaction::SINATRA_PREFIX
when :middleware then ::NewRelic::Agent::Transaction::MIDDLEWARE_PREFIX
when :grape then ::NewRelic::Agent::Transaction::GRAPE_PREFIX
Expand Down
33 changes: 33 additions & 0 deletions lib/new_relic/agent/instrumentation/roda.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 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 'roda/instrumentation'

DependencyDetection.defer do
named :roda

depends_on do
defined?(Roda) &&
Gem::Version.new(Roda::RodaVersion) >= Gem::Version.new('3.19.0') &&
Roda::RodaPlugins::Base::ClassMethods.private_method_defined?(:build_rack_app) &&
Roda::RodaPlugins::Base::InstanceMethods.method_defined?(:_roda_handle_main_route)
end

executes do
require_relative '../../rack/agent_hooks'
require_relative '../../rack/browser_monitoring'

NewRelic::Agent.logger.info('Installing Roda instrumentation')

if use_prepend?
require_relative 'roda/prepend'
prepend_instrument Roda.singleton_class, NewRelic::Agent::Instrumentation::Roda::Build::Prepend
prepend_instrument Roda, NewRelic::Agent::Instrumentation::Roda::Prepend
else
require_relative 'roda/chain'
chain_instrument NewRelic::Agent::Instrumentation::Roda::Build::Chain
chain_instrument NewRelic::Agent::Instrumentation::Roda::Chain
end
end
end
43 changes: 43 additions & 0 deletions lib/new_relic/agent/instrumentation/roda/chain.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 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 Roda
module Chain
def self.instrument!
::Roda.class_eval do
include ::NewRelic::Agent::Instrumentation::Roda::Tracer

alias_method(:_roda_handle_main_route_without_tracing, :_roda_handle_main_route)

def _roda_handle_main_route(*args)
_roda_handle_main_route_with_tracing(*args) do
_roda_handle_main_route_without_tracing(*args)
end
end
end
end
end

module Build
module Chain
def self.instrument!
::Roda.class_eval do
include ::NewRelic::Agent::Instrumentation::Roda::Tracer

class << self
alias_method(:build_rack_app_without_tracing, :build_rack_app)

def build_rack_app
build_rack_app_with_tracing do
build_rack_app_without_tracing
end
end
end
end
end
end
end
end
end
52 changes: 52 additions & 0 deletions lib/new_relic/agent/instrumentation/roda/instrumentation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 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 Roda
module Tracer
include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation

def self.included(clazz)
clazz.extend(self)
end

def newrelic_middlewares
middlewares = [NewRelic::Rack::BrowserMonitoring]
kaylareopelle marked this conversation as resolved.
Show resolved Hide resolved
if NewRelic::Rack::AgentHooks.needed?
middlewares << NewRelic::Rack::AgentHooks
end
middlewares
end

def build_rack_app_with_tracing
unless NewRelic::Agent.config[:disable_roda_auto_middleware]
newrelic_middlewares.each do |middleware_class|
self.use middleware_class
end
end
yield
end

# Roda makes use of Rack, so we can get params from the request object
def rack_request_params
begin
@_request.params
rescue => e
NewRelic::Agent.logger.debug('Failed to get params from Rack request.', e)
NewRelic::EMPTY_HASH
end
end

def _roda_handle_main_route_with_tracing(*args)
perform_action_with_newrelic_trace(
category: :roda,
name: TransactionNamer.transaction_name(request),
params: ::NewRelic::Agent::ParameterFiltering::apply_filters(request.env, rack_request_params)
) do
yield
end
end
end
end
end
24 changes: 24 additions & 0 deletions lib/new_relic/agent/instrumentation/roda/prepend.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 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 Roda
module Prepend
include ::NewRelic::Agent::Instrumentation::Roda::Tracer

def _roda_handle_main_route(*args)
_roda_handle_main_route_with_tracing(*args) { super }
end
end

module Build
module Prepend
include ::NewRelic::Agent::Instrumentation::Roda::Tracer
def build_rack_app
build_rack_app_with_tracing { super }
end
end
end
end
end
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

module NewRelic
module Agent
module Instrumentation
module Roda
module TransactionNamer
extend self

ROOT = '/'.freeze
hannahramadan marked this conversation as resolved.
Show resolved Hide resolved
REGEX_MULTIPLE_SLASHES = %r{^[/^\A]*(.*?)[/$?\z]*$}.freeze

def transaction_name(request)
path = request.path || ::NewRelic::Agent::UNKNOWN_METRIC
name = path.gsub(REGEX_MULTIPLE_SLASHES, '\1') # remove any rogue slashes
name = ROOT if name.empty?
name = "#{request.request_method} #{name}" if request.respond_to?(:request_method)

name
rescue => e
::NewRelic::Agent.logger.debug("#{e.class} : #{e.message} - Error encountered trying to identify Roda transaction name")
::NewRelic::Agent::UNKNOWN_METRIC
end
end
end
end
end
end
3 changes: 2 additions & 1 deletion lib/new_relic/agent/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ class Transaction
RAKE_PREFIX = "#{OTHER_TRANSACTION_PREFIX}Rake/"
MESSAGE_PREFIX = "#{OTHER_TRANSACTION_PREFIX}Message/"
RACK_PREFIX = "#{CONTROLLER_PREFIX}Rack/"
RODA_PREFIX = "#{CONTROLLER_PREFIX}Roda/"
SINATRA_PREFIX = "#{CONTROLLER_PREFIX}Sinatra/"
GRAPE_PREFIX = "#{CONTROLLER_PREFIX}Grape/"
ACTION_CABLE_PREFIX = "#{CONTROLLER_PREFIX}ActionCable/"

WEB_TRANSACTION_CATEGORIES = [:web, :controller, :uri, :rack, :sinatra, :grape, :middleware, :action_cable].freeze
WEB_TRANSACTION_CATEGORIES = %i[action_cable controller grape middleware rack roda sinatra web uri].freeze

MIDDLEWARE_SUMMARY_METRICS = ['Middleware/all'].freeze
WEB_SUMMARY_METRIC = 'HttpDispatcher'
Expand Down
20 changes: 20 additions & 0 deletions lib/new_relic/control/frameworks/roda.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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 'new_relic/control/frameworks/ruby'
module NewRelic
class Control
module Frameworks
# Contains basic control logic for Roda
class Roda < NewRelic::Control::Frameworks::Ruby
protected
fallwith marked this conversation as resolved.
Show resolved Hide resolved

def install_shim
super
::Roda.class_eval { include NewRelic::Agent::Instrumentation::ControllerInstrumentation::Shim }
end
end
end
end
end
2 changes: 1 addition & 1 deletion test/multiverse/lib/multiverse/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def execute_suites(filter, opts)
'background_2' => ['rake'],
'database' => %w[elasticsearch mongo redis sequel],
'rails' => %w[active_record active_record_pg rails rails_prepend activemerchant],
'frameworks' => %w[sinatra padrino grape],
'frameworks' => %w[grape padrino roda sinatra],
'httpclients' => %w[curb excon httpclient],
'httpclients_2' => %w[typhoeus net_http httprb],
'infinite_tracing' => ['infinite_tracing'],
Expand Down
20 changes: 20 additions & 0 deletions test/multiverse/suites/roda/Envfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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

instrumentation_methods :chain, :prepend

RODA_VERSIONS = [
[nil, 2.4],
['3.19.0', 2.4]
]

def gem_list(roda_version = nil)
<<~RB
gem 'roda'#{roda_version}
gem 'rack'
gem 'rack-test', '>= 0.8.0', :require => 'rack/test'
RB
end

create_gemfiles(RODA_VERSIONS)
19 changes: 19 additions & 0 deletions test/multiverse/suites/roda/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:
roda: <%= $instrumentation_method %>
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
Loading
Loading