This document demonstrates using the New Relic Java agent API to instrument a simple client and server application. The instrumentation has these goals:
- To record external HTTP and datastore transactions.
- To link external transactions between two applications running New Relic agents (known as cross application tracing or CAT).
See the Java agent API Javadoc for full descriptions of the available API classes and methods.
Important
For best results when using the API, ensure that you have the latest Java agent release. Several APIs used in the examples require Java agent 3.36.0 or higher.
Client-side example
Here is an example of the client-side code for a simple client-server application:
package com.newrelic.example;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
// New Relic API imports
import com.newrelic.api.agent.ExternalParameters;
import com.newrelic.api.agent.HeaderType;
import com.newrelic.api.agent.HttpParameters;
import com.newrelic.api.agent.InboundHeaders;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.OutboundHeaders;
import com.newrelic.api.agent.Trace;
import com.newrelic.api.agent.TracedMethod;
import fi.iki.elonen.NanoHTTPD;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
public class NewRelicApiExample extends NanoHTTPD {
public NewRelicApiExample() throws IOException, URISyntaxException {
super(8080);
// Set Dispatcher name and version
NewRelic.setServerInfo("NanoHttp", "2.3.1");
// Set Appserver port for JVM identification
NewRelic.setAppServerPort(8080);
// Set JVM instance name
NewRelic.setInstanceName("Client");
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
System.out.println("Running at: http://localhost:8080/");
}
public static void main(String[] args) throws URISyntaxException {
try {
new NewRelicApiExample();
} catch (IOException ioe) {
System.err.println("Unable to start the server:\n" + ioe);
}
}
@Trace(dispatcher = true)
@Override
public Response serve(IHTTPSession session) {
URI uri = null;
int status = 0;
try {
createDB();
Thread.sleep(1000);
uri = new URI("http://localhost:8081");
status = makeExternalCall(uri);
} catch (URISyntaxException | InterruptedException | IOException e) {
e.printStackTrace();
}
if (status == 200) {
return newFixedLengthResponse("<html><body><h1>Successful Response</h1>\n</body></html>\n");
} else {
return newFixedLengthResponse("<html><body><h1>Error\n" + status + "</h1>\n</body></html>\n");
}
}
@Trace
public int makeExternalCall(URI uri) throws IOException {
HttpUriRequest request = RequestBuilder.get().setUri(uri).build();
// Wrap the outbound Request object
OutboundWrapper outboundHeaders = new OutboundWrapper(request);
// Obtain a reference to the method currently being traced
TracedMethod tracedMethod = NewRelic.getAgent().getTracedMethod();
// Add headers for outbound external request
tracedMethod.addOutboundRequestHeaders(outboundHeaders);
CloseableHttpClient connection = HttpClientBuilder.create().build();
CloseableHttpResponse response = connection.execute(request);
// Wrap the incoming Response object
InboundWrapper inboundHeaders = new InboundWrapper(response);
// Create an input parameter object for a call to an external HTTP service
ExternalParameters params = HttpParameters
.library("HttpClient")
.uri(uri)
.procedure("execute")
.inboundHeaders(inboundHeaders)
.build();
// Report a call to an external HTTP service
tracedMethod.reportAsExternal(params);
return response.getStatusLine().getStatusCode();
}
// Implement OutboundHeaders interface to create a wrapper for the outgoing Request headers
class OutboundWrapper implements OutboundHeaders {
private final HttpUriRequest delegate;
public OutboundWrapper(HttpUriRequest request) {
this.delegate = request;
}
@Override
public void setHeader(String name, String value) {
delegate.addHeader(name, value);
}
@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}
}
// Implement InboundHeaders interface to create a wrapper for the incoming Response headers
class InboundWrapper implements InboundHeaders {
private final CloseableHttpResponse responseHeaders;
public InboundWrapper(CloseableHttpResponse requestHeaders) {
this.responseHeaders = requestHeaders;
}
@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}
@Override
public String getHeader(String name) {
return responseHeaders.getFirstHeader(name).getValue();
}
}
@Trace
public void createDB() {
Connection c = null;
Statement stmt = null;
try {
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:/tmp/test.db");
System.out.println("Opened database successfully");
stmt = c.createStatement();
String dropSql = "DROP TABLE IF EXISTS COMPANY;";
stmt.executeUpdate(dropSql);
String sql = "CREATE TABLE COMPANY " +
"(ID INT PRIMARY KEY NOT NULL," +
" NAME TEXT NOT NULL, " +
" AGE INT NOT NULL, " +
" ADDRESS CHAR(50), " +
" SALARY REAL)";
stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " +
"VALUES (1, 'Paul', 32, 'California', 20000.00 );";
stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " +
"VALUES (2, 'Allen', 25, 'Texas', 15000.00 );";
stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " +
"VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );";
stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " +
"VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );";
stmt.executeUpdate(sql);
stmt.close();
c.close();
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
}
}
Here is the same client app code divided into sections that describe how the API is used:
This section calls the Java agent API imports used to add cross application tracing to the client application later in the example code.
import java.io.IOException;import java.net.URI;import java.net.URISyntaxException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.Statement;
// New Relic API importsimport com.newrelic.api.agent.ExternalParameters;import com.newrelic.api.agent.HeaderType;import com.newrelic.api.agent.InboundHeaders;import com.newrelic.api.agent.NewRelic;import com.newrelic.api.agent.OutboundHeaders;import com.newrelic.api.agent.Trace;import com.newrelic.api.agent.TracedMethod;
import fi.iki.elonen.NanoHTTPD;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpUriRequest;import org.apache.http.client.methods.RequestBuilder;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClientBuilder;
This section starts up the client server on port 8080 and uses the NewRelic
class from the API to call the methods setServerInfo
, setAppServerPort
, and setInstanceName
. These API calls affect what is shown in the New Relic UI.
public NewRelicApiExample() throws IOException, URISyntaxException { super(8080);
// Set Dispatcher name and version NewRelic.setServerInfo("NanoHttp", "2.3.1"); // Set Appserver port for jvm identification NewRelic.setAppServerPort(8080); // Set JVM instance name NewRelic.setInstanceName("Client");
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); System.out.println("Running at: http://localhost:8080/"); }
public static void main(String[] args) throws URISyntaxException { try { new NewRelicApiExample(); } catch (IOException ioe) { System.err.println("Unable to start the server:\n" + ioe); } }
This method creates a sample database, sleeps the thread, and makes an external call to the server app listening on port 8081. The @Trace(dispatcher = true)
annotation tells the agent to start a new transaction when the serve
method is called, if it is not called as part of an existing transaction (and in this case, it is not). If it were called as part of an existing transaction, it would simply be included as part of that transaction rather than start a new one.
@Trace(dispatcher = true) @Override public Response serve(IHTTPSession session) { URI uri = null; int status = 0;
try { createDB(); Thread.sleep(1000); uri = new URI("http://localhost:8081"); status = makeExternalCall(uri); } catch (URISyntaxException | InterruptedException | IOException e) { e.printStackTrace(); }
if (status == 200) { return newFixedLengthResponse("<html><body><h1>Successful Response</h1>\n</body></html>\n"); } else { return newFixedLengthResponse("<html><body><h1>Error\n" + status + "</h1>\n</body></html>\n"); } }
This section contains the code that initiates cross application tracing to the application making the request. The @Trace
annotation tells the agent to track this method as part of an existing transaction as started by the serve
method.
The request object is wrapped by a class that implements the Java agent API's OutboundHeaders
interface, which ensures that the proper HeaderType
is set (in this case HTTP
). A call to addOutboundRequestHeaders
adds the headers to the request
and the request is sent to the server.
When the response
returns it is wrapped by a class implementing the Java agent API's InboundHeaders
interface. The inboundHeaders
, along with the "library"
, URI
, and "procedure"
arguments, are used to build an HttpParameters
object. The params
object is then passed as an argument to the reportAsExternal
method, which reports the TracedMethod
as an external HTTP call.
@Trace public int makeExternalCall(URI uri) throws IOException { HttpUriRequest request = RequestBuilder.get().setUri(uri).build();
// Wrap the outbound Request object OutboundWrapper outboundHeaders = new OutboundWrapper(request);
// Obtain a reference to the method currently being traced TracedMethod tracedMethod = NewRelic.getAgent().getTracedMethod(); // Add headers for outbound external request tracedMethod.addOutboundRequestHeaders(outboundHeaders);
CloseableHttpClient connection = HttpClientBuilder.create().build(); CloseableHttpResponse response = connection.execute(request);
// Wrap the incoming Response object InboundWrapper inboundHeaders = new InboundWrapper(response);
// Create an input parameter object for a call to an external HTTP service ExternalParameters params = HttpParameters .library("HttpClient") .uri(uri) .procedure("execute") .inboundHeaders(inboundHeaders) .build();
// Report a call to an external HTTP service tracedMethod.reportAsExternal(params);
return response.getStatusLine().getStatusCode(); }
An implementation of the Java agent API's OutboundHeaders
Interface is used to wrap the request object of the client server, which in this example is of type HttpUriRequest
. The request is passed into the constructor of the OutboundWrapper
class and implementations of the setHeader
and getHeaderType
methods are provided.
The getHeaderType
method returns a HeaderType
enum that can either be HeaderType.HTTP
or HeaderType.MESSAGE
, as defined by the Java agent API. In this example the external call protocol is HTTP, so HeaderType.HTTP
is returned.
// Implement OutboundHeaders interface to create a wrapper for the outgoing Request headers class OutboundWrapper implements OutboundHeaders { private final HttpUriRequest delegate;
public OutboundWrapper(HttpUriRequest request) { this.delegate = request; }
@Override public void setHeader(String name, String value) { delegate.addHeader(name, value); }
@Override public HeaderType getHeaderType() { return HeaderType.HTTP; } }
An implementation of the Java agent API's InboundHeaders
Interface is used to wrap the response object returned to the client server, which in this example is of type CloseableHttpResponse
. The response is passed into the constructor of the InboundWrapper
class, where implementations of the getHeaderType
and getHeader
methods are provided.
The getHeaderType
method returns a HeaderType
enum that can either be HeaderType.HTTP
or HeaderType.MESSAGE
, as defined by the Java agent API. In this example the external call protocol is HTTP so HeaderType.HTTP
is returned.
// Implement InboundHeaders interface to create a wrapper for the incoming Response headers class InboundWrapper implements InboundHeaders { private final CloseableHttpResponse responseHeaders;
public InboundWrapper(CloseableHttpResponse requestHeaders) { this.responseHeaders = requestHeaders; }
@Override public HeaderType getHeaderType() { return HeaderType.HTTP; }
@Override public String getHeader(String name) { return responseHeaders.getFirstHeader(name).getValue(); } }
This method simply creates an example SQLite database. The @Trace
annotation tells the agent to track this method as part of an existing transaction as started by the serve
method.
@Trace public void createDB() { Connection c = null; Statement stmt = null;
try { Class.forName("org.sqlite.JDBC"); c = DriverManager.getConnection("jdbc:sqlite:/tmp/test.db"); System.out.println("Opened database successfully");
stmt = c.createStatement();
String dropSql = "DROP TABLE IF EXISTS COMPANY;"; stmt.executeUpdate(dropSql);
String sql = "CREATE TABLE COMPANY " + "(ID INT PRIMARY KEY NOT NULL," + " NAME TEXT NOT NULL, " + " AGE INT NOT NULL, " + " ADDRESS CHAR(50), " + " SALARY REAL)"; stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (1, 'Paul', 32, 'California', 20000.00 );"; stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (2, 'Allen', 25, 'Texas', 15000.00 );"; stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );"; stmt.executeUpdate(sql);
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " + "VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );"; stmt.executeUpdate(sql);
stmt.close(); c.close(); } catch (Exception e) { System.err.println(e.getClass().getName() + ": " + e.getMessage()); System.exit(0); } }
Server-side example
Here is the server-side code for this example application:
package com.newrelic.example;
import java.io.IOException;
import java.net.URISyntaxException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collections;
import java.util.Enumeration;
// New Relic API imports
import com.newrelic.api.agent.DatastoreParameters;
import com.newrelic.api.agent.ExtendedRequest;
import com.newrelic.api.agent.HeaderType;
import com.newrelic.api.agent.NewRelic;
import com.newrelic.api.agent.Trace;
import com.newrelic.api.agent.TracedMethod;
import com.newrelic.api.agent.Transaction;
import fi.iki.elonen.NanoHTTPD;
public class NewRelicApiServer extends NanoHTTPD {
public NewRelicApiServer() throws IOException, URISyntaxException {
super(8081);
// Set Dispatcher name and version
NewRelic.setServerInfo("NanoHttp", "2.3.1");
// Set Appserver port for jvm identification
NewRelic.setAppServerPort(8081);
// Set JVM instance name
NewRelic.setInstanceName("Server");
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
System.out.println("\nRunning on http://localhost:8081/ \n");
}
public static void main(String[] args) throws URISyntaxException {
try {
new NewRelicApiServer();
} catch (IOException ioe) {
System.err.println("Unable to start the server:\n" + ioe);
}
}
@Trace(dispatcher = true)
@Override
public Response serve(IHTTPSession session) {
// Obtain a reference to the current Transaction
Transaction tx = NewRelic.getAgent().getTransaction();
// Set the name of the current transaction
NewRelic.setTransactionName("Custom", "ExternalHTTPServer");
// Wrap the Request object
ExtendedRequest req = new RequestWrapper(session);
// Set the request for the current transaction and convert it into a web transaction
tx.setWebRequest(req);
queryDB();
Response res = newFixedLengthResponse("<html><body><h1>SuccessfulResponse</h1>\n</body></html>\n");
// Set the response for the current transaction and convert it into a web transaction
tx.setWebResponse(new ResponseWrapper(res));
// Instruct the transaction to write the outbound response headers
tx.addOutboundResponseHeaders();
// Mark the time when the response left the server
tx.markResponseSent();
return res;
}
// Implement Response interface to create a wrapper for the outgoing Response object
public class ResponseWrapper implements com.newrelic.api.agent.Response {
private final Response httpResponse;
public ResponseWrapper(Response httpResponse) {
this.httpResponse = httpResponse;
}
@Override
public int getStatus() throws Exception {
return 200;
}
@Override
public String getStatusMessage() throws Exception {
return null;
}
@Override
public void setHeader(String name, String value) {
httpResponse.addHeader(name, value);
}
@Override
public String getContentType() {
return "";
}
@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}
}
// Extend ExtendedRequest class to create a wrapper for the Request object
class RequestWrapper extends ExtendedRequest {
private IHTTPSession session;
public RequestWrapper(IHTTPSession session) {
super();
this.session = session;
}
@Override
public String getRequestURI() {
return session.getUri();
}
@Override
public String getHeader(String name) {
return session.getHeaders().get(name.toLowerCase());
}
@Override
public String getRemoteUser() {
return null;
}
@SuppressWarnings("rawtypes")
@Override
public Enumeration getParameterNames() {
return Collections.enumeration(session.getParms().keySet());
}
@Override
public String[] getParameterValues(String name) {
return new String[]{session.getParms().get(name)};
}
@Override
public Object getAttribute(String name) {
return null;
}
@Override
public String getCookieValue(String name) {
return null;
}
@Override
public HeaderType getHeaderType() {
return HeaderType.HTTP;
}
@Override
public String getMethod() {
return session.getMethod().toString();
}
}
@Trace
public void queryDB() {
Connection c = null;
Statement stmt = null;
try {
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:/tmp/test.db");
c.setAutoCommit(false);
System.out.println("Opened database successfully");
stmt = c.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM COMPANY;");
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String address = rs.getString("address");
float salary = rs.getFloat("salary");
System.out.println("ID = " + id);
System.out.println("NAME = " + name);
System.out.println("AGE = " + age);
System.out.println("ADDRESS = " + address);
System.out.println("SALARY = " + salary);
System.out.println();
}
rs.close();
stmt.close();
c.close();
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
// Obtain a reference to the method currently being traced
TracedMethod method = NewRelic.getAgent().getTracedMethod();
// Create a DatastoreParameters object and report a call to an external datastore service
method.reportAsExternal(
DatastoreParameters
.product("sqlite")
.collection("test.db")
.operation("select")
.instance("localhost", 8080)
.databaseName("test.db")
.build());
}
}
Here is the same example server code broken into sections that describe how the API is used:
This section shows the relevant Java agent API imports needed to add cross application tracing and reporting of external datastore calls to the server application.
import java.io.IOException;import java.net.URISyntaxException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;import java.util.Collections;import java.util.Enumeration;
// New Relic API importsimport com.newrelic.api.agent.DatastoreParameters;import com.newrelic.api.agent.ExtendedRequest;import com.newrelic.api.agent.HeaderType;import com.newrelic.api.agent.NewRelic;import com.newrelic.api.agent.Trace;import com.newrelic.api.agent.TracedMethod;import com.newrelic.api.agent.Transaction;
import fi.iki.elonen.NanoHTTPD;
This section starts up the server on port 8081 and uses the NewRelic
class from the API to call the methods setServerInfo
, setAppServerPort
, and setInstanceName
. These API calls affect what is shown in the APM UI.
public NewRelicApiServer() throws IOException, URISyntaxException { super(8081);
// Set Dispatcher name and version NewRelic.setServerInfo("NanoHttp", "2.3.1"); // Set Appserver port for jvm identification NewRelic.setAppServerPort(8081); // Set JVM instance name NewRelic.setInstanceName("Server");
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); System.out.println("\nRunning on http://localhost:8081/ \n"); }
public static void main(String[] args) throws URISyntaxException { try { new NewRelicApiServer(); } catch (IOException ioe) { System.err.println("Unable to start the server:\n" + ioe); } }
The @Trace(dispatcher = true)
annotation tells the agent to start a new transaction when the serve
method is called if it is not called as part of an existing transaction (which in this case, it is not). If it were called as part of an existing transaction, it would simply be included as part of that transaction rather than start a new one.
A reference to the current Transaction
is obtained via a call to getTransaction
and the name of the transaction is set via a call to the setTransactionName
method.
The request object, which in this example is of type IHTTPSession
, is then wrapped using a class extending the Java agent API's ExtendedRequest
class. The current Transaction
is then converted to a web transaction via a call to setWebRequest
which takes the wrapped ExtendedRequest
as an argument.
A call to the database is then made and the response object is generated and wrapped by a class implementing the Java agent API's Response
interface. The wrapped response object is passed as an argument to setWebResponse
which converts the current Transaction
into a web transaction.
Once the web request and response are set, a call to addOutboundResponseHeaders
adds the headers to the response. This must be called after setWebRequest
and setWebResponse
, which together provide the agent with the inbound request headers and a place to record the outbound headers.
A call to markResponseSent
marks the time when the last byte of the response left the server as the current timestamp.
@Trace(dispatcher = true) @Override public Response serve(IHTTPSession session) { // Obtain a reference to the current Transaction Transaction tx = NewRelic.getAgent().getTransaction(); // Set the name of the current transaction NewRelic.setTransactionName("Custom", "ExternalHTTPServer");
// Wrap the Request object ExtendedRequest req = new RequestWrapper(session);
// Set the request for the current transaction and convert it into a web transaction tx.setWebRequest(req);
queryDB(); Response res = newFixedLengthResponse("<html><body><h1>SuccessfulResponse</h1>\n</body></html>\n");
// Set the response for the current transaction and convert it into a web transaction tx.setWebResponse(new ResponseWrapper(res));
// Instruct the transaction to write the outbound response headers tx.addOutboundResponseHeaders(); // Mark the time when the response left the server tx.markResponseSent();
return res; }
An implementation of the Java agent API's Response
Interface is used to wrap the response object of the server, which in this example is of type fi.iki.elonen.NanoHTTPD.Response
. The response is passed into the constructor of the ResponseWrapper
class and implementations of the getStatus
, getStatusMessage
, setHeader
, getContentType
and getHeaderType
methods are provided.
The getHeaderType
method returns a HeaderType
enum that can either be HeaderType.HTTP
or HeaderType.MESSAGE
, as defined by the Java agent API. In this example the external call protocol is HTTP, so HeaderType.HTTP
is returned.
// Implement Response interface to create a wrapper for the outgoing Response object public class ResponseWrapper implements com.newrelic.api.agent.Response {
private final Response httpResponse;
public ResponseWrapper(Response httpResponse) { this.httpResponse = httpResponse; }
@Override public int getStatus() throws Exception { return 200; }
@Override public String getStatusMessage() throws Exception { return null; }
@Override public void setHeader(String name, String value) { httpResponse.addHeader(name, value); }
@Override public String getContentType() { return ""; }
@Override public HeaderType getHeaderType() { return HeaderType.HTTP; } }
A subclass of the Java agent API's ExtendedRequest
class is used to wrap the request object of the server, which in this example is of type IHTTPSession
. The request is passed into the constructor of the RequestWrapper
class, which provides implementations of the getRequestURI
, getHeader
, getRemoteUser
, getParameterNames
, getParameterValues
, getAttribute
, getCookieValue
, getHeaderType
, and getMethod
methods.
The getHeaderType
method returns a HeaderType
enum that can either be HeaderType.HTTP
or HeaderType.MESSAGE
, as defined by the Java agent API. In this example the external call protocol is HTTP so HeaderType.HTTP
is returned.
// Extend ExtendedRequest class to create a wrapper for the Request object class RequestWrapper extends ExtendedRequest { private IHTTPSession session;
public RequestWrapper(IHTTPSession session) { super(); this.session = session; }
@Override public String getRequestURI() { return session.getUri(); }
@Override public String getHeader(String name) { return session.getHeaders().get(name.toLowerCase()); }
@Override public String getRemoteUser() { return null; }
@SuppressWarnings("rawtypes") @Override public Enumeration getParameterNames() { return Collections.enumeration(session.getParms().keySet()); }
@Override public String[] getParameterValues(String name) { return new String[]{session.getParms().get(name)}; }
@Override public Object getAttribute(String name) { return null; }
@Override public String getCookieValue(String name) { return null; }
@Override public HeaderType getHeaderType() { return HeaderType.HTTP; }
@Override public String getMethod() { return session.getMethod().toString(); } }
This method makes an external call to the SQLite database that is created by the client. The @Trace
annotation tells the agent to track this method as part of an existing transaction as started by the serve
method.
A reference to the current TracedMethod
is obtained via a call to getTracedMethod
. A DatastoreParameters
object is then created using the builder pattern. The ExternalParameters
object is then passed as an argument to the reportAsExternal
method, which has the effect of reporting the TracedMethod
as an external datastore call.
@Trace public void queryDB() { Connection c = null; Statement stmt = null; try { Class.forName("org.sqlite.JDBC"); c = DriverManager.getConnection("jdbc:sqlite:/tmp/test.db"); c.setAutoCommit(false); System.out.println("Opened database successfully");
stmt = c.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM COMPANY;"); while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); String address = rs.getString("address"); float salary = rs.getFloat("salary"); System.out.println("ID = " + id); System.out.println("NAME = " + name); System.out.println("AGE = " + age); System.out.println("ADDRESS = " + address); System.out.println("SALARY = " + salary); System.out.println(); } rs.close(); stmt.close(); c.close(); } catch (Exception e) { System.err.println(e.getClass().getName() + ": " + e.getMessage()); System.exit(0); } // Obtain a reference to the method currently being traced TracedMethod method = NewRelic.getAgent().getTracedMethod();
// Create a DatastoreParameters object and report a call to an external datastore service method.reportAsExternal( DatastoreParameters .product("sqlite") .collection("test.db") .operation("select") .instance("localhost", 8080) .databaseName("test.db") .build()); }
For more help
If you need more help, check out these support and learning resources:
- Browse the Explorers Hub to get help from the community and join in discussions.
- Find answers on our sites and learn how to use our support portal.
- Run New Relic Diagnostics, our troubleshooting tool for Linux, Windows, and macOS.
- Review New Relic's and and documentation.