On this page:

Related Pages
Your Rating:
Results:
PatheticBadOKGoodOutstanding!
1 rates

This topic introduces the Java Agent API and describes the common use cases for the API.

About the Java Agent API

Introduction

AppDynamics automatically detects an application activity out-of-the-box. The agent ships with out-of-the-box configuration that - for supported application frameworks - tells it where Business Transaction activity starts, where thread handoffs happen and where calls to downstream systems are made and how to inject correlation headers into outbound messages and retrieve them from inbound messages such that AppDynamics can establish the end to end transaction flow through the application architecture.

For frameworks that do not benefit from out-of-the-box support, the agent provides a set of APIs allowing the application developer to make code changes to add calls to the agent to allow it to identify and follow Business Transactions as they execute, providing end-to-end visibility for any application.

Use of the agent API can be seamlessly combined with the out-of-the-box instrumentation to support situations where a mixture of supported and unsupported frameworks are in use (for example, a standard servlet making an external call using a proprietary API).

The Java Agent API enables you to programatically:

  • Define Business Transactions
  • Define Exit Calls
  • Inform the agent when the application hands off transaction processing between threads
  • Add application data to snapshots or transaction analytics
  • Report custom metrics

When instrumenting any application, the agent API design prioritises the success of the application transactions over instrumentation. For this reason, if any of the agent API calls fail, they do not throw exceptions disrupting the transaction flow, but rather log messages to aid diagnosis. This also means that there is no hard dependency between the application and the presence of the AppDynamics Java agent within the JVM.

Installing the Dependency

The agent API jar can be accessed directly or downloaded from Maven Central, or it can be downloaded from the AppDynamics portal. The library version changes with each new API release, and is not tightly coupled to the version of the underlying agent, which must be a minimum of version 4.5.11.

For use with Maven Central, add the dependency to your build files in Gradle:

dependencies {
    compile group: 'com.appdynamics.agent', name: 'agent-api', version: '4.5.13.27526'
}

or Maven:

<dependency>
     <groupId>com.appdynamics.agent</groupId>
     <artifactId>agent-api</artifactId>
     <version>4.5.13.27526</version>
</dependency>

See, https://docs.appdynamics.com/javadocs/java-agent-api/v4.5/ for Javadoc reference for the agent API.

Common Use Cases

Starting and Ending a Synchronous Business Transaction

The following example shows code that starts a Business Transaction called 'Checkout' whenever the method checkout() is called. The Business Transaction ends when the method does. Encapsulating the method body in a try/finally block ensures that you end the Business Transaction even if the method itself throws an exception or otherwise terminates without reaching the end.

 

public String checkout(List<ItemOrders> orders) {
   Transaction transaction = null;
   try {
        transaction = AppdynamicsAgent.startTransaction("Checkout", null, false);
 
        /*******************
         * Method Body Here
         *******************/
    } finally {
        if (transaction != null) {
           transaction.end();
        }
    }
}

Alternatively you can use try-with-resources pattern:

 

public String checkout(List<ItemOrders> orders) {
   try (Transaction transaction = AppdynamicsAgent.startTransaction("Checkout", null, false)) {
        /*******************
         * Method Body Here
         *******************/
    }
}

In this case, the Business Transaction ends when the try block closes.

Starting and Ending an Asynchronous Business Transaction

This example shows code that starts a Business Transaction called 'CheckoutAsync' whenever the method checkoutAsync() is called.  The originating segment created for the Business Transaction ends when the method endSegment() is called on the Business Transaction, or when it is closed if used in a try-with-resources construct.  Encapsulating the method body in a try/finally block ensures that we end the segment even if the method itself throws an exception or otherwise terminates without reaching the end.

//The thread where the Business Transaction starts
public String checkoutAsync(List<ItemOrders> orders) {
   Transaction transaction = null;
   try {
        transaction = AppdynamicsAgent.startTransaction("CheckoutAsync", null, true);
		//mark handoff to link this segment with the end segment
		transaction.markHandoff(commonPayload);

        /*******************
         * Method Body Here
         *******************/
    } finally {
        if (transaction != null) {
           transaction.endSegment();
        }
    }
}

 

Alternatively, try-with-resources pattern is supported:

//The thread where the Business Transaction starts
public String checkoutAsync(List<ItemOrders> orders) {
   try (Transaction transaction = AppdynamicsAgent.startTransaction("CheckoutAsync", null, true)) {
		//mark handoff to link this segment with the end segment
		transaction.markHandoff(commonPayload);

        /*******************
         * Method Body Here
         *******************/
    }
}

This ends the segment in the thread where the Business Transaction was started when the try block closes. The Business Transaction itself needs to be ended in the method where async Business Transaction ends.

 

