Skip to content

feat: distributed tracing with OpenTelemetry + Jaeger#4

Merged
vitorhugo-java merged 5 commits intomainfrom
copilot/add-distributed-tracing
Apr 13, 2026
Merged

feat: distributed tracing with OpenTelemetry + Jaeger#4
vitorhugo-java merged 5 commits intomainfrom
copilot/add-distributed-tracing

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 13, 2026

Adds end-to-end distributed tracing to the Spring Boot app using the Micrometer→OpenTelemetry bridge, exporting spans to Jaeger via OTLP gRPC. Every inbound HTTP request produces a trace spanning the security filter, controller, service, repository, and JDBC layers automatically.

Dependencies

  • spring-boot-starter-actuator — activates Micrometer Observations
  • micrometer-tracing-bridge-otel — bridges Micrometer Tracer to OTel SDK
  • opentelemetry-exporter-otlp — OTLP gRPC export to Jaeger
  • micrometer-registry-prometheus — Prometheus metrics coexistence (unchanged behaviour)
  • logstash-logback-encoder — structured JSON logging for production

All versions managed by Spring Boot 3.2.5 BOM — no explicit pins needed.

Manual spans

Critical business operations are wrapped with named child spans for granular visibility:

Span span = tracer.nextSpan().name("create-application").start();
try (Tracer.SpanInScope ignored = tracer.withSpan(span)) {
    // ... business logic
} catch (BadCredentialsException e) {
    // Expected auth failure – do not mark as span error
    throw e;
} catch (Exception e) {
    span.error(e);
    throw e;
} finally {
    span.end();
}

Covered: create-application (ApplicationService), login and token-refresh (AuthService). No PII or tokens tagged. BadCredentialsException is intentionally excluded from span.error() to avoid inflating error rates with expected authentication failures.

Log correlation

logback-spring.xml injects traceId and spanId into every log line via MDC — automatically populated by the bridge. Uses Spring profile-based configuration:

  • prod profile: structured JSON via LogstashEncoder (MDC fields included automatically)
  • !prod profile: human-readable console output with inline trace correlation:
10:30:01.123 [http-nio-8080-exec-1] INFO [4f9e2a1b0c3d7e8f] [a1b2c3d4] c.j.service.ApplicationService - ...

DEBUG logging for com.jobtracker and org.hibernate.SQL is scoped to the non-production profile only.

Configuration

  • management.tracing.sampling.probability defaults to 1.0, overridable via TRACING_SAMPLING_PROBABILITY env var for production tuning
  • management.otlp.tracing.endpoint defaults to http://localhost:4317, overridable via OTEL_EXPORTER_OTLP_ENDPOINT
  • Actuator endpoints exposed: health, info, metrics, prometheus

Docker

  • docker-compose.yml — full stack: Jaeger (jaegertracing/all-in-one:1.57.0, pinned) with COLLECTOR_OTLP_ENABLED=true; UI at :16686, OTLP at :4317; Prometheus and Grafana retained
  • Dockerfile — multi-stage build using maven:3.9-eclipse-temurin-21; runs as a non-root user (appuser) for container hardening; exposes management port 8081 with a health check
  • DB_PASSWORD, DB_ROOT_PASSWORD, and JWT_SECRET are required (no insecure defaults) via Docker Compose :? syntax
  • .env.example provided; .env added to .gitignore

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces distributed tracing and log correlation for the Spring Boot backend using Micrometer Tracing with an OpenTelemetry bridge, exporting traces over OTLP to Jaeger, and adds local Docker Compose infrastructure to run the stack.

Changes:

  • Add tracing + OTLP exporter dependencies and management configuration for sampling and endpoint.
  • Add manual child spans around key service operations (application creation, login, token refresh).
  • Add Jaeger to docker-compose.yml, add container build via backend/Dockerfile, and add log correlation via logback-spring.xml.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
