Skip to content

feat(java): observability contrib middleware#346

Merged
pratyush618 merged 2 commits into
masterfrom
feat/java-contrib-observability
Jul 1, 2026
Merged

feat(java): observability contrib middleware#346
pratyush618 merged 2 commits into
masterfrom
feat/java-contrib-observability

Conversation

@pratyush618

@pratyush618 pratyush618 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

What

Adds two optional observability middlewares under org.byteveda.taskito.contrib. Both peer deps are compileOnly — out of the default fat JAR and GraalVM path; consumers opt in.

  • TaskitoObservation — wraps each task in a Micrometer Observation: one instrumentation yielding both a metric (timer) and a trace span. Backend-agnostic — plug in OpenTelemetry (or any) by configuring the ObservationRegistry the app passes in.
    • Observation created in before, scope opened for the handler, stopped in after/onError (records the error). State carried on the per-task TaskContext.attributes().
    • Optional name + taskFilter to scope which tasks are instrumented.
  • SentryMiddleware — thin error-capture: reports a task failure to Sentry in onError with task context. Not a parallel metrics/tracing stack — just error capture.

Test

ObservationMiddlewareTest — uses micrometer-observation-test to assert an observation starts/stops per task and records errors. SentryMiddlewareTest — asserts a failure is captured.

./gradlew build green (JDK 17 build leg + 21/25 test legs).

Summary by CodeRabbit

  • New Features

    • Added optional integrations for task observability and error reporting in Java.
    • Introduced support for tracking task execution with metrics/observations.
    • Added support for sending task failures and dead-letter events to Sentry.
  • Documentation

    • Added package-level documentation for the new optional integration components.
  • Tests

    • Added coverage for successful tracking, error handling, filtering, and safe behavior when error reporting isn’t initialized.

TaskitoObservation wraps each task in a Micrometer Observation (metrics +
trace span, OTel-pluggable); SentryMiddleware reports failures and
dead-letters. Both deps are compileOnly so consumers opt in.
Observation success/error/filter via TestObservationRegistry; Sentry
no-op safety + filter.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an optional org.byteveda.taskito.contrib package with two new Middleware implementations: TaskitoObservation for Micrometer-based observability and SentryMiddleware for Sentry error/dead-letter reporting. It adds corresponding compileOnly/testImplementation dependencies, package-level Javadoc, and unit tests.

Changes

Observability middleware integrations

Layer / File(s) Summary
Optional dependency declarations
sdks/java/build.gradle.kts
Adds micrometer-observation, micrometer-observation-test, and sentry as compileOnly/testImplementation dependencies.
Micrometer observation middleware
sdks/java/src/main/java/org/byteveda/taskito/contrib/TaskitoObservation.java, sdks/java/src/test/java/org/byteveda/taskito/contrib/ObservationMiddlewareTest.java
TaskitoObservation starts/stops a Micrometer Observation around task execution via before/after/onError, tags it, supports task-name filtering, and marks errors; tests verify success, error, and filtered scenarios.
Sentry error reporting middleware
sdks/java/src/main/java/org/byteveda/taskito/contrib/SentryMiddleware.java, sdks/java/src/test/java/org/byteveda/taskito/contrib/SentryMiddlewareTest.java
SentryMiddleware captures exceptions in onError and fatal messages in onDeadLetter with task/job tags, respecting an optional task filter; tests confirm safe no-op behavior and filtering.
Package documentation
sdks/java/src/main/java/org/byteveda/taskito/contrib/package-info.java
Adds package-level Javadoc describing the optional, compileOnly nature of the contrib middleware integrations.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TaskExecutor
  participant TaskitoObservation
  participant ObservationRegistry
  participant SentryMiddleware
  participant Sentry

  TaskExecutor->>TaskitoObservation: before(context)
  TaskitoObservation->>ObservationRegistry: create & start Observation
  TaskExecutor->>TaskExecutor: run task
  alt task succeeds
    TaskExecutor->>TaskitoObservation: after(context, result)
    TaskitoObservation->>ObservationRegistry: stop Observation
  else task fails
    TaskExecutor->>TaskitoObservation: onError(context, error)
    TaskitoObservation->>ObservationRegistry: mark error & stop
    TaskExecutor->>SentryMiddleware: onError(context, error)
    SentryMiddleware->>Sentry: captureException (tagged)
  end
