-
Notifications
You must be signed in to change notification settings - Fork 145
Transactions
These are the options for starting a transaction with the Java agent.
Trace annotation documentation
Example
package com.example;
import com.newrelic.api.agent.Trace;
public class MyClass {
public static void main(String[] args) {
while (true) {
myMethod();
}
}
@Trace(dispatcher = true)
public static void myMethod() {
System.out.println("Hello, World");
}
}
XML instrumentation doumentation
Example
<?xml version="1.0" encoding="UTF-8"?>
<extension xmlns="https://newrelic.com/docs/java/xsd/v1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="newrelic-extension extension.xsd"
name="customExtension" version="1.0">
<instrumentation metricPrefix="Example">
<pointcut transactionStartPoint="true">
<className>com.example.MyClass</className>
<method>
<name>myMethod</name>
</method>
</pointcut>
</instrumentation>
</extension>
This generates XML but does so using an online UI that allows you to select methods to instrument from thread profiles. The XML is sent down to the agent which stores it in memory.
Using internal Transaction API to get and start a transaction: com.newrelic.agent.Transaction.getTransaction(true)
/**
* Get this thread's reference to a transaction, creating it if this thread has no Transaction reference and
* creation is requested and allowable.
*
* @param createIfNotExists true to request creation of the object
* @return the transaction. Return null if createIfNotExists is false and the transaction has not previously been
* created, or if the current thread is an Agent thread.
*/
public static Transaction getTransaction(boolean createIfNotExists) {
}
Using internal AgentBridge API to get and start a transaction:
AgentBridge.getAgent().getTransaction(true)
/**
* Get the transaction stored in a thread local.
*
* @param createIfNotExists if true, create a transaction if needed.
* @return the transaction in the thread local or possibly null if createIfExists == false
*/
Transaction getTransaction(boolean createIfNotExists);
Transaction represents a single execution path of code.
/**
* Represents a single transaction in the instrumented application. A transaction is a single top-level activity, such
* as servicing a single web request or a large-grained unit of background work.<br/>
* <br/>
* Transactions are composed of one or more per-task work units, each of which is represented by a TransactionActivity
* object. The Transaction and the TransactionActivity are each referenced by a thread local variable.<br/>
* <br/>
* Transactions are created by instrumented method invocations. Creation of the Transaction always creates a
* TransactionActivity. If an instrumented thread declares itself asynchronous, its Transaction is discarded and its
* TransactionActivity becomes associated with the top-level Transaction that originated it.<br/>
* <br/>
* TransactionActivities are closed when the last tracer on their call stack is popped off. Transactions are closed when
* their last child TransactionActivity is finished and no further activities are pending.<br/>
* <br/>
* This class is thread-safe.
*/
public class Transaction {
}
TransactionActivity represents all of the activity on a specific thread.
/**
* This class tracks the state of a single asynchronous activity within a transaction. An instance of this class may be
* associated with e.g. a thread, the execution of a task on pool thread, or the execution of a "coroutine" on a thread.<br>
* <br>
* This class is not threadsafe, but is typically referenced by two distinct threads during its lifetime. First, it
* holds state for its associated thread (or task, etc.) until its root tracer completes. It may then be queued
* (indirectly through its owning transaction) for harvest, during which it is referenced by the harvest thread. These
* two usages never overlap in time. Memory visibility is assured because the transaction will pass through a
* synchronized object (e.g. atomic or concurrent collection) that has the effect of inserting a full barrier between
* the async activity's last write and the harvest thread's first read.
*/
public class TransactionActivity {
}
A Tracer records information about a method invocation. There are a number of Tracer
implementations for different use cases (e.g. DefaultTracer
, DefaultSqlTracer
, and UltraLightTracer
).
/**
* A tracer records information about a method invocation - primarily the start and stop time of the invocation. A
* tracer instance is associated with a single method invocation.
*
* Tracers are created by {@link TracerFactory} instances.
*/
public interface Tracer extends TimedItem, ExitTracer, ErrorTracer {
}
Segment is similar to a TracedMethod
but represents an arbitrary piece of time rather than the time it takes for a method to execute.
/**
* <p>
* Represents a timed unit of work. Like a {@link TracedMethod}, reports a single metric,
* generates a single segment in a transaction trace, and can be reported as an external call. Unlike a TracedMethod,
* a Segment's timed duration may encompass arbitrary application work; it is not limited to a single method or thread.
* </p>
* <p>
* Timing begins when the instance is created via {@link Transaction#startSegment} and ends when the {@link #end()}
* or {@link #ignore()} method is called. These calls can be issued from distinct application threads. If a Segment is
* not explicitly ignored or ended it will be timed out according to the <code>segment_timeout</code> value which is
* user configurable in the yaml file or by a Java system property.
* </p>
* <p>
* A {@link Segment} will show up in the Transaction Breakdown table, as well as the Transaction Trace page in APM.
* </p>
*/
public interface Segment extends AttributeHolder {
}
TracedMethod is extended by Tracer
and represents the time it takes for a method to execute.
/**
* Represents a single instance of the timing mechanism associated with a method that is instrumented using the
* {@link Trace} annotation.
*
* @see Agent#getTracedMethod()
*/
public interface TracedMethod extends AttributeHolder {
}
A Token is used to link asynchronous units of work to the originating Transaction
.
/**
* Tokens are passed between threads to link asynchronous units of work to the originating {@link Transaction}
* associated with the token. This results in two or more pieces of work being tied together into a single
* {@link Transaction}. A token is created by a call to {@link Transaction#getToken()}.
*/
public interface Token {
}
A Transaction
is created on the initiating thread and has a TransactionActivity
representing the activity on each thread. Each TransactionActivity
has a Tracer
for each TracedMethod
that was invoked on its associated thread. A TransactionActivity
occurring on a thread other than the initiating thread is linked to the Transaction
using a Token
passed from the initiating thread.
The lifcycle of a Transaction
is managed by the TransactionService.
- Methods annotated with
@Trace
are processed by the TraceMatchVisitor -
InstrumentationImpl.createTracer
starts the process of creating aTransaction
. -
com.newrelic.agent.Transaction.getTransaction(true)
is used to create aTransaction
. This code path is the only correct way to create aTransaction
. -
com.newrelic.agent.Transaction.postConstruct()
creates aTransactionActivity
for the initiating thread of theTransaction
. - The
Transaction
is stored in theThreadLocal
for easy access. -
InstrumentationImpl.startTracer
creates theTracer
and records the start time. -
TransactionActivity.tracerStarted
saves theTracer
on theTransactionActivity
call stack, ensuring that the intialTracer
is saved as the rootTracer
. -
com.newrelic.agent.Transaction.activityStarted
starts theTransactionActivity
for the thread. -
com.newrelic.agent.Transaction.startTransactionIfBeginning
starts theTransaction
and notifies theTransactionService
of that.
-
DefaultTracer.finish
is called when a method completes. -
DefaultTracer.performFinishWork
stops timing for theTracer
, notifies the parentTracer
(if one exists) of the childTracer
's finish, and records response time and external metrics representing the method execution. -
TransactionActivity.tracerFinished
records that aTracer
has finished and removes it from its call stack. -
TransactionActivity.finished
is called when the rootTracer
is successfully popped off the call stack, as we know that it should be the lastTracer
to be dealt with. -
com.newrelic.agent.Transaction.activityFinished
closes theTransactionActivity
and tracks each finished childTransactionActivity
. -
com.newrelic.agent.Transaction.finishTransaction
is called when all childTransactionActivities
have been finished. It then finalizes theTransaction
name and any statistic calculations. It also notifies theTransactionService
that theTransaction
has finished. -
OtherDispatcher.transactionFinished
records some final metrics andTransaction
attributes. -
TransactionService.transactionFinished
callsdoProcessTransaction
which notifies all implementations ofTransactionListener
,ExtendedTransactionListener
, andTransactionStatsListener
that theTransaction
has been finished and that they should do something based off of that. One suchTransactionListener
that gets notified is theTransactionEventsService
which creates aTransactionEvent
for theTransaction
indispatcherTransactionFinished
. Another suchTransactionListener
that gets notified is theSpanEventsServiceImpl
which creates aSpanEvent
for everyTracer
of theTransaction
indispatcherTransactionFinished
. -
com.newrelic.agent.Transaction.activityFinished
closes and removes theTransaction
from theThreadLocal
.
Given the original code, the Java agent intercepts the MyClass
class file while it is being loaded, determines that it is annotated with @Trace
, weaves new bytecode to inject New Relic APIs into the class file, and then returns the class back to the class loader to finish being loaded. Below are examples of the original code versus the weaved code.
Original code
package com.example;
import com.newrelic.api.agent.Trace;
public class MyClass {
public static void main(String[] args) {
while (true) {
myMethod();
}
}
@Trace(dispatcher = true)
public static void myMethod() {
System.out.println("Hello, World");
}
}
Code weaved with New Relic APIs
/*
* Decompiled with CFR 0.151.
*
* Could not load the following classes:
* com.newrelic.agent.bridge.AgentBridge
* com.newrelic.agent.bridge.ExitTracer
* com.newrelic.agent.instrumentation.InstrumentationType
* com.newrelic.agent.instrumentation.InstrumentedClass
* com.newrelic.agent.instrumentation.InstrumentedMethod
* com.newrelic.api.agent.Trace
*/
package com.example;
import com.newrelic.agent.bridge.AgentBridge;
import com.newrelic.agent.bridge.ExitTracer;
import com.newrelic.agent.instrumentation.InstrumentationType;
import com.newrelic.agent.instrumentation.InstrumentedClass;
import com.newrelic.agent.instrumentation.InstrumentedMethod;
import com.newrelic.api.agent.Trace;
@InstrumentedClass
public class MyClass {
public static void main(String[] args) {
while (true) {
MyClass.myMethod();
}
}
@InstrumentedMethod(dispatcher=true, instrumentationNames={"MyClass.java"}, instrumentationTypes={InstrumentationType.TraceAnnotation})
@Trace(dispatcher=true)
public static void myMethod() {
ExitTracer exitTracer = null;
try {
try {
exitTracer = AgentBridge.instrumentation.createTracer(null, 5, null, 30);
}
catch (Throwable throwable) {}
System.out.println("Hello, World");
if (exitTracer != null) {
exitTracer.finish(177, null);
}
return;
}
catch (Throwable throwable) {
if (exitTracer != null) {
throwable = throwable;
exitTracer.finish(throwable);
}
throw throwable;
}
}
}