Skip to content

Add OpenTelemetry logging backend - #143

Merged
alexander-yevsyukov merged 21 commits into
masterfrom
otel
Jun 29, 2026
Merged

Add OpenTelemetry logging backend#143
alexander-yevsyukov merged 21 commits into
masterfrom
otel

Conversation

@alexander-yevsyukov

@alexander-yevsyukov alexander-yevsyukov commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

The OpenTelemetry logging backend — the OTel half of the branch split. The config
sync and version bump live in #142 (update-config → master). The original 9 OTel
commits are preserved here.

Base: this PR is stacked on update-config (not master).

Contents

  • otel-backend (KMP) — maps Spine LogData to OTel log records: severity
    mapping, attribute mapping (incl. repeated values and the tag. namespace),
    log-site code.* attributes, event-name routing, and span correlation.
  • otel-backend-bootstrap (JVM) — OtelLogging install helpers (OTLP/HTTP and
    from-environment) returning an AutoCloseable.
  • OpenTelemetryKotlin dependency declaration (opentelemetry-kotlin 0.4.0).
  • Docs: docs/otel-backend.md, the module README, and a README backends entry.

Verification

./gradlew build dokkaGenerate is green; otel-backend:jvmTest and
otel-backend-bootstrap:test pass. The dependency report includes the OTel deps.

🤖 Generated with Claude Code

alexander-yevsyukov and others added 10 commits June 28, 2026 17:57
Build on the Phase 0/1 backend with the log-based events surface, an OTLP
bootstrap module, trace correlation, documentation, and review fixes.

- Events: the `EVENT_NAME` metadata key and `WithLogging.logEvent(...)`. The
  backend forwards the name via `Logger.emit(eventName = …)` and omits it from
  the `spine.*` attributes.
- `backends/otel-backend-bootstrap` (jvm-module): `OtelLogging.installOtlpHttp()`
  and `fromEnvironment()` build a native OTLP/HTTP SDK, install it through
  `OtelBackendSettings`, and return an `AutoCloseable` that uninstalls and shuts
  the SDK down. Adds the `exporters-core` and `exporters-otlp` coordinates.