Loading

Related Issues: Not specified in the provided diff.

Related PRs: Not specified in the provided diff.

Suggested labels: enhancement, java-sdk, observability

Suggested reviewers: Not specified in the provided diff.

🐰 A rabbit hops through Sentry's glow,
Observations start, then stop and flow,
Tags for task and job neatly tied,
Optional jars, compileOnly beside,
New middleware joins the contrib row.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: new observability middleware added under Java contrib.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/java-contrib-observability

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
sdks/java/src/main/java/org/byteveda/taskito/contrib/SentryMiddleware.java (2)

24-26: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard against null taskFilter.

If a caller passes null, taskFilter.test(...) in onError/onDeadLetter will NPE on every task, silently breaking failure reporting for the whole application.

🛡️ Proposed fix
+import java.util.Objects;
+
     public SentryMiddleware(Predicate<String> taskFilter) {
-        this.taskFilter = taskFilter;
+        this.taskFilter = Objects.requireNonNull(taskFilter, "taskFilter");
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/contrib/SentryMiddleware.java`
around lines 24 - 26, The SentryMiddleware constructor currently accepts a
nullable taskFilter, which later causes taskFilter.test(...) in
onError/onDeadLetter to NPE and break failure reporting. Update SentryMiddleware
to reject null at construction time or fall back to a safe default Predicate in
the constructor, and keep the null-safety consistent wherever taskFilter is
used.

41-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

onDeadLetter drops richer failure context.

Only the message string is forwarded to Sentry; retry count/willRetry from OutcomeEvent aren't attached, limiting debuggability of dead-lettered events in Sentry compared to what's available.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/contrib/SentryMiddleware.java`
around lines 41 - 51, The onDeadLetter handler in SentryMiddleware only sends a
message string to Sentry and drops useful OutcomeEvent context. Update the
Sentry.withScope block in onDeadLetter to attach retry-related fields such as
event.retryCount and event.willRetry as tags or extras alongside the existing
task/job tags, and keep the captured message focused on the dead-letter failure
so Sentry shows the full event context for debugging.
sdks/java/src/test/java/org/byteveda/taskito/contrib/SentryMiddlewareTest.java (1)

20-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add symmetric filter test for onDeadLetter.

Only onError's filter-skip is tested; onDeadLetter uses the same filter but isn't covered.

✅ Suggested addition
+    `@Test`
+    void filterSkipsUnreportedDeadLetterTasks() {
+        SentryMiddleware middleware = new SentryMiddleware(task -> task.equals("included"));
+        assertDoesNotThrow(() -> middleware.onDeadLetter(
+                new OutcomeEvent(EventName.DEAD, "j", "excluded", "err", 0, false)));
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sdks/java/src/test/java/org/byteveda/taskito/contrib/SentryMiddlewareTest.java`
around lines 20 - 24, Add a symmetric filter-skip test for
SentryMiddleware.onDeadLetter, since
SentryMiddlewareTest.filterSkipsUnreportedTasks only covers onError. Reuse the
same predicate setup used in filterSkipsUnreportedTasks and verify that calling
onDeadLetter with an excluded task does not throw, mirroring the existing
onError assertion so both code paths using the filter are covered.
sdks/java/src/main/java/org/byteveda/taskito/contrib/TaskitoObservation.java (1)

59-72: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Wrap stop() body in try/finally to avoid a leaked observation.

If scope.close() throws, observation.stop() is skipped, leaving the observation's timer running / span unclosed indefinitely.

🔧 Proposed fix
     private void stop(TaskContext context, Throwable error) {
         Observation.Scope scope = (Observation.Scope) context.attributes().remove(SCOPE);
-        if (scope != null) {
-            scope.close();
-        }
         Observation observation = (Observation) context.attributes().remove(OBSERVATION);
-        if (observation == null) {
-            return;
-        }
-        if (error != null) {
-            observation.error(error);
+        try {
+            if (scope != null) {
+                scope.close();
+            }
+        } finally {
+            if (observation != null) {
+                if (error != null) {
+                    observation.error(error);
+                }
+                observation.stop();
+            }
         }
-        observation.stop();
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/java/src/main/java/org/byteveda/taskito/contrib/TaskitoObservation.java`
around lines 59 - 72, Wrap the body of TaskitoObservation.stop in a try/finally
so the observation is always stopped even if Observation.Scope.close throws.
Keep the current lookup/removal of SCOPE and OBSERVATION in stop(TaskContext,
Throwable), but move observation.error(error) and observation.stop() into the
finally path, guarding observation being null, so a failure while closing the
scope cannot prevent cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@sdks/java/src/main/java/org/byteveda/taskito/contrib/SentryMiddleware.java`:
- Around line 24-26: The SentryMiddleware constructor currently accepts a
nullable taskFilter, which later causes taskFilter.test(...) in
onError/onDeadLetter to NPE and break failure reporting. Update SentryMiddleware
to reject null at construction time or fall back to a safe default Predicate in
the constructor, and keep the null-safety consistent wherever taskFilter is
used.
- Around line 41-51: The onDeadLetter handler in SentryMiddleware only sends a
message string to Sentry and drops useful OutcomeEvent context. Update the
Sentry.withScope block in onDeadLetter to attach retry-related fields such as
event.retryCount and event.willRetry as tags or extras alongside the existing
task/job tags, and keep the captured message focused on the dead-letter failure
so Sentry shows the full event context for debugging.

In
`@sdks/java/src/main/java/org/byteveda/taskito/contrib/TaskitoObservation.java`:
- Around line 59-72: Wrap the body of TaskitoObservation.stop in a try/finally
so the observation is always stopped even if Observation.Scope.close throws.
Keep the current lookup/removal of SCOPE and OBSERVATION in stop(TaskContext,
Throwable), but move observation.error(error) and observation.stop() into the
finally path, guarding observation being null, so a failure while closing the
scope cannot prevent cleanup.

In
`@sdks/java/src/test/java/org/byteveda/taskito/contrib/SentryMiddlewareTest.java`:
- Around line 20-24: Add a symmetric filter-skip test for
SentryMiddleware.onDeadLetter, since
SentryMiddlewareTest.filterSkipsUnreportedTasks only covers onError. Reuse the
same predicate setup used in filterSkipsUnreportedTasks and verify that calling
onDeadLetter with an excluded task does not throw, mirroring the existing
onError assertion so both code paths using the filter are covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f45ec2da-8791-44f6-9248-5255d8ea6425

📥 Commits

Reviewing files that changed from the base of the PR and between 58e024e and faddb90.

📒 Files selected for processing (6)
  • sdks/java/build.gradle.kts
  • sdks/java/src/main/java/org/byteveda/taskito/contrib/SentryMiddleware.java
  • sdks/java/src/main/java/org/byteveda/taskito/contrib/TaskitoObservation.java
  • sdks/java/src/main/java/org/byteveda/taskito/contrib/package-info.java
  • sdks/java/src/test/java/org/byteveda/taskito/contrib/ObservationMiddlewareTest.java
  • sdks/java/src/test/java/org/byteveda/taskito/contrib/SentryMiddlewareTest.java

@pratyush618 pratyush618 self-assigned this Jul 1, 2026
@pratyush618 pratyush618 merged commit 3b16a0b into master Jul 1, 2026
19 checks passed
@pratyush618 pratyush618 deleted the feat/java-contrib-observability branch July 1, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant