Feature/phase3/monitoring logs#21
Conversation
add request correlation + request/latency metrics filter add explicit error counters in exception handler configure actuator readiness/liveness groups and metrics exposure enable JSON console logging configuration
… secure centralized logs
There was a problem hiding this comment.
Pull request overview
This PR introduces a Phase 3 observability baseline for the TinyURL Spring Boot service by enabling structured JSON logging with correlation IDs, adding Prometheus-ready metrics, hardening Actuator health grouping/exposure, and providing operational documentation for alerts and log aggregation.
Changes:
- Add JSON console logging (Logstash encoder) and a request filter to propagate/log
X-Correlation-Id. - Expose Prometheus/metrics endpoints and add custom request/error Micrometer metrics.
- Add Phase 3 observability documentation: alert baselines and log aggregation/storage guidance.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tinyurl/src/main/resources/logback-spring.xml | Adds JSON console logging with service field and MDC correlation_id. |
| tinyurl/src/main/resources/application.yaml | Configures Actuator health groups and exposes metrics/prometheus endpoints; adds metrics tags/distributions. |
| tinyurl/src/main/java/com/tinyurl/observability/RequestObservabilityFilter.java | Adds correlation-id propagation, request logging, and custom request metrics. |
| tinyurl/src/main/java/com/tinyurl/controller/GlobalExceptionHandler.java | Adds an error counter metric emitted from exception handlers. |
| tinyurl/build.gradle.kts | Adds Prometheus registry + logstash logback encoder dependencies. |
| docs/implementation/PHASE_3_OBSERVABILITY.md | Links baseline reference docs for Phase 3. |
| docs/implementation/PHASE_3_LOG_STORAGE_MIGRATION_LOKI_PROMTAIL_GRAFANA.md | Adds a Loki/Promtail/Grafana migration/runbook document. |
| docs/implementation/PHASE_3_LOG_AGGREGATION_BASELINE.md | Adds minimal baseline requirements for structured log aggregation. |
| docs/implementation/PHASE_3_IMPLEMENTATION_EXPLANATION.md | Documents what was implemented and suggested verification steps. |
| docs/implementation/PHASE_3_ALERT_BASELINE.md | Adds starter alert definitions tied to the new metrics/endpoints. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| show-components: always | ||
| show-details: always |
| web: | ||
| exposure: | ||
| include: health | ||
| include: health,metrics,prometheus |
| String route = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); | ||
| if (route == null || route.isBlank()) { | ||
| route = request.getRequestURI(); | ||
| } |
| filterChain.doFilter(request, response); | ||
| statusCode = response.getStatus(); | ||
| } finally { | ||
| String method = request.getMethod(); | ||
| String route = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); | ||
| if (route == null || route.isBlank()) { | ||
| route = request.getRequestURI(); | ||
| } | ||
|
|
||
| String status = Integer.toString(statusCode); | ||
| String statusClass = (statusCode / 100) + "xx"; | ||
| String outcome = statusCode >= 400 ? "error" : "success"; | ||
|
|
||
| double durationSeconds = (System.nanoTime() - startNanos) / 1_000_000_000.0; | ||
| Timer.builder("tinyurl.http.server.request.duration") | ||
| .tag("method", method) | ||
| .tag("route", route) | ||
| .tag("status", status) | ||
| .register(meterRegistry) | ||
| .record((long) (durationSeconds * 1_000_000_000L), java.util.concurrent.TimeUnit.NANOSECONDS); | ||
|
|
||
| Counter.builder("tinyurl.http.server.requests.total") | ||
| .tag("method", method) | ||
| .tag("route", route) | ||
| .tag("status_class", statusClass) | ||
| .tag("outcome", outcome) | ||
| .register(meterRegistry) | ||
| .increment(); | ||
|
|
||
| log.info( | ||
| "http_request method={} route={} status={} duration_ms={} client_ip={} user_agent={}", | ||
| method, | ||
| route, | ||
| statusCode, | ||
| Math.round(durationSeconds * 1000), | ||
| request.getRemoteAddr(), | ||
| sanitize(request.getHeader("User-Agent")) | ||
| ); | ||
|
|
| double durationSeconds = (System.nanoTime() - startNanos) / 1_000_000_000.0; | ||
| Timer.builder("tinyurl.http.server.request.duration") | ||
| .tag("method", method) |
| String code = ex.getMessage() == null ? "INVALID_REQUEST" : ex.getMessage(); | ||
| HttpStatus status = "INVALID_EXPIRY".equals(code) || "INVALID_URL".equals(code) | ||
| ? HttpStatus.BAD_REQUEST | ||
| : HttpStatus.INTERNAL_SERVER_ERROR; | ||
| incrementErrorMetric(status, code); |
…r unmapped paths - Fix duration precision with direct nanosecond computation instead of double conversion - Ensure MDC cleanup runs even if metrics/logging fails (nested finally block) - Normalize error codes to bounded set to prevent cardinality explosion - Restrict health component details visibility to when_authorized (security) - Restrict metrics/prometheus endpoints to dev profiles only (production uses application-prod.yaml)
There was a problem hiding this comment.
Pull request overview
Adds Phase 3 “observability” capabilities to the TinyURL Spring Boot service by enabling structured JSON logging, correlation IDs, Prometheus/Micrometer metrics, and improved Actuator health grouping, along with rollout documentation.
Changes:
- Enable JSON console logging (logstash encoder) and add a request filter to propagate/log
X-Correlation-Id. - Expose and configure Actuator health groups + metrics/prometheus endpoints (with prod exposure restricted).
- Add Micrometer counters/timers for request and error tracking, plus operational baselines/docs for alerts and log aggregation/storage.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tinyurl/src/main/resources/logback-spring.xml | New Logback config to emit JSON logs and include correlation_id MDC field. |
| tinyurl/src/main/resources/application.yaml | Configures health probes/groups and exposes metrics/prometheus endpoints by default; adds metrics tags and histograms. |
| tinyurl/src/main/resources/application-prod.yaml | Restricts Actuator web exposure to health in production profile. |
| tinyurl/src/main/java/com/tinyurl/observability/RequestObservabilityFilter.java | New filter for correlation ID propagation, request logging, and custom request metrics. |
| tinyurl/src/main/java/com/tinyurl/controller/GlobalExceptionHandler.java | Adds an error metric counter incremented from exception handlers. |
| tinyurl/build.gradle.kts | Adds Prometheus registry + logstash encoder dependency. |
| docs/implementation/PHASE_3_OBSERVABILITY.md | Links additional Phase 3 baseline reference docs. |
| docs/implementation/PHASE_3_LOG_STORAGE_MIGRATION_LOKI_PROMTAIL_GRAFANA.md | Adds a Loki/Promtail/Grafana migration plan and operational guidance. |
| docs/implementation/PHASE_3_LOG_AGGREGATION_BASELINE.md | Defines required log fields, baseline pipeline, and alert/triage expectations. |
| docs/implementation/PHASE_3_IMPLEMENTATION_EXPLANATION.md | Documents what was implemented and how to verify Phase 3 changes. |
| docs/implementation/PHASE_3_ALERT_BASELINE.md | Adds starter alert definitions aligned to metrics/health endpoints. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| String correlationId = request.getHeader(CORRELATION_ID_HEADER); | ||
| if (correlationId == null || correlationId.isBlank()) { | ||
| correlationId = UUID.randomUUID().toString(); | ||
| } | ||
|
|
||
| MDC.put(CORRELATION_ID_MDC_KEY, correlationId); | ||
| response.setHeader(CORRELATION_ID_HEADER, correlationId); | ||
|
|
| Timer.builder("tinyurl.http.server.request.duration") | ||
| .tag("method", method) | ||
| .tag("route", route) | ||
| .tag("status", status) | ||
| .register(meterRegistry) | ||
| .record(durationNanos, java.util.concurrent.TimeUnit.NANOSECONDS); | ||
|
|
||
| Counter.builder("tinyurl.http.server.requests.total") | ||
| .tag("method", method) | ||
| .tag("route", route) | ||
| .tag("status_class", statusClass) | ||
| .tag("outcome", outcome) | ||
| .register(meterRegistry) | ||
| .increment(); |
| if (value == null) { | ||
| return "unknown"; | ||
| } | ||
| return value.replaceAll("[\r\n]", " "); |
| implementation("org.flywaydb:flyway-core") | ||
| implementation("org.flywaydb:flyway-database-postgresql") | ||
| implementation("io.micrometer:micrometer-registry-prometheus") | ||
| implementation("net.logstash.logback:logstash-logback-encoder:7.4") |
|
|
||
| 1. Unit/integration tests: | ||
|
|
||
| - run tests for app context, service logic, and encoder tests |
No description provided.