Skip to content

refactor(demo): use stock Spring Boot OTLP, drop JamJet @Configuration workaround#6

Merged
sunilp merged 1 commit intomainfrom
feat/spring-ai-stock-otlp
May 8, 2026
Merged

refactor(demo): use stock Spring Boot OTLP, drop JamJet @Configuration workaround#6
sunilp merged 1 commit intomainfrom
feat/spring-ai-stock-otlp

Conversation

@sunilp
Copy link
Copy Markdown
Member

@sunilp sunilp commented May 8, 2026

Summary

Replaces the 73-LOC JamjetCloudConfiguration workaround with stock Spring Boot OTLP tracing. Now that jamjet-cloud PR #16 ships OTLP/protobuf intake to api.jamjet.dev, the demo can use the standard Spring Boot tracing autoconfig — no JamJet SDK on the classpath, no @Configuration, no observation handler.

Why the workaround existed

jamjet-cloud-spring-boot-starter:0.2.0 had two problems that made it unsuitable for a Spring-AI-only demo:

  1. Its autoconfig hard-references LangChain4j classes, which would force langchain4j-core onto the classpath of every Spring AI app.
  2. JamjetObservationHandler.supportsContext was called by Micrometer with the context name still null, so the handler never selected itself for Spring AI observations.

The workaround sidestepped both by skipping the starter, configuring JamjetCloud directly, and registering a context-class-filtered observation handler manually. That's now ~all unnecessary.

What changed

File Change
pom.xml Remove dev.jamjet:jamjet-cloud-sdk + io.micrometer:micrometer-observation; add io.micrometer:micrometer-tracing-bridge-otel + io.opentelemetry:opentelemetry-exporter-otlp
src/main/resources/application.yml Drop jamjet.cloud.* block, add management.otlp.tracing.* block pointing at ${JAMJET_API_URL}/v1/otlp/v1/traces with Authorization: Bearer ${JAMJET_API_KEY}
src/main/java/.../cloud/JamjetCloudConfiguration.java Deleted (-73 LOC)
src/test/java/.../DemoIntegrationTest.java Override OTLP endpoint to localhost:1 + supply placeholder bearer in tests so Spring property resolver doesn't trip on unresolved ${JAMJET_API_KEY}
README.md + .env.example Reflect the OTLP-based wiring

Net diff: +42, −99.

How observability flows now

Spring AI 1.0 chat call
  → emits Micrometer Observation (gen_ai.client.operation, gen_ai.tool, …)
    → micrometer-tracing-bridge-otel converts to OTel span
      → opentelemetry-exporter-otlp ships OTLP/HTTP-protobuf
        → POST https://api.jamjet.dev/v1/otlp/v1/traces
          → JamJet maps gen_ai.* attrs to typed fields, server-side cost fallback

No JamJet-specific code in the demo — observability is pure application.yml.

Test plan

  • mvnw clean compile — clean build (release 21)
  • mvnw test -Dtest=PreflightCheckTest — 5/5 pass
  • mvnw spring-boot:run — app starts on :8080 in 1.3s, all autoconfig wires up cleanly (Tomcat, Engram, Spring AI, OTLP), PreflightCheck fails on Engram health check as expected without docker
  • CI: full DemoIntegrationTest runs in Docker-enabled runner
  • Post-merge: run the demo end-to-end with real OPENAI_API_KEY + JAMJET_API_KEY + Engram via docker, confirm trace appears at cloud.jamjet.dev/dashboard/graph tagged service.name=spring-ai-engram-cloud-demo

Followups

  • Once jamjet-cloud-spring-boot-starter ships a Spring-AI-friendly variant (no LangChain4j hard ref + fixed observation handler), this demo can swap back to the starter for an even shorter pom — though "stock OTLP" is genuinely simpler and has no JamJet-specific class on the classpath, so we may stay on this approach
  • The Kotlin Koog demo (examples/kotlin-koog-engram-cloud-demo) still uses a custom JamjetOtlpJsonSpanExporter (~100 LOC). With protobuf intake live, it can drop the custom marshaler in favor of the stock OTel exporter — separate PR

Summary by CodeRabbit

  • Chores

    • Simplified the demo setup by transitioning from a custom SDK to standard OpenTelemetry/OTLP integration.
    • Updated example configuration files and dependencies to reflect the new observability approach.
  • Documentation

    • Updated demo documentation to clarify the streamlined tracing flow using Spring Boot's built-in OTLP support without custom configuration.

Drops the 73-LOC JamjetCloudConfiguration and the jamjet-cloud-sdk
dependency in favor of Spring Boot's standard OTLP tracing autoconfig.

The workaround was needed because:
1. jamjet-cloud-spring-boot-starter:0.2.0 hard-references LangChain4j
   classes, forcing langchain4j-core onto a Spring-AI-only classpath.
2. JamjetObservationHandler.supportsContext was called with a null
   context name, so the handler never matched.

Both are sidestepped by using OTLP instead of the SDK observation
handler. With jamjet-cloud PR #16 (OTLP/protobuf intake) deployed to
api.jamjet.dev, the demo now wires observability through:

  Spring AI 1.0 emits Micrometer Observations
    -> micrometer-tracing-bridge-otel converts to OTel spans
    -> opentelemetry-exporter-otlp ships OTLP/HTTP-protobuf
    -> JamJet's /v1/otlp/v1/traces

All wiring is in application.yml:

  management:
    tracing:
      sampling:
        probability: 1.0
    otlp:
      tracing:
        endpoint: ${JAMJET_API_URL:https://api.jamjet.dev}/v1/otlp/v1/traces
        headers:
          Authorization: "Bearer ${JAMJET_API_KEY}"

What changed:
- pom.xml: remove jamjet-cloud-sdk + micrometer-observation; add
  micrometer-tracing-bridge-otel + opentelemetry-exporter-otlp
- application.yml: drop jamjet.cloud.* block, add management.otlp.tracing.*
- Delete src/main/java/.../cloud/JamjetCloudConfiguration.java
- DemoIntegrationTest: override OTLP endpoint to localhost:1 and supply
  a placeholder bearer token so Spring's property resolver doesn't trip
  on unresolved ${JAMJET_API_KEY} during tests
- README + .env.example: reflect the OTLP-based wiring

Verified:
- mvnw clean compile clean (Java 21 release)
- mvnw test -Dtest=PreflightCheckTest: 5/5 pass
- mvnw spring-boot:run: app starts on :8080, all autoconfig wires up,
  PreflightCheck fails on Engram (expected without docker)
- DemoIntegrationTest needs Docker (testcontainers); CI will run it
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

The demo application is refactored to use Spring Boot's standard OTLP tracing infrastructure instead of the JamJet Cloud SDK. Dependencies shift to Micrometer's OTel bridge and OTLP exporter, configuration moves from custom jamjet.cloud properties to management.otlp.tracing, the custom JamjetCloudConfiguration class is removed, and all documentation and tests are updated accordingly.

Changes

Observability Architecture Refactor

Layer / File(s) Summary
Dependencies & Build Configuration
pom.xml
Maven dependencies replace jamjet-cloud-sdk and micrometer-observation with micrometer-tracing-bridge-otel and opentelemetry-exporter-otlp. The jamjet-cloud.version property is removed and comments updated to describe the Micrometer → OTel → OTLP/HTTP path.
OTLP Configuration & Environment
.env.example, application.yml
Configuration switches from custom jamjet.cloud section to Spring Boot's management.otlp.tracing with endpoint at ${JAMJET_API_URL}/v1/otlp/v1/traces, Authorization bearer header from ${JAMJET_API_KEY}, and full sampling probability. Environment variable documentation clarifies OTLP trace export usage.
Custom Configuration Class Removal
src/main/java/dev/jamjet/demo/springaiengram/cloud/JamjetCloudConfiguration.java
The entire JamjetCloudConfiguration class (73 lines) is deleted. Custom observation handler registration and JamJet Cloud initialization logic are no longer needed; Spring Boot's OTLP autoconfiguration handles all setup.
Documentation & Architecture Narrative
README.md
README reframes observability as coming from Spring Boot configuration only: Micrometer Observations flow through OTel bridge, OTLP exporter, and to JamJet's hosted intake. Removes references to JamjetCloudConfiguration and custom handlers; updates configuration table to show OTLP tracing properties.
Test Configuration Updates
src/test/java/dev/jamjet/demo/springaiengram/DemoIntegrationTest.java
Integration test now overrides management.otlp.tracing properties to prevent external trace export: redirects endpoint to non-routable address, disables sampling, and supplies placeholder Authorization header. Removes old jamjet.cloud.api-key and jamjet.cloud.api-url property overrides.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • jamjet-labs/jamjet-runtime-java#3: Refactors the same spring-ai-engram-cloud-demo example to migrate from JamJet SDK-based instrumentation to standard Spring Boot OTLP tracing with Micrometer bridge, updating dependencies, configuration, and documentation across the same files.

Poem

🐰 From SDK chains to OTLP streams so bright,
We hop through Micrometer's bridge day and night,
No custom class wrangling, just config so clean,
Spring Boot's wisdom flows through every scene!
The traces now journey through standards so true,
A simpler path forward for all to pursue. ✨

🚥 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 accurately captures the main refactoring: replacing a custom JamJet configuration workaround with standard Spring Boot OTLP tracing, which is reflected in the removal of JamjetCloudConfiguration and addition of OTLP dependencies.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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/spring-ai-stock-otlp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@examples/spring-ai-engram-cloud-demo/README.md`:
- Line 104: Update the README table entry for management.otlp.tracing.endpoint
to match the application.yml default by including the fallback value; change the
displayed expression to include the fallback :https://api.jamjet.dev so it
mirrors the config's JAMJET_API_URL fallback used in application.yml and avoids
implying JAMJET_API_URL is mandatory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 721a08a1-853f-4415-a7b0-95079d70e9a8

📥 Commits

Reviewing files that changed from the base of the PR and between b4a33f8 and 4d3561c.

📒 Files selected for processing (6)
  • examples/spring-ai-engram-cloud-demo/.env.example
  • examples/spring-ai-engram-cloud-demo/README.md
  • examples/spring-ai-engram-cloud-demo/pom.xml
  • examples/spring-ai-engram-cloud-demo/src/main/java/dev/jamjet/demo/springaiengram/cloud/JamjetCloudConfiguration.java
  • examples/spring-ai-engram-cloud-demo/src/main/resources/application.yml
  • examples/spring-ai-engram-cloud-demo/src/test/java/dev/jamjet/demo/springaiengram/DemoIntegrationTest.java
💤 Files with no reviewable changes (1)
  • examples/spring-ai-engram-cloud-demo/src/main/java/dev/jamjet/demo/springaiengram/cloud/JamjetCloudConfiguration.java

| `spring.ai.openai.chat.options.model` | `gpt-4o-mini` | OpenAI model for chat |
| `jamjet.cloud.api-key` | `${JAMJET_API_KEY}` | JamJet Cloud project key |
| `jamjet.cloud.api-url` | `https://api.jamjet.dev` | JamJet Cloud ingest endpoint |
| `management.otlp.tracing.endpoint` | `${JAMJET_API_URL}/v1/otlp/v1/traces` | OTLP intake URL — defaults to JamJet's hosted intake |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align README default endpoint expression with actual config default.

Line 104 omits the fallback (:https://api.jamjet.dev) that exists in application.yml, so readers may assume JAMJET_API_URL is mandatory.

Suggested doc fix
-| `management.otlp.tracing.endpoint` | `${JAMJET_API_URL}/v1/otlp/v1/traces` | OTLP intake URL — defaults to JamJet's hosted intake |
+| `management.otlp.tracing.endpoint` | `${JAMJET_API_URL:https://api.jamjet.dev}/v1/otlp/v1/traces` | OTLP intake URL — defaults to JamJet's hosted intake |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `management.otlp.tracing.endpoint` | `${JAMJET_API_URL}/v1/otlp/v1/traces` | OTLP intake URL — defaults to JamJet's hosted intake |
| `management.otlp.tracing.endpoint` | `${JAMJET_API_URL:https://api.jamjet.dev}/v1/otlp/v1/traces` | OTLP intake URL — defaults to JamJet's hosted intake |
🤖 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 `@examples/spring-ai-engram-cloud-demo/README.md` at line 104, Update the
README table entry for management.otlp.tracing.endpoint to match the
application.yml default by including the fallback value; change the displayed
expression to include the fallback :https://api.jamjet.dev so it mirrors the
config's JAMJET_API_URL fallback used in application.yml and avoids implying
JAMJET_API_URL is mandatory.

@sunilp sunilp merged commit 6fdf4d2 into main May 8, 2026
2 checks passed
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