Custom Metric Collection
The New Relic agent has an API that allows an application to collect custom metrics. Custom metrics are used to augment the data that RPM already collects and are most useful when applied to areas of your application that you know are important.
Metrics are streams of data about "interesting things". For example, RPM collects a metric for the amount of CPU that the current process is burning. It also collects a metric that tracks every Controller action invocation. However, the out of the box metrics can only go so far. This is where custom metrics are useful. For example, at New Relic, one of the things we use custom metrics for is to collect detailed data about the Web Service that our agent talks to. We also collect customer metrics that tell us the annualized value of our current subscriptions.
Custom metrics are visible in a few places in RPM. These are:
- Transaction views in RPM Developer Mode
- Breakdown graphs on the Controller page
- The Details tab of a Transaction Trace
- Custom Views, where any metric (including business metrics) can be shown on a user created custom view
NOTE: Collecting too many metrics can impact the performance of your application and of RPM. New Relic reserves the right to disable accounts which collect excessive metrics. For example, it's a bad idea to create metrics that track each individual user on your site - this could result in thousands or millions of metrics.
Collecting custom metrics with Method Tracers
A Method Tracer is a software probe that you can put on a method of any class.
Adding Method Tracers (Java)
You can add custom metric tracers using an annotation on the method(s) of interest. You can either define your own trace annotation class, or you can compile against the New Relic agent jar and use the com.newrelic.agent.Trace class. To enable the custom tracing feature in the agent, set the enable_custom_tracing flag in the common section of the newrelic.yml configuration file to true (add it if it doesn't exist).
Using the com.newrelic.agent.Trace Annotation
Example:
import com.newrelic.agent.Trace;
....
@Trace
public void test() {}
The above will generate a metric using the class and method name.
Defining a Custom Trace Annotation class
If you're using version 1.2.004.1 or later of the Java agent, you can define your own trace annotation class:
package com.test;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Trace {
public static final String NULL = "";
String metricName() default NULL;
boolean dispatcher() default false;
String tracerFactoryName() default NULL;
}
Configure the agent to use this annotation class in newrelic.yml:
class_transformer:
trace_annotation_class_name: com.test.Trace
Adding Method Tracers (Ruby)
Method Tracers must be applied after the target method has been defined. The probe uses alias method chaining to insert itself into the executing of the target method.
To add a Method Tracer within a class, include the MethodTracer
module and invoke add_method_tracer:
add_method_tracer <method_name>
For Rails, a common way to add instrumentation is to create an
initializer and monkey patch the the instrumentation
directives.
For example, to add a method tracer to MyCache#get,
put the following in a file called
config/initializers/rpm_instrumentation.rb:
require 'new_relic/agent/method_tracer.rb'
MyCache.class_eval do
include NewRelic::Agent::MethodTracer
add_method_tracer :get
end
Note that this requires the MyCache class is loaded prior adding the method tracer.
Method tracers can also be used within normal class definition, just make sure that the target method has been defined.
For example:
require 'new_relic/agent/method_tracer.rb'
class Foo
include NewRelic::Agent::MethodTracer
def generate_image
...
end
add_method_tracer :generate_image, 'Custom/generate_image'
end
To instrument a class method, you need to add the method tracer in the class singleton like this:
require 'new_relic/agent/method_tracer.rb'
class Foo
def self.generate_image
...
end
class << self
include NewRelic::Agent::MethodTracer
add_method_tracer :generate_image, 'Custom/generate_image'
end
end
The +add_method_tracer+ call takes an optional metric name and a hash of options. For details refer to the Ruby agent rdocs.
Metric Names
Metric names are paths delimited by the "/" character. For custom instrumentation the pattern is:
<category>/<class>/<method>
You can override the default metric name by passing a second
argument to add_method_tracer.
This may be necessary if you want to specify a category other than
the default 'Custom' category name,
or if the method and class name do not represent the name of the
metric well.
Reserved categories include:
- ActiveRecord
- Apdex
- CPU
- Controller
- Database
- Memory
- View
- Custom
For custom metric names the recommended convention is:
Custom/<class>/<method> or Custom/<category>/<name>
Capturing Other Metrics (Ruby)
In addition to adding visibility on method executions, you can instrument other non-web transactions such as background tasks with the same level of transaction and error detail as web transactions. Refer to the documentation on instrumenting background jobs.
You can also collect metric data about things other than method
invocations. To do this, you'll need to manipulate metric data
directly.
First, get a handle to a Stat instance by making the
following API call:
stat = NewRelic::Agent.agent.stats_engine.get_stats_no_scope(<String:metric_name>)
Second, update the stat with the data. This increments the
Stat.call_count field and adds the value to the
Stat.total_call_time fields (see The Gritty Details
section below).
stat.record_data_point(<Float:value>)
Here's an easy example that shows how you might use metrics to track dollars flowing through a site:
class Cart
def checkout()
amount = compute_cart_total # computes the amount to charge the customer
NewRelic::Agent.agent.stats_engine.get_stats_no_scope('Custom/Cart/charge_amount').record_data_point(amount)
charge_customer(amount)
...
end
end
To lean more about how data aggregates over time, check out the Stat Aggregation Policy section below.
The Gritty Details - Metrics & Stats in the Ruby Agent
There are two basic data structures used for collecting metric
data. The first is a Metric and the second is a
Stat.
A Metric describes the thing for which we have
data. The following pseudo Ruby shows how it is defined:
class Metric
attr_accessor :name # String - metric name
attr_accessor :scope # String - current controller action
end
It's a pretty simple structure. The name gives details about the "thing" we have data for. The optional scope is the name of the controller action handling the current request. A metric is "global" if the scope is empty.
A Stat is a class used to record data about a
metric. Stats are geared towards collecting data about method
execution, however the fields can be overridden to store data about
anything. The following pseudo Ruby defines a Stat:
class Stat
attr_accessor :call_count # Integer - method invocation count
attr_accessor :total_call_time # Float - total method call time (in seconds)
attr_accessor :total_exclusive_time # Float - total time spent in the traced method minus any child time (in seconds)
attr_accessor :min_call_time # Float - the smallest method invocation time (in seconds)
attr_accessor :max_call_time # Float - the largest method invocation time (in seconds)
attr_accessor :sum_of_squares # Float - the sum of squares of response times - used for standard deviation computation
attr_accessor :begin_time # Time - the start of the time window for this data
attr_accessor :end_time # Time - end of the time window for this data
end
Stat Aggregation Policy
One of the strengths of RPM is its ability to aggregate data over time. The aggregation policy can be important to know when collecting custom metrics. Aggregation is an act of combining several things into one. The aggregation policy for each field is:
call_count- additiontotal_call_time- additiontotal_exclusive_time- additionmin_call_time- the min() of each min_call_timemax_call_time- the max() of each max_call_timesum_of_squares- additionbegin_time- the min() of each begin_timeend_time- the max() of each end_time