docker-compose.yml Adds MariaDB + Jaeger + app services for local tracing setup
backend/pom.xml Adds actuator, Micrometer tracing bridge, OTLP exporter, Prometheus registry
backend/src/main/resources/application.yml Configures management tracing sampling and OTLP endpoint
backend/src/main/resources/logback-spring.xml Adds MDC-based traceId/spanId log pattern
backend/src/main/java/com/jobtracker/service/ApplicationService.java Adds manual span around create operation with a trace tag
backend/src/main/java/com/jobtracker/service/AuthService.java Adds manual spans around login and refresh flows
backend/src/test/java/com/jobtracker/JobTrackerApplicationTests.java Disables tracing sampling during context-load test
backend/Dockerfile Adds multi-stage build for backend container image
.gitignore Ignores .env
.env.example Provides example env vars for local runs

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/main/java/com/jobtracker/service/AuthService.java
Comment thread docker-compose.yml Outdated
Comment thread docker-compose.yml Outdated
Comment thread backend/src/main/resources/logback-spring.xml Outdated
Comment thread backend/Dockerfile Outdated
Comment thread backend/src/main/resources/application.yml Outdated
return applicationMapper.toResponse(applicationRepository.save(app));
Span span = tracer.nextSpan().name("create-application").start();
try (Tracer.SpanInScope ignored = tracer.withSpan(span)) {
span.tag("vacancy", request.vacancyName() != null ? request.vacancyName() : "unknown");
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

Tagging spans with the free-form vacancyName risks very high-cardinality trace attributes (and potentially sensitive user-entered data), which can significantly increase tracing storage/query costs. Consider removing this tag, tagging a stable identifier instead, and/or truncating/normalizing the value; also the null-check is redundant since vacancyName is @NotBlank in ApplicationRequest.

Suggested change
span.tag("vacancy", request.vacancyName() != null ? request.vacancyName() : "unknown");

Copilot uses AI. Check for mistakes.
@vitorhugo-java
Copy link
Copy Markdown
Owner

@copilot apply changes based on the comments in this thread

- Resolve all 6 conflicted files (pom.xml, application.yml, logback-spring.xml,
  AuthService.java, Dockerfile, docker-compose.yml)
- Remove 4 leftover merge-artifact files (docker-compose_BACKUP/BASE/LOCAL/REMOTE)
- Remove all .orig backup files
- pom.xml: keep tracing deps + logstash + testcontainers (both sides merged)
- application.yml: single management block with tracing/OTLP config + springdoc groups
- logback-spring.xml: origin/main structure with traceId/spanId added to CONSOLE pattern
- AuthService: do not call span.error() for BadCredentialsException; fix refresh()
  to correctly return RefreshResponse instead of AuthResponse
- ApplicationService: remove high-cardinality vacancy tag from span
- Dockerfile: use maven:3.9-eclipse-temurin-21 image; add non-root user; root-level paths
- docker-compose.yml: full merge – Jaeger 1.57.0 + Prometheus/Grafana; require DB
  passwords via :? syntax; env-var driven JWT_SECRET
- ApplicationServiceTest/AuthServiceTest: add @mock(RETURNS_DEEP_STUBS) Tracer
- application-test.yml: disable tracing (probability=0.0) in test profile

Agent-Logs-Url: https://github.com/vitorhugo-java/SpringBoot-JobApplyTracker/sessions/1f21e07a-4b0e-477b-b5db-c700a3631326

Co-authored-by: vitorhugo-java <65777252+vitorhugo-java@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 13, 2026

@copilot apply changes based on the comments in this thread

All conflicts resolved and reviewer feedback applied in commit c74fb51:

  • All 6 conflicted files resolved (pom.xml, application.yml, logback-spring.xml, AuthService.java, Dockerfile, docker-compose.yml)
  • 4 leftover merge-artifact files deleted (docker-compose_BACKUP/BASE/LOCAL/REMOTE_20088.yml) and all .orig files removed
  • AuthService.login: BadCredentialsException is now re-thrown without marking the span as error; only unexpected exceptions call span.error()
  • AuthService.refresh: fixed bug where buildAuthResponse(user) was called instead of returning new RefreshResponse(accessToken, newRefreshToken.getToken())
  • ApplicationService: removed high-cardinality vacancy tag from span
  • Dockerfile: uses maven:3.9-eclipse-temurin-21 official image; non-root user added; paths corrected to root level
  • docker-compose.yml: full merge (Jaeger 1.57.0, Prometheus, Grafana); DB_PASSWORD and DB_ROOT_PASSWORD now required via :? syntax; JWT_SECRET required
  • application.yml: single management: block with tracing/OTLP config merged in; duplicate block removed; springdoc group-configs kept
  • logback-spring.xml: origin/main's profile-based structure kept; [%X{traceId:-}] [%X{spanId:-}] added to the dev console pattern
  • Unit tests: added @Mock(answer = RETURNS_DEEP_STUBS) Tracer tracer to both ApplicationServiceTest and AuthServiceTest; all 38 unit tests pass

Copilot AI requested a review from vitorhugo-java April 13, 2026 18:23
@vitorhugo-java vitorhugo-java merged commit 50c35a1 into main Apr 13, 2026
@vitorhugo-java vitorhugo-java deleted the copilot/add-distributed-tracing branch April 13, 2026 18:24
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.

3 participants