//The thread where the Business Transaction ends
public String checkoutAsyncEnd(List<ItemOrders> orders, Transaction transaction, Object commonPayload) {
   //link to the originating segment
   Transaction transactionSegment = AppdynamicsAgent.startSegment(commonPayload);

   /*******************
   * Method Body Here
   *******************/
   if (transactionSegment != null) {
      transactionSegment.endSegment();
   }
   if (transaction != null) {
      transaction.end();
   }
}

Defining an Exit Call

Given an inventoryServer.verifyQuantities(orders) which makes a request to another process, you can monitor that request as an Exit Call to continue monitoring the Business Transaction through the call to the downstream server, and identify the time spent in the remote service. You can do this by modifying the method as follows:

 

public void verifyQuantities(List<ItemOrders> orders) {
    ExitCall exitCall = null;
    try {
        exitCall = AppdynamicsAgent.getTransaction().startExitCall("Quantity Check", "Inventory Server", false);
         
       /*******************
        * Method Body
        *******************/
    } finally {
        if (exitCall != null) {
            exitCall.end();
        }
    }
}

The above code modifications defines the Exit Call that manifests it as a remote service in the controller.  To tag and follow the request into an instrumented downstream tier, add a correlation header:

 

public void verifyQuantities(List<ItemOrders> orders) {
    ExitCall exitCall = null;
    try {
        exitCall = AppdynamicsAgent.getTransaction().startExitCall("Quantity Check", "Inventory Server", false);
 
        // Generate the appdynamics correlation header
        String correlationHeader = exitCall.getCorrelationHeader();
 
        // ... Method code including request creation
 
        // Modify the request/payload to include the correlation header
        inventoryRequest.addHeader(AppdynamicsAgent.TRANSACTION_CORRELATION_HEADER, correlationHeader);
     
    } finally {
        if (exitCall != null) {
            exitCall.end();
        }
    }
}

Defining an Asynchronous Thread Handoff

If your checkout method also does a thread handoff and executes some business logic you would be interested in monitoring in a separate thread, register the worker thread with the Business Transaction.

 

public String checkout(List<ItemOrders> orders) {
    Transaction transaction = null;
    try {
        transaction = AppdynamicsAgent.startTransaction("Checkout", null, false);
 
        // ... Method code
 
        // Custom thread handoff using custom queue
        asyncTaskQueue.add(task);
         
    } finally {
        if (transaction != null) {
           transaction.end();
        }
    }
}

To instrument this, modify the add method to mark a thread handoff and then start a new segment where the thread begins running.

 

public class AsyncTaskQueue {
    public void add(Task task) {
        AppdynamicsAgent.getTransaction().markHandoff(task);
         
        /*******************
         * Method Body
         *******************/
}

 

 

public class Task {
    public void run() {
        Transaction transaction = null;
        try {
            transaction = AppdynamicsAgent.startSegment(this);
 
            /*******************
             * Method Body
             *******************/
        } finally {
            if (transaction != null) {
                transaction.endSegment();
            }
        }
    }
 
    public void cancel() {
        AppdynamicsAgent.cancelHandoff(this);
 
        /*******************
         * Method Body
         *******************/
    }
}
The task object is used by the agent to link the segments. Correlating thread segments using the agent API requires that the agent is running in Executor mode.

Adding Data to Snapshot or Analytics

Often there are values of interest in the code that are helpful to add to snapshots to aid in root cause diagnosis of issues, or to send to the AppDynamics Business Transaction analytics to help answer real time business-oriented questions about your application.  Data reported using this API appears in the same way as if it had been collected with a Method Invocation Data collector

To report a total checkout amount to Business Transaction analytics and have it present in APM snapshots, use the following code:

 

private static final Set<DataScope> dataScopeSet = new HashSet(Arrays.asList(DataScope.ANALYTICS, DataScope.SNAPSHOTS));
 
public String checkout(List<ItemOrders> orders) {
    Transaction transaction = null;
    try {
        transaction = AppdynamicsAgent.startTransaction("Checkout", null, false);
 
        // ... Method code
 
        double shoppingCartTotal = total(orders);
        transaction.collectData("cart total", Double.toString(shoppingCartTotal), dataScopeSet);
         
    } finally {
        if (transaction != null) {
           transaction.end();
        }
    }
}

Defining a Custom Metric or Event

It can also be useful to report a value as a custom metric:

 

public String checkout(List<ItemOrders> orders) {
 
    // ... Method code
 
    double shoppingCartTotal = total(orders);
    AppdynamicsAgent.getMetricPublisher().reportSumMetric("Cart Total", (long) shoppingCartTotal);
 
}

 

Reporting custom metrics and events is possible irrespective of the Business Transaction context.

  • No labels