Java agent API: Custom instrumentation with annotation of an example app

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.

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.

If you copy and paste example code, be sure to use appropriate spacing on your command lines.

Complete API call example
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) {
            // 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 New Relic APM's Transaction trace call chart.

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);
}

This part of the API call instructs web transactions containing a storeId value to appear in New Relic 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 void
saveNewRelicInfo(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.

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 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 {

This part of the API call defines what to include in the HttpServletResponse. For manual instrumentation of New Relic Browser 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 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();
}

This part of the API call defines the remaining information to include in the HttpServletResponse response.

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);
    }
}

For more help

Join the discussion about Java monitoring in the New Relic Online Technical Community! The Technical Community is a public platform to discuss and troubleshoot your New Relic toolset.

If you need additional help, get support at support.newrelic.com.