Skip to content

Commit

Permalink
Merge branch 'dev' into elasticsearch-port
Browse files Browse the repository at this point in the history
  • Loading branch information
kaylareopelle committed Sep 11, 2023
2 parents 5c46139 + efb6eb1 commit ac3d55b
Show file tree
Hide file tree
Showing 50 changed files with 705 additions and 21 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/label_community_cards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ jobs:
labels: ['community']
})
if: |
github.event.issue.user.login != 'fallwith' && github.event.issue.user.login != 'kaylareopelle' && github.event.issue.user.login != 'tannalynn' && github.event.issue.user.login != 'angelatan2' && github.event.issue.user.login != 'elucus' && github.event.issue.user.login != 'hannahramadan' &&
github.event.pull_request.user.login != 'fallwith' && github.event.pull_request.user.login != 'kaylareopelle' && github.event.pull_request.user.login != 'tannalynn' && github.event.pull_request.user.login != 'angelatan2' && github.event.pull_request.user.login != 'elucus' && github.event.pull_request.user.login != 'hannahramadan'
github.event.issue.user.login != 'fallwith' && github.event.issue.user.login != 'kaylareopelle' && github.event.issue.user.login != 'tannalynn' && github.event.issue.user.login != 'kford-newrelic' && github.event.issue.user.login != 'elucus' && github.event.issue.user.login != 'hannahramadan' &&
github.event.pull_request.user.login != 'fallwith' && github.event.pull_request.user.login != 'kaylareopelle' && github.event.pull_request.user.login != 'tannalynn' && github.event.pull_request.user.login != 'kford-newrelic' && github.event.pull_request.user.login != 'elucus' && github.event.pull_request.user.login != 'hannahramadan'
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

## dev

Version <dev> allows the agent to record additional response information on a transaction when middleware instrumentation is disabled, introduces new `:'sidekiq.args.include'` and `:'sidekiq.args.exclude:` configuration options to permit capturing only certain Sidekiq job arguments, and fixes a bug in `NewRelic::Rack::AgentHooks.needed?`.
Version <dev> introduces Stripe instrumentation, allows the agent to record additional response information on a transaction when middleware instrumentation is disabled, introduces new `:'sidekiq.args.include'` and `:'sidekiq.args.exclude:` configuration options to permit capturing only certain Sidekiq job arguments, and fixes a bug in `NewRelic::Rack::AgentHooks.needed?`.

