-
Notifications
You must be signed in to change notification settings - Fork 75
[Feat] [SDK-399] Add java agent for network telemetry events #374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
buongarzoni
wants to merge
35
commits into
master
Choose a base branch
from
feat/SDK-399/add-java-agent-for-network-telemetry-events
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
5762538
feat(java-agent): add rollbar-java-agent module skeleton
buongarzoni 70573e5
feat(java-agent): instrument HttpURLConnection and java.net.http.Http…
buongarzoni 3ee326d
feat(java-agent): instrument Apache HttpClient 4.x and 5.x
buongarzoni e5226c1
test(java-agent): add integration tests for instrumented HTTP clients
buongarzoni 8ab880c
docs(java-agent): add README with installation and manual testing guide
buongarzoni ad0b2ac
chore(java-agent): upgrade wiremock to org.wiremock:wiremock:3.13.2
buongarzoni 64e7df9
refactor(java-agent): replace resetForTesting with injectable Provide…
buongarzoni 212ce90
fix(java-agent): resolve checkstyle violations in rollbar-java-agent
buongarzoni fe91c9a
fix(java-agent): resolve checkstyle violations in rollbar-java-agent
buongarzoni 866b20b
fix(rollbar-java-agent): declare explicit dependency on jar task in test
buongarzoni 6791575
feat(java-agent): instrument HttpClient.sendAsync() for async network…
buongarzoni ba9f0d8
fix(java-agent): fix Apache HC4 transformer to handle all execute() o…
buongarzoni 02fe650
fix(java-agent): fix Apache HC5 transformer to handle all execute() o…
buongarzoni ebf0c39
docs: update readme of rollbar-java-agent
buongarzoni 5259919
style(java-agent): fix checkstyle line length violations in HC4 and H…
buongarzoni d9237e4
fix(java-agent): record full URL in HC5 instrumentation
buongarzoni 9510ed8
fix(java-agent): decouple getTelemetryTracker() from INSTANCE identity
buongarzoni 439a3d2
refactor(java-agent): rename AgentTelemetryStore.init() to initForTes…
buongarzoni 9b106d0
fix(java-agent): disable plain jar to prevent overwriting shaded arti…
buongarzoni ab969f1
fix(java-agent): always install HC4/HC5 transformers regardless of cl…
buongarzoni efd4c20
feat(java-agent): capture HttpURLConnection requests that skip getRes…
buongarzoni b46aa7b
fix(java-agent): strip query and userinfo in UrlSanitizer fallback path
buongarzoni 7aadaaa
fix(java-agent): make shadowJar the sole artifact, prevent thin jar f…
buongarzoni 97c3ac3
fix(java-agent): preserve host in UrlSanitizer for underscore hostnam…
buongarzoni ac57d62
fix(java-agent): prevent getInputStream advice recursion on connectio…
buongarzoni af9cdee
fix: lint
buongarzoni 69fc32a
chore: update readme
buongarzoni cbab9d5
fix(java-agent): instrument Apache HC doExecute() to cover all execut…
buongarzoni ffd8ea2
fix(java-agent): bound scheme check in composeUrl to the authority po…
buongarzoni 336de42
fix(java-agent): use raw URI components when stripping userinfo
buongarzoni e5ff2dd
fix(java-agent): support current JDKs and surface instrumentation fai…
buongarzoni 6ff3783
fix(java-agent): shade only Byte Buddy, not the Rollbar SDK
buongarzoni 036f414
chore(java-agent): drop unused byte-buddy-agent dependency
buongarzoni 8808df7
docs(java-agent): narrow the zero code change claim to HTTP call sites
buongarzoni dd89925
fix(ci): exclude rollbar-java-agent on JDKs older than 17
buongarzoni File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,228 @@ | ||
| # Rollbar Java Agent | ||
|
|
||
| A Java instrumentation agent that automatically captures HTTP network errors (4xx and 5xx responses) as Rollbar telemetry events, with no changes at your HTTP call sites. | ||
|
|
||
| It works by attaching to the JVM at startup via `-javaagent:` and using ByteBuddy to intercept HTTP calls across all major clients. Your request code stays exactly as it is, and you add no HTTP-related library dependencies. Setup is a one-time wiring step in your Rollbar configuration — see [What "no code changes" means here](#what-no-code-changes-means-here). | ||
|
|
||
| ## Instrumented HTTP clients | ||
|
|
||
| | Client | Condition | | ||
| |--------|-----------| | ||
| | `java.net.HttpURLConnection` | Always (JDK built-in) | | ||
| | `java.net.http.HttpClient` — `send()` and `sendAsync()` | Java 11+ only | | ||
| | Apache HttpClient 4.x (`org.apache.http`) | If present on classpath | | ||
| | Apache HttpClient 5.x (`org.apache.hc.client5`) | If present on classpath | | ||
|
|
||
| Only 4xx and 5xx responses are recorded, along with requests that fail before a response arrives (connection refused, DNS failure, timeout). Successful requests (< 400) produce no telemetry. | ||
|
|
||
| **Apache HC4/HC5:** every `execute(...)` overload is covered — the request-only forms, the target-host forms (`execute(HttpHost, request)`), and the response-handler forms. The agent instruments the protected `doExecute(HttpHost, request, context)` method that all of them converge on, rather than any individual `execute()` overload, so no dispatch path is missed. Requests issued through a target-host overload carry only a path, so the agent rejoins the host from the `HttpHost` argument to record a complete URL. | ||
|
|
||
| ### HttpURLConnection entry points | ||
|
|
||
| `HttpURLConnection` is captured through three entry points, so a failed request is recorded regardless of how your code consumes the response: | ||
|
|
||
| | Entry point | Why it is covered | | ||
| |-------------|-------------------| | ||
| | `getResponseCode()` | The caller checks the status code explicitly. | | ||
| | `getInputStream()` | The caller reads the body directly and only ever sees the `IOException` that a 4xx/5xx throws. | | ||
| | `getErrorStream()` | The caller inspects the error stream after `connect()`, or after catching the `IOException` from `getInputStream()`. | | ||
|
|
||
| Exactly one event is recorded per connection, even when your code hits several of these entry points (for example `getInputStream()` throwing and then `getErrorStream()` being read) — the agent deduplicates on the connection instance. | ||
|
|
||
| ## Requirements | ||
|
|
||
| - Java 11 or higher **to run** the agent | ||
| - Java 17 or higher **to build** it from source — the shadow plugin that packages the fat JAR | ||
| requires a Java 17+ JVM, so on an older JDK the module is excluded from the build entirely and | ||
| `:rollbar-java-agent` tasks fail as unknown. The JAR it produces still targets Java 11. | ||
| - `rollbar-java` on the application classpath (for `Rollbar.init(...)`) | ||
|
|
||
| The agent bundles only ByteBuddy, under a relocated package name. It does **not** bundle the | ||
| Rollbar SDK: `rollbar-api` and `rollbar-java` are ordinary dependencies resolved from your | ||
| application's classpath, so the agent records telemetry against the same SDK classes your | ||
| application uses and never pins or shadows your chosen SDK version. | ||
|
|
||
| ## Installation | ||
|
|
||
| ### What "no code changes" means here | ||
|
|
||
| All four steps below are **required**. Steps 3 and 4 touch your application once, at setup: | ||
|
|
||
| - **What you never change:** your HTTP call sites. Every request through `HttpURLConnection`, | ||
| `java.net.http.HttpClient`, or Apache HC 4.x/5.x is instrumented as written — no wrappers, no | ||
| interceptors, no per-call bookkeeping, and nothing to remember when you add the next HTTP call. | ||
| - **What you change once:** the agent JAR goes on your application classpath (step 3), and your | ||
| `Rollbar.init(...)` passes `RollbarAgent.getTelemetryTracker()` to the config builder (step 4). | ||
|
|
||
| That wiring cannot be made automatic today. `ConfigBuilder.build()` installs its default | ||
| `RollbarTelemetryEventTracker` whenever `telemetryEventTracker(...)` was not called, and the SDK | ||
| exposes no global registry or `ServiceLoader` hook that an agent could claim instead — so the | ||
| tracker has to be handed to the builder by the application. Skipping step 4 is silent: the agent | ||
| still records events, but into a store nothing ever reads (see [Behavior](#behavior)). | ||
|
|
||
| ### 1. Build the agent JAR | ||
|
|
||
| ```bash | ||
| ./gradlew :rollbar-java-agent:shadowJar | ||
| ``` | ||
|
|
||
| The fat JAR (with ByteBuddy bundled and relocated) is written to: | ||
|
|
||
| ``` | ||
| rollbar-java-agent/build/libs/rollbar-java-agent-<version>.jar | ||
| ``` | ||
|
|
||
| This fat JAR is the module's only artifact — the thin `jar` task is disabled, and the shaded JAR is what Gradle consumers and the published Maven artifact resolve to. So the JAR you pass to `-javaagent:` and the JAR you put on the classpath (steps 2 and 3) are always the same file. | ||
|
|
||
| ### 2. Add the agent JVM flag | ||
|
|
||
| Add `-javaagent:` to your JVM startup arguments, pointing at the JAR built above: | ||
|
|
||
| ``` | ||
| -javaagent:/path/to/rollbar-java-agent-<version>.jar | ||
| ``` | ||
|
|
||
| **Gradle:** | ||
| ```kotlin | ||
| jvmArgs("-javaagent:/path/to/rollbar-java-agent-<version>.jar") | ||
| ``` | ||
|
|
||
| **Maven Surefire / Failsafe:** | ||
| ```xml | ||
| <argLine>-javaagent:/path/to/rollbar-java-agent-<version>.jar</argLine> | ||
| ``` | ||
|
|
||
| **Docker / environment variable:** | ||
| ```bash | ||
| JAVA_TOOL_OPTIONS="-javaagent:/path/to/rollbar-java-agent-<version>.jar" | ||
| ``` | ||
|
|
||
| ### 3. Also add the JAR to your classpath (required, for compile time) | ||
|
|
||
| At runtime the JVM already appends a `-javaagent:` JAR to the system class path, so this step is not | ||
| about making the agent load. It is about **compiling** step 4: your build needs the JAR as an | ||
| ordinary dependency to resolve the `RollbarAgent` symbol. | ||
|
|
||
| **Gradle:** | ||
| ```kotlin | ||
| dependencies { | ||
| implementation(files("/path/to/rollbar-java-agent-<version>.jar")) | ||
| } | ||
| ``` | ||
|
|
||
| **Maven:** | ||
| ```xml | ||
| <dependency> | ||
| <groupId>com.rollbar</groupId> | ||
| <artifactId>rollbar-java-agent</artifactId> | ||
| <version>${rollbar.version}</version> | ||
| </dependency> | ||
| ``` | ||
|
|
||
| ### 4. Wire into your Rollbar configuration (required) | ||
|
|
||
| ```java | ||
| import com.rollbar.agent.RollbarAgent; | ||
| import com.rollbar.notifier.Rollbar; | ||
|
|
||
| import static com.rollbar.notifier.config.ConfigBuilder.withAccessToken; | ||
|
|
||
| Rollbar rollbar = Rollbar.init( | ||
| withAccessToken("your-access-token") | ||
| .environment("production") | ||
| .telemetryEventTracker(RollbarAgent.getTelemetryTracker()) | ||
| .build() | ||
| ); | ||
| ``` | ||
|
|
||
| That's the last application change you make. From here on, every HTTP call — including ones you add later — automatically produces a telemetry event in the Rollbar error report for any 4xx or 5xx response, with no further code changes. | ||
|
|
||
| ## Behavior | ||
|
|
||
| | Scenario | Action | | ||
| |----------|--------| | ||
| | Response status `< 400` | No telemetry recorded | | ||
| | Response status `>= 400` | Records a network telemetry event with `Level.CRITICAL` | | ||
| | Connection failure / I/O error (connection refused, DNS failure, timeout) | Records a `Network error: <message>` telemetry event with `Level.CRITICAL` | | ||
| | The same request seen through several entry points | Deduplicated — one event per request | | ||
| | Installation step 4 not done | **Misconfiguration.** Events accumulate in the agent store (capacity 100) and are never sent — the agent is recording into a tracker your `Rollbar` instance does not read. Silent apart from the missing telemetry. | | ||
|
|
||
| The agent never throws into your application: every advice body swallows all errors, so a failure inside the instrumentation cannot break an HTTP call. | ||
|
|
||
| ## Security | ||
|
|
||
| URLs can carry sensitive data in query parameters or basic-auth credentials. The agent **strips userinfo, query parameters, and the URL fragment** before recording. | ||
|
|
||
| For example, a request to: | ||
| ``` | ||
| https://user:secret@api.example.com/charge?token=sk_live_abc#section | ||
| ``` | ||
| is recorded as: | ||
| ``` | ||
| https://api.example.com/charge | ||
| ``` | ||
|
|
||
| ## Internal API | ||
|
|
||
| Two methods exist for tests only. Do not call them in production code — use `RollbarAgent.getTelemetryTracker()` as shown above. | ||
|
|
||
| - `AgentTelemetryStore.initForTesting(Provider<Long> timestampProvider)` — replaces the internal tracker with one backed by the given timestamp provider, so tests can assert on event timestamps. | ||
| - `NetworkEventBridge.resetRecordedForTesting()` — clears the deduplication state, so events from a previous test do not suppress recording in the next one. | ||
|
|
||
| ## Testing | ||
|
|
||
| ### Automated tests | ||
|
|
||
| ```bash | ||
| ./gradlew :rollbar-java-agent:test | ||
| ``` | ||
|
|
||
| This runs the full test suite (WireMock-backed integration tests for each instrumented client). | ||
|
|
||
| ### Manual smoke test | ||
|
|
||
| 1. Build the agent JAR: | ||
| ```bash | ||
| ./gradlew :rollbar-java-agent:shadowJar | ||
| ``` | ||
|
|
||
| 2. Write a small program that triggers a 4xx or 5xx: | ||
| ```java | ||
| import com.rollbar.agent.RollbarAgent; | ||
| import com.rollbar.notifier.Rollbar; | ||
|
|
||
| import java.net.HttpURLConnection; | ||
| import java.net.URL; | ||
|
|
||
| import static com.rollbar.notifier.config.ConfigBuilder.withAccessToken; | ||
|
|
||
| public class SmokeTest { | ||
| public static void main(String[] args) throws Exception { | ||
| Rollbar rollbar = Rollbar.init( | ||
| withAccessToken("your-access-token") | ||
| .environment("test") | ||
| .telemetryEventTracker(RollbarAgent.getTelemetryTracker()) | ||
| .build() | ||
| ); | ||
|
|
||
| // Trigger a 404 — captured as a telemetry event on the next error report | ||
| HttpURLConnection conn = (HttpURLConnection) new URL("https://httpstat.us/404").openConnection(); | ||
| int code = conn.getResponseCode(); | ||
| conn.disconnect(); | ||
|
|
||
| System.out.println("Response: " + code); | ||
|
|
||
| // Send an error to Rollbar — the 404 telemetry event will appear alongside it | ||
| rollbar.error(new RuntimeException("smoke test error")); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| 3. Run with the agent: | ||
| ```bash | ||
| java -javaagent:rollbar-java-agent/build/libs/rollbar-java-agent-<version>.jar \ | ||
| -cp "rollbar-java-agent/build/libs/rollbar-java-agent-<version>.jar:your-app.jar" \ | ||
| SmokeTest | ||
| ``` | ||
|
|
||
| 4. Check your Rollbar dashboard — the error report for "smoke test error" should show a **Network** telemetry event for the 404 in the telemetry timeline. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| plugins { | ||
| `java-library` | ||
| // Successor to the abandoned com.github.johnrengelman.shadow. Required at 9.x: Byte Buddy 1.18 | ||
| // ships Java 24 class files under META-INF/versions/24 (its bridge to the JDK's own class file | ||
| // API), which older shadow releases cannot read. 9.5+ needs Gradle 9, so 9.4.3 is the ceiling | ||
| // until this build's Gradle is upgraded. | ||
| id("com.gradleup.shadow") version "9.4.3" | ||
| } | ||
|
|
||
| // Dependencies that get relocated into the fat jar, kept apart from the ones that must stay | ||
| // external. shadowJar merges runtimeClasspath by default, which would embed the Rollbar SDK and | ||
| // its transitive dependencies (SLF4J) unrelocated: the agent sits alongside the application's own | ||
| // Rollbar SDK, so duplicate com.rollbar.* classes can pin an older SDK version or — where the | ||
| // agent and the application resolve them from different classloaders — split class identity, so | ||
| // the TelemetryEvent the agent records is not the TelemetryEvent the SDK expects. | ||
| val shaded: Configuration by configurations.creating | ||
|
|
||
| // compileOnly: these are inside the jar, so they must not also be published as runtime | ||
| // dependencies of the agent. | ||
| configurations.compileOnly.configure { extendsFrom(shaded) } | ||
|
|
||
| dependencies { | ||
| // Byte Buddy must be able to parse the class files of the JDK it runs on: the agent | ||
| // instruments JDK classes, which always carry the running JDK's class file version. A version | ||
| // older than the runtime fails to transform them (see the compatibility table at | ||
| // https://github.com/raphw/byte-buddy#java-version-compatibility), so keep this current. | ||
| // byte-buddy alone: AgentBuilder ships in the core artifact. byte-buddy-agent supplies | ||
| // ByteBuddyAgent/VirtualMachine for attaching to a *running* JVM, which is the attaching | ||
| // process's job, not this agent's — premain/agentmain receive their Instrumentation from the | ||
| // JVM directly. | ||
| shaded("net.bytebuddy:byte-buddy:1.18.11") | ||
| api(project(":rollbar-api")) | ||
| implementation(project(":rollbar-java")) | ||
| compileOnly("org.apache.httpcomponents:httpclient:4.5.14") | ||
| compileOnly("org.apache.httpcomponents.client5:httpclient5:5.3.1") | ||
|
|
||
| testImplementation(platform("org.junit:junit-bom:5.14.3")) | ||
| testImplementation("org.junit.jupiter:junit-jupiter") | ||
| testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||
| testImplementation("org.mockito:mockito-core:5.11.0") | ||
| testImplementation("org.wiremock:wiremock:3.13.2") | ||
| testImplementation("org.apache.httpcomponents:httpclient:4.5.14") | ||
| testImplementation("org.apache.httpcomponents.client5:httpclient5:5.3.1") | ||
| } | ||
|
|
||
| tasks.jar { | ||
| enabled = false | ||
| } | ||
|
|
||
| // java-library wires tasks.jar into apiElements/runtimeElements; replace it with shadowJar so | ||
| // Gradle's variant system and vanniktech publishing both see the fat jar as the primary artifact. | ||
| listOf(configurations.apiElements, configurations.runtimeElements).forEach { cfg -> | ||
| cfg.configure { | ||
| outgoing.artifacts.clear() | ||
| outgoing.artifact(tasks.shadowJar) | ||
| } | ||
| } | ||
|
|
||
| tasks.shadowJar { | ||
| archiveClassifier.set("") | ||
| // Embed only the `shaded` configuration, not the default runtimeClasspath. Everything else — | ||
| // rollbar-api, rollbar-java, SLF4J — stays an ordinary external dependency resolved from the | ||
| // application's own classpath. | ||
| configurations.set(listOf(shaded)) | ||
| manifest { | ||
| attributes( | ||
| "Premain-Class" to "com.rollbar.agent.RollbarAgent", | ||
| "Agent-Class" to "com.rollbar.agent.RollbarAgent", | ||
| "Can-Redefine-Classes" to "true", | ||
| "Can-Retransform-Classes" to "true" | ||
| ) | ||
| } | ||
| relocate("net.bytebuddy", "com.rollbar.agent.shaded.bytebuddy") | ||
| mergeServiceFiles() | ||
| } | ||
|
|
||
| // Override root's Java 8 compatibility — this agent targets Java 11+ to support | ||
| // java.net.http.HttpClient instrumentation. | ||
| tasks.withType<JavaCompile>().configureEach { | ||
| options.release.set(11) | ||
| } | ||
|
|
||
| tasks.test { | ||
| useJUnitPlatform() | ||
| val agentJar = tasks.shadowJar.get().archiveFile.get().asFile | ||
| // Load as Java agent (instruments HTTP classes on startup) | ||
| jvmArgs("-javaagent:$agentJar") | ||
| // Also put on test classpath — the TCCL reflection bridge finds agent classes via the | ||
| // system classloader; mirrors production use where rollbar-java-agent is a Gradle/Maven dep | ||
| classpath += files(agentJar) | ||
| dependsOn(tasks.shadowJar) | ||
| } | ||
28 changes: 28 additions & 0 deletions
28
rollbar-java-agent/src/main/java/com/rollbar/agent/AgentTelemetryStore.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.rollbar.agent; | ||
|
|
||
| import com.rollbar.api.payload.data.TelemetryEvent; | ||
| import com.rollbar.notifier.provider.Provider; | ||
| import com.rollbar.notifier.telemetry.RollbarTelemetryEventTracker; | ||
| import com.rollbar.notifier.telemetry.TelemetryEventTracker; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public final class AgentTelemetryStore { | ||
|
|
||
| private static volatile TelemetryEventTracker INSTANCE = | ||
| new RollbarTelemetryEventTracker(System::currentTimeMillis, 100); | ||
|
|
||
| private AgentTelemetryStore() {} | ||
|
|
||
| public static TelemetryEventTracker getInstance() { | ||
| return INSTANCE; | ||
| } | ||
|
|
||
| public static List<TelemetryEvent> getAll() { | ||
| return INSTANCE.getAll(); | ||
| } | ||
|
|
||
| public static void initForTesting(Provider<Long> timestampProvider) { | ||
| INSTANCE = new RollbarTelemetryEventTracker(timestampProvider, 100); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Keep Rollbar SDK classes out of the shaded agent
shadowJarmergesruntimeClasspathby default, so thisimplementationdependency and therollbar-apiapidependency are embedded unrelocated in the fat JAR (along with transitive dependencies such as SLF4J); only Byte Buddy is relocated below. The README also puts the agent alongside the application's Rollbar SDK, creating duplicatecom.rollbar.*classes that can pin/override another SDK version or split class identity in containers. Please shade only agent-private dependencies (for example via a dedicated Byte Buddy configuration) and leave the Rollbar API/SDK as ordinary external dependencies.Shadow's default dependency behavior: https://gradleup.com/shadow/configuration/dependencies/