|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This is the New Relic Java Agent, a bytecode instrumentation agent that monitors Java applications at runtime. The agent attaches via the `-javaagent` JVM flag, uses ASM (version 9.9.1) for bytecode manipulation, and employs a "weaver" pattern to insert monitoring code into target classes as they are loaded by the JVM classloader. |
| 8 | + |
| 9 | +The agent captures transactions, traces, metrics, errors, and logs from 440+ supported frameworks and libraries, sending telemetry data to the New Relic platform. |
| 10 | + |
| 11 | +The product optimizes for: |
| 12 | +- safety and stability above all else (should never crash the JVM being monitored) |
| 13 | +- minimal impact on CPU and memory resources of the JVM being monitored |
| 14 | +- backwards compatibility targeting the Java 8 runtime |
| 15 | +- collection of application profiling data that is useful and accurate |
| 16 | +- clarity over cleverness |
| 17 | +- accessible object-oriented design |
| 18 | + |
| 19 | +Avoid over-engineering. If a simpler solution exists, use it. |
| 20 | + |
| 21 | +## Critical Constraints |
| 22 | + |
| 23 | +These rules are non-negotiable: |
| 24 | + |
| 25 | +- **Java 8 language level** for all production code (no newer language features) |
| 26 | +- **Do NOT use lambda expressions in weaver instrumentation modules** — the weaver's bytecode rewriting cannot reliably transform lambdas due to `invokedynamic` bootstrap methods and classloader isolation; use anonymous classes instead |
| 27 | +- **Do NOT change public APIs** without explicit instructions |
| 28 | +- **Do NOT change weaver code** without explicit instructions |
| 29 | +- **Preserve backward compatibility** for all shared components |
| 30 | +- **Flag major architectural changes** before implementing — don't just do it |
| 31 | +- **Instrumentation modules must be self-contained** — dependencies must be shaded |
| 32 | +- **New dependencies require shading and licensing** — only add if they provide significant value and always ask for permission first |
| 33 | + |
| 34 | +## Tech Stack |
| 35 | + |
| 36 | +- Gradle for build and dependency management |
| 37 | +- JUnit + Mockito for tests |
| 38 | +- ASM 9.9.1 for bytecode manipulation |
| 39 | +- Caffeine for high-performance caching |
| 40 | +- Apache `HttpClient` for data transport to the New Relic backend |
| 41 | +- Shadow Gradle plugin for relocating packages of project dependencies |
| 42 | +- Spotbugs for static code analysis |
| 43 | +- Jacoco for code coverage |
| 44 | + |
| 45 | +## Key File Paths |
| 46 | + |
| 47 | +| Entry Point | Path | |
| 48 | +|-----------------------|-----------------------------------------------------------------------------------| |
| 49 | +| Agent bootstrap | `newrelic-agent/src/main/java/com/newrelic/bootstrap/BootstrapAgent.java` | |
| 50 | +| Configuration | `newrelic-agent/src/main/java/com/newrelic/agent/config/AgentConfigImpl.java` | |
| 51 | +| Service registry | `newrelic-agent/src/main/java/com/newrelic/agent/service/ServiceManagerImpl.java` | |
| 52 | +| Weave engine | `newrelic-weaver/src/main/java/com/newrelic/weave/ClassWeave.java` | |
| 53 | +| Weave manifest cache | `gradle/script/cache_weave_attributes.gradle.kts` | |
| 54 | +| Code style definition | `dev-tools/code-style/java-agent-code-style.xml` | |
| 55 | + |
| 56 | +## Architecture |
| 57 | + |
| 58 | +The agent uses Java's `Instrumentation` API (JSR 163) with two entry points in `BootstrapAgent`: |
| 59 | + |
| 60 | +``` |
| 61 | +premain(String agentArgs, Instrumentation inst) ← normal startup via -javaagent |
| 62 | +agentmain(String agentArgs, Instrumentation inst) ← dynamic attach |
| 63 | +``` |
| 64 | + |
| 65 | +### Core Modules |
| 66 | + |
| 67 | +| Module | Purpose | |
| 68 | +|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| |
| 69 | +| `newrelic-agent` | Main agent implementation; ServiceManager, configuration, harvest cycle, data transport. Built as a shadow JAR with all dependencies relocated. | |
| 70 | +| `newrelic-weaver` | Bytecode weaving engine using ASM. Matches weave classes to target classes and applies transformations at class-load time. | |
| 71 | +| `newrelic-weaver-api` | Annotations for authoring weave classes (`@Weave`, `@WeaveAllImplementations`, `@NewField`, `@WeaveWithAnnotation`). | |
| 72 | +| `newrelic-api` | Public API for custom instrumentation (`@Trace`, `NewRelic.getAgent()`, custom events/metrics). Ships with no-op implementations. | |
| 73 | +| `agent-bridge` | Runtime bridge between instrumentation modules and agent core. Provides `AgentBridge` static facade with volatile references swapped in when agent loads. | |
| 74 | +| `agent-bridge-datastore` | Datastore-specific bridge interfaces (connection URL parsing, vendor detection, instance metrics). | |
| 75 | +| `agent-interfaces` | Internal agent interfaces shared across modules. | |
| 76 | +| `agent-model` | Shared data models (metric names, error data, custom insight events). | |
| 77 | +| `infinite-tracing` | Infinite Tracing via gRPC streaming to trace observer. | |
| 78 | + |
| 79 | +### Instrumentation Modules (`instrumentation/`) |
| 80 | + |
| 81 | +443+ modules, each targeting a specific framework/version (e.g., `spring-webmvc-6.0.0`, `kafka-clients-3.6.0`). Each module contains: |
| 82 | +- **Weave classes** (`@Weave`) — Code injected into target classes |
| 83 | +- **Utility classes** — Helpers available to woven code |
| 84 | +- **SkipIfPresent classes** — Conditions for when the module should not apply |
| 85 | + |
| 86 | +Additional modules: Scala support (`newrelic-scala-api`, `newrelic-scala3-api`, Cats/ZIO/Monix integrations), build infrastructure (`buildSrc`, `instrumentation-build`), and test utilities (`functional_test`, `instrumentation-test`). |
| 87 | + |
| 88 | +### Key Architectural Patterns |
| 89 | + |
| 90 | +- **Classloader isolation** — Agent code on bootstrap and `JVMAgentClassLoader` classloaders, instrumentation in separate classloaders. `agent-bridge` classes are visible at runtime to instrumentation but follow a different compile-time dependency path. |
| 91 | +- **Manifest-based weave attributes** — Cached in JAR manifests to avoid scanning every class (see `cache_weave_attributes.gradle.kts`) |
| 92 | +- **Service lifecycle** — `ServiceManager`/`ServiceFactory` manage all agent services |
| 93 | +- **Configuration-driven** — Extensive YAML-based configuration for selective features |
| 94 | + |
| 95 | +## Coding Conventions |
| 96 | + |
| 97 | +- Format code using `./gradlew googleJavaFormat` (verify with `./gradlew verifyGoogleJavaFormat`) |
| 98 | +- Use descriptive names; avoid acronyms where possible |
| 99 | +- Comments only when intent is non-obvious |
| 100 | +- No dead code or commented-out blocks in commits |
| 101 | +- Error handling must be explicit — no silent catches |
| 102 | +- Error messages must include what happened AND, if possible, what to do next |
| 103 | +- **Wide version compatibility** — instrumentation code may look overcomplicated for compatibility reasons; read all comments before changing |
| 104 | + |
| 105 | +## Common Pitfalls |
| 106 | + |
| 107 | +- **Don't `clean` unnecessarily** — The shadow JAR build for `newrelic-agent` is slow. Only use `clean` when switching branches or fixing stale class issues. For iterative development, `./gradlew :module:test` without `clean` is much faster. |
| 108 | +- **Instrumentation tests need the agent JAR** — Functional tests require `newrelic-agent/build/newrelicJar/newrelic.jar` to exist. Build it first with `./gradlew jar` if missing. |
| 109 | +- **Module-scoped tasks** — Always scope Gradle tasks to the module you're working on (e.g., `./gradlew :newrelic-agent:test`, `./gradlew :instrumentation:spring-webmvc-6.0.0:test`) rather than running repo-wide. |
| 110 | +- **Java version for newer modules** — If an instrumentation module targets Java 11+/17+, you must pass `-Ptest11` or `-Ptest17` to gradle or the instrumentation won't apply and tests will fail even though they appear to run. |
| 111 | +- **Docker for Testcontainers** — Some instrumentation tests require Docker. If tests fail with connection errors, check Docker is running. |
| 112 | + |
| 113 | +## Testing and Quality |
| 114 | + |
| 115 | +Before considering any task complete: |
| 116 | +1. Run `./gradlew :module:verifyGoogleJavaFormat` on modified modules |
| 117 | +2. Run `./gradlew :module:googleJavaFormat` to fix formatting if needed |
| 118 | +3. Run relevant tests for modified logic |
| 119 | + |
| 120 | +Testing rules: |
| 121 | +- Unit tests for all reusable logic |
| 122 | +- Empty state, null state, and error state must all be handled |
| 123 | + |
| 124 | +## File Placement |
| 125 | + |
| 126 | +- Feature-specific logic that won't be reused → co-locate with the feature |
| 127 | +- Do not create a new abstraction for a one-off use case |
| 128 | +- Prefer editing an existing component over creating a near-duplicate |
| 129 | + |
| 130 | +## Commands |
| 131 | + |
| 132 | +### Build |
| 133 | + |
| 134 | +JDK 8 is required (`JAVA_HOME` must point to JDK 8). Configure JDK paths in `~/.gradle/gradle.properties`: |
| 135 | +``` |
| 136 | +jdk8=/path/to/jdk8 |
| 137 | +jdk17=/path/to/jdk17 |
| 138 | +``` |
| 139 | + |
| 140 | +Verify environment: `./gradlew --version` (should show JVM 1.8.x) |
| 141 | + |
| 142 | +- Build agent jar only: `./gradlew clean jar --parallel` |
| 143 | +- Build agent with all checks: `./gradlew clean build --parallel` |
| 144 | + |
| 145 | +**Artifacts:** |
| 146 | +- Agent: `newrelic-agent/build/newrelicJar/newrelic.jar` |
| 147 | +- API: `newrelic-api/build/libs/newrelic-api-*.jar` |
| 148 | + |
| 149 | +### Code Analysis |
| 150 | + |
| 151 | +- Run Spotbugs: `./gradlew :module:spotbugsMain` |
| 152 | + |
| 153 | +### Tests |
| 154 | + |
| 155 | +#### Unit Tests |
| 156 | + |
| 157 | +- All unit tests: `./gradlew -PnoInstrumentation test --continue --parallel` |
| 158 | +- Single module: `./gradlew -PnoInstrumentation :newrelic-weaver:test --parallel` |
| 159 | +- Single test: `./gradlew -PnoInstrumentation :newrelic-weaver:test --tests "com.newrelic.weave.LineNumberWeaveTest.testRemoveWeaveLineNumbers" --parallel` |
| 160 | +- With specific JDK: add `-Ptest17` |
| 161 | +- Include Scala: add `-PincludeScala` |
| 162 | + |
| 163 | +#### Functional Tests |
| 164 | + |
| 165 | +Require the agent JAR to be built first (`./gradlew jar`). |
| 166 | + |
| 167 | +- All: `./gradlew functional_test:test --continue --parallel` |
| 168 | +- Single: `./gradlew functional_test:test --tests test.newrelic.test.agent.AgentTest --parallel` |
| 169 | + |
| 170 | +#### Instrumentation Tests |
| 171 | + |
| 172 | +- Single module: `./gradlew :instrumentation:akka-http-core-10.0.11:test --parallel` |
| 173 | +- Single test: `./gradlew :instrumentation:vertx-web-3.2.0:test --tests com.nr.vertx.instrumentation.RoutingTest --parallel` |
| 174 | +- Scala module: `./gradlew -PincludeScala :instrumentation:sttp-2.13_2.2.3:test --parallel` |
| 175 | +- Java 17+ module: `./gradlew -Ptest17 :instrumentation:module-name:test --parallel` |
0 commit comments