# 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 'forwardable'
# @api public
module NewRelic
# This module contains most of the public API methods for the Ruby Agent.
#
# For adding custom instrumentation to method invocations, see
# the docs for {NewRelic::Agent::MethodTracer} and
# {NewRelic::Agent::MethodTracer::ClassMethods}.
#
# For information on how to trace transactions in non-Rack contexts,
# see {NewRelic::Agent::Instrumentation::ControllerInstrumentation}.
#
# For general documentation about the Ruby agent, see:
# https://docs.newrelic.com/docs/agents/ruby-agent
#
# @api public
#
module Agent
extend self
extend Forwardable
require 'new_relic/version'
require 'new_relic/local_environment'
require 'new_relic/metric_spec'
require 'new_relic/metric_data'
require 'new_relic/noticed_error'
require 'new_relic/agent/noticeable_error'
require 'new_relic/supportability_helper'
require 'new_relic/thread_local_storage'
require 'new_relic/agent/encoding_normalizer'
require 'new_relic/agent/stats'
require 'new_relic/agent/chained_call'
require 'new_relic/agent/agent'
require 'new_relic/agent/method_tracer'
require 'new_relic/agent/worker_loop'
require 'new_relic/agent/event_loop'
require 'new_relic/agent/stats_engine'
require 'new_relic/agent/transaction_sampler'
require 'new_relic/agent/sql_sampler'
require 'new_relic/agent/commands/thread_profiler_session'
require 'new_relic/agent/error_collector'
require 'new_relic/agent/error_filter'
require 'new_relic/agent/sampler'
require 'new_relic/agent/database'
require 'new_relic/agent/database_adapter'
require 'new_relic/agent/datastores'
require 'new_relic/agent/pipe_channel_manager'
require 'new_relic/agent/configuration'
require 'new_relic/agent/rules_engine'
require 'new_relic/agent/http_clients/uri_util'
require 'new_relic/agent/system_info'
require 'new_relic/agent/external'
require 'new_relic/agent/deprecator'
require 'new_relic/agent/logging'
require 'new_relic/agent/distributed_tracing'
require 'new_relic/agent/attribute_pre_filtering'
require 'new_relic/agent/attribute_processing'
require 'new_relic/agent/linking_metadata'
require 'new_relic/agent/local_log_decorator'
require 'new_relic/agent/llm'
require 'new_relic/agent/aws'
require 'new_relic/agent/instrumentation/controller_instrumentation'
require 'new_relic/agent/samplers/cpu_sampler'
require 'new_relic/agent/samplers/memory_sampler'
require 'new_relic/agent/samplers/object_sampler'
require 'new_relic/agent/samplers/delayed_job_sampler'
require 'new_relic/agent/samplers/vm_sampler'
require 'set'
require 'thread'
require 'resolv'
extend NewRelic::SupportabilityHelper
# An exception that is thrown by the server if the agent license is invalid.
class LicenseException < StandardError; end
# An exception that forces an agent to stop reporting until its mongrel is restarted.
class ForceDisconnectException < StandardError; end
# An exception that forces an agent to restart.
class ForceRestartException < StandardError
def message
"#{super}, restarting."
end
end
# Used to blow out of a periodic task without logging a an error, such as for routine
# failures.
class ServerConnectionException < StandardError; end
# When a post is either too large or poorly formatted we should
# drop it and not try to resend
class UnrecoverableServerException < ServerConnectionException; end
# An unrecoverable client-side error that prevents the agent from continuing
class UnrecoverableAgentException < ServerConnectionException; end
# An error while serializing data for the collector
class SerializationError < StandardError; end
# placeholder name used when we cannot determine a transaction's name
UNKNOWN_METRIC = '(unknown)'.freeze
LLM_FEEDBACK_MESSAGE = 'LlmFeedbackMessage'
attr_reader :error_group_callback
attr_reader :llm_token_count_callback
@agent = nil
@error_group_callback = nil
@llm_token_count_callback = nil
@logger = nil
@tracer_lock = Mutex.new
@tracer_queue = []
@metrics_already_recorded = Set.new
# The singleton Agent instance. Used internally.
def agent # :nodoc:
return @agent if @agent
NewRelic::Agent.logger.warn("Agent unavailable as it hasn't been started.")
NewRelic::Agent.logger.warn(caller.join("\n"))
nil
end
def agent=(new_instance) # :nodoc:
@agent = new_instance
add_deferred_method_tracers_now
end
alias instance agent # :nodoc:
# Primary interface to logging is fronted by this accessor
# Access via ::NewRelic::Agent.logger
def logger
@logger || StartupLogger.instance
end
def logger=(log)
@logger = log
end
# A third-party class may call add_method_tracer before the agent
# is initialized; these methods enable us to defer these calls
# until we have started up and can process them.
#
def add_or_defer_method_tracer(receiver, method_name, metric_name, options)
@tracer_lock.synchronize do
options[:code_information] = NewRelic::Agent::MethodTracerHelpers.code_information(receiver, method_name)
if @agent
receiver.send(:_nr_add_method_tracer_now, method_name, metric_name, options)
else
@tracer_queue << [receiver, method_name, metric_name, options]
end
end
end
def add_deferred_method_tracers_now
@tracer_lock.synchronize do
@tracer_queue.each do |receiver, method_name, metric_name, options|
receiver.send(:_nr_add_method_tracer_now, method_name, metric_name, options)
end
@tracer_queue = []
end
end
def config
@config ||= Configuration::Manager.new
end
# For testing
# Important that we don't change the instance or we orphan callbacks
def reset_config
config.reset_to_defaults
end
# @!group Recording custom metrics
# Record a value for the given metric name.
#
# This method should be used to record event-based metrics such as method
# calls that are associated with a specific duration or magnitude.
#
# +metric_name+ should follow a slash separated path convention. Application
# specific metrics should begin with "Custom/".
#
# +value+ should be either a single Numeric value representing the duration/
# magnitude of the event being recorded, or a Hash containing :count,
# :total, :min, :max, and :sum_of_squares keys. The latter form is useful
# for recording pre-aggregated metrics collected externally.
#
# This method is safe to use from any thread.
#
# @api public
def record_metric(metric_name, value) # THREAD_LOCAL_ACCESS
record_api_supportability_metric(:record_metric)
return unless agent
if value.is_a?(Hash)
stats = NewRelic::Agent::Stats.new
value = stats.hash_merge(value)
end
agent.stats_engine.tl_record_unscoped_metrics(metric_name, value)
end
def record_metric_once(metric_name, value = 0.0)
return unless @metrics_already_recorded.add?(metric_name)
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.
#
# +metric_name+ should follow a slash separated path convention. Application
# specific metrics should begin with "Custom/".
#
# This method is safe to use from any thread.
#
# @api public
#
def increment_metric(metric_name, amount = 1) # THREAD_LOCAL_ACCESS
return unless agent
if amount == 1
metrics = [metric_name, SUPPORTABILITY_INCREMENT_METRIC]
agent.stats_engine.tl_record_unscoped_metrics(metrics) { |stats| stats.increment_count }
else
agent.stats_engine.tl_record_unscoped_metrics(metric_name) { |stats| stats.increment_count(amount) }
agent.stats_engine.tl_record_unscoped_metrics(SUPPORTABILITY_INCREMENT_METRIC) { |stats| stats.increment_count }
end
end
# @!endgroup
# @!group Recording custom errors
# Set a filter to be applied to errors that the Ruby agent will
# track. The block should evaluate to the exception to track
# (which could be different from the original exception) or nil to
# ignore this exception.
#
# The block is yielded to with the exception to filter.
#
# Return the new block or the existing filter Proc if no block is passed.
#
# @api public
#
def ignore_error_filter(&block)
record_api_supportability_metric(:ignore_error_filter)
if block
NewRelic::Agent::ErrorCollector.ignore_error_filter = block
else
NewRelic::Agent::ErrorCollector.ignore_error_filter
end
end
# Send an error to New Relic.
#
# @param [Exception] exception Error you wish to send
# @param [Hash] options Modify how New Relic processes the error
# @option options [Hash] :custom_params Custom parameters to attach to the trace
# @option options [Boolean] :expected Only record the error trace
# (do not affect error rate or Apdex status)
# @option options [String] :uri Request path, minus request params or query string
# (usually not needed)
# @option options [String] :metric Metric name associated with the transaction
# (usually not needed)
#
# Any option keys other than the ones listed here are treated as
# :custom_params
.
#
# *Note:* Previous versions of the agent allowed passing
# :request_params
, but those are now ignored. If you
# need to record the request parameters, call this method inside a
# transaction or pass the information in
# :custom_params
.
#
# Most of the time, you do not need to specify the
# :uri
or :metric
options; only pass
# them if you are calling notice_error
outside a
# transaction.
#
# @api public
#
def notice_error(exception, options = {})
record_api_supportability_metric(:notice_error)
Transaction.notice_error(exception, options)
nil # don't return a noticed error data structure. it can only hurt.
end
# Set a callback proc for determining an error's error group name
#
# @param callback_proc [Proc] the callback proc
#
# Typically this method should be called only once to set a callback for
# use with all noticed errors. If it is called multiple times, each new
# callback given will replace the old one.
#
# The proc will be called with a single hash as its input argument and is
# expected to return a string representing the desired error group.
#
# see https://docs.newrelic.com/docs/errors-inbox/errors-inbox/#groups
#
# The hash passed to the customer defined callback proc has the following
# keys:
#
# :error => The Ruby error class instance, likely inheriting from
# StandardError. Call `#class`, `#message`, and `#backtrace` on
# the error object to retrieve the error's class, message, and
# backtrace.
# :customAttributes => Any customer defined custom attributes that have been
# associated with the current transaction.
# :'request.uri' => The current request URI if available
# :'http.statusCode' => The HTTP status code (200, 404, etc.) if available
# :'http.method' => The HTTP method (GET, PUT, etc.) if available
# :'error.expected' => Whether (true) or not (false) the error was expected
# :options => The options hash passed to `NewRelic::Agent.notice_error`
#
# @api public
#
def set_error_group_callback(callback_proc)
unless callback_proc.is_a?(Proc)
NewRelic::Agent.logger.error("#{self}.#{__method__}: expected an argument of type Proc, " \
"got #{callback_proc.class}")
return
end
record_api_supportability_metric(:set_error_group_callback)
@error_group_callback = callback_proc
end
# @!endgroup
# @!group Recording custom Insights events
# Record a custom event to be sent to New Relic Insights.
# The recorded event will be buffered in memory until the next time the
# agent sends data to New Relic's servers.
#
# If you want to be able to tie the information recorded via this call back
# to the web request or background job that it happened in, you may want to
# instead use the add_custom_attributes API call to attach attributes to
# the Transaction event that will automatically be generated for the
# request.
#
# A timestamp will be automatically added to the recorded event when this
# method is called.
#
# @param [Symbol or String] event_type The name of the event type to record. Event
# types must consist of only alphanumeric
# characters, '_', ':', or ' '.
#
# @param [Hash] event_attrs A Hash of attributes to be attached to the event.
# Keys should be strings or symbols, and values
# may be strings, symbols, numeric values or
# booleans.
#
# @api public
#
def record_custom_event(event_type, event_attrs)
record_api_supportability_metric(:record_custom_event)
if agent && NewRelic::Agent.config[:'custom_insights_events.enabled']
agent.custom_event_aggregator.record(event_type, event_attrs)
end
nil
end
# Records user feedback events for LLM applications. This API must pass
# the current trace id as a parameter, which can be obtained using:
#
# NewRelic::Agent::Tracer.current_trace_id
#
# @param [String] ID of the trace where the chat completion(s) related
# to the feedback occurred.
#
# @param [String or Integer] Rating provided by an end user
# (ex: “Good", "Bad”, 1, 2, 5, 8, 10).
#
# @param [optional, String] Category of the feedback as provided by the
# end user (ex: “informative”, “inaccurate”).
#
# @param start_time [optional, String] Freeform text feedback from an
# end user.
#
# @param [optional, Hash] Set of key-value pairs to store any other
# desired data to submit with the feedback event.
#
# @api public
#
def record_llm_feedback_event(trace_id:,
rating:,
category: nil,
message: nil,
metadata: NewRelic::EMPTY_HASH)
record_api_supportability_metric(:record_llm_feedback_event)
unless NewRelic::Agent.config[:'distributed_tracing.enabled']
return NewRelic::Agent.logger.error('Distributed tracing must be enabled to record LLM feedback')
end
feedback_message_event = {
'trace_id': trace_id,
'rating': rating,
'category': category,
'message': message,
'id': NewRelic::Agent::GuidGenerator.generate_guid,
'ingest_source': NewRelic::Agent::Llm::LlmEvent::INGEST_SOURCE
}
feedback_message_event.merge!(metadata) unless metadata.empty?
NewRelic::Agent.record_custom_event(LLM_FEEDBACK_MESSAGE, feedback_message_event)
rescue ArgumentError
raise
rescue => exception
NewRelic::Agent.logger.error('record_llm_feedback_event', exception)
end
# @!endgroup
# @!group LLM callbacks
# Set a callback proc for calculating `token_count` attributes for
# LlmEmbedding and LlmChatCompletionMessage events
#
# @param callback_proc [Proc] the callback proc
#
# This method should be called only once to set a callback for
# use with all LLM token calculations. If it is called multiple times, each
# new callback will replace the old one.
#
# The proc will be called with a single hash as its input argument and
# must return an Integer representing the number of tokens used for that
# particular prompt, completion message, or embedding. Values less than or
# equal to 0 will not be attached to an event.
#
# The hash has the following keys:
#
# :model => [String] The name of the LLM model
# :content => [String] The message content or prompt
#
# @api public
#
def set_llm_token_count_callback(callback_proc)
unless callback_proc.is_a?(Proc)
NewRelic::Agent.logger.error("#{self}.#{__method__}: expected an argument of type Proc, " \
"got #{callback_proc.class}")
return
end
record_api_supportability_metric(:set_llm_token_count_callback)
@llm_token_count_callback = callback_proc
end
# @!endgroup
# @!group Manual agent configuration and startup/shutdown
# Call this to manually start the agent in situations where the agent does
# not auto-start.
#
# When the app environment loads, so does the agent. However, the
# agent will only connect to the service if a web front-end is found. If
# you want to selectively monitor ruby processes that don't use
# web plugins, then call this method in your code and the agent
# will fire up and start reporting to the service.
#
# Options are passed in as overrides for values in the
# newrelic.yml, such as app_name. In addition, the option +log+
# will take a logger that will be used instead of the standard
# file logger. The setting for the newrelic.yml section to use
# (ie, RAILS_ENV) can be overridden with an :env argument.
#
# @api public
#
def manual_start(options = {})
raise 'Options must be a hash' unless Hash === options
NewRelic::Control.instance.init_plugin({:agent_enabled => true, :sync_startup => true}.merge(options))
record_api_supportability_metric(:manual_start)
end
# Register this method as a callback for processes that fork
# jobs.
#
# If the master/parent connects to the agent prior to forking the
# agent in the forked process will use that agent_run. Otherwise
# the forked process will establish a new connection with the
# server.
#
# Use this especially when you fork the process to run background
# jobs or other work. If you are doing this with a web dispatcher
# that forks worker processes then you will need to force the
# agent to reconnect, which it won't do by default. Passenger and
# Unicorn are already handled, nothing special needed for them.
#
# Options:
# * :force_reconnect => true to force the spawned process to
# establish a new connection, such as when forking a long running process.
# The default is false--it will only connect to the server if the parent
# had not connected.
# * :keep_retrying => false if we try to initiate a new
# connection, this tells me to only try it once so this method returns
# quickly if there is some kind of latency with the server.
#
# @api public
#
def after_fork(options = {})
record_api_supportability_metric(:after_fork)
# the following line needs else branch coverage
agent.after_fork(options) if agent # rubocop:disable Style/SafeNavigation
end
# Shutdown the agent. Call this before exiting. Sends any queued data
# and kills the background thread.
#
# @param options [Hash] Unused options Hash, for back compatibility only
#
# @api public
#
def shutdown(options = {})
record_api_supportability_metric(:shutdown)
agent&.shutdown
end
# Clear out any data the agent has buffered but has not yet transmitted
# to the collector.
#
# @api public
def drop_buffered_data
# the following line needs else branch coverage
agent.drop_buffered_data if agent # rubocop:disable Style/SafeNavigation
record_api_supportability_metric(:drop_buffered_data)
end
# Add instrumentation files to the agent. The argument should be
# a glob matching ruby scripts which will be executed at the time
# instrumentation is loaded. Since instrumentation is not loaded
# when the agent is not running it's better to use this method to
# register instrumentation than just loading the files directly,
# although that probably also works.
#
# @api public
#
def add_instrumentation(file_pattern)
record_api_supportability_metric(:add_instrumentation)
NewRelic::Control.instance.add_instrumentation(file_pattern)
end
# Require agent testing helper methods
#
# @api public
def require_test_helper
record_api_supportability_metric(:require_test_helper)
require File.expand_path('../../../test/agent_helper', __FILE__)
end
# This method sets the block sent to this method as a sql
# obfuscator. The block will be called with a single String SQL
# statement to obfuscate. The method must return the obfuscated
# String SQL. If chaining of obfuscators is required, use type =
# :before or :after
#
# type = :before, :replace, :after
#
# Example:
#
# NewRelic::Agent.set_sql_obfuscator(:replace) do |sql|
# my_obfuscator(sql)
# end
#
# @api public
#
def set_sql_obfuscator(type = :replace, &block)
record_api_supportability_metric(:set_sql_obfuscator)
NewRelic::Agent::Database.set_sql_obfuscator(type, &block)
end
# @!endgroup
# @!group Ignoring or excluding data
# This method disables the recording of the current transaction. No metrics,
# traced errors, transaction traces, Insights events, slow SQL traces,
# or RUM injection will happen for this transaction.
#
# @api public
#
def ignore_transaction
record_api_supportability_metric(:ignore_transaction)
NewRelic::Agent::Transaction.tl_current&.ignore!
end
# This method disables the recording of Apdex metrics in the current
# transaction.
#
# @api public
#
def ignore_apdex
record_api_supportability_metric(:ignore_apdex)
NewRelic::Agent::Transaction.tl_current&.ignore_apdex!
end
# This method disables browser monitoring javascript injection in the
# current transaction.
#
# @api public
#
def ignore_enduser
record_api_supportability_metric(:ignore_enduser)
NewRelic::Agent::Transaction.tl_current&.ignore_enduser!
end
# Yield to the block without collecting any metrics or traces in
# any of the subsequent calls. If executed recursively, will keep
# track of the first entry point and turn on tracing again after
# leaving that block. This uses the thread local Tracer::State.
#
# @api public
#
def disable_all_tracing
record_api_supportability_metric(:disable_all_tracing)
return yield unless agent
begin
agent.push_trace_execution_flag(false)
yield
ensure
agent.pop_trace_execution_flag
end
end
# This method sets the state of sql recording in the transaction
# sampler feature. Within the given block, no sql will be recorded
#
# usage:
#
# NewRelic::Agent.disable_sql_recording do
# ...
# end
#
# @api public
#
def disable_sql_recording
record_api_supportability_metric(:disable_sql_recording)
return yield unless agent
state = agent.set_record_sql(false)
begin
yield
ensure
agent.set_record_sql(state)
end
end
# @!endgroup
# Check to see if we are capturing metrics currently on this thread.
def tl_is_execution_traced?
NewRelic::Agent::Tracer.state.is_execution_traced?
end
# @!group Adding custom attributes to traces
# Add attributes to the transaction trace, Insights Transaction event, and
# any traced errors recorded for the current transaction.
#
# If Browser Monitoring is enabled, and the
# browser_monitoring.attributes.enabled configuration setting is true,
# these custom attributes will also be present in the script injected into
# the response body, making them available on Insights PageView events.
#
#
# @param [Hash] params A Hash of attributes to be attached to the transaction event.
# Keys should be strings or symbols, and values
# may be strings, symbols, numeric values or
# booleans.
#
# @api public
#
def add_custom_attributes(params) # THREAD_LOCAL_ACCESS
record_api_supportability_metric(:add_custom_attributes)
unless params.is_a?(Hash)
::NewRelic::Agent.logger.warn("Bad argument passed to #add_custom_attributes. Expected Hash but got #{params.class}")
return
end
if NewRelic::Agent.agent&.serverless?
::NewRelic::Agent.logger.warn('Custom attributes are not supported in serverless mode')
return
end
Transaction.tl_current&.add_custom_attributes(params)
segment = ::NewRelic::Agent::Tracer.current_segment
add_new_segment_attributes(params, segment) if segment
end
def add_new_segment_attributes(params, segment)
# Make sure not to override existing segment-level custom attributes
segment_custom_keys = segment.attributes.custom_attributes.keys.map(&:to_sym)
segment.add_custom_attributes(params.reject do |k, _v|
segment_custom_keys.include?(k.to_sym) if k.respond_to?(:to_sym) # param keys can be integers
end)
end
# Add custom attributes to the span event for the current span. Attributes will be visible on spans in the
# New Relic Distributed Tracing UI and on span events in New Relic Insights.
#
# Custom attributes will not be transmitted when +high_security+ setting is enabled or
# +custom_attributes+ setting is disabled.
#
# @param [Hash] params A Hash of attributes to be attached to the span event.
# Keys should be strings or symbols, and values
# may be strings, symbols, numeric values or
# booleans.
#
# @see https://docs.newrelic.com/docs/using-new-relic/welcome-new-relic/get-started/glossary#span
# @api public
def add_custom_span_attributes(params)
record_api_supportability_metric(:add_custom_span_attributes)
if params.is_a?(Hash)
if segment = NewRelic::Agent::Tracer.current_segment
segment.add_custom_attributes(params)
end
else
::NewRelic::Agent.logger.warn("Bad argument passed to #add_custom_span_attributes. Expected Hash but got #{params.class}")
end
end
# Add global custom attributes to log events for the current agent instance. As these attributes are global to the
# agent instance, they will be attached to all log events generated by the agent, and this methods usage isn't
# suitable for setting dynamic values.
#
# @param [Hash] params A Hash of attributes to attach to log
# events. The agent accepts up to 240 custom
# log event attributes.
#
# Keys will be coerced into Strings and must
# be less than 256 characters. Keys longer
# than 255 characters will be truncated.
#
# Values may be Strings, Symbols, numeric
# values or Booleans and must be less than
# 4095 characters. If the value is a String
# or a Symbol, values longer than 4094
# characters will be truncated. If the value
# exceeds 4094 characters and is of a
# different class, the attribute pair will
# be dropped.
#
# This API can be called multiple times.
# If the same key is passed more than once,
# the value associated with the last call
# will be preserved.
#
# Attribute pairs with empty or nil contents
# will be dropped.
# @api public
def add_custom_log_attributes(params)
record_api_supportability_metric(:add_custom_log_attributes)
if params.is_a?(Hash)
NewRelic::Agent.agent.log_event_aggregator.add_custom_attributes(params)
else
NewRelic::Agent.logger.warn("Bad argument passed to #add_custom_log_attributes. Expected Hash but got #{params.class}.")
end
end
# Set the user id for the current transaction. When present, this value will be included in the agent attributes for transaction and error events as 'enduser.id'.
#
# @param [String] user_id The user id to add to the current transaction attributes
#
# @api public
def set_user_id(user_id)
record_api_supportability_metric(:set_user_id)
if user_id.nil? || user_id.empty?
::NewRelic::Agent.logger.warn('NewRelic::Agent.set_user_id called with a nil or empty user id.')
return
end
default_destinations = NewRelic::Agent::AttributeFilter::DST_TRANSACTION_TRACER |
NewRelic::Agent::AttributeFilter::DST_TRANSACTION_EVENTS |
NewRelic::Agent::AttributeFilter::DST_ERROR_COLLECTOR
NewRelic::Agent::Transaction.add_agent_attribute(:'enduser.id', user_id, default_destinations)
end
# @!endgroup
# @!group Transaction naming
# Set the name of the current running transaction. The agent will
# apply a reasonable default based on framework routing, but in
# cases where this is insufficient, this can be used to manually
# control the name of the transaction.
#
# The category of transaction can be specified via the +:category+ option.
# The following are the only valid categories:
#
# * :category => :controller indicates that this is a
# controller action and will appear with all the other actions.
# * :category => :task indicates that this is a
# background task and will show up in New Relic with other background
# tasks instead of in the controllers list
# * :category => :middleware if you are instrumenting a rack
# middleware call. The :name is optional, useful if you
# have more than one potential transaction in the #call.
# * :category => :uri indicates that this is a
# web transaction whose name is a normalized URI, where 'normalized'
# means the URI does not have any elements with data in them such
# as in many REST URIs.
#
# The default category is the same as the running transaction.
#
# @api public
#
def set_transaction_name(name, options = {})
record_api_supportability_metric(:set_transaction_name)
Transaction.set_overriding_transaction_name(name, options[:category])
end
# Get the name of the current running transaction. This is useful if you
# want to modify the default name.
#
# @api public
#
def get_transaction_name # THREAD_LOCAL_ACCESS
record_api_supportability_metric(:get_transaction_name)
txn = Transaction.tl_current
if txn
namer = Instrumentation::ControllerInstrumentation::TransactionNamer
txn.best_name.sub(Regexp.new("\\A#{Regexp.escape(namer.prefix_for_category(txn))}"), '')
end
end
# @!endgroup
# Yield to a block that is run with a database metric name context. This means
# the Database instrumentation will use this for the metric name if it does not
# otherwise know about a model. This is reentrant.
#
# @param [String,Class,#to_s] model the DB model class
#
# @param [String] method the name of the finder method or other method to
# identify the operation with.
#
def with_database_metric_name(model, method = nil, product = nil, &block)
if txn = Tracer.current_transaction
txn.with_database_metric_name(model, method, product, &block)
else
yield
end
end
# Subscribe to events of +event_type+, calling the given +handler+
# when one is sent.
def subscribe(event_type, &handler)
agent.events.subscribe(event_type, &handler)
end
# Fire an event of the specified +event_type+, passing it an the given +args+
# to any registered handlers.
def notify(event_type, *args)
agent.events.notify(event_type, *args)
rescue
NewRelic::Agent.logger.debug('Ignoring exception during %p event notification' % [event_type])
end
# @!group Trace and Entity metadata
TRACE_ID_KEY = 'trace.id'.freeze
SPAN_ID_KEY = 'span.id'.freeze
ENTITY_NAME_KEY = 'entity.name'.freeze
ENTITY_TYPE_KEY = 'entity.type'.freeze
ENTITY_GUID_KEY = 'entity.guid'.freeze
HOSTNAME_KEY = 'hostname'.freeze
ENTITY_TYPE = 'SERVICE'.freeze
# Returns a new hash containing trace and entity metadata that can be used
# to relate data to a trace or to an entity in APM.
#
# This hash includes:
# * trace.id - The current trace id, if there is a current trace id. This
# value may be omitted.
# * span.id - The current span id, if there is a current span. This
# value may be omitted.
# * entity.name - The name of the current application. This is read from
# the +app_name+ key in your config. If there are multiple application
# names, the first one is used.
# * entity.type - The entity type is hardcoded to the string +'SERVICE'+.
# * entity.guid - The guid of the current entity.
# * hostname - The fully qualified hostname.
#
# @api public
def linking_metadata
metadata = Hash.new
LinkingMetadata.append_service_linking_metadata(metadata)
LinkingMetadata.append_trace_linking_metadata(metadata)
metadata
end
# @!endgroup
# @!group Manual browser monitoring configuration
# This method returns a string suitable for inclusion in a page - known as
# 'manual instrumentation' for Real User Monitoring. Can return either a
# script tag with associated javascript, or in the case of disabled Real
# User Monitoring, an empty string
#
# This is the header string - it should be placed as high in the page as is
# reasonably possible - that is, before any style or javascript inclusions,
# but after any header-related meta tags
#
# In previous agents there was a corresponding footer required, but all the
# work is now done by this single method.
#
# @param [String] nonce The nonce to use in the javascript tag for browser instrumentation
#
# @api public
#
def browser_timing_header(nonce = nil)
record_api_supportability_metric(:browser_timing_header)
return EMPTY_STR unless agent
agent.javascript_instrumentor.browser_timing_header(nonce)
end
# @!endgroup
def_delegator :'NewRelic::Agent::PipeChannelManager', :register_report_channel
end
end