- **Feature: Add Stripe instrumentation**
[Stripe](https://stripe.com/) calls are now automatically instrumented. Additionally, new `:'stripe.user_data.include'` and `:'stripe.user_data.exclude'` configuration options permit capturing custom `user_data` key-value pairs that can be stored in [Stripe events](https://github.com/stripe/stripe-ruby#instrumentation). No `user_data` key-value pairs are captured by default. The agent currently supports Stripe versions 5.38.0+. [PR#2180](https://github.com/newrelic/newrelic-ruby-agent/pull/2180)

- **Feature: Report transaction HTTP status codes when middleware instrumentation is disabled**
Previously, when `disable_middleware_instrumentation` was set to `true`, the agent would not record the value of the response code or content type on the transaction. This was due to the possibility that a middleware could alter the response, which would not be captured by the agent when the middleware instrumentation was disabled. However, based on customer feedback, the agent will now report the HTTP status code and content type on a transaction when middleware instrumentation is disabled. [PR#2175](https://github.com/newrelic/newrelic-ruby-agent/pull/2175)
Expand Down
11 changes: 11 additions & 0 deletions lib/new_relic/agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,17 @@ def record_metric_once(metric_name, value = 0.0)
record_metric(metric_name, value)
end

def record_instrumentation_invocation(library)
record_metric_once("Supportability/#{library}/Invoked")
end

# see ActiveSupport::Inflector.demodulize
def base_name(klass_name)
return klass_name unless ridx = klass_name.rindex('::')

klass_name[(ridx + 2), klass_name.length]
end

SUPPORTABILITY_INCREMENT_METRIC = 'Supportability/API/increment_metric'.freeze

# Increment a simple counter metric.
Expand Down
35 changes: 35 additions & 0 deletions lib/new_relic/agent/configuration/default_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,41 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of Sinatra at start up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
},
:'instrumentation.stripe' => {
:default => 'enabled',
:public => true,
:type => String,
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of Stripe at startup. May be one of: `enabled`, `disabled`.'
},
:'stripe.user_data.include' => {
default: NewRelic::EMPTY_ARRAY,
public: true,
type: Array,
dynamic_name: true,
allowed_from_server: false,
:transform => DefaultSource.method(:convert_to_list),
:description => <<~DESCRIPTION
An array of strings to specify which keys inside a Stripe event's `user_data` hash should be reported
to New Relic. Each string in this array will be turned into a regular expression via `Regexp.new` to
permit advanced matching. Setting the value to `["."]` will report all `user_data`.
DESCRIPTION
},
:'stripe.user_data.exclude' => {
default: NewRelic::EMPTY_ARRAY,
public: true,
type: Array,
dynamic_name: true,
allowed_from_server: false,
:transform => DefaultSource.method(:convert_to_list),
:description => <<~DESCRIPTION
An array of strings to specify which keys and/or values inside a Stripe event's `user_data` hash should
not be reported to New Relic. Each string in this array will be turned into a regular expression via
`Regexp.new` to permit advanced matching. For each hash pair, if either the key or value is matched the
pair will not be reported. By default, no `user_data` is reported, so this option should only be used if
the `stripe.user_data.include` option is being used.
DESCRIPTION
},
:'instrumentation.thread' => {
:default => 'auto',
:public => true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ module NewRelic
module Agent
module Instrumentation
module ActiveSupportLogger
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

# Mark @skip_instrumenting on any broadcasted loggers to instrument Rails.logger only
def broadcast_with_tracing(logger)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

NewRelic::Agent::Instrumentation::Logger.mark_skip_instrumenting(logger)
yield
rescue => error
Expand Down
9 changes: 9 additions & 0 deletions lib/new_relic/agent/instrumentation/bunny/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Bunny
DEFAULT_NAME = 'Default'
DEFAULT_TYPE = :direct
SLASH = '/'
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

def exchange_name(name)
name.empty? ? DEFAULT_NAME : name
Expand All @@ -28,6 +29,8 @@ module Exchange
include Bunny

def publish_with_tracing(payload, opts = {})
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

begin
destination = exchange_name(name)

Expand Down Expand Up @@ -62,6 +65,8 @@ module Queue
include Bunny

def pop_with_tracing
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

bunny_error, delivery_info, message_properties, _payload = nil, nil, nil, nil
begin
t0 = Process.clock_gettime(Process::CLOCK_REALTIME)
Expand Down Expand Up @@ -104,6 +109,8 @@ def pop_with_tracing
end

def purge_with_tracing
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

begin
type = server_named? ? :temporary_queue : :queue
segment = NewRelic::Agent::Tracer.start_message_broker_segment(
Expand All @@ -129,6 +136,8 @@ module Consumer
include Bunny

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

delivery_info, message_properties, _ = args
queue_name = queue.respond_to?(:name) ? queue.name : queue

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
module NewRelic::Agent::Instrumentation
module ConcurrentRuby
SEGMENT_NAME = 'Concurrent/Task'
SUPPORTABILITY_METRIC = 'Supportability/ConcurrentRuby/Invoked'
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

def add_task_tracing(&task)
NewRelic::Agent.record_metric_once(SUPPORTABILITY_METRIC)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

NewRelic::Agent::Tracer.thread_block_with_current_transaction(
segment_name: SEGMENT_NAME,
Expand Down
4 changes: 4 additions & 0 deletions lib/new_relic/agent/instrumentation/curb/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ def header_str_with_tracing
module Multi
include NewRelic::Agent::MethodTracer

INSTRUMENTATION_NAME = 'Curb'

# Add CAT with callbacks if the request is serial
def add_with_tracing(curl)
if curl.respond_to?(:_nr_serial) && curl._nr_serial
Expand All @@ -81,6 +83,8 @@ def add_with_tracing(curl)
def perform_with_tracing
return yield if first_request_is_serial?

NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

trace_execution_scoped('External/Multiple/Curb::Multi/perform') do
yield
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ module DelayedJobTracer
include NewRelic::Agent::Instrumentation::ControllerInstrumentation

NR_TRANSACTION_CATEGORY = 'OtherTransaction/DelayedJob'.freeze
INSTRUMENTATION_NAME = 'DelayedJob'

def invoke_job_with_tracing
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

options = {
:category => NR_TRANSACTION_CATEGORY,
:path => ::NewRelic::Agent::Instrumentation::DelayedJob::Naming.name_from_payload(payload_object)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ module NewRelic::Agent::Instrumentation
module Elasticsearch
PRODUCT_NAME = 'Elasticsearch'
OPERATION = 'perform_request'
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

def perform_request_with_tracing(method, path, params = {}, body = nil, headers = nil)
return yield unless NewRelic::Agent::Tracer.tracing_enabled?

NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

segment = NewRelic::Agent::Tracer.start_datastore_segment(
product: PRODUCT_NAME,
operation: nr_operation || OPERATION,
Expand Down
3 changes: 3 additions & 0 deletions lib/new_relic/agent/instrumentation/excon/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module ::Excon
module Middleware
class NewRelicCrossAppTracing
TRACE_DATA_IVAR = :@newrelic_trace_data
INSTRUMENTATION_NAME = 'Excon'

def initialize(stack)
@stack = stack
Expand All @@ -18,6 +19,8 @@ def request_call(datum) # THREAD_LOCAL_ACCESS
# :idempotent in the options, but there will be only a single
# accompanying response_call/error_call.
if datum[:connection] && !datum[:connection].instance_variable_get(TRACE_DATA_IVAR)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

wrapped_request = ::NewRelic::Agent::HTTPClients::ExconHTTPRequest.new(datum)
segment = NewRelic::Agent::Tracer.start_external_request_segment(
library: wrapped_request.type,
Expand Down
4 changes: 4 additions & 0 deletions lib/new_relic/agent/instrumentation/grape/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module Grape
module Instrumentation
extend self

INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

# Since 1.2.0, the class `Grape::API` no longer refers to an API instance, rather, what used to be `Grape::API` is `Grape::API::Instance`
# https://github.com/ruby-grape/grape/blob/c20a73ac1e3f3ba1082005ed61bf69452373ba87/UPGRADING.md#upgrading-to--120
def instrumented_class
Expand Down Expand Up @@ -46,6 +48,8 @@ def prepare!
def handle_transaction(endpoint, class_name, version)
return unless endpoint && route = endpoint.route

NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

name_transaction(route, class_name, version)
capture_params(endpoint)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ module GRPC
module Client
include NewRelic::Agent::Instrumentation::GRPC::Helper

INSTRUMENTATION_NAME = 'gRPC_Client'

def issue_request_with_tracing(grpc_type, method, requests, marshal, unmarshal,
deadline:, return_op:, parent:, credentials:, metadata:)
return yield unless trace_with_newrelic?

NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

segment = request_segment(method)
request_wrapper = NewRelic::Agent::Instrumentation::GRPC::Client::RequestWrapper.new(@host)
# do not insert CAT headers for gRPC requests https://github.com/newrelic/newrelic-ruby-agent/issues/1730
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module GRPC
module Server
include NewRelic::Agent::Instrumentation::GRPC::Helper

INSTRUMENTATION_NAME = 'gRPC_Server'

DT_KEYS = [NewRelic::NEWRELIC_KEY, NewRelic::TRACEPARENT_KEY, NewRelic::TRACESTATE_KEY].freeze
INSTANCE_VAR_HOST = :@host_nr
INSTANCE_VAR_PORT = :@port_nr
Expand All @@ -23,6 +25,8 @@ module Server
def handle_with_tracing(streamer_type, active_call, mth, _inter_ctx)
return yield unless trace_with_newrelic?

NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

metadata = metadata_for_call(active_call)
txn = NewRelic::Agent::Transaction.start_new_transaction(NewRelic::Agent::Tracer.state,
CATEGORY,
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/agent/instrumentation/grpc_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
end

executes do
supportability_name = 'gRPC_Client'
supportability_name = NewRelic::Agent::Instrumentation::GRPC::Client::INSTRUMENTATION_NAME
if use_prepend?
prepend_instrument GRPC::ClientStub, NewRelic::Agent::Instrumentation::GRPC::Client::Prepend, supportability_name
else
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/agent/instrumentation/grpc_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
end

executes do
supportability_name = 'gRPC_Server'
supportability_name = NewRelic::Agent::Instrumentation::GRPC::Client::INSTRUMENTATION_NAME
if use_prepend?
prepend_instrument GRPC::RpcServer, NewRelic::Agent::Instrumentation::GRPC::Server::RpcServerPrepend, supportability_name
prepend_instrument GRPC::RpcDesc, NewRelic::Agent::Instrumentation::GRPC::Server::RpcDescPrepend, supportability_name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
module NewRelic::Agent::Instrumentation
module HTTPClient
module Instrumentation
INSTRUMENTATION_NAME = 'HTTPClient'

def with_tracing(request, connection)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

wrapped_request = NewRelic::Agent::HTTPClients::HTTPClientRequest.new(request)
segment = NewRelic::Agent::Tracer.start_external_request_segment(
library: wrapped_request.type,
Expand Down
4 changes: 4 additions & 0 deletions lib/new_relic/agent/instrumentation/httprb/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

module NewRelic::Agent::Instrumentation
module HTTPrb
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

def with_tracing(request)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

wrapped_request = ::NewRelic::Agent::HTTPClients::HTTPRequest.new(request)

begin
Expand Down
3 changes: 3 additions & 0 deletions lib/new_relic/agent/instrumentation/logger/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ module NewRelic
module Agent
module Instrumentation
module Logger
INSTRUMENTATION_NAME = 'Logger'

def skip_instrumenting?
defined?(@skip_instrumenting) && @skip_instrumenting
end
Expand Down Expand Up @@ -51,6 +53,7 @@ def format_message_with_tracing(severity, datetime, progname, msg)
mark_skip_instrumenting

unless ::NewRelic::Agent.agent.nil?
::NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
::NewRelic::Agent.agent.log_event_aggregator.record(formatted_message, severity)
formatted_message = LocalLogDecorator.decorate(formatted_message)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ module Tracer
LOCALHOST = 'localhost'
MULTIGET_METRIC_NAME = 'get_multi_request'
MEMCACHED = 'Memcached'
INSTRUMENTATION_NAME = 'Dalli'

def with_newrelic_tracing(operation, *args)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

segment = NewRelic::Agent::Tracer.start_datastore_segment(
product: MEMCACHED,
operation: operation
Expand All @@ -28,6 +31,8 @@ def with_newrelic_tracing(operation, *args)
end

def server_for_key_with_newrelic_tracing
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

yield.tap do |server|
begin
if txn = ::NewRelic::Agent::Tracer.current_transaction
Expand All @@ -43,6 +48,8 @@ def server_for_key_with_newrelic_tracing
end

def get_multi_with_newrelic_tracing(method_name)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

segment = NewRelic::Agent::Tracer.start_segment(
name: "Ruby/Memcached/Dalli/#{method_name}"
)
Expand All @@ -55,6 +62,8 @@ def get_multi_with_newrelic_tracing(method_name)
end

def send_multiget_with_newrelic_tracing(keys)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

segment = ::NewRelic::Agent::Tracer.start_datastore_segment(
product: MEMCACHED,
operation: MULTIGET_METRIC_NAME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ module NewRelic
module Agent
module Instrumentation
module NetHTTP
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

def request_with_tracing(request)
NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)

wrapped_request = NewRelic::Agent::HTTPClients::NetHTTPRequest.new(self, request)

segment = NewRelic::Agent::Tracer.start_external_request_segment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ def self.subscribe(pattern)
end
end

# The agent doesn't use the traditional ActiveSupport::Notifications.subscribe
# pattern due to threading issues discovered on initial instrumentation.
# Instead we define a #start and #finish method, which Rails responds to.
# See: https://github.com/rails/rails/issues/12069
def start(name, id, payload)
return unless state.is_execution_traced?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

module NewRelic::Agent::Instrumentation
module Padrino
INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)

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

begin
env['newrelic.last_route'] = args[0].original_path
rescue => e
Expand Down
Loading

0 comments on commit ac3d55b

Please sign in to comment.