The New Relic Java agent API lets you set up custom instrumentation for your Java application. This document shows an example of using custom instrumentation with annotation in an imaginary application.
Important
For best results when using the API, ensure you have the latest Java agent release.
Complete example app using API
Below is an example of an imaginary store app's servlet using the Java agent API.
Tip
If you copy and paste example code, be sure to use appropriate spacing on your command lines.
package test;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// Java agent API imports
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Trace;
public class TestServlet extends HttpServlet {
// instrumentation via annotation
@Trace(dispatcher = true)
protected void
processRequest(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
saveNewRelicInfo(req);
doRequestWork(req);
writeResponse(resp);
}
private void saveNewRelicInfo(HttpServletRequest req) {
String storeId = req.getParameter("storeId");
if (storeId != null) {
// set the name of the Transaction
NewRelic.setTransactionName(null, "/store");
if (storeId.equals("betaStore")) {
// prevent the method from contributing to the Apdex score
NewRelic.ignoreApdex();
}
}
String userId = req.getParameter("userId");
if (userId != null) {
// Tracks the user ID to the current transaction by setting the enduser.id agent attribute
NewRelic.setUserId(userId);
// set the user name to associate with the RUM JavaScript footer
// for the current web transaction
NewRelic.setUserName(userId);
// add a key/value pair to the current transaction
NewRelic.addCustomParameter("userId", userId);
}
String promotionId = req.getParameter("promotionId");
if (promotionId != null) {
// increment the metric counter for the given name
NewRelic.incrementCounter("Custom/Promotion");
}
}
protected void
doRequestWork(HttpServletRequest req) {
try {
long millisToSleep = new Random().nextInt(5000);
Thread.sleep(millisToSleep);
// record a response time in milliseconds for the given metric name
NewRelic.recordResponseTimeMetric("Custom/RandomSleep",
millisToSleep);
} catch (InterruptedException e) {
// report a handled exception
NewRelic.noticeError(e, false);
}
}
protected void
writeResponse(HttpServletResponse resp)
throws IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head>");
// get the RUM JavaScript header for the current web transaction
out.println(NewRelic.getBrowserTimingHeader());
out.println("<title>NewRelic API example servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>API example</h1>");
// get the RUM JavaScript footer for the current web transaction
out.println(NewRelic.getBrowserTimingFooter());
out.println("</body>");
out.println("</html>");
out.close();
}
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
processRequest(req, resp);
}
protected void doPost(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
processRequest(req, resp);
}
}
How the example uses the API
Here is the same example app divided into sections that describe how the API is used:
This part of the example shows the imports needed for the example application and Java agent API.
package test;
import java.io.IOException;import java.io.PrintWriter;import java.util.Random;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;// Java agent API imports import com.newrelic.api.agent.NewRelic;import com.newrelic.api.agent.Trace;
This part of the API call provides instructions to instrument this call using New Relic's trace annotation @Trace
. Any requests that hit processRequest
will now show a segment in APM's Transaction trace call chart.
public class TestServlet extends HttpServlet { // instrumentation via annotation @Trace(dispatcher = true) protected voidprocessRequest(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException { saveNewRelicInfo(req); doRequestWork(req); writeResponse(resp);}
This part of the API call instructs web transactions containing a storeId
value to appear in APM's Transactions page with the custom transaction name you set. A request to any one store will appear under the same, aggregate name.
private voidsaveNewRelicInfo(HttpServletRequest req) { String storeId = req.getParameter("storeId"); if (storeId != null) { // set the name of the Transaction NewRelic.setTransactionName(null, "/store"); }}
This part of the API call excludes the non-public beta storeID
from affecting the Apdex score.
if (storeId.equals("betaStore")) { // prevent the method from contributing to the Apdex score NewRelic.ignoreApdex();}
This part of the API call inserts additional metadata into the page load timing request so that browser traces can be tied with the userId
. It also records the userId
as a custom parameter on the transaction so that it appears in the parameter details of a transaction trace. (Page load timing sometimes is referred to as real user monitoring or RUM.)
String userId = req.getParameter("userId"); if (userId != null) { // set the user name to associate with the RUM JavaScript footer // for the current web transaction NewRelic.setUserName(userId); // add a key/value pair to the current transaction NewRelic.addCustomParameter("userId", userId); }
This part of the API call records the number of times a promotion was viewed so that the metrics can appear on a custom dashboard.
Important
For metrics you want to graph in custom dashboards, be sure to prepend Custom/
to the metric name; for example, Custom/Promotion
.
String promotionId = req.getParameter("promotionId"); if (promotionId != null) { // increment the metric counter for the given name NewRelic.incrementCounter("Custom/Promotion"); }
This part of the API call sends a set of instructions to the handler for processing requests and handling exceptions.
protected voiddoRequestWork(HttpServletRequest req) { try { long millisToSleep = new Random().nextInt(5000); Thread.sleep(millisToSleep); // record a response time in milliseconds for the given metric name NewRelic.recordResponseTimeMetric("Custom/RandomSleep",millisToSleep); } catch (InterruptedException e) { // report a handled exception NewRelic.noticeError(e, false); } }protected voidwriteResponse(HttpServletResponse resp) throws IOException {
This part of the API call defines what to include in the HttpServletResponse
. For manual instrumentation of to monitor page load timing (sometimes referred to as real user monitoring or RUM):
Set the header after the <head> tag.
Set the footer at the end of <body> tag.
resp.setContentType("text/html;charset=UTF-8");PrintWriter out = resp.getWriter();out.println("<html>");out.println("<head>");// get the RUM JavaScript header for the current web transactionout.println(NewRelic.getBrowserTimingHeader());out.println("<title>NewRelic API example servlet</title>");out.println("</head>");out.println("<body>");out.println("<h1>API example</h1>");// get the RUM JavaScript footer for the current web transactionout.println(NewRelic.getBrowserTimingFooter());out.println("</body>");out.println("</html>");out.close();}
This part of the API call defines the remaining information to include in the HttpServletResponse
response.
protected voiddoGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException { processRequest(req, resp); }protected voiddoPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException { processRequest(req, resp); }}