Choosing Between New Relic Java Agent Auto‑Instrumentation and Manual Custom Instrumentation for Java Microservices
Guide to decide between New Relic Java agent auto‑instrumentation and manual custom instrumentation for Java microservices, with constraints, a comparison table, trade‑offs, implementation steps, validation, limitations, and rollback.
You need deep visibility into a Java‑based microservice while keeping code changes minimal. The decision is whether to rely on the New Relic Java agent’s auto‑instrumentation or to add manual custom instrumentation via the New Relic API. Constraints that shape this choice are:
Java 8+ compatibility (the agent jar must run on the target JDK).
Acceptable overhead – aim for less than 5% additional CPU usage.
Data retention policies – avoid blowing up ingestion limits with high‑cardinality custom attributes.
Operational simplicity – prefer zero‑code changes if they satisfy observability goals.
Options Comparison
Option
What it provides
Code changes
Typical overhead
Auto‑instrumentation (agent)
Catches HTTP requests, JDBC calls, Redis, messaging, and common frameworks out of the box.
None – just add the agent jar to the JVM start‑up.
~2‑4% CPU on average workloads; adds ~5 MB resident memory.
Manual custom instrumentation
Lets you record business‑logic spans, custom attributes, and custom events via @Trace annotations or the New Relic Java SDK.
Required – add annotations or API calls in the source.
Negligible if used sparingly; each traced method adds a few microseconds.
Trade‑offs
Auto‑instrumentation gives you a broad view almost instantly, which is valuable when you need to understand latency across external calls without touching the codebase. However, it may miss domain‑specific events (e.g., a custom fraud‑check step) and the agent jar adds a small, constant footprint.
Manual instrumentation requires developer effort but yields precise, low‑overhead metrics that match your service’s SLOs. It also lets you keep cardinality low by attaching only the attributes you truly need, helping stay within ingestion limits. The downside is that every new metric you want to track requires a code change and a redeploy.
Implementation Steps (Auto‑Instrumentation)
Obtain the agent – download the version‑specific newrelic.jar from your New Relic account (match it to your JDK version).
Place the jar – copy it to a directory readable by the service runtime, e.g., /opt/newrelic/newrelic.jar.
Configure newrelic.yml – create or edit the file in the same directory:
Replace YOUR_LICENSE_KEY with the license key from your New Relic UI and YOUR_APP_NAME with the service identifier you want to see in APM.
Add the JVM argument – edit the service start‑up script or container entrypoint to include:
-javaagent:/opt/newrelic/newrelic.jar
Restart the service – ensure the process picks up the agent. No code rebuild is needed.
Validation Checklist
After restart, check the New Relic UI under APM > Services for your app_name. You should see a non‑zero throughput graph within 1‑2 minutes.
Open the Transactions page, filter by a recent request (e.g., /api/orders), and view a transaction trace. Verify that auto‑captured spans appear (e.g., HTTP, JDBC, Redis).
If you added manual instrumentation, invoke a code path that includes a @Trace annotated method or a call to NewRelic.getAgent().getTracedMethod().addCustomAttribute("key", "value"). In the transaction details, look under the Attributes section for the custom key/value.
Monitor the APM > Summary page for CPU usage of the Java process; confirm it stays below your 5% overhead target.
Limitations and How to Check Results
Agent‑JDK mismatch – using an agent built for a newer JDK than the runtime can cause ClassNotFoundException or UnsupportedClassVersionError. Verify compatibility by checking the agent’s release notes; the safest approach is to download the agent that matches your java -version output.
License‑key exposure – the newrelic.yml file contains the license key; if it is world‑readable, anyone could submit data to your New Relic account. Set file permissions to 600 (owner read/write only) and store the file outside of publicly accessible directories.
High‑cardinality custom attributes – adding many unique values (e.g., user IDs) can inflate ingestion and hit limits. Before deploying, estimate the unique value count; if it exceeds a few hundred, consider aggregating or using custom events instead.
Verification of overhead – use the host’s monitoring tools (e.g., top, pidstat) to compare CPU usage of the Java process before and after agent addition. Look for a sustained increase; if it exceeds 5%, review the agent version or disable certain auto‑instrumented modules via newrelic.yml (transaction_tracer.enabled: false for specific classes).
Rollback (if needed)
Because adding the agent changes the JVM start‑up configuration, rollback consists of:
Remove the -javaagent:/opt/newrelic/newrelic.jar flag from the start‑up script or container definition.
Delete or rename the newrelic.jar and newrelic.yml files (optional, to free disk space).
Restart the service. The JVM will start without the agent, and New Relic will stop receiving data from that instance.
After rollback, confirm in the New Relic UI that the service’s throughput drops to zero and no new transaction traces appear.