- Add a trace-correlation test (the record carries the active span's ids) and the
  module `README.md` (usage, mapping, events, and the consumer-side domain-event
  recipe).
- Review fixes: restore the no-op holder after each test, safe-cast in `close()`,
  null-message guard in `handleError`, a widening KDoc note, README line length,
  and a resolved KDoc link.

Tests: otel-backend 13, bootstrap 2. detekt and Dokka clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address findings from spine-code-review, kotlin-engineer, dependency-audit, and
review-docs:

- Revert an unintended `rootProject.name` rename (`logging` -> `spine-logging`)
  that a formatter slipped into the Phase 0 commit.
- Add tests for `handleError`, `Tags` expansion (label-only / single / list), and
  boolean and double attribute values (otel-backend now has 16 tests).
- `OpenTelemetryKotlin`: add the project URL comment.
- Use `requireNotNull` instead of `!!` in the `StubLogData` test fixture.
- README: fix an example comment; capitalize a `@return` description.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- `OtelLoggerBackend` no longer captures the OpenTelemetry logger at creation. It
  resolves from `OtelBackendSettings.current()` on every access (a `logger`
  property), so a backend created and cached per logging class before the SDK is
  installed picks it up once `use()` runs, and follows a later replacement (for
  example, the bootstrap restoring the no-op instance on shutdown).
- Publishing: add `otel-backend-bootstrap` to the root `spinePublishing.modules`,
  and configure `otel-backend` (a KMP module) with `kmp-publish` + `customPublishing`
  so it publishes as `spine-logging-otel-backend`. Verified via `publishToMavenLocal`.
- Update the backend tests for the lazy-resolution change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Raise patch coverage for the OpenTelemetry backend: exercise the single and
repeated attribute value-type branches (numeric widening; boolean/double/string
lists; non-primitive `toString` fallback), the unknown line-number / source-file
log site, the factory `toString()`, and the bootstrap default-endpoint install.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the `spine.` prefix on metadata-derived attributes. Spine labels are dot-free
identifiers and cannot collide with OpenTelemetry's dotted semantic conventions, so
the prefix was convention, not collision-prevention — metadata keys now map to bare
attributes named after their label.

Keep a prefix for `Tags`, de-`spine`d to `tag.`: tag names and metadata labels share
the same identifier namespace, so a same-named tag and metadata key would otherwise
overwrite each other. The KDoc now states that actual rationale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alexander-yevsyukov alexander-yevsyukov self-assigned this Jun 28, 2026
@alexander-yevsyukov alexander-yevsyukov moved this to 🏗 In progress in v2.0 Jun 28, 2026
Base automatically changed from update-config to master June 28, 2026 18:29
alexander-yevsyukov and others added 3 commits June 28, 2026 19:59
otel-backend-bootstrap:
- Document DEFAULT_OTLP_HTTP_ENDPOINT as the OpenTelemetry-standard port and its
  override paths; resolve the endpoint through a testable endpointFromEnvironment().
- Build the lifecycle test against a free ephemeral port instead of 4318 so it
  cannot collide with a real collector or a parallel suite; verify default
  resolution as pure logic.
- Report a failed shutdown() (OperationResultCode.Failure) via JUL instead of
  discarding it silently.

backends:
- Add BackendFactory.loggerName() as the single home for the `$`-to-`.` logger-name
  convention and adopt it in the JUL, Log4j2, and OTel factories. JUL now derives
  the name in its factory; the backend no longer re-converts.
- Fix stale KDoc: "Spine Logging backends", and drop the spine.* attribute mention
  removed in 4e467c5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alexander-yevsyukov
alexander-yevsyukov marked this pull request as ready for review June 28, 2026 19:07
Copilot AI review requested due to automatic review settings June 28, 2026 19:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds an OpenTelemetry (OTel) logging backend to Spine Logging, mapping LogData to OTel log records (including severity, attributes, log-site code.*, tags, and span correlation) and providing a small JVM bootstrap module for turnkey OTLP/HTTP wiring.

Changes:

  • Introduces otel-backend (KMP module, JVM target) with LogDataLogger.emit(...) mapping and a log-based events API (logEvent / EVENT_NAME).
  • Adds otel-backend-bootstrap (JVM module) to install an OTLP/HTTP pipeline and manage SDK lifecycle via AutoCloseable.
  • Updates build/publishing wiring, docs, and aligns existing backends on a shared logger-name convention ($.).

Reviewed changes

Copilot reviewed 29 out of 31 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
settings.gradle.kts Renames root project and includes the new OTel backend modules.
README.md Updates project status messaging and documents the OTel backend entry.
logging/src/commonMain/kotlin/io/spine/logging/backend/BackendFactory.kt Adds shared loggerName() helper for consistent backend logger naming.
docs/otel-backend.md New guide describing setup, mapping, and events for the OTel backend.
docs/dependencies/pom.xml Refreshes dependency report to include OTel Kotlin artifacts and related deps.
buildSrc/src/main/kotlin/io/spine/dependency/lib/OpenTelemetryKotlin.kt Adds centralized dependency coordinates for opentelemetry-kotlin (0.4.0).
build.gradle.kts Adds otel-backend-bootstrap to published modules list.
backends/otel-backend/src/jvmTest/kotlin/io/spine/logging/backend/otel/OtelLoggerBackendSpec.kt Adds JVM tests covering mapping behavior, events, and span correlation.
backends/otel-backend/src/jvmTest/kotlin/io/spine/logging/backend/otel/given/StubMetadata.kt Test fixture for mutable Metadata.
backends/otel-backend/src/jvmTest/kotlin/io/spine/logging/backend/otel/given/StubLogSite.kt Test fixture for LogSite.
backends/otel-backend/src/jvmTest/kotlin/io/spine/logging/backend/otel/given/StubLogData.kt Test fixture for mutable LogData.
backends/otel-backend/src/jvmTest/kotlin/io/spine/logging/backend/otel/given/RecordingLogRecordProcessor.kt Test processor to capture emitted OTel log records.
backends/otel-backend/src/jvmTest/kotlin/io/spine/logging/backend/otel/given/NoOpSpanProcessor.kt Test span processor to enable valid span contexts for correlation tests.
backends/otel-backend/src/jvmMain/kotlin/io/spine/logging/backend/otel/OtelBackendFactory.kt JVM BackendFactory registered via @AutoService / KSP.
backends/otel-backend/src/commonMain/kotlin/io/spine/logging/backend/otel/SeverityMapping.kt Implements LevelSeverityNumber threshold mapping.
backends/otel-backend/src/commonMain/kotlin/io/spine/logging/backend/otel/OtelLoggerBackend.kt Implements the core LoggerBackend mapping and error handling.
backends/otel-backend/src/commonMain/kotlin/io/spine/logging/backend/otel/OtelEvents.kt Adds EVENT_NAME metadata key and WithLogging.logEvent(...) helper.
backends/otel-backend/src/commonMain/kotlin/io/spine/logging/backend/otel/OtelBackendSettings.kt Adds injectable global OpenTelemetry instance holder (defaults to noop).
backends/otel-backend/src/commonMain/kotlin/io/spine/logging/backend/otel/AttributeMapping.kt Implements metadata/tags/log-site → OTel attributes mapping.
backends/otel-backend/README.md Module README linking to the full OTel backend guide.
backends/otel-backend/build.gradle.kts Configures KMP module deps, publishing, and JVM test task setup (JUnit Platform).
backends/otel-backend-bootstrap/src/test/kotlin/io/spine/logging/backend/otel/bootstrap/OtelLoggingSpec.kt Adds tests for endpoint resolution and install/shutdown path.
backends/otel-backend-bootstrap/src/main/kotlin/io/spine/logging/backend/otel/bootstrap/OtelLogging.kt Adds OTLP/HTTP bootstrap installer with shutdown handling and warning-on-failure.
backends/otel-backend-bootstrap/build.gradle.kts Configures bootstrap module dependencies (SDK + exporters + coroutines).
backends/log4j2-backend/src/main/kotlin/io/spine/logging/backend/log4j2/Log4j2BackendFactory.kt Switches to shared BackendFactory.loggerName() convention.
backends/jul-backend/src/main/kotlin/io/spine/logging/backend/jul/JulBackendFactory.kt Switches to shared BackendFactory.loggerName() convention.
backends/jul-backend/src/main/kotlin/io/spine/logging/backend/jul/JulBackend.kt Updates KDoc to reflect logger-name derivation now handled in the factory.
backends/jul-backend/src/main/kotlin/io/spine/logging/backend/jul/AbstractJulBackend.kt Removes redundant $. conversion (now done by factories).
.agents/tasks/otel-backend-report.md Adds internal research/spec document for the OTel backend work.
.agents/tasks/otel-backend-implementation.md Adds internal implementation plan / execution log for the OTel backend work.

`WithLogging.logEvent` now takes `message: () -> String` instead of a `String`, so
the body is built only when the event is enabled — matching the fluent `log {}` API
and the "a disabled event costs nothing" contract. The default still resolves to the
event name, and the trailing-lambda form reads naturally: `logEvent(name) { "body" }`.

Adds a test covering the supplied-message path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c59160d53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/otel-backend.md
@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.98305% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 17.67%. Comparing base (8bd4485) to head (f2f6cff).

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #143      +/-   ##
============================================
+ Coverage     14.20%   17.67%   +3.46%     
- Complexity      120      149      +29     
============================================
  Files           102      109       +7     
  Lines          2443     2558     +115     
  Branches        379      404      +25     
============================================
+ Hits            347      452     +105     
- Misses         2065     2068       +3     
- Partials         31       38       +7     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

alexander-yevsyukov and others added 2 commits June 28, 2026 20:39
- AttributeMapping.putValues: dispatch on the whole list, not just its first element,
  so a repeated<Any> key holding mixed types falls back to a string list instead of
  risking a ClassCastException. Adds a regression test.
- OtelLogging.close(): also warn (via JUL) when the SDK is not a TelemetryCloseable, so
  a skipped shutdown is never silent — previously only OperationResultCode.Failure warned.
- docs/otel-backend.md: note that programmatic setup (OtelBackendSettings.use / logEvent)
  needs the backend as an `implementation` dependency, not `runtimeOnly`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address Codex review: `endpointFromEnvironment()` now prefers the signal-specific
`OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` over the generic `OTEL_EXPORTER_OTLP_ENDPOINT`
before the localhost default, so logs reach the configured collector. The value is a
base URL — the bundled `otlpHttpLogRecordExporter` always appends `/v1/logs` — kept
consistent with the default and the generic variable.

Resolution is split into a `getenv`-injected overload so the precedence is covered by
deterministic unit tests without mutating the process environment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread docs/otel-backend.md
alexander-yevsyukov and others added 3 commits June 28, 2026 21:21
Extract SDK shutdown reporting into a testable `internal reportShutdown(sdk, result,
logger)` so both warning paths (non-closeable SDK, OperationResultCode.Failure) and the
silent-success path are unit-tested via a capturing JUL handler. Add smoke tests for the
no-arg installOtlpHttp() and fromEnvironment() wiring.

Raises otel-backend-bootstrap src/main line coverage from 75% to ~97%, clearing the
codecov/project threshold; the new module's uncovered warning branches were dragging the
src/main aggregate below base.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alexander-yevsyukov
alexander-yevsyukov merged commit 60201ef into master Jun 29, 2026
10 checks passed
@alexander-yevsyukov
alexander-yevsyukov deleted the otel branch June 29, 2026 13:13
@github-project-automation github-project-automation Bot moved this from 🏗 In progress to ✅ Done in v2.0 Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants