You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2026/10/09 12:58:15 UTC
(causeway) branch CAUSEWAY-4068-v4 updated (d1a1f385db5 -> 41d8db75a06)
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a change to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
from d1a1f385db5 CAUSEWAY-4068: reworking otel howto some more
new 32ce795bd23 CAUSEWAY-4068: record general span filtering exploration backlog
new 3b52d532fe7 CAUSEWAY-4068: archive observation classification and correlation
new 81e1bc7ccde CAUSEWAY-4068: propose removing JPA duration filtering
new 39faf40ded6 CAUSEWAY-4068: remove JPA duration filtering
new 40b05d43871 CAUSEWAY-4068: archive JPA duration filtering removal
new c645011fe65 CAUSEWAY-4068 : further improvements to otel docs
new a7c3d3584e6 CAUSEWAY-4068: propose built-in agent observation bridge
new 906a897df37 CAUSEWAY-4068: provide automatic agent observation bridge
new 570ced3d7a6 CAUSEWAY-4068: archive built-in agent observation bridge
new 41d8db75a06 CAUSEWAY-4068: clarify telemetry setup and agent adoption
The 10 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../ROOT/pages/2026/4.0.0-M3/otel-howto.adoc | 224 +++++++--------------
.../modules/ROOT/pages/observability.adoc | 95 ++-------
core/config/pom.xml | 7 +
core/config/src/main/java/module-info.java | 2 +
.../CausewayAgentEnvironmentPostProcessor.java | 75 +++++++
.../CausewayAgentObservationAutoConfiguration.java | 63 ++++++
.../CausewayObservationAutoConfiguration.java | 26 +--
.../CausewayObservationIntegration.java | 103 +---------
.../observation/CausewayObservationPolicy.java | 44 ----
.../additional-spring-configuration-metadata.json | 6 -
.../src/main/resources/META-INF/spring.factories | 2 +
...rk.boot.autoconfigure.AutoConfiguration.imports | 1 +
...sewayAgentObservationAutoConfigurationTest.java | 127 ++++++++++++
.../CausewayObservationAutoConfigurationTest.java | 35 ----
.../ObservationWithTimeThresholdTest.java | 120 -----------
.../ia/InteractionServiceObservationTest.java | 5 +-
.../.openspec.yaml | 0
.../design.md | 0
.../proposal.md | 0
.../specs/interaction-trace-correlation/spec.md | 0
.../observation-execution-classification/spec.md | 0
.../specs/observation-policy/spec.md | 0
.../tasks.md | 0
.../validation.md | 0
.../.openspec.yaml | 2 +-
.../design.md | 27 +++
.../proposal.md | 23 +++
.../specs/agent-observation-bridge/spec.md | 45 +++++
.../tasks.md | 11 +
.../validation.md | 12 ++
.../.openspec.yaml | 2 +-
.../design.md | 27 +++
.../proposal.md | 26 +++
.../specs/main-observation-foundation/spec.md | 5 +
.../specs/observation-policy/spec.md | 31 +++
.../tasks.md | 9 +
.../validation.md | 30 +++
.../general-span-duration-filtering.md | 20 ++
.../planned-changes/otel-forward-port-roadmap.md | 18 +-
openspec/specs/agent-observation-bridge/spec.md | 49 +++++
.../specs/interaction-trace-correlation/spec.md | 6 +-
openspec/specs/main-observation-foundation/spec.md | 32 +--
.../observation-execution-classification/spec.md | 6 +-
openspec/specs/observation-policy/spec.md | 61 +++---
.../jpa/integration/entity/JpaEntityFacet.java | 4 +-
.../entity/JpaObservationPolicyTest.java | 14 +-
.../tracing/EntryPointTracingFixture.java | 3 +-
.../tracing/MicrometerTracingAgentFixture.java | 30 +--
.../MicrometerTracingCompatibilityTest.java | 43 ++--
.../sample-htmx-petclinic/README.adoc | 16 +-
.../webcomponents/sample-htmx-petclinic/pom.xml | 19 --
.../sample-htmx-petclinic/run-with-telemetry.sh | 4 -
.../main/resources/application-agent.properties | 27 ---
.../petclinic/AgentObservationConfiguration.java | 51 -----
54 files changed, 782 insertions(+), 806 deletions(-)
create mode 100644 core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentEnvironmentPostProcessor.java
create mode 100644 core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfiguration.java
delete mode 100644 core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationPolicy.java
create mode 100644 core/config/src/main/resources/META-INF/spring.factories
create mode 100644 core/config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
create mode 100644 core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfigurationTest.java
delete mode 100644 core/config/src/test/java/org/apache/causeway/core/config/observation/ObservationWithTimeThresholdTest.java
rename openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/.openspec.yaml (100%)
rename openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/design.md (100%)
rename openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/proposal.md (100%)
copy openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/specs/interaction-trace-correlation/spec.md (100%)
copy openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/specs/observation-execution-classification/spec.md (100%)
rename openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/specs/observation-policy/spec.md (100%)
rename openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/tasks.md (100%)
rename openspec/changes/{classify-and-correlate-observations => archive/2026-10-07-classify-and-correlate-observations}/validation.md (100%)
copy openspec/changes/archive/{2026-08-04-reconcile-command-recording-core-policy => 2026-10-07-provide-agent-observation-bridge}/.openspec.yaml (50%)
create mode 100644 openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/design.md
create mode 100644 openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/proposal.md
create mode 100644 openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
create mode 100644 openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/tasks.md
create mode 100644 openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/validation.md
copy openspec/changes/archive/{2026-08-04-reconcile-command-recording-core-policy => 2026-10-07-remove-jpa-duration-filtering}/.openspec.yaml (50%)
create mode 100644 openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/design.md
create mode 100644 openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/proposal.md
create mode 100644 openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md
create mode 100644 openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/specs/observation-policy/spec.md
create mode 100644 openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/tasks.md
create mode 100644 openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/validation.md
create mode 100644 openspec/planned-changes/general-span-duration-filtering.md
create mode 100644 openspec/specs/agent-observation-bridge/spec.md
rename openspec/{changes/classify-and-correlate-observations => }/specs/interaction-trace-correlation/spec.md (94%)
rename openspec/{changes/classify-and-correlate-observations => }/specs/observation-execution-classification/spec.md (94%)
delete mode 100644 viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties
delete mode 100644 viewers/webcomponents/sample-htmx-petclinic/src/observation/java/org/apache/causeway/viewer/webcomponents/sample/htmx/petclinic/AgentObservationConfiguration.java
(causeway) 07/10: CAUSEWAY-4068: propose built-in agent observation bridge
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit a7c3d3584e65b7ffd2e612e99d58858427d08435
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 12:05:00 2026 +0100
CAUSEWAY-4068: propose built-in agent observation bridge
---
.../.openspec.yaml | 2 +
.../provide-agent-observation-bridge/design.md | 27 +++++++++++++
.../provide-agent-observation-bridge/proposal.md | 23 +++++++++++
.../specs/agent-observation-bridge/spec.md | 45 ++++++++++++++++++++++
.../provide-agent-observation-bridge/tasks.md | 11 ++++++
5 files changed, 108 insertions(+)
diff --git a/openspec/changes/provide-agent-observation-bridge/.openspec.yaml b/openspec/changes/provide-agent-observation-bridge/.openspec.yaml
new file mode 100644
index 00000000000..a26662f7a5f
--- /dev/null
+++ b/openspec/changes/provide-agent-observation-bridge/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-10-07
diff --git a/openspec/changes/provide-agent-observation-bridge/design.md b/openspec/changes/provide-agent-observation-bridge/design.md
new file mode 100644
index 00000000000..ba6ad9c637b
--- /dev/null
+++ b/openspec/changes/provide-agent-observation-bridge/design.md
@@ -0,0 +1,27 @@
+## Context
+
+Petclinic and the tracing fixture duplicate an OtelTracer/ObservationRegistry bridge. Manual exclusions select the agent as trace owner. Core configuration already selects the single or primary registry and tracer.
+
+## Goals / Non-Goals
+
+Goals: configuration-only adoption, one trace owner, preserved Boot defaults and Micrometer metrics, tested production bridge.
+Non-goals: downloading or attaching the agent automatically; span duration filtering; changing instrumentation boundaries.
+
+## Decisions
+
+Use a guarded Boot auto-configuration with optional Micrometer OTel bridge dependencies. With both profiles active, create missing tracer and registry beans; the tracer wraps GlobalOpenTelemetry and creates no SDK or exporter. An application registry retains responsibility for its handlers. The existing fallback registry is limited to observation without agent, avoiding early duplicate registration.
+
+Use an EnvironmentPostProcessor after ConfigData to merge the supported baseline's tracing/WebMVC exclusions whenever the agent profile is active, including when Causeway observations are inactive. This prevents a competing Boot trace owner independently of Causeway activation. Disable Boot OTEL environment mapping and trace export for this profile, retaining Micrometer metrics despite OTEL_METRICS_EXPORTER=none. Preserve unrelated application exclusions. A missing bridge dependency with [...]
+
+Use real agent and Boot exported-trace fixtures without application bridge copies or agent exclusions to verify ownership, ancestry, classification and inactive behavior.
+
+## Risks / Trade-offs
+
+- Boot class names evolve → pin exclusions to current baseline and validate real Boot startup.
+- Agent profile without an attached agent → bridge uses the global API (possibly no-op); documentation makes attachment explicit and no SDK is constructed.
+- HTTP observation exclusion removes Boot HTTP request meters → document this; JVM and Causeway Micrometer meters remain available.
+- Custom registry handlers can target another pipeline → preserve customization and document responsibility to share the selected tracer.
+
+## Migration Plan
+
+Delete app-owned bridge and manual agent exclusions; retain the BOM-managed bridge dependency, attach the agent and activate observation,agent. To return to Boot mode remove attachment and agent profile. Existing explicit exclusions remain compatible.
diff --git a/openspec/changes/provide-agent-observation-bridge/proposal.md b/openspec/changes/provide-agent-observation-bridge/proposal.md
new file mode 100644
index 00000000000..458978ccfea
--- /dev/null
+++ b/openspec/changes/provide-agent-observation-bridge/proposal.md
@@ -0,0 +1,23 @@
+## Why
+
+Agent-managed tracing currently requires application-owned bridge code and Boot exclusions. This makes a supported telemetry setup unnecessarily difficult to adopt and maintain.
+
+## What Changes
+
+- Supply the agent observation bridge in Causeway when `observation,agent` profiles are active.
+- Automatically exclude competing Boot tracing and HTTP observation configuration for the `agent` profile, preserving existing exclusions and Micrometer metrics.
+- Keep the bridge dependency optional and respect application-provided registry/tracer beans.
+- Remove sample/fixture bridge copies and update the M3 how-to and user guide to show configuration-only adoption.
+
+## Capabilities
+
+### New Capabilities
+- `agent-observation-bridge`: Built-in agent-owned tracing setup with optional dependencies, profile activation and application overrides.
+
+### Modified Capabilities
+
+None. This implements the existing single-owner telemetry guarantees without changing instrumentation boundaries.
+
+## Impact
+
+Core configuration, Petclinic observation profile, exported-trace compatibility fixtures, M3 how-to and observability user guide. No duration filtering or exporter is introduced.
diff --git a/openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md b/openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
new file mode 100644
index 00000000000..38f02a93a01
--- /dev/null
+++ b/openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
@@ -0,0 +1,45 @@
+## ADDED Requirements
+
+### Requirement: Agent tracing is available without application bridge code
+
+Causeway SHALL supply a missing Micrometer tracer wrapping GlobalOpenTelemetry and a missing observation registry using that tracer when observation and agent profiles are active with the OTel bridge dependency. It SHALL NOT construct an SDK or exporter. A missing bridge dependency SHALL produce an actionable startup error. Agent attachment SHALL remain the application's responsibility.
+
+#### Scenario: Attached agent observes framework work
+- **WHEN** an application attaches the agent and activates observation,agent without custom bridge code
+- **THEN** framework spans, identity/correlation attributes and entry classification join automatic HTTP and JDBC spans in one trace without duplicate framework export
+
+#### Scenario: Optional dependency is absent in Boot mode
+- **WHEN** the agent profile is inactive and the bridge dependency is absent
+- **THEN** ordinary Causeway startup remains supported
+
+#### Scenario: Required dependency is absent in agent mode
+- **WHEN** both profiles are active without the bridge dependency
+- **THEN** startup explains which dependency to add
+
+### Requirement: Agent profile selects one trace owner
+
+The agent profile SHALL automatically suppress competing Boot SDK, tracing exporter, tracing handler and WebMVC observation configuration. It SHALL preserve unrelated user exclusions and keep Micrometer metrics independent of agent-only OTEL environment settings. Boot-managed configuration SHALL remain unchanged without that profile.
+
+#### Scenario: Agent tracing with Micrometer metrics
+- **WHEN** the agent profile is active with OTEL_METRICS_EXPORTER=none
+- **THEN** Boot does not create a competing trace pipeline and Micrometer metrics remain enabled by their application configuration
+
+#### Scenario: Causeway observations are inactive
+- **WHEN** only the agent profile is active
+- **THEN** automatic agent HTTP/JDBC tracing remains available without Causeway framework spans
+
+#### Scenario: Existing application exclusions
+- **WHEN** an application already excludes another auto-configuration
+- **THEN** that exclusion remains alongside the agent ownership exclusions
+
+### Requirement: Application telemetry customization remains supported
+
+The bridge SHALL back off from application-provided registry or tracer beans. The framework SHALL use single or primary candidates and reject ambiguous active candidates. Custom registry owners SHALL remain responsible for their observation handlers.
+
+#### Scenario: Custom tracer
+- **WHEN** an application supplies one tracer with both profiles active
+- **THEN** the default agent registry and Causeway entry classifier share that tracer
+
+#### Scenario: Custom registry
+- **WHEN** an application supplies a registry with both profiles active
+- **THEN** the bridge does not replace it or install additional handlers into it
diff --git a/openspec/changes/provide-agent-observation-bridge/tasks.md b/openspec/changes/provide-agent-observation-bridge/tasks.md
new file mode 100644
index 00000000000..868a03e4094
--- /dev/null
+++ b/openspec/changes/provide-agent-observation-bridge/tasks.md
@@ -0,0 +1,11 @@
+## 1. Framework wiring
+
+- [ ] 1.1 Add optional OTel bridge dependency and guarded agent tracer/registry auto-configuration.
+- [ ] 1.2 Add early agent ownership defaults preserving user exclusions and metrics.
+- [ ] 1.3 Test profiles, optional dependency, custom beans and exclusion merging.
+
+## 2. Adoption and verification
+
+- [ ] 2.1 Remove Petclinic and fixture bridge copies/manual agent exclusions; exercise production wiring.
+- [ ] 2.2 Update M3 how-to, user guide, sample instructions and roadmap.
+- [ ] 2.3 Validate focused core tests, exported Boot/agent trace regressions and sample packaging.
(causeway) 03/10: CAUSEWAY-4068: propose removing JPA duration filtering
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 81e1bc7ccde6b949d17d0e6570401f39491c4867
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 11:12:51 2026 +0100
CAUSEWAY-4068: propose removing JPA duration filtering
---
.../remove-jpa-duration-filtering/.openspec.yaml | 2 ++
.../remove-jpa-duration-filtering/design.md | 27 +++++++++++++++++++
.../remove-jpa-duration-filtering/proposal.md | 26 ++++++++++++++++++
.../specs/main-observation-foundation/spec.md | 5 ++++
.../specs/observation-policy/spec.md | 31 ++++++++++++++++++++++
.../changes/remove-jpa-duration-filtering/tasks.md | 9 +++++++
6 files changed, 100 insertions(+)
diff --git a/openspec/changes/remove-jpa-duration-filtering/.openspec.yaml b/openspec/changes/remove-jpa-duration-filtering/.openspec.yaml
new file mode 100644
index 00000000000..a26662f7a5f
--- /dev/null
+++ b/openspec/changes/remove-jpa-duration-filtering/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-10-07
diff --git a/openspec/changes/remove-jpa-duration-filtering/design.md b/openspec/changes/remove-jpa-duration-filtering/design.md
new file mode 100644
index 00000000000..9e04dc805f6
--- /dev/null
+++ b/openspec/changes/remove-jpa-duration-filtering/design.md
@@ -0,0 +1,27 @@
+## Context
+
+The completed classification/correlation change is archived as `2026-10-07-classify-and-correlate-observations`. The JPA facet wraps every generated observation using a configurable duration threshold. At completion, the wrapper marks short successful spans for discard; independently exported descendants cannot be retracted. The agent does not consume the Spring predicate, resulting in different policies between trace owners.
+
+## Goals / Non-Goals
+
+**Goals:** remove duration-based suppression and its configuration/API, retain JPA ancestry and diagnostic spans, simplify documentation and launcher wiring, and record future exploration.
+
+**Non-goals:** implement general filtering, alter sampling/export ownership, rename spans, change user metadata, or remove explicit transaction discard behavior.
+
+## Decisions
+
+- Delete the duration wrapper, threshold property and `CausewayObservationPolicy` record, which has no remaining fields. Reduce integration construction to the selected registry and remove configuration binding. JPA uses the regular observation provider.
+- Keep `DiscardedSpanExportingPredicate` and explicit discard helpers because transaction instrumentation deliberately discards unused transaction observations. This independent behavior is outside the removed feature.
+- Remove duration-wrapper unit tests. Replace JPA/export filtering assertions with retention, failure and ancestry assertions, including a legacy threshold property which no longer influences instrumentation. Keep real Boot/agent and JDBC export tests and metadata checks.
+- Remove environment threshold overrides, filtering walkthroughs and agent restrictions from the launcher/guides. Document removal and retain a short explanation of normal sampling/export limits.
+- Defer a general policy to the backlog. Duration is known at completion; parent/child consistency, failed descendants, buffering bounds, late spans and both trace owners must be addressed before shipping a replacement.
+
+## Risks / Trade-offs
+
+- More spans for users who enabled filtering → normal sampling/export options remain available; no performance/storage saving is promised by this change.
+- Removal of public policy/wrapper API → compile affected modules and update all in-repository consumers; these milestone APIs/configuration are removed intentionally.
+- Existing property files may retain obsolete settings → explain removal; Boot's normal handling of unbound properties applies and no new deprecated binding is introduced.
+
+## Migration Plan
+
+Remove the JPA threshold property/environment override and use standard observations in manually constructed integrations. No database or exporter migration. Reverse this code change to restore the previous narrow policy, if necessary; general filtering remains an exploration item.
diff --git a/openspec/changes/remove-jpa-duration-filtering/proposal.md b/openspec/changes/remove-jpa-duration-filtering/proposal.md
new file mode 100644
index 00000000000..e791a5ef8aa
--- /dev/null
+++ b/openspec/changes/remove-jpa-duration-filtering/proposal.md
@@ -0,0 +1,26 @@
+## Why
+
+Dropping a short JPA parent while retaining independently exported JDBC children fragments trace trees. The policy also differs between Boot-managed and agent-managed export, so it does not provide coherent database-operation filtering.
+
+## What Changes
+
+- **BREAKING:** Remove `causeway.observation.jpa-duration-threshold`, the duration wrapper and the now-empty observation policy configuration/API.
+- Retain successful and failed Causeway JPA spans regardless of duration, subject to normal sampling and export; retain existing names, attributes and ancestry.
+- Remove filtering examples and agent-only threshold overrides from the guides and Petclinic launcher.
+- Preserve explicit discard behavior used by transaction instrumentation; it is independent of duration filtering.
+- Track possible general duration filtering for all span types in `openspec/planned-changes/general-span-duration-filtering.md`, including descendant handling and Boot/agent consistency. No replacement filtering is implemented here.
+
+## Capabilities
+
+### New Capabilities
+
+None.
+
+### Modified Capabilities
+
+- `main-observation-foundation`: remove the duration-wrapper lifecycle contract while preserving other observation guarantees.
+- `observation-policy`: replace JPA threshold configuration with duration-independent span retention and update operator guidance.
+
+## Impact
+
+`core/config`, `persistence/jpa/integration`, runtime observation tests, tracing compatibility fixtures, the Petclinic launcher/configuration and the M3/user observability guides. No new dependency, exporter, SDK, persistence schema or span-name migration. Existing user identity, root UUID and execution-mode attributes remain automatic.
diff --git a/openspec/changes/remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md b/openspec/changes/remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md
new file mode 100644
index 00000000000..1fa3d006385
--- /dev/null
+++ b/openspec/changes/remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md
@@ -0,0 +1,5 @@
+## REMOVED Requirements
+
+### Requirement: Duration filtering survives observation composition
+**Reason**: The threshold wrapper and duration policy are removed rather than extended to independently exported descendants.
+**Migration**: Use regular observations and normal sampling/export settings. Observation lifecycle and failure cleanup requirements remain in force.
diff --git a/openspec/changes/remove-jpa-duration-filtering/specs/observation-policy/spec.md b/openspec/changes/remove-jpa-duration-filtering/specs/observation-policy/spec.md
new file mode 100644
index 00000000000..aabacddb920
--- /dev/null
+++ b/openspec/changes/remove-jpa-duration-filtering/specs/observation-policy/spec.md
@@ -0,0 +1,31 @@
+## REMOVED Requirements
+
+### Requirement: JPA duration policy is explicit and validated
+**Reason**: JPA-only duration suppression fragments traces and cannot enforce the same decision for independently exported JDBC children or agent-owned export.
+**Migration**: Remove `causeway.observation.jpa-duration-threshold` and its environment override. Causeway no longer provides duration-based JPA suppression; use normal sampling/export controls while general filtering is explored.
+
+## ADDED Requirements
+
+### Requirement: JPA observations are independent of duration
+Causeway SHALL retain successful and failed JPA observations regardless of their duration, subject to ordinary sampling and export policies. It SHALL NOT expose a JPA duration threshold or automatically mark these observations for discard by duration. Existing operation/contextual names, metadata and ancestry SHALL remain intact in Boot-managed and agent-managed tracing. Explicit discard behavior outside duration filtering SHALL be preserved.
+
+#### Scenario: Short successful operation
+- **WHEN** a short JPA operation completes successfully under either supported tracing owner
+- **THEN** its JPA span remains eligible for export with its children and parentage intact
+
+#### Scenario: Failed operation
+- **WHEN** a JPA operation fails
+- **THEN** its error and observation lifecycle remain intact regardless of duration
+
+#### Scenario: Obsolete configuration
+- **WHEN** an application still supplies the removed JPA threshold property
+- **THEN** it has no effect on Causeway's JPA observation retention and is absent from configuration metadata
+
+## MODIFIED Requirements
+
+### Requirement: Telemetry migration and limitations are documented
+The observability guide SHALL describe operation/contextual names, automatic identity attributes and the removal of JPA duration filtering. It SHALL distinguish Micrometer operation identity from exported contextual names and explain that normal sampling/export policy still determines available traces. It SHALL NOT present the removed threshold as supported configuration or require agent-specific duration overrides. Migration SHALL NOT introduce duplicate legacy spans.
+
+#### Scenario: Existing deployment migrates
+- **WHEN** an operator follows the guide
+- **THEN** they can remove obsolete threshold settings and inspect complete eligible JPA/JDBC ancestry without configuring a replacement Causeway filter
diff --git a/openspec/changes/remove-jpa-duration-filtering/tasks.md b/openspec/changes/remove-jpa-duration-filtering/tasks.md
new file mode 100644
index 00000000000..7c98be0f43e
--- /dev/null
+++ b/openspec/changes/remove-jpa-duration-filtering/tasks.md
@@ -0,0 +1,9 @@
+## 1. Remove duration filtering
+
+- [ ] 1.1 Delete the threshold wrapper/configuration and simplify the registry integration and JPA provider, preserving explicit transaction discard behavior.
+- [ ] 1.2 Replace removed-policy tests with JPA retention/failure and Boot/agent export ancestry evidence, including obsolete-setting no-op behavior.
+
+## 2. Guidance and validation
+
+- [ ] 2.1 Remove threshold overrides and filtering guidance from Petclinic and the observability guides; link the general filtering backlog in the roadmap.
+- [ ] 2.2 Verify affected compilation/tests, configuration metadata, launcher forwarding and documentation; record commands/results/limits in validation.md.
(causeway) 10/10: CAUSEWAY-4068: clarify telemetry setup and agent adoption
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 41d8db75a06f89f20d73130a8aa8891bd87e1b57
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Oct 9 12:38:43 2026 +0100
CAUSEWAY-4068: clarify telemetry setup and agent adoption
---
.../ROOT/pages/2026/4.0.0-M3/otel-howto.adoc | 104 +++++++++++----------
1 file changed, 56 insertions(+), 48 deletions(-)
diff --git a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
index b185e4d538c..8bab124d42e 100644
--- a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
+++ b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
@@ -3,10 +3,11 @@
:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or ag [...]
:page-partial:
-This guide starts with Boot-managed tracing and metrics, then changes tracing ownership to the OpenTelemetry Java agent.
-Both modes send traces to Jaeger and Micrometer metrics to Prometheus, with Grafana for exploration.
-Only one SDK/export pipeline owns tracing in each run.
-Use the configuration below to see request, interaction and persistence spans.
+This guide explains how Causeway supports open telemetry tracing and metrics, using first Boot-managed tracing and then using Agent-initiated tracing.
+It uses the petclinic application to demonstrate.
+
+It also outlines how to enable for your own applications.
+
== Start the local telemetry services
@@ -193,10 +194,15 @@ Note that Prometheus metric names can differ from contextual span names, includi
== Switch to agent-managed tracing
The principal benefit of using agent-managed tracing is that the agent will instrument libraries, such as JDBC access.
-When an agent is configured, the agent now owns the tracing SDK, exporter and automatic HTTP/JDBC instrumentation.
-Causeway supplies a registry bridge to join its observations to that context, and the `agent` profile automatically disables competing Boot tracing auto-configuration.
+This setup requires both:
+
+* The JVM option `-javaagent:/path/to/opentelemetry-javaagent.jar`, which attaches the agent to supply the tracing SDK, exporter and automatic HTTP/JDBC instrumentation.
+* The Spring profiles `observation,agent`: `observation` enables Causeway instrumentation, while `agent` selects Causeway's bridge to the agent and disables competing Boot tracing auto-configuration.
+
+Activating the profiles does not attach the Java agent, and attaching the agent does not activate the profiles.
+The sample launcher handles both settings for you when passed `--agent`.
-### Try it out
+=== Try it out
Stop the Boot-managed Petclinic process, keep all three telemetry services running, then add `--agent`:
@@ -206,7 +212,7 @@ Stop the Boot-managed Petclinic process, keep all three telemetry services runni
----
On first use, Maven downloads the (compatible) agent into the sample's `target/agents/` directory.
-The launcher attaches it to the application JVM with `spring-boot.run.agents`.
+The launcher attaches it to the application JVM with `spring-boot.run.agents` (the Maven equivalent of `-javaagent`) and activates `observation,agent` with `spring-boot.run.profiles`.
Alternatively, the location of the agent JAR can be specified using an env var:
@@ -222,46 +228,6 @@ This setup also disables agent log export.
The launcher sets OTLP HTTP trace export, full trace sampling and service name *Causeway Pet Clinic* by default.
-Some config can be overridden using env vars:
-
-* `OTEL_SERVICE_NAME` - agent's trace service name
-* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` - agent's trace destination
-
-
-[#apply-the-same-change-to-another-application]
-=== Configure agent-managed tracing in another application
-
-Keep the metrics configuration above and add the BOM-managed `io.micrometer:micrometer-tracing-bridge-otel` dependency.
-Causeway supplies the registry bridge automatically when both `observation` and `agent` profiles are active; no application Java configuration or manual Boot exclusions are needed.
-The bridge uses the agent's global OpenTelemetry instance without constructing another SDK or exporter.
-
-The `agent` profile automatically excludes competing Boot tracing and WebMVC observation configuration, while preserving any unrelated application exclusions.
-The WebMVC exclusion lets the agent own the HTTP entry span without a second Boot HTTP observation around it; foreground classification tags the agent's span.
-It also removes Boot's HTTP request observation meters in this configuration; JVM and Causeway Micrometer metrics remain available.
-
-If your application supplies its own registry or tracer, Causeway backs off from creating the corresponding bean.
-A custom registry must register its own tracing handlers using the same single or primary tracer that the entry-span classifier uses.
-
-Activate `observation,agent` alongside any other required application profiles and attach the agent to the application JVM:
-
-[source,bash]
-----
-export OTEL_SERVICE_NAME=my-causeway-app
-export OTEL_TRACES_EXPORTER=otlp
-export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
-export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
-export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
-export OTEL_TRACES_SAMPLER=always_on
-export OTEL_METRICS_EXPORTER=none
-export OTEL_LOGS_EXPORTER=none
-java -javaagent:/absolute/path/to/opentelemetry-javaagent.jar \
- -jar application.jar --spring.profiles.active=observation,agent
-----
-
-`OTEL_METRICS_EXPORTER=none` disables the agent's metrics exporter.
-Causeway sets `management.opentelemetry.map-environment-variables=false` for the `agent` profile, preventing Boot from mapping that agent flag onto Micrometer and disabling its metrics too.
-The shared `management.otlp.metrics.export.enabled=true` configuration therefore retains Micrometer's separate metrics pipeline.
-When returning to Boot-managed tracing, remove `-javaagent`, deactivate the `agent` profile, and unset the exported `OTEL_*` variables above so that Boot does not inherit the agent-only settings.
== Stop the local services
@@ -325,6 +291,48 @@ Traces are sent to Jaeger, and metrics to Prometheus using the endpoints above.
The configuration records every trace (sampling probability) and exports metrics every ten seconds.
+=== To configure agent-managed tracing
+
+Keep the starter and Micrometer metrics configuration above, and add the BOM-managed OpenTelemetry bridge dependency:
+
+[source,xml]
+.pom.xml
+----
+<dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-tracing-bridge-otel</artifactId>
+</dependency>
+----
+
+With `observation,agent` active, Causeway automatically connects its observations to the agent and disables competing Boot tracing configuration.
+No custom Java configuration or manual Boot exclusions are needed.
+The agent supplies trace sampling and export, so the Boot tracing settings above are unused in this mode; the Micrometer metrics settings still apply.
+
+Obtain an OpenTelemetry Java agent JAR (the sample uses version 2.31.1), configure its export settings, and launch with both `-javaagent` and the `observation,agent` profiles:
+
+[source,bash]
+----
+export OTEL_SERVICE_NAME=my-causeway-app
+export OTEL_TRACES_EXPORTER=otlp
+export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
+export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
+export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
+export OTEL_TRACES_SAMPLER=always_on
+export OTEL_METRICS_EXPORTER=none
+export OTEL_LOGS_EXPORTER=none
+java -javaagent:/absolute/path/to/opentelemetry-javaagent.jar \
+ -jar application.jar --spring.profiles.active=observation,agent
+----
+
+These settings send traces to Jaeger and leave metrics with Micrometer.
+Causeway prevents the agent's `OTEL_METRICS_EXPORTER=none` setting from disabling Micrometer's metrics exporter.
+JVM and Causeway metrics remain available; Boot's HTTP request observation meters are absent because the agent supplies HTTP instrumentation.
+
+If you already provide a custom tracer or observation registry, Causeway uses it.
+A custom registry must configure its tracing handlers to use the same single or primary tracer as Causeway.
+
+
+
== Further reading
* https://spring.io/blog/2025/11/18/opentelemetry-with-spring-boot[OpenTelemetry with Spring Boot]
(causeway) 05/10: CAUSEWAY-4068: archive JPA duration filtering removal
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 40b05d438714b43540bb263381e0fd6facae518c
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 11:18:24 2026 +0100
CAUSEWAY-4068: archive JPA duration filtering removal
---
.../.openspec.yaml | 0
.../design.md | 0
.../proposal.md | 0
.../specs/main-observation-foundation/spec.md | 0
.../specs/observation-policy/spec.md | 0
.../tasks.md | 0
.../validation.md | 2 ++
.../general-span-duration-filtering.md | 2 +-
.../planned-changes/otel-forward-port-roadmap.md | 4 ++-
openspec/specs/main-observation-foundation/spec.md | 32 +----------------
openspec/specs/observation-policy/spec.md | 40 ++++++++++------------
11 files changed, 26 insertions(+), 54 deletions(-)
diff --git a/openspec/changes/remove-jpa-duration-filtering/.openspec.yaml b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/.openspec.yaml
similarity index 100%
rename from openspec/changes/remove-jpa-duration-filtering/.openspec.yaml
rename to openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/.openspec.yaml
diff --git a/openspec/changes/remove-jpa-duration-filtering/design.md b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/design.md
similarity index 100%
rename from openspec/changes/remove-jpa-duration-filtering/design.md
rename to openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/design.md
diff --git a/openspec/changes/remove-jpa-duration-filtering/proposal.md b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/proposal.md
similarity index 100%
rename from openspec/changes/remove-jpa-duration-filtering/proposal.md
rename to openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/proposal.md
diff --git a/openspec/changes/remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md
similarity index 100%
rename from openspec/changes/remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md
rename to openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/specs/main-observation-foundation/spec.md
diff --git a/openspec/changes/remove-jpa-duration-filtering/specs/observation-policy/spec.md b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/specs/observation-policy/spec.md
similarity index 100%
rename from openspec/changes/remove-jpa-duration-filtering/specs/observation-policy/spec.md
rename to openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/specs/observation-policy/spec.md
diff --git a/openspec/changes/remove-jpa-duration-filtering/tasks.md b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/tasks.md
similarity index 100%
rename from openspec/changes/remove-jpa-duration-filtering/tasks.md
rename to openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/tasks.md
diff --git a/openspec/changes/remove-jpa-duration-filtering/validation.md b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/validation.md
similarity index 93%
rename from openspec/changes/remove-jpa-duration-filtering/validation.md
rename to openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/validation.md
index 5492650e2be..1119f33b3e7 100644
--- a/openspec/changes/remove-jpa-duration-filtering/validation.md
+++ b/openspec/changes/archive/2026-10-07-remove-jpa-duration-filtering/validation.md
@@ -26,3 +26,5 @@ Clean rebuild verified that the threshold property is absent from generated meta
Shell syntax and mock-Maven invocations verify launcher profile/agent forwarding with no threshold setting in either mode. Both observability guides and the sample README render with Asciidoctor and their YAML examples parse. `git diff --check` and strict OpenSpec change validation pass. Full repository regressions and live browser/backend sessions were not repeated.
Archive checkpoint validation uses the same focused `test` goals (without clean/install) and the tracing suite after the implementation commit, as required by the one-shot workflow.
+
+Post-checkpoint archive validation passed: the same 25 focused and 9 tracing/JPA tests succeeded after `39faf40ded6`. All tasks are complete and both capability deltas have been synced during archive.
diff --git a/openspec/planned-changes/general-span-duration-filtering.md b/openspec/planned-changes/general-span-duration-filtering.md
index 245f2735626..bd7faea53b3 100644
--- a/openspec/planned-changes/general-span-duration-filtering.md
+++ b/openspec/planned-changes/general-span-duration-filtering.md
@@ -17,4 +17,4 @@ Questions to investigate:
Do not promise duration-based suppression at span creation: duration is known only when the span ends. Prefer complete trace trees until a coherent, tested policy is available.
-Predecessor: `remove-jpa-duration-filtering`, following `classify-and-correlate-observations` on CAUSEWAY-4068-v4.
+Predecessor: [remove JPA duration filtering](../changes/archive/2026-10-07-remove-jpa-duration-filtering/proposal.md), following [classification/correlation](../changes/archive/2026-10-07-classify-and-correlate-observations/proposal.md) on CAUSEWAY-4068-v4.
diff --git a/openspec/planned-changes/otel-forward-port-roadmap.md b/openspec/planned-changes/otel-forward-port-roadmap.md
index e06e788a1e8..a1b5dc1dc74 100644
--- a/openspec/planned-changes/otel-forward-port-roadmap.md
+++ b/openspec/planned-changes/otel-forward-port-roadmap.md
@@ -115,4 +115,6 @@ Filtering simplification: `causeway.observation.jpa-duration-threshold` is the o
Classification/correlation archived on 2026-10-07 after fresh focused and tracing validation passed (45 tests), with all delta specs synced. General duration filtering is tracked in [the exploration backlog](general-span-duration-filtering.md); the narrow JPA-only threshold is being removed in a separate change.
-The [remove-jpa-duration-filtering](../changes/remove-jpa-duration-filtering/proposal.md) change removes the narrow JPA-only policy while preserving explicit transaction discard behavior. Replacement filtering is deferred to [general span duration filtering](general-span-duration-filtering.md).
+The [remove-jpa-duration-filtering](../changes/archive/2026-10-07-remove-jpa-duration-filtering/proposal.md) change removes the narrow JPA-only policy while preserving explicit transaction discard behavior. Replacement filtering is deferred to [general span duration filtering](general-span-duration-filtering.md).
+
+Removal completed and archived on 2026-10-07. Implementation checkpoint `39faf40ded6`; 34 Java tests passed before and after the checkpoint, Petclinic packaged successfully, and metadata/docs/launcher checks passed. Specs were synced; all completed changes are archived. General span filtering remains backlog exploration.
diff --git a/openspec/specs/main-observation-foundation/spec.md b/openspec/specs/main-observation-foundation/spec.md
index 5027255ac11..e301c78d0a2 100644
--- a/openspec/specs/main-observation-foundation/spec.md
+++ b/openspec/specs/main-observation-foundation/spec.md
@@ -1,9 +1,7 @@
## Purpose
-Defines the observation lifecycle, opt-in activation, telemetry ownership, duration filtering, and compatibility guarantees that support main-branch telemetry forward ports.
-
+Defines the observation lifecycle, opt-in activation, telemetry ownership and compatibility guarantees that support main-branch telemetry forward ports.
## Requirements
-
### Requirement: Observation ownership has deterministic cleanup
The lifecycle helper SHALL close each successfully opened scope and attempt to stop each owned started observation exactly once, in that order, and SHALL release references even when cleanup fails. It SHALL reject a second start while an observation is active.
@@ -76,34 +74,6 @@ The implementation SHALL preserve a supported Boot-managed configuration and doc
- **WHEN** a failing observed request is followed by another request
- **THEN** the failure is recorded and the later request does not inherit stale observation state
-### Requirement: Duration filtering survives observation composition
-
-Threshold observations SHALL retain their stop policy through fluent customization, start and lifecycle-helper use. Duration filtering SHALL be disabled by default. When explicitly enabled in configurations supporting duration filtering, successful observations below the threshold SHALL be suppressed and observations at or above it SHALL remain eligible for export. Failed observations SHALL NOT be suppressed solely for short duration. Any agent-mode limitation on discard export SHALL be [...]
-
-#### Scenario: Fluent customization before start
-- **WHEN** a threshold observation is customized through its fluent methods and run through the lifecycle helper
-- **THEN** its threshold policy still runs when it stops
-
-#### Scenario: Successful short observation
-- **WHEN** a successful observation completes below the threshold with filtering explicitly enabled in a configuration supporting duration filtering
-- **THEN** it is discarded from export
-
-#### Scenario: Successful longer observation
-- **WHEN** a successful observation reaches or exceeds the threshold
-- **THEN** duration filtering does not discard it
-
-#### Scenario: Short failed observation
-- **WHEN** an observation fails below the threshold
-- **THEN** duration filtering does not discard the failure
-
-#### Scenario: Agent exporter cannot enforce discard
-- **WHEN** the documented agent configuration cannot apply the existing discard policy
-- **THEN** duration suppression is explicitly disabled for that configuration, collected spans confirm retention, and the configuration guide explains the limitation
-
-#### Scenario: Filtering disabled by default
-- **WHEN** a successful observation completes below the threshold without explicitly enabled duration filtering
-- **THEN** duration filtering does not discard it
-
### Requirement: Foundation reconciliation preserves main instrumentation
The change SHALL preserve main's existing interaction, action, property modification, transaction, execution-publishing and JPA instrumentation boundaries. It SHALL use main-compatible dependency and toolchain versions rather than maintenance's compatibility pins.
diff --git a/openspec/specs/observation-policy/spec.md b/openspec/specs/observation-policy/spec.md
index 2ea87e2929b..50bb2949bcb 100644
--- a/openspec/specs/observation-policy/spec.md
+++ b/openspec/specs/observation-policy/spec.md
@@ -1,6 +1,6 @@
## Purpose
-Defines stable observation operation names, safe contextual metadata, automatic interaction identity, and configurable JPA duration filtering.
+Defines stable observation operation names, safe contextual metadata, automatic interaction identity, and duration-independent JPA span retention.
## Requirements
### Requirement: Existing dynamic observations separate operation and context
@@ -22,29 +22,12 @@ Causeway SHALL use fixed operation names for the existing JPA, action invocation
- **WHEN** a safe static identifier is unavailable
- **THEN** the operation remains observable without falling back to object or query descriptions
-### Requirement: JPA duration policy is explicit and validated
-
-Causeway SHALL expose only `causeway.observation.jpa-duration-threshold` for JPA duration filtering, defaulting to `0ms`. Zero SHALL disable filtering. A positive threshold SHALL mark successful JPA observations strictly below the threshold for discard; failed observations SHALL remain eligible for export. Negative or malformed thresholds SHALL fail startup. No separate duration-filtering-enabled property SHALL exist. Agent-owned export SHALL require a zero threshold because it does not [...]
-
-#### Scenario: Default or zero threshold
-- **WHEN** the threshold is zero or unset
-- **THEN** short successful JPA spans are retained
-
-#### Scenario: Positive threshold
-- **WHEN** the threshold is positive
-- **THEN** shorter successful JPA spans are marked for discard while failed spans remain eligible
-
-#### Scenario: Invalid threshold
-- **WHEN** the threshold is negative or malformed
-- **THEN** startup reports a configuration error
-
### Requirement: Telemetry migration and limitations are documented
-
-The observability guide SHALL describe operation/contextual names, automatic identity attributes, duration defaults and configuration examples. It SHALL distinguish Micrometer operation identity from exported contextual names, explain that enabled duration filtering can leave missing parents, and retain the supported agent requirement to disable filtering. Migration SHALL NOT introduce duplicate legacy spans.
+The observability guide SHALL describe operation/contextual names, automatic identity attributes and the removal of JPA duration filtering. It SHALL distinguish Micrometer operation identity from exported contextual names and explain that normal sampling/export policy still determines available traces. It SHALL NOT present the removed threshold as supported configuration or require agent-specific duration overrides. Migration SHALL NOT introduce duplicate legacy spans.
#### Scenario: Existing deployment migrates
-- **WHEN** an operator follows the migration guide
-- **THEN** they can inspect automatically included identity attributes and choose supported duration filtering without another SDK or duplicate observations
+- **WHEN** an operator follows the guide
+- **THEN** they can remove obsolete threshold settings and inspect complete eligible JPA/JDBC ancestry without configuring a replacement Causeway filter
### Requirement: Interaction identity metadata is automatic
@@ -58,3 +41,18 @@ Causeway SHALL always include nonempty username and multitenancy-token values un
- **WHEN** an observed interaction has empty or missing identity values
- **THEN** those attributes are omitted without inventing values
+### Requirement: JPA observations are independent of duration
+Causeway SHALL retain successful and failed JPA observations regardless of their duration, subject to ordinary sampling and export policies. It SHALL NOT expose a JPA duration threshold or automatically mark these observations for discard by duration. Existing operation/contextual names, metadata and ancestry SHALL remain intact in Boot-managed and agent-managed tracing. Explicit discard behavior outside duration filtering SHALL be preserved.
+
+#### Scenario: Short successful operation
+- **WHEN** a short JPA operation completes successfully under either supported tracing owner
+- **THEN** its JPA span remains eligible for export with its children and parentage intact
+
+#### Scenario: Failed operation
+- **WHEN** a JPA operation fails
+- **THEN** its error and observation lifecycle remain intact regardless of duration
+
+#### Scenario: Obsolete configuration
+- **WHEN** an application still supplies the removed JPA threshold property
+- **THEN** it has no effect on Causeway's JPA observation retention and is absent from configuration metadata
+
(causeway) 06/10: CAUSEWAY-4068 : further improvements to otel docs
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit c645011fe65e6e0c7b76513281eff9621b87f43b
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 11:59:55 2026 +0100
CAUSEWAY-4068 : further improvements to otel docs
---
.../ROOT/pages/2026/4.0.0-M3/otel-howto.adoc | 50 ++++++++++++----------
1 file changed, 27 insertions(+), 23 deletions(-)
diff --git a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
index 11ee6da8de9..eb043efad60 100644
--- a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
+++ b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
@@ -34,6 +34,8 @@ All published ports bind to localhost.
|http://localhost:3000 |Grafana; initial login `admin` / `admin`
|===
+Grafana queries the internal Docker address `http://prometheus:9090`; Petclinic sends to `localhost:9090` from the host.
+
Jaeger stores traces in memory, so
restarting Jaeger will lose earlier traces.
Similarly Prometheus and Grafana data directories use size-limited tmpfs; there are no persistent data volumes or host data directories.
@@ -75,7 +77,6 @@ The launcher activates the Maven `observation` profile for dependencies and the
Boot owns the tracing, no Java agent is attached in this mode.
Micrometer meanwhile exports metrics.
-
The petclinic sample is configured so that traces are fully sampled and metrics are exported every ten seconds (this can be adjusted, see xref:#configuring-your-own-app[below].)
@@ -118,7 +119,7 @@ NOTE: instance identifiers, object titles or argument values and _not_ included
* on the top-level "entry" span, inspect its *attributes*:
-* causeway.execution.mode
+** `causeway.execution.mode`
+
Records if the interaction was performed in the foreground (as the result of a user request) or in the background (from the command log extension's xref:refguide:extensions:index/commandlog/applib/job/RunBackgroundCommandsJob.adoc[] ).
@@ -154,16 +155,17 @@ Repeat operations on different owners:
* the Causeway member/JPA span names and member/entity metadata describe the operation and type
-Open Prometheus directly or log in to Grafana (`admin` / `admin`).
+In Prometheus:
+
+* open directly
In Grafana:
+* log in (`admin` / `admin`).
* choose *Explore*
* select *Prometheus* in the data-source dropdown at the top, and switch to *Code* mode for PromQL.
-
-The built-in *Grafana* data source and its *Random Walk* query show generated sample data, not Petclinic metrics.
-
-* The provisioned *Prometheus* source is also listed under *Connections → Data sources*.
++
+The provisioned *Prometheus* source is also listed under *Connections → Data sources*.
After exercising Petclinic, wait 10–20 seconds and try these queries separately:
@@ -185,14 +187,17 @@ count by (__name__) ({__name__=~"causeway_.*"})
Note that Prometheus metric names can differ from contextual span names, including dots translated to underscores.
-The username and tenancy attributes are high-cardinality span metadata; they are not added as Micrometer metric labels.
-
-* Grafana queries the internal Docker address `http://prometheus:9090`; Petclinic sends to `localhost:9090` from the host.
== Switch to agent-managed tracing
+The principal benefit of using agent-managed tracing is that the agent will instrument libraries, such as JDBC access.
+When an agent is configured, the agent now owns the tracing SDK, exporter and automatic HTTP/JDBC instrumentation.
+A registry bridge joins Causeway observations to that context, and the `agent` profile disables competing Boot tracing auto-configuration.
+
+### Try it out
+
Stop the Boot-managed Petclinic process, keep all three telemetry services running, then add `--agent`:
[source,bash]
@@ -200,16 +205,10 @@ Stop the Boot-managed Petclinic process, keep all three telemetry services runni
./viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh --agent
----
-The same Jaeger and Prometheus/Grafana UIs remain available.
-User attributes are included in both modes; inspect new traces after exercising the app again.
-The agent now owns the tracing SDK, exporter and automatic HTTP/JDBC instrumentation.
-An explicit registry bridge joins Causeway observations to that context, and the `agent` profile disables competing Boot tracing auto-configuration.
-Micrometer continues to export metrics to Prometheus in both modes; the agent's own metrics exporter is disabled to avoid duplicate JVM metrics.
-This setup also disables agent log export.
+On first use, Maven downloads the (compatible) agent into the sample's `target/agents/` directory.
+The launcher attaches it to the application JVM with `spring-boot.run.agents`.
-On first use, Maven obtains agent 2.31.1 (matching the compatibility fixture) into the sample's `target/agents/` directory.
-The launcher attaches it to the application JVM with `spring-boot.run.agents`, not to Maven itself.
-Use an existing JAR if preferred:
+Alternatively, the location of the agent JAR can be specified using an env var:
[source,bash]
----
@@ -217,12 +216,17 @@ OTEL_JAVAAGENT_PATH=/absolute/path/to/opentelemetry-javaagent.jar \
./viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh --agent
----
+
+Micrometer continues to export metrics to Prometheus in both modes; the agent's own metrics exporter is disabled to avoid duplicate JVM metrics.
+This setup also disables agent log export.
+
The launcher sets OTLP HTTP trace export, full trace sampling and service name *Causeway Pet Clinic* by default.
-`OTEL_SERVICE_NAME` and `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` can override the agent's trace service name and destination.
-Micrometer's destination remains the separately configured metrics URL.
-Inspect a database-backed operation in Jaeger: framework spans should join automatic HTTP/JDBC context without duplicate framework export.
-The launcher reserves profile, agent and application-argument settings so conflicting Maven options cannot silently replace this wiring.
+Some config can be overridden using env vars:
+
+* `OTEL_SERVICE_NAME` - agent's trace service name
+* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` - agent's trace destination
+
[#apply-the-same-change-to-another-application]
=== Apply the same change to another application
(causeway) 09/10: CAUSEWAY-4068: archive built-in agent observation bridge
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 570ced3d7a61e8a086ff8966111b36a0ca998298
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 12:12:01 2026 +0100
CAUSEWAY-4068: archive built-in agent observation bridge
---
.../2026-10-07-provide-agent-observation-bridge}/.openspec.yaml | 0
.../2026-10-07-provide-agent-observation-bridge}/design.md | 0
.../2026-10-07-provide-agent-observation-bridge}/proposal.md | 0
.../specs/agent-observation-bridge/spec.md | 0
.../2026-10-07-provide-agent-observation-bridge}/tasks.md | 0
.../2026-10-07-provide-agent-observation-bridge}/validation.md | 2 +-
openspec/planned-changes/otel-forward-port-roadmap.md | 4 +++-
.../specs/agent-observation-bridge/spec.md | 6 +++++-
8 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/openspec/changes/provide-agent-observation-bridge/.openspec.yaml b/openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/.openspec.yaml
similarity index 100%
rename from openspec/changes/provide-agent-observation-bridge/.openspec.yaml
rename to openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/.openspec.yaml
diff --git a/openspec/changes/provide-agent-observation-bridge/design.md b/openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/design.md
similarity index 100%
rename from openspec/changes/provide-agent-observation-bridge/design.md
rename to openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/design.md
diff --git a/openspec/changes/provide-agent-observation-bridge/proposal.md b/openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/proposal.md
similarity index 100%
rename from openspec/changes/provide-agent-observation-bridge/proposal.md
rename to openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/proposal.md
diff --git a/openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md b/openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
similarity index 100%
copy from openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
copy to openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
diff --git a/openspec/changes/provide-agent-observation-bridge/tasks.md b/openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/tasks.md
similarity index 100%
rename from openspec/changes/provide-agent-observation-bridge/tasks.md
rename to openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/tasks.md
diff --git a/openspec/changes/provide-agent-observation-bridge/validation.md b/openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/validation.md
similarity index 83%
rename from openspec/changes/provide-agent-observation-bridge/validation.md
rename to openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/validation.md
index 5e55ff19bcf..4bc2cfe64c4 100644
--- a/openspec/changes/provide-agent-observation-bridge/validation.md
+++ b/openspec/changes/archive/2026-10-07-provide-agent-observation-bridge/validation.md
@@ -9,4 +9,4 @@
The framework creates no SDK/exporter and does not attach the agent. Custom registry owners remain responsible for tracing handlers. Boot HTTP observation meters are absent in agent mode by design; other Micrometer metrics remain available. No interactive Docker/browser verification was performed in this change.
-Before archive, the focused and exported-trace suites are rerun after the implementation checkpoint; results recorded below.
+Post-checkpoint verification after `906a897df37`: the same 19 focused tests and 9 exported-trace/JPA tests passed, Petclinic packaged against the final installed bridge, both AsciiDoc pages rendered, and strict validation passed all 54 OpenSpec items.
diff --git a/openspec/planned-changes/otel-forward-port-roadmap.md b/openspec/planned-changes/otel-forward-port-roadmap.md
index d44a966fda1..5734ebe858d 100644
--- a/openspec/planned-changes/otel-forward-port-roadmap.md
+++ b/openspec/planned-changes/otel-forward-port-roadmap.md
@@ -121,4 +121,6 @@ Removal completed and archived on 2026-10-07. Implementation checkpoint `39faf40
## Built-in agent bridge follow-up (2026-10-07)
-[provide-agent-observation-bridge](../changes/provide-agent-observation-bridge/proposal.md) moves the sample-owned bridge and manual exclusions into core configuration. Consumers add the optional BOM-managed OTel bridge dependency, attach the Java agent and activate `observation,agent`; Micrometer metrics remain independent. Exported-trace fixtures exercise the production configuration. This is an adoption simplification on CAUSEWAY-4068, with general duration filtering still deferred.
+[provide-agent-observation-bridge](../changes/archive/2026-10-07-provide-agent-observation-bridge/proposal.md) moves the sample-owned bridge and manual exclusions into core configuration. Consumers add the optional BOM-managed OTel bridge dependency, attach the Java agent and activate `observation,agent`; Micrometer metrics remain independent. Exported-trace fixtures exercise the production configuration. This is an adoption simplification on CAUSEWAY-4068, with general duration filterin [...]
+
+Agent bridge completed and archived on 2026-10-07: planning `a7c3d3584e6`, implementation `906a897df37`. All 28 focused/exported-trace tests passed before and after the checkpoint, including Micrometer metrics export in agent mode; Petclinic packaging and documentation rendering passed. The new agent bridge requirements are synced to the main specs.
diff --git a/openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md b/openspec/specs/agent-observation-bridge/spec.md
similarity index 92%
rename from openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
rename to openspec/specs/agent-observation-bridge/spec.md
index 38f02a93a01..f7c867a1d4d 100644
--- a/openspec/changes/provide-agent-observation-bridge/specs/agent-observation-bridge/spec.md
+++ b/openspec/specs/agent-observation-bridge/spec.md
@@ -1,5 +1,8 @@
-## ADDED Requirements
+# agent-observation-bridge Specification
+## Purpose
+Defines built-in bridging of Causeway observations to Java-agent-owned tracing, profile-based telemetry ownership and application customization guarantees.
+## Requirements
### Requirement: Agent tracing is available without application bridge code
Causeway SHALL supply a missing Micrometer tracer wrapping GlobalOpenTelemetry and a missing observation registry using that tracer when observation and agent profiles are active with the OTel bridge dependency. It SHALL NOT construct an SDK or exporter. A missing bridge dependency SHALL produce an actionable startup error. Agent attachment SHALL remain the application's responsibility.
@@ -43,3 +46,4 @@ The bridge SHALL back off from application-provided registry or tracer beans. Th
#### Scenario: Custom registry
- **WHEN** an application supplies a registry with both profiles active
- **THEN** the bridge does not replace it or install additional handlers into it
+
(causeway) 02/10: CAUSEWAY-4068: archive observation classification and correlation
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 3b52d532fe75311cc9982a1a733a102447981d01
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 11:11:17 2026 +0100
CAUSEWAY-4068: archive observation classification and correlation
---
.../.openspec.yaml | 0
.../design.md | 0
.../proposal.md | 0
.../specs/interaction-trace-correlation/spec.md | 0
.../observation-execution-classification/spec.md | 0
.../specs/observation-policy/spec.md | 0
.../tasks.md | 0
.../validation.md | 0
.../planned-changes/otel-forward-port-roadmap.md | 8 +--
.../specs/interaction-trace-correlation/spec.md | 6 ++-
.../observation-execution-classification/spec.md | 6 ++-
openspec/specs/observation-policy/spec.md | 59 +++++++++-------------
12 files changed, 40 insertions(+), 39 deletions(-)
diff --git a/openspec/changes/classify-and-correlate-observations/.openspec.yaml b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/.openspec.yaml
similarity index 100%
rename from openspec/changes/classify-and-correlate-observations/.openspec.yaml
rename to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/.openspec.yaml
diff --git a/openspec/changes/classify-and-correlate-observations/design.md b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/design.md
similarity index 100%
rename from openspec/changes/classify-and-correlate-observations/design.md
rename to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/design.md
diff --git a/openspec/changes/classify-and-correlate-observations/proposal.md b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/proposal.md
similarity index 100%
rename from openspec/changes/classify-and-correlate-observations/proposal.md
rename to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/proposal.md
diff --git a/openspec/changes/classify-and-correlate-observations/specs/interaction-trace-correlation/spec.md b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/specs/interaction-trace-correlation/spec.md
similarity index 100%
copy from openspec/changes/classify-and-correlate-observations/specs/interaction-trace-correlation/spec.md
copy to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/specs/interaction-trace-correlation/spec.md
diff --git a/openspec/changes/classify-and-correlate-observations/specs/observation-execution-classification/spec.md b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/specs/observation-execution-classification/spec.md
similarity index 100%
copy from openspec/changes/classify-and-correlate-observations/specs/observation-execution-classification/spec.md
copy to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/specs/observation-execution-classification/spec.md
diff --git a/openspec/changes/classify-and-correlate-observations/specs/observation-policy/spec.md b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/specs/observation-policy/spec.md
similarity index 100%
rename from openspec/changes/classify-and-correlate-observations/specs/observation-policy/spec.md
rename to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/specs/observation-policy/spec.md
diff --git a/openspec/changes/classify-and-correlate-observations/tasks.md b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/tasks.md
similarity index 100%
rename from openspec/changes/classify-and-correlate-observations/tasks.md
rename to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/tasks.md
diff --git a/openspec/changes/classify-and-correlate-observations/validation.md b/openspec/changes/archive/2026-10-07-classify-and-correlate-observations/validation.md
similarity index 100%
rename from openspec/changes/classify-and-correlate-observations/validation.md
rename to openspec/changes/archive/2026-10-07-classify-and-correlate-observations/validation.md
diff --git a/openspec/planned-changes/otel-forward-port-roadmap.md b/openspec/planned-changes/otel-forward-port-roadmap.md
index f1095c252d9..7660f8128e9 100644
--- a/openspec/planned-changes/otel-forward-port-roadmap.md
+++ b/openspec/planned-changes/otel-forward-port-roadmap.md
@@ -12,7 +12,7 @@ Main already contains CAUSEWAY-3975, including instrumentation absent from the s
|---|---|---|---|
| 1 | 3975: foundation reconciliation | **Archived:** [completed change](../changes/archive/2026-09-27-reconcile-main-observation-foundation/proposal.md). Lifecycle cleanup, activation/registry selection, Boot/agent compatibility evidence, threshold behavior, preservation of main instrumentation. | `9b8b66d5b7b`, `e6752f882b5`; compare main's original 3975 implementation |
| 1b | CAUSEWAY-4096: standardize observation names, metadata and duration-filtering policy | **Archived (2026-10-05):** [standardize observation policy](../changes/archive/2026-10-05-standardize-observation-policy/proposal.md) on branch `CAUSEWAY-4096`. Follow-up to 3975: stable operation names vs contextual names; review bookmark/query-description names, identity attributes, hard-coded duration threshold and missing exported parents. Set policy before adding new semantic names. | Main [...]
-| 2 | CAUSEWAY-4068 (3975/4068 provenance): classification and correlation | **Implemented; awaiting archive:** [classify and correlate observations](../changes/classify-and-correlate-observations/proposal.md). Foreground/background classification, root interaction ID, fixed execution-mode attribute key. Depends on foundation; adapt to main's interaction layers. | `024bd33128d`, `0325232a3c7`, `3083a51eabd` |
+| 2 | CAUSEWAY-4068 (3975/4068 provenance): classification and correlation | **Archived (2026-10-07):** [classify and correlate observations](../changes/archive/2026-10-07-classify-and-correlate-observations/proposal.md). Foreground/background classification, root interaction ID, fixed execution-mode attribute key. Depends on foundation; adapt to main's interaction layers. | `024bd33128d`, `0325232a3c7`, `3083a51eabd` |
| 3 | 4058: current action context | Planned. Exact-current-action queries and rule-checking state; nested restoration and physical mixin receiver semantics. Can proceed independently of most telemetry work. | `6d037db6609`; account for `f66b8f17a89` |
| 4 | 4062: semantic member names | Planned. Bounded operation categories with domain-facing contextual names; mixed-in property/collection access distinguished from actions. Depends on foundation and naming policy. | `a2ae5877c99`, `d5ecd49a1f3`, `45e01ff6f42` |
| 5 | 4059 + relevant 4062: Wicket regions/preparation | Planned. Entity-page preparation, region rendering and action prompts; Ajax ancestry, serialization and failure cleanup. The 4062 source touches Wicket as well as core: extract the relevant portions here rather than blindly applying step 4 commits. | `a8264fd1f9b`, relevant parts of `a2ae5877c99` / `45e01ff6f42` |
@@ -99,16 +99,18 @@ Implementation checkpoint: planning committed as `5390171e070`; all 11 implement
## Classification and correlation proposal (2026-10-06)
-Step 2 is captured in [classify-and-correlate-observations](../changes/classify-and-correlate-observations/proposal.md), based on local `6f10ac34cac` after CAUSEWAY-4096. CAUSEWAY-3975/4068 identify maintenance provenance. The user selected `CAUSEWAY-4068-v4` for the main-side implementation under CAUSEWAY-4068.
+Step 2 is captured in [classify-and-correlate-observations](../changes/archive/2026-10-07-classify-and-correlate-observations/proposal.md), based on local `6f10ac34cac` after CAUSEWAY-4096. CAUSEWAY-3975/4068 identify maintenance provenance. The user selected `CAUSEWAY-4068-v4` for the main-side implementation under CAUSEWAY-4068.
The design adapts current-span classification to main's shared Micrometer tracer for both Boot and agent ownership, uses the fixed `causeway.execution.mode` attribute, and correlates only the existing root interaction observation. It explicitly handles main's replay-time replacement of command identity, avoids introducing job spans when instrumentation supplies none, and keeps semantic naming and rendering detail for later steps.
Before creating the proposal, completed `port-domain-object-locking` was validated and [archived](../changes/archive/2026-10-06-port-domain-object-locking/proposal.md), with its requirements synced. The focused reactor verification passed, including all eight pessimistic-locking regressions. The archive checkpoint was committed as `e65505dbe0c` after session permissions allowed Git writes; the new planning artifacts follow in a separate proposal commit.
-Implementation checkpoint (2026-10-06): all 12 tasks completed on `CAUSEWAY-4068-v4`. [Validation evidence](../changes/classify-and-correlate-observations/validation.md) records 31 focused tests, 9 tracing/JPA compatibility tests, Petclinic packaging, generated configuration metadata and documentation checks. The M3 how-to and observability guide now cover foreground/background entry attributes, root interaction UUIDs, separate Jaeger searches and the background fixture. Agent setup shar [...]
+Implementation checkpoint (2026-10-06): all 12 tasks completed on `CAUSEWAY-4068-v4`. [Validation evidence](../changes/archive/2026-10-07-classify-and-correlate-observations/validation.md) records 31 focused tests, 9 tracing/JPA compatibility tests, Petclinic packaging, generated configuration metadata and documentation checks. The M3 how-to and observability guide now cover foreground/background entry attributes, root interaction UUIDs, separate Jaeger searches and the background fixtur [...]
Review follow-up: removed the execution-mode key override at the user’s request. Both entry points always use `causeway.execution.mode`; configuration, tests and operator guidance have been simplified accordingly.
Review follow-up (2026-10-07): username and tenancy are now automatic nonempty interaction attributes alongside the root UUID and fixed entry execution mode. Removed identity opt-in properties and launcher scenarios; duration-filtering policy remains available.
Filtering simplification: `causeway.observation.jpa-duration-threshold` is the only filtering setting, with `0ms` disabling filtering. The launcher no longer takes `--scenario`; set `CAUSEWAY_OBSERVATION_JPA_DURATION_THRESHOLD` for Boot-managed runs.
+
+Classification/correlation archived on 2026-10-07 after fresh focused and tracing validation passed (45 tests), with all delta specs synced. General duration filtering is tracked in [the exploration backlog](general-span-duration-filtering.md); the narrow JPA-only threshold is being removed in a separate change.
diff --git a/openspec/changes/classify-and-correlate-observations/specs/interaction-trace-correlation/spec.md b/openspec/specs/interaction-trace-correlation/spec.md
similarity index 94%
rename from openspec/changes/classify-and-correlate-observations/specs/interaction-trace-correlation/spec.md
rename to openspec/specs/interaction-trace-correlation/spec.md
index 5e692039fb8..531b4bd49af 100644
--- a/openspec/changes/classify-and-correlate-observations/specs/interaction-trace-correlation/spec.md
+++ b/openspec/specs/interaction-trace-correlation/spec.md
@@ -1,5 +1,8 @@
-## ADDED Requirements
+# interaction-trace-correlation Specification
+## Purpose
+TBD - created by archiving change classify-and-correlate-observations. Update Purpose after archive.
+## Requirements
### Requirement: Root interaction UUID correlation
When observation is active, the existing root interaction span SHALL contain high-cardinality string attribute `causeway.interaction.id`, equal to the canonical UUID returned by its own interaction's `getInteractionId()` at completion. The attribute SHALL be independent of username and tenancy attributes and SHALL NOT become a metric label.
@@ -39,3 +42,4 @@ The observability guide SHALL show execution-mode attributes on the applicable e
#### Scenario: Verify default and custom classification
- **WHEN** an operator follows the foreground and background examples with the default or an overridden key
- **THEN** the guide identifies the span to inspect, the exact expected attribute and the requirement for an existing instrumented job span, without implying that every child carries the attribute
+
diff --git a/openspec/changes/classify-and-correlate-observations/specs/observation-execution-classification/spec.md b/openspec/specs/observation-execution-classification/spec.md
similarity index 94%
rename from openspec/changes/classify-and-correlate-observations/specs/observation-execution-classification/spec.md
rename to openspec/specs/observation-execution-classification/spec.md
index 15ba98b5396..2f4e611eddc 100644
--- a/openspec/changes/classify-and-correlate-observations/specs/observation-execution-classification/spec.md
+++ b/openspec/specs/observation-execution-classification/spec.md
@@ -1,5 +1,8 @@
-## ADDED Requirements
+# observation-execution-classification Specification
+## Purpose
+TBD - created by archiving change classify-and-correlate-observations. Update Purpose after archive.
+## Requirements
### Requirement: Foreground HTTP entry classification
With the observation profile active and a supported tracing bridge, Causeway SHALL annotate the existing HTTP entry span with execution mode `foreground`. It SHALL NOT create or rename spans, alter parentage, or tag a nested security/viewer span in place of the entry span. Classification SHALL cover framework servlet routes including static resources and SHALL NOT mean that an authenticated human initiated the request.
@@ -47,3 +50,4 @@ Classification SHALL use the configured single/primary tracing bridge and SHALL
#### Scenario: Missing or ambiguous tracer
- **WHEN** no tracer is available, or multiple active tracers have no primary
- **THEN** absence results in safe no-op classification, while ambiguity produces an actionable configuration error
+
diff --git a/openspec/specs/observation-policy/spec.md b/openspec/specs/observation-policy/spec.md
index 924c10c5651..2ea87e2929b 100644
--- a/openspec/specs/observation-policy/spec.md
+++ b/openspec/specs/observation-policy/spec.md
@@ -1,9 +1,7 @@
## Purpose
-Defines stable observation operation names, safe contextual metadata, opt-in interaction identity, and configurable JPA duration filtering.
-
+Defines stable observation operation names, safe contextual metadata, automatic interaction identity, and configurable JPA duration filtering.
## Requirements
-
### Requirement: Existing dynamic observations separate operation and context
Causeway SHALL use fixed operation names for the existing JPA, action invocation, property update and execution-publishing observations. Contextual names and model metadata SHALL use only static entity type, member identity or declared named-query identity. Instance identifiers, bookmarks, object titles, query descriptions and arguments SHALL NOT be introduced into these names or attributes. Execution subscriber counts SHALL NOT be included in names or low-cardinality dimensions. Existin [...]
@@ -24,46 +22,39 @@ Causeway SHALL use fixed operation names for the existing JPA, action invocation
- **WHEN** a safe static identifier is unavailable
- **THEN** the operation remains observable without falling back to object or query descriptions
-### Requirement: Identity metadata requires independent opt-in
-
-Causeway SHALL omit username and multitenancy-token attributes by default. It SHALL support independent `causeway.observation.include-user-name` and `causeway.observation.include-multi-tenancy-token` options, both defaulting to false. Explicitly enabled nonempty values SHALL retain their existing attribute keys and high-cardinality classification. Disabled values SHALL NOT be copied into other Causeway-generated names or attributes. Non-identity interaction metadata SHALL remain available.
-
-#### Scenario: Defaults
-- **WHEN** an observed interaction has a named user and tenancy token without identity opt-ins
-- **THEN** neither identity value is emitted by Causeway interaction tagging
-
-#### Scenario: Independent options
-- **WHEN** exactly one identity option is enabled
-- **THEN** only its corresponding nonempty value is emitted under the existing key
-
-#### Scenario: Both options and empty values
-- **WHEN** both options are enabled
-- **THEN** both nonempty values are emitted, and missing or empty values are omitted
-
### Requirement: JPA duration policy is explicit and validated
-Causeway SHALL default `causeway.observation.duration-filtering-enabled` to false and SHALL support `causeway.observation.jpa-duration-threshold`, defaulting to 2 ms. The threshold SHALL accept zero and positive durations and reject malformed or negative values at startup, including when filtering is disabled. The threshold SHALL apply only to the existing JPA duration-filtered boundaries and SHALL NOT change domain outcomes or observation activation.
+Causeway SHALL expose only `causeway.observation.jpa-duration-threshold` for JPA duration filtering, defaulting to `0ms`. Zero SHALL disable filtering. A positive threshold SHALL mark successful JPA observations strictly below the threshold for discard; failed observations SHALL remain eligible for export. Negative or malformed thresholds SHALL fail startup. No separate duration-filtering-enabled property SHALL exist. Agent-owned export SHALL require a zero threshold because it does not [...]
-#### Scenario: Default parent retention
-- **WHEN** a short successful JPA observation has an exported JDBC child in the deterministically sampled compatibility fixture with default duration policy
-- **THEN** the framework parent remains exported in both supported telemetry configurations and the child's parent ID resolves to it
+#### Scenario: Default or zero threshold
+- **WHEN** the threshold is zero or unset
+- **THEN** short successful JPA spans are retained
-#### Scenario: Configured and zero thresholds
-- **WHEN** Boot-mode filtering is explicitly enabled with a configured threshold
-- **THEN** successful observations below it are suppressed, observations equal to or above it are retained, and zero suppresses none by duration
+#### Scenario: Positive threshold
+- **WHEN** the threshold is positive
+- **THEN** shorter successful JPA spans are marked for discard while failed spans remain eligible
-#### Scenario: Invalid duration
+#### Scenario: Invalid threshold
- **WHEN** the threshold is negative or malformed
-- **THEN** startup reports an actionable configuration failure regardless of whether duration filtering is enabled
-
-#### Scenario: Observation profile inactive
-- **WHEN** policy options are configured but the observation profile is inactive
-- **THEN** Causeway framework observations remain disabled and application observations remain independent
+- **THEN** startup reports a configuration error
### Requirement: Telemetry migration and limitations are documented
-The observability guide SHALL describe changed operation/contextual names, identity opt-ins, duration defaults and configuration examples. It SHALL distinguish Micrometer operation identity from exported contextual names, state that opt-in duration filtering can leave missing parents, and retain the supported agent requirement to disable filtering. It SHALL NOT imply control over application/agent metadata or externally sampled/exported spans. Migration SHALL NOT introduce duplicate lega [...]
+The observability guide SHALL describe operation/contextual names, automatic identity attributes, duration defaults and configuration examples. It SHALL distinguish Micrometer operation identity from exported contextual names, explain that enabled duration filtering can leave missing parents, and retain the supported agent requirement to disable filtering. Migration SHALL NOT introduce duplicate legacy spans.
#### Scenario: Existing deployment migrates
- **WHEN** an operator follows the migration guide
-- **THEN** they can update affected queries and explicitly choose identity and supported duration filtering without requiring another SDK or duplicate observations
+- **THEN** they can inspect automatically included identity attributes and choose supported duration filtering without another SDK or duplicate observations
+
+### Requirement: Interaction identity metadata is automatic
+
+Causeway SHALL always include nonempty username and multitenancy-token values under the existing high-cardinality keys `causeway.user.name` and `causeway.user.multiTenancyToken` on interaction spans. There SHALL be no configuration properties to enable or disable these attributes. Empty values SHALL be omitted and neither value SHALL become a metric label. Other interaction metadata SHALL remain available.
+
+#### Scenario: Defaults
+- **WHEN** an observed interaction has a named user and tenancy token
+- **THEN** both identity values are emitted without configuration
+
+#### Scenario: Missing values
+- **WHEN** an observed interaction has empty or missing identity values
+- **THEN** those attributes are omitted without inventing values
+
(causeway) 01/10: CAUSEWAY-4068: record general span filtering exploration backlog
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 32ce795bd23ac486463da4723ff7b3b3d644ac13
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 11:10:53 2026 +0100
CAUSEWAY-4068: record general span filtering exploration backlog
---
.../general-span-duration-filtering.md | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/openspec/planned-changes/general-span-duration-filtering.md b/openspec/planned-changes/general-span-duration-filtering.md
new file mode 100644
index 00000000000..245f2735626
--- /dev/null
+++ b/openspec/planned-changes/general-span-duration-filtering.md
@@ -0,0 +1,20 @@
+# General span duration filtering
+
+Status: backlog / exploration; no implementation ticket assigned.
+
+Explore whether trivial spans of any kind can be removed by duration, consistently in Boot-managed and Java-agent-managed tracing. This includes Causeway framework spans and automatic HTTP/JDBC/library spans, rather than a JPA-only policy.
+
+The removed JPA threshold decided at parent completion, after children might already have been exported. Dropping a parent alone leaves orphaned JDBC spans and an incomplete trace tree. A replacement must explicitly resolve that problem.
+
+Questions to investigate:
+
+- Where should filtering live: SDK/agent extension, collector, or backend presentation?
+- Can spans and descendants be buffered until a decision is possible, within bounded memory and time limits, including concurrent/async and remote children?
+- When a short parent has a useful child, should the parent be retained, descendants discarded as a group, or surviving children safely reparented? Preserve meaningful ancestry.
+- Retain failures and their useful diagnostic context, even when a failed child belongs to a short successful parent.
+- Distinguish actual export/storage savings from UI-only hiding and from whole-trace sampling. Assess runtime overhead and lost diagnostic value.
+- Define handling for late/missing spans, shutdown, exporter failures and incomplete traces; verify consistent Boot and agent behavior with real exported traces.
+
+Do not promise duration-based suppression at span creation: duration is known only when the span ends. Prefer complete trace trees until a coherent, tested policy is available.
+
+Predecessor: `remove-jpa-duration-filtering`, following `classify-and-correlate-observations` on CAUSEWAY-4068-v4.
(causeway) 04/10: CAUSEWAY-4068: remove JPA duration filtering
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 39faf40ded694586fe632409801d322fe1ea7802
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 11:16:52 2026 +0100
CAUSEWAY-4068: remove JPA duration filtering
---
.../ROOT/pages/2026/4.0.0-M3/otel-howto.adoc | 37 +------
.../modules/ROOT/pages/observability.adoc | 36 ++-----
.../CausewayObservationAutoConfiguration.java | 18 ++--
.../CausewayObservationIntegration.java | 103 +-----------------
.../observation/CausewayObservationPolicy.java | 44 --------
.../additional-spring-configuration-metadata.json | 6 --
.../CausewayObservationAutoConfigurationTest.java | 35 ------
.../ObservationWithTimeThresholdTest.java | 120 ---------------------
.../ia/InteractionServiceObservationTest.java | 5 +-
.../changes/remove-jpa-duration-filtering/tasks.md | 8 +-
.../remove-jpa-duration-filtering/validation.md | 28 +++++
.../planned-changes/otel-forward-port-roadmap.md | 2 +
.../jpa/integration/entity/JpaEntityFacet.java | 4 +-
.../entity/JpaObservationPolicyTest.java | 14 ++-
.../tracing/MicrometerTracingAgentFixture.java | 5 -
.../MicrometerTracingCompatibilityTest.java | 24 ++---
.../sample-htmx-petclinic/README.adoc | 14 +--
.../sample-htmx-petclinic/run-with-telemetry.sh | 4 -
.../main/resources/application-agent.properties | 2 -
19 files changed, 68 insertions(+), 441 deletions(-)
diff --git a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
index 434c110d034..11ee6da8de9 100644
--- a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
+++ b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
@@ -6,7 +6,7 @@
This guide starts with Boot-managed tracing and metrics, then changes tracing ownership to the OpenTelemetry Java agent.
Both modes send traces to Jaeger and Micrometer metrics to Prometheus, with Grafana for exploration.
Only one SDK/export pipeline owns tracing in each run.
-Start with the default configuration to see request, interaction and persistence spans, then try duration filtering in the duration-filtering example below.
+Use the configuration below to see request, interaction and persistence spans.
== Start the local telemetry services
@@ -146,36 +146,6 @@ These attributes are _not_ added to the enclosing HTTP server span.
Note that static asset requests, such as `pdf.worker.min.mjs`, may contain only HTTP/security spans and therefore have no interaction UUID.
-=== Enable duration filtering
-
-The JPA duration threshold defaults to `0ms`, which disables filtering.
-To filter short successful JPA spans, stop Petclinic with Ctrl-C, leave the telemetry services running, and set a positive threshold for the next Boot-managed run:
-
-[source,bash]
-----
-export CAUSEWAY_OBSERVATION_JPA_DURATION_THRESHOLD=1s
-./viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh
-----
-
-This environment variable binds `causeway.observation.jpa-duration-threshold`.
-The example uses one second to make the filtering effect easy to see; choose a smaller threshold such as `2ms` for less aggressive filtering.
-Repeat a database-backed action and compare a new trace with one from the unfiltered run.
-Existing traces are unchanged.
-In Jaeger, look for contextual names such as *Fetch by Bookmark* and *Refresh*, followed by the entity type; these correspond to `causeway.jpa.fetch-by-bookmark` and `causeway.jpa.refresh`.
-Short successful spans with these names below the threshold should disappear when filtering is enabled, while *Transaction*, *Transaction Creation*, *Transaction Completion* and *Action* remain because they are outside the JPA duration filter.
-
-Duration filtering is a span export policy, not a guarantee about metric suppression.
-It can leave exported children whose JPA parent was discarded.
-To check context cleanup, trigger an exception during observed work, then a successful action in the same run; a rejected UI validation alone need not produce an error span.
-
-To return to the defaults, stop Petclinic and unset the overrides before restarting:
-
-[source,bash]
-----
-unset CAUSEWAY_OBSERVATION_JPA_DURATION_THRESHOLD
-----
-
-
=== Micrometer
Underlying Micrometer operation names are categories such as `causeway.member.action` and `causeway.jpa.persist`.
@@ -251,8 +221,6 @@ The launcher sets OTLP HTTP trace export, full trace sampling and service name *
`OTEL_SERVICE_NAME` and `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` can override the agent's trace service name and destination.
Micrometer's destination remains the separately configured metrics URL.
-The launcher disables duration filtering in agent mode: the agent exporter does not consume Spring's discard predicate.
-Use Boot mode to enable duration filtering.
Inspect a database-backed operation in Jaeger: framework spans should join automatic HTTP/JDBC context without duplicate framework export.
The launcher reserves profile, agent and application-argument settings so conflicting Maven options cannot silently replace this wiring.
@@ -319,9 +287,6 @@ management:
tracing:
export:
enabled: false
-causeway:
- observation:
- jpa-duration-threshold: 0ms
----
The exclusions above match the Spring Boot 4.2.0-M1 baseline used by this checkout.
diff --git a/antora/components/userguide/modules/ROOT/pages/observability.adoc b/antora/components/userguide/modules/ROOT/pages/observability.adoc
index e6d42204144..51500b04358 100644
--- a/antora/components/userguide/modules/ROOT/pages/observability.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/observability.adoc
@@ -78,9 +78,6 @@ management:
tracing:
export:
enabled: false
-causeway:
- observation:
- jpa-duration-threshold: 0ms
----
Launch the JVM with `-javaagent:/path/to/opentelemetry-javaagent.jar` and configure the agent's service name, sampler and OTLP endpoint through its normal settings.
@@ -117,33 +114,12 @@ User identity attributes are included automatically alongside interaction correl
In Jaeger, select *All Span Names* and search separately by execution mode or interaction ID: they appear on different spans.
Searches can only find traces that were sampled, exported and retained by the backend.
-== Duration filtering and failures
+== JPA span retention
-The JPA duration threshold defaults to `0ms`, which disables filtering so short successful JPA parents remain available when their children are exported.
-This can increase span volume compared with the previous default.
-It does not override sampling, exporter failures or application-defined filtering.
-
-To opt into the lossy Boot-managed duration filter:
-
-[source,yaml]
-----
-causeway:
- observation:
- jpa-duration-threshold: 2ms
-----
-
-The threshold accepts nonnegative durations; `0ms` suppresses no spans by duration.
-Malformed or negative thresholds fail startup.
-Successful JPA observations strictly below the configured threshold are marked for discard; observations at or above it and failed observations remain eligible for export.
-The Boot-managed export predicate honors that marker.
-
-Filtering can leave children whose parent span was suppressed: discarding a JPA parent cannot retract an independently exported JDBC child.
-This option does not implement tail sampling or a per-request span budget.
-
-The agent-owned exporter does not consume Spring's span-export predicates.
-Keep `jpa-duration-threshold=0ms` in agent mode; the tested behavior retains those spans rather than claiming they were suppressed.
-The setting controls Causeway's JPA duration policy, not agent sampling or application-created observations.
-An explicitly discarded observation is still subject to the exporter's filtering capabilities.
+Causeway retains JPA observations regardless of duration in both Boot-managed and agent-managed tracing.
+The former `causeway.observation.jpa-duration-threshold` property has been removed; remove it and its environment override from existing configurations.
+Filtering a JPA parent independently could leave its JDBC children without an exported parent.
+Normal sampling, exporter failures and application-defined policies still determine which spans reach the backend.
== Observation names and metadata migration (CAUSEWAY-4096)
@@ -185,7 +161,7 @@ These are the tested versions, not a requirement to override the application's C
The child-process fixture uses production interaction and action services and the JPA facet with mocked non-telemetry collaborators, a local JDK HTTP server, H2, and a local OTLP receiver.
Its JPA backend is controlled; agent mode instruments real H2 JDBC automatically, while Boot mode explicitly wraps the SQL call in a fixture observation.
-It proves agent HTTP/framework/JPA/JDBC ancestry, one framework span per invocation, a failure followed by a successful request on the same worker, Boot-managed export, duration filtering, inactive-profile behavior and operation without an agent/exporter.
+It proves agent HTTP/framework/JPA/JDBC ancestry, one framework span per invocation, a failure followed by a successful request on the same worker, Boot-managed export, duration-independent JPA retention, inactive-profile behavior and operation without an agent/exporter.
The additional entry-point fixture uses real Tomcat servlet and Quartz instrumentation with controlled business collaborators, verifying fixed-key classification and effective replay UUIDs in both trace ownership modes.
It does not prove every servlet container or viewer integration, nor a full persisted-command replay.
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java
index 9ba0c8fc4de..816db189e9d 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java
@@ -22,7 +22,6 @@
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
@@ -53,7 +52,6 @@
* own observations or make registry injection ambiguous.
*/
@AutoConfiguration
-@EnableConfigurationProperties(CausewayObservationPolicy.class)
@ConditionalOnClass(ObservationRegistry.class)
@Import({
DiscardedSpanExportingPredicate.class
@@ -61,11 +59,11 @@
public class CausewayObservationAutoConfiguration {
/**
- * Lets the Spring-managed tracing pipeline drop spans marked by Causeway's
- * duration filter or an explicit discard. The marker alone does not suppress
+ * Lets the Spring-managed tracing pipeline drop observations explicitly marked
+ * for discard by Causeway. The marker alone does not suppress
* export: the pipeline must consume this predicate. In particular, an
- * agent-owned exporter does not discover Spring beans, so duration filtering
- * is explicitly disabled in the documented agent configuration.
+ * agent-owned exporter does not discover Spring beans, so explicit discards
+ * are subject to that exporter's policy.
*/
public record DiscardedSpanExportingPredicate() implements SpanExportingPredicate {
@Override
@@ -116,14 +114,10 @@ public CausewayTraceClassifier causewayTraceClassifier(
@Bean
public CausewayObservationIntegration causewayObservationIntegration(
final ObjectProvider<ObservationRegistry> registries,
- final Environment environment,
- final CausewayObservationPolicy policy) {
- // Bind policy even with the profile inactive, so invalid configuration
- // fails at startup. This does not resolve or modify application registries.
+ final Environment environment) {
return new CausewayObservationIntegration(environment.acceptsProfiles(Profiles.of("observation"))
? registries.getIfAvailable(() -> ObservationRegistry.NOOP)
- : ObservationRegistry.NOOP,
- policy);
+ : ObservationRegistry.NOOP);
}
}
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationIntegration.java b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationIntegration.java
index 77ea5b90a71..a3a80cc2b36 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationIntegration.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationIntegration.java
@@ -18,14 +18,12 @@
*/
package org.apache.causeway.core.config.observation;
-import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Function;
-import java.util.function.LongSupplier;
import java.util.function.UnaryOperator;
import org.jspecify.annotations.Nullable;
@@ -39,7 +37,6 @@
import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;
import io.micrometer.observation.Observation.Context;
-import io.micrometer.observation.ObservationConvention;
import io.micrometer.observation.ObservationRegistry;
/**
@@ -54,16 +51,8 @@
* observations to the chosen tracing pipeline and parent context; the
* registry is not itself an SDK or exporter. A null registry is treated
* as no-op for manually constructed instances.
- * @param policy metadata and duration settings, independent of activation and
- * sampling. Duration filtering is opt-in and requires an exporter that
- * consumes the Spring-managed discard predicate.
*/
-public record CausewayObservationIntegration(
- ObservationRegistry observationRegistry, CausewayObservationPolicy policy) {
-
- public CausewayObservationIntegration(final ObservationRegistry observationRegistry) {
- this(observationRegistry, CausewayObservationPolicy.DEFAULT);
- }
+public record CausewayObservationIntegration(ObservationRegistry observationRegistry) {
public CausewayObservationIntegration(
final Optional<ObservationRegistry> observationRegistryOpt) {
@@ -71,7 +60,6 @@ public CausewayObservationIntegration(
}
public CausewayObservationIntegration {
- java.util.Objects.requireNonNull(policy, "policy");
observationRegistry = observationRegistry!=null
? observationRegistry
: ObservationRegistry.NOOP;
@@ -107,15 +95,6 @@ public static UnaryOperator<Observation> withModuleName(final String moduleName)
: obs;
}
- /**
- * Retains all spans at zero; a positive threshold marks short successful
- * observations for discard by the Spring-managed exporter. Failed work is retained.
- */
- public Observation withTimeThreshold(final Observation observation, final Duration threshold) {
- if (threshold.isNegative()) throw new IllegalArgumentException("threshold must not be negative");
- return threshold.isZero() ? observation : new ObservationWithTimeThreshold(observation, threshold);
- }
-
// -- COMMON KEY-VALUES
public static KeyValue currentThreadId() {
@@ -176,84 +155,4 @@ public static void discard(@Nullable final Observation obs) {
ObservationClosure.discard(obs);
}
- /**
- * Measures the observation from start to stop and marks successful work
- * below the threshold for discard. Failed work remains eligible for export
- * regardless of duration. This is an export filter, so it does not avoid the
- * cost of recording the observation or its children.
- *
- * <p>Fluent methods and {@code start()} return this wrapper, not the delegate:
- * callers such as ObservationClosure retain the returned observation and
- * must still reach this wrapper's {@code stop()} to apply the policy.
- */
- public record ObservationWithTimeThreshold(Observation delegate, Duration threshold, Timer timer) implements Observation {
- static class Timer {
- final LongSupplier clock;
- Timer() { this(System::nanoTime); }
- Timer(final LongSupplier clock) { this.clock = clock; }
- long startNanos;
- void start() { this.startNanos = clock.getAsLong(); }
- long elapsedNanos() { return clock.getAsLong() - startNanos; }
- }
- public ObservationWithTimeThreshold(final Observation delegate, final Duration threshold) {
- this(delegate, threshold, new Timer());
- }
- @Override public Observation contextualName(@Nullable final String contextualName) {
- delegate.contextualName(contextualName);
- return this;
- }
- @Override public Observation parentObservation(@Nullable final Observation parentObservation) {
- delegate.parentObservation(parentObservation);
- return this;
- }
- @Override public Observation lowCardinalityKeyValue(final KeyValue keyValue) {
- delegate.lowCardinalityKeyValue(keyValue);
- return this;
- }
- @Override public Observation lowCardinalityKeyValue(final String key, final String value) {
- delegate.lowCardinalityKeyValue(key, value);
- return this;
- }
- @Override public Observation highCardinalityKeyValue(final KeyValue keyValue) {
- delegate.highCardinalityKeyValue(keyValue);
- return this;
- }
- @Override public Observation highCardinalityKeyValue(final String key, final String value) {
- delegate.highCardinalityKeyValue(key, value);
- return this;
- }
- @Override public Observation observationConvention(final ObservationConvention<?> observationConvention) {
- delegate.observationConvention(observationConvention);
- return this;
- }
- @Override public Observation error(final Throwable error) {
- delegate.error(error);
- return this;
- }
- @Override public Observation event(final Event event) {
- delegate.event(event);
- return this;
- }
- @Override public Observation start() {
- timer.start();
- delegate.start();
- return this;
- }
- @Override public Context getContext() {
- return delegate.getContext();
- }
- @Override public void stop() {
- if(delegate.getContext().getError() == null && Duration.ofNanos(timer.elapsedNanos()).compareTo(threshold) < 0) {
- discard(delegate);
- }
- delegate.stop();
- }
- @Override public Scope openScope() {
- return delegate.openScope();
- }
- @Override public String toString() {
- return delegate.toString();
- }
- }
-
}
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationPolicy.java b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationPolicy.java
deleted file mode 100644
index 90cec646897..00000000000
--- a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationPolicy.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.causeway.core.config.observation;
-
-import java.time.Duration;
-import java.util.Objects;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.bind.DefaultValue;
-
-/**
- * Duration policy, independent of registry ownership and activation.
- * Retaining short parents avoids holes above exported children.
- */
-@ConfigurationProperties("causeway.observation")
-public record CausewayObservationPolicy(
- @DefaultValue("0ms") Duration jpaDurationThreshold) {
-
- public static final CausewayObservationPolicy DEFAULT =
- new CausewayObservationPolicy(Duration.ZERO);
-
- public CausewayObservationPolicy {
- Objects.requireNonNull(jpaDurationThreshold, "jpa-duration-threshold");
- if (jpaDurationThreshold.isNegative()) {
- throw new IllegalArgumentException("causeway.observation.jpa-duration-threshold must not be negative");
- }
- }
-}
diff --git a/core/config/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/core/config/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 8e3337e664a..e70737e3f43 100644
--- a/core/config/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/core/config/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -1,11 +1,5 @@
{
"properties": [
- {
- "name": "causeway.observation.jpa-duration-threshold",
- "type": "java.time.Duration",
- "defaultValue": "0ms",
- "description": "JPA duration threshold. Zero disables filtering; positive values discard shorter successful spans."
- },
{
"name": "causeway.value-types.temporal.editing.date-pattern",
"type": "java.lang.String",
diff --git a/core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfigurationTest.java b/core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfigurationTest.java
index 8157739eb1e..26835f06efc 100644
--- a/core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfigurationTest.java
+++ b/core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfigurationTest.java
@@ -80,41 +80,6 @@ class CausewayObservationAutoConfigurationTest {
while (cause.getCause() != null) cause = cause.getCause();
assertInstanceOf(NoUniqueBeanDefinitionException.class, cause);
}
- @Test void policyDefaultsAndProgrammaticDefaultsAgree() {
- try (var context = context(true)) {
- var integration = context.getBean(CausewayObservationIntegration.class);
- assertEquals(CausewayObservationPolicy.DEFAULT, integration.policy());
- assertEquals(integration.policy(), new CausewayObservationIntegration(ObservationRegistry.NOOP).policy());
- }
- }
- @Test void bindsExplicitPolicyWithoutActivatingFrameworkObservations() {
- try (var context = configured(java.util.Map.of(
- "causeway.observation.jpa-duration-threshold", "7ms"))) {
- var integration = context.getBean(CausewayObservationIntegration.class);
- assertTrue(integration.isNoop());
- assertEquals(new CausewayObservationPolicy(java.time.Duration.ofMillis(7)), integration.policy());
- }
- }
- @Test void acceptsZeroThreshold() {
- try (var context = configured(java.util.Map.of("causeway.observation.jpa-duration-threshold", "0ms"))) {
- assertEquals(java.time.Duration.ZERO, context.getBean(CausewayObservationPolicy.class).jpaDurationThreshold());
- }
- }
- @Test void rejectsInvalidThreshold() {
- for (String value : new String[]{"-1ms", "not-a-duration"}) {
- var failure = assertThrows(Exception.class, () -> configured(
- java.util.Map.of("causeway.observation.jpa-duration-threshold", value)));
- assertTrue(failure.toString().contains("causeway.observation"), failure.toString());
- }
- }
- private AnnotationConfigApplicationContext configured(java.util.Map<String, Object> properties) {
- var context = new AnnotationConfigApplicationContext();
- context.getEnvironment().getPropertySources().addFirst(
- new org.springframework.core.env.MapPropertySource("test", properties));
- context.register(CausewayObservationAutoConfiguration.class);
- try { context.refresh(); return context; }
- catch (RuntimeException | Error ex) { context.close(); throw ex; }
- }
private AnnotationConfigApplicationContext context(boolean active, Class<?>... userConfigs) {
var context = new AnnotationConfigApplicationContext();
if (active) context.getEnvironment().setActiveProfiles("observation");
diff --git a/core/config/src/test/java/org/apache/causeway/core/config/observation/ObservationWithTimeThresholdTest.java b/core/config/src/test/java/org/apache/causeway/core/config/observation/ObservationWithTimeThresholdTest.java
deleted file mode 100644
index 06864cdcd3a..00000000000
--- a/core/config/src/test/java/org/apache/causeway/core/config/observation/ObservationWithTimeThresholdTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.causeway.core.config.observation;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.time.Duration;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.junit.jupiter.api.Test;
-
-import org.apache.causeway.commons.internal.observation.ObservationClosure;
-import org.apache.causeway.core.config.observation.CausewayObservationIntegration.ObservationWithTimeThreshold;
-
-import io.micrometer.observation.Observation;
-import io.micrometer.observation.ObservationHandler;
-import io.micrometer.observation.ObservationRegistry;
-
-class ObservationWithTimeThresholdTest {
- @Test void compositionRetainsWrapperAndShortSuccessIsDiscarded() {
- var clock = new AtomicLong();
- var observation = observation(clock);
- assertSame(observation, observation.contextualName("context"));
- assertSame(observation, observation.lowCardinalityKeyValue("kind", "test"));
- assertSame(observation, observation.highCardinalityKeyValue("id", "test"));
- assertSame(observation, observation.parentObservation(null));
- try (var closure = new ObservationClosure().startAndOpenScope(observation)) {
- assertSame(observation, closure.observation());
- clock.set(9);
- }
- assertNotNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- @Test void atAndAboveThresholdAreRetained() {
- for (long duration : new long[]{10, 11}) {
- var clock = new AtomicLong();
- var observation = observation(clock);
- observation.start();
- clock.set(duration);
- observation.stop();
- assertNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- }
- @Test void shortFailureIsRetained() {
- var observation = observation(new AtomicLong());
- var failure = new AssertionError("work");
- assertSame(failure, assertThrows(AssertionError.class, () -> observation.observe(() -> { throw failure; })));
- assertSame(failure, observation.getContext().getError());
- assertNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- @Test void failureRetainsWrapperAndIsNotDiscarded() {
- var observation = observation(new AtomicLong());
- var failure = new AssertionError("work");
- assertSame(observation, observation.start());
- assertSame(observation, observation.error(failure));
- observation.stop();
- assertSame(failure, observation.getContext().getError());
- assertNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- @Test void providerPatternAndZeroThreshold() {
- var registry = registry();
- var integration = new CausewayObservationIntegration(registry);
- var provider = integration.provider(getClass(),
- CausewayObservationIntegration.withModuleName("causeway.jpa")
- .andThen(obs -> integration.withTimeThreshold(obs, Duration.ofDays(1))));
- var observation = provider.get("Persist");
- observation.observe(() -> {});
- assertNotNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- var disabled = new CausewayObservationIntegration(registry);
- var delegate = disabled.createNotStarted(getClass(), "Persist");
- assertSame(delegate, disabled.withTimeThreshold(delegate, Duration.ZERO));
- delegate.observe(() -> {});
- assertNull(delegate.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- @Test void zeroThresholdRetainsInstantSuccess() {
- var observation = new ObservationWithTimeThreshold(Observation.createNotStarted("test", registry()),
- Duration.ZERO, new ObservationWithTimeThreshold.Timer(() -> 0L));
- observation.observe(() -> {});
- assertNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- @Test void largeConfiguredThresholdDoesNotOverflowDuringCleanup() {
- var observation = new ObservationWithTimeThreshold(Observation.createNotStarted("test", registry()),
- Duration.ofDays(1000000), new ObservationWithTimeThreshold.Timer(() -> 0L));
- assertDoesNotThrow(() -> observation.observe(() -> {}));
- assertNotNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- @Test void defaultPolicyDoesNotWrapOrDiscard() {
- var integration = new CausewayObservationIntegration(registry());
- var observation = integration.createNotStarted(getClass(), "test");
- assertSame(observation, integration.withTimeThreshold(observation, integration.policy().jpaDurationThreshold()));
- observation.observe(() -> {});
- assertNull(observation.getContext().getLowCardinalityKeyValue("causeway.discard"));
- }
- private ObservationWithTimeThreshold observation(AtomicLong clock) {
- return new ObservationWithTimeThreshold(Observation.createNotStarted("test", registry()),
- Duration.ofNanos(10), new ObservationWithTimeThreshold.Timer(clock::get));
- }
- private ObservationRegistry registry() {
- var registry = ObservationRegistry.create();
- registry.observationConfig().observationHandler(new ObservationHandler<Observation.Context>() {
- public boolean supportsContext(Observation.Context context) { return true; }
- });
- return registry;
- }
-}
diff --git a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/ia/InteractionServiceObservationTest.java b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/ia/InteractionServiceObservationTest.java
index 2e44f120e97..3e0d4a23d50 100644
--- a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/ia/InteractionServiceObservationTest.java
+++ b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/ia/InteractionServiceObservationTest.java
@@ -163,8 +163,7 @@ private static class Fixture {
final ArrayList<Observation.Context> stopped = new ArrayList<>();
final TransactionServiceSpring transactions = mock(TransactionServiceSpring.class);
final InteractionServiceDefault service;
- Fixture() { this(org.apache.causeway.core.config.observation.CausewayObservationPolicy.DEFAULT); }
- Fixture(org.apache.causeway.core.config.observation.CausewayObservationPolicy policy) {
+ Fixture() {
registry.observationConfig().observationHandler(new ObservationHandler<Observation.Context>() {
public boolean supportsContext(Observation.Context context) { return true; }
public void onStop(Observation.Context context) { stopped.add(context); }
@@ -177,7 +176,7 @@ private static class Fixture {
var sequence = new java.util.concurrent.atomic.AtomicLong();
when(executionContext.idGenerator().interactionId()).thenAnswer(__ -> new UUID(0, sequence.incrementAndGet()));
service = new InteractionServiceDefault(beanFactory, mock(ServiceInjector.class), transactions,
- mock(ClockService.class), () -> null, executionContext, new CausewayObservationIntegration(registry, policy));
+ mock(ClockService.class), () -> null, executionContext, new CausewayObservationIntegration(registry));
}
}
}
diff --git a/openspec/changes/remove-jpa-duration-filtering/tasks.md b/openspec/changes/remove-jpa-duration-filtering/tasks.md
index 7c98be0f43e..3bab60ee0b0 100644
--- a/openspec/changes/remove-jpa-duration-filtering/tasks.md
+++ b/openspec/changes/remove-jpa-duration-filtering/tasks.md
@@ -1,9 +1,9 @@
## 1. Remove duration filtering
-- [ ] 1.1 Delete the threshold wrapper/configuration and simplify the registry integration and JPA provider, preserving explicit transaction discard behavior.
-- [ ] 1.2 Replace removed-policy tests with JPA retention/failure and Boot/agent export ancestry evidence, including obsolete-setting no-op behavior.
+- [x] 1.1 Delete the threshold wrapper/configuration and simplify the registry integration and JPA provider, preserving explicit transaction discard behavior.
+- [x] 1.2 Replace removed-policy tests with JPA retention/failure and Boot/agent export ancestry evidence, including obsolete-setting no-op behavior.
## 2. Guidance and validation
-- [ ] 2.1 Remove threshold overrides and filtering guidance from Petclinic and the observability guides; link the general filtering backlog in the roadmap.
-- [ ] 2.2 Verify affected compilation/tests, configuration metadata, launcher forwarding and documentation; record commands/results/limits in validation.md.
+- [x] 2.1 Remove threshold overrides and filtering guidance from Petclinic and the observability guides; link the general filtering backlog in the roadmap.
+- [x] 2.2 Verify affected compilation/tests, configuration metadata, launcher forwarding and documentation; record commands/results/limits in validation.md.
diff --git a/openspec/changes/remove-jpa-duration-filtering/validation.md b/openspec/changes/remove-jpa-duration-filtering/validation.md
new file mode 100644
index 00000000000..5492650e2be
--- /dev/null
+++ b/openspec/changes/remove-jpa-duration-filtering/validation.md
@@ -0,0 +1,28 @@
+# Validation: remove JPA duration filtering
+
+Validated 2026-10-07 on `CAUSEWAY-4068-v4`. Planning checkpoint: `81e1bc7ccde`. Previous classification/correlation archive: `3b52d532fe7`.
+
+## Toolchain
+
+Eclipse Temurin Java 25.0.4, Maven 3.9.13, macOS aarch64. Checkout BOM: Boot 4.2.0-M1, Micrometer Observation 1.18.0-M1 / Tracing 1.8.0-M1, OpenTelemetry 1.64.0, Java agent 2.31.1. Maven commands use `JAVA_HOME=/Users/danhaywood/.sdkman/candidates/java/25.0.4-tem`; required dependencies are cached offline. Export tests have local socket access.
+
+## Commands and results
+
+```sh
+mvn -o -pl core/config,core/runtimeservices,core/webapp,persistence/jpa/integration,extensions/core/commandlog/applib -am \
+ -Dtest=CausewayTraceClassifierTest,CausewayObservationAutoConfigurationTest,InteractionServiceObservationTest,TransactionObservationTest,CausewayForegroundTraceFilterTest,RunBackgroundCommandsJobTraceClassificationTest \
+ -Dsurefire.failIfNoSpecifiedTests=false -Dmaven.source.skip=true -Dmaven.javadoc.skip=true clean install
+mvn -o -f regressiontests/tracing-compatibility/pom.xml test
+mvn -o -f viewers/webcomponents/pom.xml -pl sample-htmx-petclinic -am \
+ -Pobservation -DskipTests -Dmaven.source.skip=true -Dmaven.javadoc.skip=true package
+```
+
+Passed: 25 focused Java tests (11 configuration/classification, 8 interaction/transaction, 3 servlet-filter, 3 background job), 9 tracing/JPA tests and Petclinic packaging. The packaging invocation skips Java tests; the reactor's configured JavaScript checks also passed.
+
+Export evidence covers both supported tracing owners, retained successful and failed JPA observations, real JDBC children, correct ancestry, automatic identity attributes, root UUID correlation, entry classification, inactive observations and no exporter. The obsolete-threshold test supplies the removed property with a one-day value and verifies no duration discard or missing JPA parents in Boot and agent modes. Fixture business/persistence collaborators remain controlled test doubles; a [...]
+
+Clean rebuild verified that the threshold property is absent from generated metadata, and the policy and threshold-wrapper classes are absent from compiled output. Existing explicit discard support remains in the registry integration and Spring predicate because transaction instrumentation uses it independently of duration.
+
+Shell syntax and mock-Maven invocations verify launcher profile/agent forwarding with no threshold setting in either mode. Both observability guides and the sample README render with Asciidoctor and their YAML examples parse. `git diff --check` and strict OpenSpec change validation pass. Full repository regressions and live browser/backend sessions were not repeated.
+
+Archive checkpoint validation uses the same focused `test` goals (without clean/install) and the tracing suite after the implementation commit, as required by the one-shot workflow.
diff --git a/openspec/planned-changes/otel-forward-port-roadmap.md b/openspec/planned-changes/otel-forward-port-roadmap.md
index 7660f8128e9..e06e788a1e8 100644
--- a/openspec/planned-changes/otel-forward-port-roadmap.md
+++ b/openspec/planned-changes/otel-forward-port-roadmap.md
@@ -114,3 +114,5 @@ Review follow-up (2026-10-07): username and tenancy are now automatic nonempty i
Filtering simplification: `causeway.observation.jpa-duration-threshold` is the only filtering setting, with `0ms` disabling filtering. The launcher no longer takes `--scenario`; set `CAUSEWAY_OBSERVATION_JPA_DURATION_THRESHOLD` for Boot-managed runs.
Classification/correlation archived on 2026-10-07 after fresh focused and tracing validation passed (45 tests), with all delta specs synced. General duration filtering is tracked in [the exploration backlog](general-span-duration-filtering.md); the narrow JPA-only threshold is being removed in a separate change.
+
+The [remove-jpa-duration-filtering](../changes/remove-jpa-duration-filtering/proposal.md) change removes the narrow JPA-only policy while preserving explicit transaction discard behavior. Replacement filtering is deferred to [general span duration filtering](general-span-duration-filtering.md).
diff --git a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java
index 870e905fa27..f59f7db59c6 100644
--- a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java
+++ b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java
@@ -81,10 +81,8 @@ protected JpaEntityFacet(
this.entityClass = entityClass;
this.primaryKeyType = idStringifierLookupService
.primaryKeyTypeFor(entityClass, getPrimaryKeyType());
- var timeThreshold = observationIntegration.policy().jpaDurationThreshold();
this.observationProvider = observationIntegration.provider(getClass(),
- CausewayObservationIntegration.withModuleName(CausewayModulePersistenceJpaIntegration.NAMESPACE)
- .andThen(obs->observationIntegration.withTimeThreshold(obs, timeThreshold)));
+ CausewayObservationIntegration.withModuleName(CausewayModulePersistenceJpaIntegration.NAMESPACE));
}
@Override
diff --git a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/persistence/jpa/integration/entity/JpaObservationPolicyTest.java b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/persistence/jpa/integration/entity/JpaObservationPolicyTest.java
index 87c2f568195..14a7588a80e 100644
--- a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/persistence/jpa/integration/entity/JpaObservationPolicyTest.java
+++ b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/persistence/jpa/integration/entity/JpaObservationPolicyTest.java
@@ -27,7 +27,6 @@
import org.apache.causeway.applib.query.Query;
import org.apache.causeway.applib.services.bookmark.Bookmark;
import org.apache.causeway.core.config.observation.CausewayObservationIntegration;
-import org.apache.causeway.core.config.observation.CausewayObservationPolicy;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
@@ -35,7 +34,7 @@
class JpaObservationPolicyTest {
@Test void realFacetUsesStableNamesAndExcludesInstanceValues() {
var stopped = new ArrayList<Observation.Context>();
- var facet = JpaObservationFixture.create(integration(stopped, CausewayObservationPolicy.DEFAULT), () -> {});
+ var facet = JpaObservationFixture.create(integration(stopped), () -> {});
for (String id : List.of("sentinel-id-one", "sentinel-id-two")) {
facet.fetchByBookmark(Bookmark.forLogicalTypeNameAndIdentifier("fixture.Entity", id));
facet.fetchByQuery(Query.named(JpaObservationFixture.Entity.class, "Entity.byName").withParameter("name", id));
@@ -57,24 +56,23 @@ class JpaObservationPolicyTest {
}
assertEquals("Entity.byName", stopped.get(1).getLowCardinalityKeyValue("causeway.query.name").getValue());
}
- @Test void facetUsesConfiguredThresholdAndRetainsErrors() {
+ @Test void facetRetainsSuccessfulAndFailedObservations() {
var stopped = new ArrayList<Observation.Context>();
- var policy = new CausewayObservationPolicy(java.time.Duration.ofDays(1));
- var integration = integration(stopped, policy);
+ var integration = integration(stopped);
JpaObservationFixture.create(integration, () -> {}).persist(new JpaObservationFixture.Entity());
- assertNotNull(stopped.get(0).getLowCardinalityKeyValue("causeway.discard"));
+ assertNull(stopped.get(0).getLowCardinalityKeyValue("causeway.discard"));
var failure = new IllegalStateException("work");
var facet = JpaObservationFixture.create(integration, () -> { throw failure; });
assertSame(failure, assertThrows(IllegalStateException.class, () -> facet.persist(new JpaObservationFixture.Entity())));
assertSame(failure, stopped.get(1).getError());
assertNull(stopped.get(1).getLowCardinalityKeyValue("causeway.discard"));
}
- private CausewayObservationIntegration integration(List<Observation.Context> stopped, CausewayObservationPolicy policy) {
+ private CausewayObservationIntegration integration(List<Observation.Context> stopped) {
var registry = ObservationRegistry.create();
registry.observationConfig().observationHandler(new ObservationHandler<Observation.Context>() {
public boolean supportsContext(Observation.Context context) { return true; }
public void onStop(Observation.Context context) { stopped.add(context); }
});
- return new CausewayObservationIntegration(registry, policy);
+ return new CausewayObservationIntegration(registry);
}
}
diff --git a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java
index 7f8ddfa9b13..524dccdd861 100644
--- a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java
+++ b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java
@@ -126,11 +126,6 @@ public static void main(String[] args) throws Exception {
throw new AssertionError("unexpected HTTP result: " + connection.getResponseCode());
connection.disconnect();
}
- var shortSpan = integration.withTimeThreshold(integration.createNotStarted(MicrometerTracingAgentFixture.class, "threshold-success"), integration.policy().jpaDurationThreshold());
- shortSpan.observe(() -> {});
- var failedSpan = integration.withTimeThreshold(integration.createNotStarted(MicrometerTracingAgentFixture.class, "threshold-failure"), integration.policy().jpaDurationThreshold());
- try { failedSpan.observe(() -> { throw new IllegalStateException("short failure"); }); }
- catch (IllegalStateException expected) { }
System.out.println("CAUSEWAY_TRACING_FIXTURE_OK");
} finally { server.stop(0); executor.shutdownNow(); }
}
diff --git a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java
index 72f766a2bf6..29c310120a3 100644
--- a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java
+++ b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java
@@ -57,28 +57,22 @@ class MicrometerTracingCompatibilityTest {
assertJpaAncestry(spans, action, true);
assertIdentity(root, "sentinel-user", "sentinel-tenant");
}
- assertTrue(spans.stream().anyMatch(s -> s.getName().equals("threshold-success")), names(spans));
- assertTrue(spans.stream().anyMatch(s -> s.getName().equals("threshold-failure") && s.getStatus().getCode() == Status.StatusCode.STATUS_CODE_ERROR), names(spans));
}
}
- @Test void bootManagedExportAndDiscardPolicy() throws Exception {
- try (var collector = new Collector()) {
- launch("boot", collector, "--causeway.observation.jpa-duration-threshold=1d");
- assertEquals(2, collector.spans.stream().filter(s -> s.getName().equals("Causeway Root Interaction")).count(), names(collector.spans));
- assertFalse(collector.spans.stream().anyMatch(s -> s.getName().equals("threshold-success")), names(collector.spans));
- assertFalse(collector.spans.stream().anyMatch(s -> s.getName().startsWith("Persist ")), names(collector.spans));
- var children = collector.spans.stream().filter(s -> s.getName().equals("fixture.jdbc")).toList();
- assertEquals(2, children.size(), names(collector.spans));
- // Explicit filtering is lossy: independent children survive their suppressed JPA parents.
- for (var child : children) assertFalse(collector.spans.stream()
- .anyMatch(s -> s.getSpanId().equals(child.getParentSpanId())), names(collector.spans));
- assertTrue(collector.spans.stream().anyMatch(s -> s.getName().equals("threshold-failure")), names(collector.spans));
+ @Test void obsoleteJpaThresholdDoesNotDiscardExportedParents() throws Exception {
+ for (String mode : List.of("boot", "agent")) {
+ try (var collector = new Collector()) {
+ launch(mode, collector, "--causeway.observation.jpa-duration-threshold=1d");
+ var actions = collector.spans.stream().filter(s -> s.getName().startsWith("Action ")).toList();
+ assertEquals(2, actions.size(), names(collector.spans));
+ for (var action : actions) assertJpaAncestry(collector.spans, action, mode.equals("agent"));
+ assertFalse(collector.spans.stream().anyMatch(s -> attribute(s, "causeway.discard") != null));
+ }
}
}
@Test void bootDefaultsRetainJpaParentsAndIncludeIdentity() throws Exception {
try (var collector = new Collector()) {
launch("boot", collector);
- assertTrue(collector.spans.stream().anyMatch(s -> s.getName().equals("threshold-success")), names(collector.spans));
var actions = collector.spans.stream().filter(s -> s.getName().startsWith("Action ")).toList();
assertEquals(2, actions.size(), names(collector.spans));
for (var action : actions) assertJpaAncestry(collector.spans, action, false);
diff --git a/viewers/webcomponents/sample-htmx-petclinic/README.adoc b/viewers/webcomponents/sample-htmx-petclinic/README.adoc
index a47f4597b28..c5431752e41 100644
--- a/viewers/webcomponents/sample-htmx-petclinic/README.adoc
+++ b/viewers/webcomponents/sample-htmx-petclinic/README.adoc
@@ -73,20 +73,10 @@ Stop Petclinic with Ctrl-C and switch tracing ownership by adding one flag:
The agent owns traces and automatic HTTP/JDBC instrumentation; Micrometer still owns metrics.
The optional registry bridge joins Causeway observations to agent context and the agent profile excludes competing Boot tracing configuration.
-Agent metrics/log export and Causeway duration filtering are disabled; Micrometer metrics stay enabled.
+Agent metrics/log export is disabled; Micrometer metrics stay enabled.
Maven obtains agent 2.31.1 on first use, or set `OTEL_JAVAAGENT_PATH` to an existing JAR.
-Nonempty user identity attributes are always included. Duration filtering is disabled by default.
-To enable it in Boot-managed mode:
-
-[source,bash]
-----
-export CAUSEWAY_OBSERVATION_JPA_DURATION_THRESHOLD=1s
-./viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh
-----
-
-The JPA threshold defaults to `0ms` (no filtering); a positive threshold filters shorter successful spans.
-Unset the variable before returning to the default. The launcher disables filtering in agent mode.
+Nonempty user identity attributes are always included. Causeway JPA spans are retained regardless of duration in both tracing modes.
Check identity attributes on `Causeway Root Interaction` spans, not static-resource HTTP spans.
The bypass user normally has no tenancy token; that attribute is omitted when empty.
diff --git a/viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh b/viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh
index 7ad0890c9c8..67c0cb7e2f0 100755
--- a/viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh
+++ b/viewers/webcomponents/sample-htmx-petclinic/run-with-telemetry.sh
@@ -27,8 +27,6 @@ Usage: run-with-telemetry.sh [--agent] [-- Maven options...]
Boot-managed tracing by default; --agent uses agent-managed tracing.
Both modes export Micrometer metrics to Prometheus and traces to Jaeger.
-Enable Boot duration filtering with CAUSEWAY_OBSERVATION_JPA_DURATION_THRESHOLD=1s.
-Agent mode keeps duration filtering disabled.
Start Jaeger and the metrics pair separately before running Petclinic:
./scripts/jaeger-local.sh start
@@ -79,8 +77,6 @@ if [[ "$MODE" == agent ]]; then
export OTEL_METRICS_EXPORTER=none
export OTEL_LOGS_EXPORTER=none
- # The agent exporter does not consume Spring's duration-discard predicate.
- export CAUSEWAY_OBSERVATION_JPA_DURATION_THRESHOLD=0ms
PROFILES=observation,agent
AGENT_ARGS=("-Dspring-boot.run.agents=$AGENT")
fi
diff --git a/viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties b/viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties
index 22a026e8e2f..0c0be7c69f7 100644
--- a/viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties
+++ b/viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties
@@ -23,5 +23,3 @@ spring.autoconfigure.exclude=org.springframework.boot.opentelemetry.autoconfigur
management.opentelemetry.map-environment-variables=false
management.tracing.export.enabled=false
# Micrometer metrics remain enabled by application-observation.properties.
-# Spring's discard predicate cannot filter spans exported by the agent.
-causeway.observation.jpa-duration-threshold=0ms
(causeway) 08/10: CAUSEWAY-4068: provide automatic agent observation bridge
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4068-v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 906a897df3770ee40c7db9a0187401f1f5f3200f
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Oct 7 12:10:50 2026 +0100
CAUSEWAY-4068: provide automatic agent observation bridge
---
.../ROOT/pages/2026/4.0.0-M3/otel-howto.adoc | 79 ++-----------
.../modules/ROOT/pages/observability.adoc | 59 ++--------
core/config/pom.xml | 7 ++
core/config/src/main/java/module-info.java | 2 +
.../CausewayAgentEnvironmentPostProcessor.java | 75 ++++++++++++
.../CausewayAgentObservationAutoConfiguration.java | 63 ++++++++++
.../CausewayObservationAutoConfiguration.java | 8 +-
.../src/main/resources/META-INF/spring.factories | 2 +
...rk.boot.autoconfigure.AutoConfiguration.imports | 1 +
...sewayAgentObservationAutoConfigurationTest.java | 127 +++++++++++++++++++++
.../provide-agent-observation-bridge/tasks.md | 12 +-
.../provide-agent-observation-bridge/validation.md | 12 ++
.../planned-changes/otel-forward-port-roadmap.md | 4 +
.../tracing/EntryPointTracingFixture.java | 3 +-
.../tracing/MicrometerTracingAgentFixture.java | 25 +---
.../MicrometerTracingCompatibilityTest.java | 19 ++-
.../sample-htmx-petclinic/README.adoc | 2 +-
.../webcomponents/sample-htmx-petclinic/pom.xml | 19 ---
.../main/resources/application-agent.properties | 25 ----
.../petclinic/AgentObservationConfiguration.java | 51 ---------
20 files changed, 342 insertions(+), 253 deletions(-)
diff --git a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
index eb043efad60..b185e4d538c 100644
--- a/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
+++ b/antora/components/relnotes/modules/ROOT/pages/2026/4.0.0-M3/otel-howto.adoc
@@ -194,7 +194,7 @@ Note that Prometheus metric names can differ from contextual span names, includi
The principal benefit of using agent-managed tracing is that the agent will instrument libraries, such as JDBC access.
When an agent is configured, the agent now owns the tracing SDK, exporter and automatic HTTP/JDBC instrumentation.
-A registry bridge joins Causeway observations to that context, and the `agent` profile disables competing Boot tracing auto-configuration.
+Causeway supplies a registry bridge to join its observations to that context, and the `agent` profile automatically disables competing Boot tracing auto-configuration.
### Try it out
@@ -229,74 +229,19 @@ Some config can be overridden using env vars:
[#apply-the-same-change-to-another-application]
-=== Apply the same change to another application
+=== Configure agent-managed tracing in another application
-Keep the metrics configuration above, add the BOM-managed `io.micrometer:micrometer-tracing-bridge-otel` dependency, and register the following configuration through component scanning or an explicit import:
+Keep the metrics configuration above and add the BOM-managed `io.micrometer:micrometer-tracing-bridge-otel` dependency.
+Causeway supplies the registry bridge automatically when both `observation` and `agent` profiles are active; no application Java configuration or manual Boot exclusions are needed.
+The bridge uses the agent's global OpenTelemetry instance without constructing another SDK or exporter.
-[source,java]
-----
-import java.util.List;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Profile;
-
-import io.micrometer.observation.ObservationRegistry;
-import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
-import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
-import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
-import io.micrometer.tracing.otel.bridge.OtelTracer;
-import io.opentelemetry.api.GlobalOpenTelemetry;
-
-/** Bridges Causeway observations into the agent's HTTP/JDBC trace context. */
-@Configuration(proxyBeanMethods = false)
-@Profile("agent")
-public class AgentObservationConfiguration {
- @Bean
- io.micrometer.tracing.Tracer agentTracer() {
- // The attached agent owns the global SDK and exporter. Do not construct another SDK here.
- var currentContext = new OtelCurrentTraceContext();
- return new OtelTracer(GlobalOpenTelemetry.getTracer("org.apache.causeway"), currentContext,
- event -> {}, new OtelBaggageManager(currentContext, List.of(), List.of()));
- }
-
- @Bean
- ObservationRegistry agentObservationRegistry(io.micrometer.tracing.Tracer tracer) {
- var registry = ObservationRegistry.create();
- registry.observationConfig().observationHandler(new DefaultTracingObservationHandler(tracer));
- return registry;
- }
-}
-----
-
-The registry and entry-span classifier share this tracer bean, which uses the agent's global OpenTelemetry instance without constructing another SDK.
-A custom registry without a tracer bean can still create observations, but cannot classify agent-owned entry spans.
-When observations are active, supply a single tracer or designate a primary if multiple tracers exist.
-Add an agent-specific configuration that replaces tracing ownership while retaining Micrometer metrics:
+The `agent` profile automatically excludes competing Boot tracing and WebMVC observation configuration, while preserving any unrelated application exclusions.
+The WebMVC exclusion lets the agent own the HTTP entry span without a second Boot HTTP observation around it; foreground classification tags the agent's span.
+It also removes Boot's HTTP request observation meters in this configuration; JVM and Causeway Micrometer metrics remain available.
-[source,yaml]
-.application-agent.yaml
-----
-spring:
- autoconfigure:
- exclude:
- - org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration
- - org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryTracingAutoConfiguration
- - org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp.OtlpTracingAutoConfiguration
- - org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration
- - org.springframework.boot.webmvc.autoconfigure.WebMvcObservationAutoConfiguration
-management:
- opentelemetry:
- map-environment-variables: false
- tracing:
- export:
- enabled: false
-----
+If your application supplies its own registry or tracer, Causeway backs off from creating the corresponding bean.
+A custom registry must register its own tracing handlers using the same single or primary tracer that the entry-span classifier uses.
-The exclusions above match the Spring Boot 4.2.0-M1 baseline used by this checkout.
-The WebMVC exclusion lets the agent own the HTTP entry span without a second Boot HTTP observation around it; foreground classification then tags the agent's span.
-It also removes Boot's HTTP request observation meters in this configuration; JVM and Causeway Micrometer metrics remain available.
-Build the application with the bridge configuration included.
Activate `observation,agent` alongside any other required application profiles and attach the agent to the application JVM:
[source,bash]
@@ -313,8 +258,8 @@ java -javaagent:/absolute/path/to/opentelemetry-javaagent.jar \
-jar application.jar --spring.profiles.active=observation,agent
----
-`OTEL_METRICS_EXPORTER=none` disables the agent's exporter.
-`management.opentelemetry.map-environment-variables=false` prevents Boot from mapping that agent flag onto Micrometer and disabling its metrics too.
+`OTEL_METRICS_EXPORTER=none` disables the agent's metrics exporter.
+Causeway sets `management.opentelemetry.map-environment-variables=false` for the `agent` profile, preventing Boot from mapping that agent flag onto Micrometer and disabling its metrics too.
The shared `management.otlp.metrics.export.enabled=true` configuration therefore retains Micrometer's separate metrics pipeline.
When returning to Boot-managed tracing, remove `-javaagent`, deactivate the `agent` profile, and unset the exported `OTEL_*` variables above so that Boot does not inherit the agent-only settings.
diff --git a/antora/components/userguide/modules/ROOT/pages/observability.adoc b/antora/components/userguide/modules/ROOT/pages/observability.adoc
index 51500b04358..d86d505accb 100644
--- a/antora/components/userguide/modules/ROOT/pages/observability.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/observability.adoc
@@ -29,56 +29,13 @@ An active observation profile does not require an agent or exporter to start suc
== Java-agent-owned tracing
An alternative configuration lets the OpenTelemetry Java agent own the SDK, automatic HTTP/JDBC instrumentation and export.
-Provide a registry whose Micrometer tracing handler bridges to the agent's global OpenTelemetry context.
-Add the BOM-managed `io.micrometer:micrometer-tracing-bridge-otel` dependency for this application configuration.
+Add the BOM-managed `io.micrometer:micrometer-tracing-bridge-otel` dependency and activate `observation,agent`.
+Causeway supplies the missing tracer and registry beans, bridges observations to the agent's global context, and automatically excludes competing Boot SDK/tracing and WebMVC observation configuration.
+No application Java bridge or manual exclusions are needed.
-[source,java]
-----
-@Configuration(proxyBeanMethods = false)
-@Profile("agent")
-class AgentObservationConfiguration {
- @Bean
- io.micrometer.tracing.Tracer agentTracer() {
- var currentContext = new OtelCurrentTraceContext();
- return new OtelTracer(
- GlobalOpenTelemetry.getTracer("org.apache.causeway"),
- currentContext,
- event -> {},
- new OtelBaggageManager(currentContext, List.of(), List.of()));
- }
- @Bean
- ObservationRegistry agentObservationRegistry(io.micrometer.tracing.Tracer tracer) {
- var registry = ObservationRegistry.create();
- registry.observationConfig()
- .observationHandler(new DefaultTracingObservationHandler(tracer));
- return registry;
- }
-}
-----
-
-The types are from `io.micrometer.observation`, `io.micrometer.tracing.handler`, `io.micrometer.tracing.otel.bridge`, `io.opentelemetry.api`, `java.util`, and Spring's configuration annotations.
-Import this configuration into the application.
-For the validated Boot 4.2.0-M1 configuration, exclude the competing SDK/tracing auto-configurations:
-
-[source,yaml]
-----
-spring:
- profiles:
- active: observation,agent
- autoconfigure:
- exclude:
- - org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration
- - org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryTracingAutoConfiguration
- - org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp.OtlpTracingAutoConfiguration
- - org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration
- - org.springframework.boot.webmvc.autoconfigure.WebMvcObservationAutoConfiguration
-management:
- opentelemetry:
- map-environment-variables: false
- tracing:
- export:
- enabled: false
-----
+A supplied registry or tracer takes precedence over the corresponding default.
+A custom registry remains responsible for its handlers; those handlers and Causeway's entry-span classifier must share a single or primary tracer.
+Causeway does not construct an SDK or exporter, and the profiles do not attach an agent: attachment is still required for agent export.
Launch the JVM with `-javaagent:/path/to/opentelemetry-javaagent.jar` and configure the agent's service name, sampler and OTLP endpoint through its normal settings.
For example, a local tracing-only run can use:
@@ -91,12 +48,12 @@ export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
export OTEL_METRICS_EXPORTER=none
export OTEL_LOGS_EXPORTER=none
-java -javaagent:/path/to/opentelemetry-javaagent.jar -jar application.jar
+java -javaagent:/path/to/opentelemetry-javaagent.jar -jar application.jar --spring.profiles.active=observation,agent
----
The registry and entry-span classifier share the exposed Micrometer tracer.
The WebMVC exclusion avoids a second Boot HTTP observation inside the agent's entry span; Boot HTTP request observation meters are consequently absent, while other Micrometer metrics remain available.
-Disabling environment-variable mapping prevents the agent's `OTEL_METRICS_EXPORTER=none` from disabling a separately configured Micrometer metrics exporter.
+Causeway disables Boot environment-variable mapping for the `agent` profile, preventing the agent's `OTEL_METRICS_EXPORTER=none` from disabling a separately configured Micrometer metrics exporter.
Do not also construct an independent SDK/export pipeline in this configuration.
The agent configuration is an explicit application choice; it does not replace the Boot-managed default.
With the agent profile but without the `observation` profile, automatic agent spans remain active and Causeway observations remain inactive.
diff --git a/core/config/pom.xml b/core/config/pom.xml
index 4d2a2f500ce..615bd54fc86 100644
--- a/core/config/pom.xml
+++ b/core/config/pom.xml
@@ -97,6 +97,13 @@
<artifactId>micrometer-tracing</artifactId>
</dependency>
+ <!-- Agent bridge is opt-in for consuming applications. -->
+ <dependency>
+ <groupId>io.micrometer</groupId>
+ <artifactId>micrometer-tracing-bridge-otel</artifactId>
+ <optional>true</optional>
+ </dependency>
+
<!--
as per https://github.com/spring-projects/spring-boot/issues/30986, must be
before the spring-boot-configuration-processor
diff --git a/core/config/src/main/java/module-info.java b/core/config/src/main/java/module-info.java
index eba57e8c336..38892445d5f 100644
--- a/core/config/src/main/java/module-info.java
+++ b/core/config/src/main/java/module-info.java
@@ -35,6 +35,8 @@
exports org.apache.causeway.core.config.viewer.web;
requires static lombok;
+ requires static micrometer.tracing.bridge.otel;
+ requires static io.opentelemetry.api;
requires transitive org.apache.causeway.applib;
requires transitive org.apache.causeway.commons;
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentEnvironmentPostProcessor.java b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentEnvironmentPostProcessor.java
new file mode 100644
index 00000000000..241eb26963e
--- /dev/null
+++ b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentEnvironmentPostProcessor.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.config.observation;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.boot.EnvironmentPostProcessor;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.core.Ordered;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.env.Profiles;
+import org.springframework.util.ClassUtils;
+
+/** Selects agent trace ownership before Boot evaluates its auto-configurations. */
+public class CausewayAgentEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
+
+ private static final List<String> TRACE_AUTO_CONFIGURATIONS = List.of(
+ "org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration",
+ "org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryTracingAutoConfiguration",
+ "org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp.OtlpTracingAutoConfiguration",
+ "org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration",
+ "org.springframework.boot.webmvc.autoconfigure.WebMvcObservationAutoConfiguration");
+
+ @Override
+ public int getOrder() {
+ // Active profiles and application YAML must already have been loaded.
+ return ConfigDataEnvironmentPostProcessor.ORDER + 1;
+ }
+
+ @Override
+ public void postProcessEnvironment(final ConfigurableEnvironment environment,
+ final SpringApplication application) {
+ if (!environment.acceptsProfiles(Profiles.of("agent"))) {
+ return;
+ }
+ if (environment.acceptsProfiles(Profiles.of("observation"))
+ && !ClassUtils.isPresent("io.micrometer.tracing.otel.bridge.OtelTracer", application.getClassLoader())) {
+ throw new IllegalStateException("The observation,agent profiles require "
+ + "io.micrometer:micrometer-tracing-bridge-otel. Add the BOM-managed dependency "
+ + "and attach the OpenTelemetry Java agent to the application JVM.");
+ }
+ var exclusions = new LinkedHashSet<>(Binder.get(environment)
+ .bind("spring.autoconfigure.exclude", String[].class)
+ .map(List::of).orElseGet(List::of));
+ exclusions.addAll(TRACE_AUTO_CONFIGURATIONS);
+ // Ownership is a profile contract, so these values override command-line
+ // defaults too. Existing unrelated exclusions are retained above.
+ // Avoid mapping OTEL_METRICS_EXPORTER=none onto Micrometer metrics.
+ environment.getPropertySources().addFirst(new MapPropertySource("causewayAgentTracing", Map.of(
+ "spring.autoconfigure.exclude", String.join(",", exclusions),
+ "management.opentelemetry.map-environment-variables", "false",
+ "management.tracing.export.enabled", "false")));
+ }
+}
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfiguration.java b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfiguration.java
new file mode 100644
index 00000000000..c3df3a3a106
--- /dev/null
+++ b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfiguration.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.config.observation;
+
+import java.util.List;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Profile;
+
+import io.micrometer.observation.ObservationRegistry;
+import io.micrometer.tracing.Tracer;
+import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
+import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
+import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
+import io.micrometer.tracing.otel.bridge.OtelTracer;
+import io.opentelemetry.api.GlobalOpenTelemetry;
+
+/**
+ * Joins Causeway observations to the attached Java agent's global context.
+ * This configuration creates neither an SDK nor an exporter. Application bean
+ * definitions take precedence; a custom registry owns its own handlers.
+ */
+@AutoConfiguration(beforeName = "org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration")
+@Profile("observation & agent")
+@ConditionalOnClass(name = {"io.micrometer.tracing.otel.bridge.OtelTracer",
+ "io.opentelemetry.api.GlobalOpenTelemetry"})
+public class CausewayAgentObservationAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(Tracer.class)
+ public Tracer causewayAgentTracer() {
+ var currentContext = new OtelCurrentTraceContext();
+ return new OtelTracer(GlobalOpenTelemetry.getTracer("org.apache.causeway"), currentContext,
+ event -> {}, new OtelBaggageManager(currentContext, List.of(), List.of()));
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(ObservationRegistry.class)
+ public ObservationRegistry causewayAgentObservationRegistry(final Tracer tracer) {
+ var registry = ObservationRegistry.create();
+ registry.observationConfig().observationHandler(new DefaultTracingObservationHandler(tracer));
+ return registry;
+ }
+}
diff --git a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java
index 816db189e9d..aa26458dbdc 100644
--- a/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java
+++ b/core/config/src/main/java/org/apache/causeway/core/config/observation/CausewayObservationAutoConfiguration.java
@@ -42,9 +42,9 @@
* produce (for example, tracing spans); this configuration does not create an SDK
* or exporter.
*
- * <p>Normally Boot configures the tracing handlers and export pipeline. An
- * application can instead supply a registry bridged to the OpenTelemetry Java
- * agent's context, with competing Boot tracing auto-configuration excluded.
+ * <p>Normally Boot configures the tracing handlers and export pipeline. The
+ * {@code observation,agent} profiles instead enable Causeway's built-in bridge
+ * to the Java agent and exclude competing Boot tracing auto-configuration.
* Both arrangements use the same Causeway integration below.
*
* <p>With the profile inactive, only Causeway's integration is disabled. We do
@@ -77,7 +77,7 @@ public boolean isExportable(final FinishedSpan span) {
* available. As with Boot's fallback, creating a registry does not itself
* enable export; observation handlers must be registered by the tracing setup.
*/
- @Profile("observation")
+ @Profile("observation & !agent")
@Bean
@ConditionalOnMissingBean
public ObservationRegistry observationRegistry() {
diff --git a/core/config/src/main/resources/META-INF/spring.factories b/core/config/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000000..c4f2abacea0
--- /dev/null
+++ b/core/config/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.EnvironmentPostProcessor=\
+org.apache.causeway.core.config.observation.CausewayAgentEnvironmentPostProcessor
diff --git a/core/config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/core/config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 00000000000..c59ba34ad0f
--- /dev/null
+++ b/core/config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+org.apache.causeway.core.config.observation.CausewayAgentObservationAutoConfiguration
diff --git a/core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfigurationTest.java b/core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfigurationTest.java
new file mode 100644
index 00000000000..f6907aa1d3c
--- /dev/null
+++ b/core/config/src/test/java/org/apache/causeway/core/config/observation/CausewayAgentObservationAutoConfigurationTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.core.config.observation;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.*;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.FilteredClassLoader;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.env.StandardEnvironment;
+
+import io.micrometer.observation.ObservationRegistry;
+import io.micrometer.tracing.Tracer;
+
+class CausewayAgentObservationAutoConfigurationTest {
+ private final ApplicationContextRunner runner = new ApplicationContextRunner()
+ .withUserConfiguration(CausewayObservationAutoConfiguration.class)
+ .withConfiguration(AutoConfigurations.of(CausewayAgentObservationAutoConfiguration.class));
+
+ @Test void bridgeCreatesOneRegistryAndTracer() {
+ runner.withPropertyValues("spring.profiles.active=observation,agent").run(context -> {
+ assertThat(context).hasSingleBean(Tracer.class).hasSingleBean(ObservationRegistry.class);
+ assertThat(context.getBean(CausewayObservationIntegration.class).observationRegistry())
+ .isSameAs(context.getBean(ObservationRegistry.class));
+ assertThat(context.getBean(ObservationRegistry.class).isNoop()).isFalse();
+ });
+ }
+
+ @Test void customTracerIsSharedWithDefaultRegistryAndClassifier() {
+ var tracer = mock(Tracer.class, withSettings().defaultAnswer(RETURNS_DEEP_STUBS));
+ runner.withPropertyValues("spring.profiles.active=observation,agent")
+ .withBean(Tracer.class, () -> tracer).run(context -> {
+ assertThat(context).hasSingleBean(Tracer.class).hasSingleBean(ObservationRegistry.class);
+ assertThat(context.getBean(Tracer.class)).isSameAs(tracer);
+ context.getBean(CausewayTraceClassifier.class)
+ .classifyCurrentSpan(CausewayTraceClassifier.ExecutionMode.FOREGROUND);
+ verify(tracer.currentSpan()).tag("causeway.execution.mode", "foreground");
+ context.getBean(CausewayObservationIntegration.class)
+ .createNotStarted(getClass(), "custom-tracer").observe(() -> {});
+ verify(tracer).nextSpan();
+ });
+ }
+
+ @Test void customRegistryIsPreserved() {
+ var custom = ObservationRegistry.create();
+ runner.withPropertyValues("spring.profiles.active=observation,agent")
+ .withBean(ObservationRegistry.class, () -> custom).run(context -> {
+ assertThat(context).hasSingleBean(ObservationRegistry.class);
+ assertThat(context.getBean(CausewayObservationIntegration.class).observationRegistry()).isSameAs(custom);
+ assertThat(custom.isNoop()).isTrue(); // no handler installed into application registry
+ });
+ }
+
+ @Test void inactiveObservationDoesNotCreateAgentBridge() {
+ runner.withPropertyValues("spring.profiles.active=agent").run(context -> {
+ assertThat(context).doesNotHaveBean(Tracer.class).doesNotHaveBean(ObservationRegistry.class);
+ assertThat(context.getBean(CausewayObservationIntegration.class).isNoop()).isTrue();
+ });
+ }
+
+ @Test void bootModeStartsWithoutOptionalBridge() {
+ runner.withClassLoader(new FilteredClassLoader("io.micrometer.tracing.otel", "io.opentelemetry"))
+ .withPropertyValues("spring.profiles.active=observation").run(context -> {
+ assertThat(context).hasNotFailed().hasSingleBean(ObservationRegistry.class);
+ assertThat(context).doesNotHaveBean(Tracer.class);
+ });
+ }
+
+ @Test void ownershipExclusionsPreserveYamlListAndMetrics() {
+ var environment = new StandardEnvironment();
+ environment.setActiveProfiles("observation", "agent");
+ environment.getPropertySources().addFirst(new MapPropertySource("app", Map.of(
+ "spring.autoconfigure.exclude[0]", "example.CustomAutoConfiguration",
+ "management.otlp.metrics.export.enabled", "true",
+ "management.tracing.export.enabled", "true")));
+ var processor = new CausewayAgentEnvironmentPostProcessor();
+ processor.postProcessEnvironment(environment, new SpringApplication());
+ processor.postProcessEnvironment(environment, new SpringApplication()); // repeat is harmless
+ assertThat(environment.getProperty("spring.autoconfigure.exclude"))
+ .contains("example.CustomAutoConfiguration", "OpenTelemetrySdkAutoConfiguration", "WebMvcObservationAutoConfiguration");
+ assertThat(environment.getProperty("management.otlp.metrics.export.enabled")).isEqualTo("true");
+ assertThat(environment.getProperty("management.opentelemetry.map-environment-variables")).isEqualTo("false");
+ assertThat(environment.getProperty("management.tracing.export.enabled")).isEqualTo("false");
+ }
+
+ @Test void missingAgentDependencyExplainsRemedy() throws Exception {
+ var environment = new StandardEnvironment();
+ environment.setActiveProfiles("observation", "agent");
+ try (var loader = new FilteredClassLoader("io.micrometer.tracing.otel")) {
+ var application = new SpringApplication(new org.springframework.core.io.DefaultResourceLoader(loader));
+ assertThatThrownBy(() -> new CausewayAgentEnvironmentPostProcessor()
+ .postProcessEnvironment(environment, application))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("micrometer-tracing-bridge-otel");
+ }
+ }
+
+ @Test void bootEnvironmentRemainsUnchanged() {
+ var environment = new StandardEnvironment();
+ environment.setActiveProfiles("observation");
+ new CausewayAgentEnvironmentPostProcessor().postProcessEnvironment(environment, new SpringApplication());
+ assertThat(environment.getPropertySources().contains("causewayAgentTracing")).isFalse();
+ }
+}
diff --git a/openspec/changes/provide-agent-observation-bridge/tasks.md b/openspec/changes/provide-agent-observation-bridge/tasks.md
index 868a03e4094..15ad126cca8 100644
--- a/openspec/changes/provide-agent-observation-bridge/tasks.md
+++ b/openspec/changes/provide-agent-observation-bridge/tasks.md
@@ -1,11 +1,11 @@
## 1. Framework wiring
-- [ ] 1.1 Add optional OTel bridge dependency and guarded agent tracer/registry auto-configuration.
-- [ ] 1.2 Add early agent ownership defaults preserving user exclusions and metrics.
-- [ ] 1.3 Test profiles, optional dependency, custom beans and exclusion merging.
+- [x] 1.1 Add optional OTel bridge dependency and guarded agent tracer/registry auto-configuration.
+- [x] 1.2 Add early agent ownership defaults preserving user exclusions and metrics.
+- [x] 1.3 Test profiles, optional dependency, custom beans and exclusion merging.
## 2. Adoption and verification
-- [ ] 2.1 Remove Petclinic and fixture bridge copies/manual agent exclusions; exercise production wiring.
-- [ ] 2.2 Update M3 how-to, user guide, sample instructions and roadmap.
-- [ ] 2.3 Validate focused core tests, exported Boot/agent trace regressions and sample packaging.
+- [x] 2.1 Remove Petclinic and fixture bridge copies/manual agent exclusions; exercise production wiring.
+- [x] 2.2 Update M3 how-to, user guide, sample instructions and roadmap.
+- [x] 2.3 Validate focused core tests, exported Boot/agent trace regressions and sample packaging.
diff --git a/openspec/changes/provide-agent-observation-bridge/validation.md b/openspec/changes/provide-agent-observation-bridge/validation.md
new file mode 100644
index 00000000000..5e55ff19bcf
--- /dev/null
+++ b/openspec/changes/provide-agent-observation-bridge/validation.md
@@ -0,0 +1,12 @@
+# Validation
+
+2026-10-07, branch CAUSEWAY-4068-v4. Java 25.0.4, Maven 3.9.13, Boot 4.2.0-M1, Micrometer 1.18.0-M1 / Tracing 1.8.0-M1, OTel 1.64.0 and Java agent 2.31.1.
+
+- Focused core/config reactor: 19 tests passed (8 agent wiring, 7 registry integration, 4 classifier).
+- Exported-trace compatibility suite: 9 tests passed. Real attached-agent HTTP/JDBC and servlet/Quartz fixtures use production auto-configuration, with no fixture bridge or manually supplied agent exclusions. Verifies single trace ownership, framework ancestry, identities, classification, replay correlation and inactive observations. The agent fixture also exports Micrometer metrics to a local OTLP receiver with OTEL_METRICS_EXPORTER=none.
+- Petclinic observation profile: clean reactor package passed after removing application-owned bridge source and agent properties. Configured frontend build completed.
+- Both updated AsciiDoc pages rendered with Asciidoctor; strict change validation and git diff whitespace check passed.
+
+The framework creates no SDK/exporter and does not attach the agent. Custom registry owners remain responsible for tracing handlers. Boot HTTP observation meters are absent in agent mode by design; other Micrometer metrics remain available. No interactive Docker/browser verification was performed in this change.
+
+Before archive, the focused and exported-trace suites are rerun after the implementation checkpoint; results recorded below.
diff --git a/openspec/planned-changes/otel-forward-port-roadmap.md b/openspec/planned-changes/otel-forward-port-roadmap.md
index a1b5dc1dc74..d44a966fda1 100644
--- a/openspec/planned-changes/otel-forward-port-roadmap.md
+++ b/openspec/planned-changes/otel-forward-port-roadmap.md
@@ -118,3 +118,7 @@ Classification/correlation archived on 2026-10-07 after fresh focused and tracin
The [remove-jpa-duration-filtering](../changes/archive/2026-10-07-remove-jpa-duration-filtering/proposal.md) change removes the narrow JPA-only policy while preserving explicit transaction discard behavior. Replacement filtering is deferred to [general span duration filtering](general-span-duration-filtering.md).
Removal completed and archived on 2026-10-07. Implementation checkpoint `39faf40ded6`; 34 Java tests passed before and after the checkpoint, Petclinic packaged successfully, and metadata/docs/launcher checks passed. Specs were synced; all completed changes are archived. General span filtering remains backlog exploration.
+
+## Built-in agent bridge follow-up (2026-10-07)
+
+[provide-agent-observation-bridge](../changes/provide-agent-observation-bridge/proposal.md) moves the sample-owned bridge and manual exclusions into core configuration. Consumers add the optional BOM-managed OTel bridge dependency, attach the Java agent and activate `observation,agent`; Micrometer metrics remain independent. Exported-trace fixtures exercise the production configuration. This is an adoption simplification on CAUSEWAY-4068, with general duration filtering still deferred.
diff --git a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/EntryPointTracingFixture.java b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/EntryPointTracingFixture.java
index 1dae5a93b22..b6a26488555 100644
--- a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/EntryPointTracingFixture.java
+++ b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/EntryPointTracingFixture.java
@@ -65,8 +65,7 @@
/** Real servlet/Quartz entry points; business collaborators are controlled fixtures. */
@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
-@Import({MicrometerTracingAgentFixture.AgentRegistry.class,
- CausewayObservationAutoConfiguration.class, WebObservationConfiguration.class})
+@Import({CausewayObservationAutoConfiguration.class, WebObservationConfiguration.class})
public class EntryPointTracingFixture {
static InteractionServiceDefault interactions;
static CausewayObservationIntegration integration;
diff --git a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java
index 524dccdd861..79b25dd545f 100644
--- a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java
+++ b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingAgentFixture.java
@@ -36,9 +36,7 @@
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
-import org.springframework.context.annotation.Profile;
import org.apache.causeway.applib.Identifier;
import org.apache.causeway.applib.id.LogicalType;
@@ -64,17 +62,11 @@
import org.apache.causeway.core.runtimeservices.ia.InteractionServiceDefault;
import org.apache.causeway.core.runtimeservices.transaction.TransactionServiceSpring;
-import io.micrometer.observation.ObservationRegistry;
-import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
-import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
-import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
-import io.micrometer.tracing.otel.bridge.OtelTracer;
-import io.opentelemetry.api.GlobalOpenTelemetry;
/** Child JVM fixture: real framework boundaries with mocked non-telemetry collaborators. */
@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
-@Import({CausewayObservationAutoConfiguration.class, MicrometerTracingAgentFixture.AgentRegistry.class})
+@Import({CausewayObservationAutoConfiguration.class})
public class MicrometerTracingAgentFixture {
public static void main(String[] args) throws Exception {
try (var context = new SpringApplicationBuilder(MicrometerTracingAgentFixture.class)
@@ -171,19 +163,4 @@ private static void executeSql() {
} catch (java.sql.SQLException ex) { throw new IllegalStateException(ex); }
}
}
- @Configuration(proxyBeanMethods = false)
- @Profile("agent")
- static class AgentRegistry {
- @Bean io.micrometer.tracing.Tracer agentTracer() {
- var currentContext = new OtelCurrentTraceContext();
- return new OtelTracer(GlobalOpenTelemetry.getTracer("org.apache.causeway"), currentContext,
- event -> {}, new OtelBaggageManager(currentContext, List.of(), List.of()));
- }
- @Bean
- ObservationRegistry agentObservationRegistry(io.micrometer.tracing.Tracer tracer) {
- var registry = ObservationRegistry.create();
- registry.observationConfig().observationHandler(new DefaultTracingObservationHandler(tracer));
- return registry;
- }
- }
}
diff --git a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java
index 29c310120a3..cf3e841a456 100644
--- a/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java
+++ b/regressiontests/tracing-compatibility/src/test/java/org/apache/causeway/regressiontests/tracing/MicrometerTracingCompatibilityTest.java
@@ -41,6 +41,7 @@ class MicrometerTracingCompatibilityTest {
@Test void agentJoinsFrameworkAndJdbcWithoutDuplicateSpans() throws Exception {
try (var collector = new Collector()) {
launch("agent", collector);
+ assertFalse(collector.metricNames.isEmpty(), "Micrometer metrics must export despite OTEL_METRICS_EXPORTER=none");
var spans = collector.spans;
var roots = spans.stream().filter(s -> s.getName().equals("Causeway Root Interaction")).toList();
assertEquals(2, roots.size(), names(spans));
@@ -180,9 +181,10 @@ private void launchFixture(Class<?> fixture, String mode, Collector collector, S
command.add(fixture.getName());
command.add("--spring.profiles.active=" + (mode.equals("inactive") ? "agent" : agent ? "observation,agent" : "observation"));
command.add("--management.tracing.sampling.probability=1.0");
+ command.add("--management.otlp.metrics.export.url=" + collector.metricsEndpoint());
command.add("--management.opentelemetry.tracing.export.otlp.endpoint=" + collector.endpoint());
- command.add("--management.tracing.export.enabled=" + mode.equals("boot"));
- if (agent || mode.equals("none")) {
+ if (!agent) command.add("--management.tracing.export.enabled=" + mode.equals("boot"));
+ if (mode.equals("none")) {
command.add("--spring.autoconfigure.exclude=" + String.join(",",
"org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration",
"org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryTracingAutoConfiguration",
@@ -190,7 +192,7 @@ private void launchFixture(Class<?> fixture, String mode, Collector collector, S
"org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration",
"org.springframework.boot.webmvc.autoconfigure.WebMvcObservationAutoConfiguration"));
}
- command.add("--management.opentelemetry.map-environment-variables=false");
+ if (!agent) command.add("--management.opentelemetry.map-environment-variables=false");
command.addAll(List.of(properties));
var builder = new ProcessBuilder(command).redirectErrorStream(true);
builder.environment().keySet().removeIf(key -> key.startsWith("OTEL_"));
@@ -221,6 +223,7 @@ private void launchFixture(Class<?> fixture, String mode, Collector collector, S
private String names(List<Span> spans) { return spans.stream().map(s -> s.getName() + ":" + s.getKind()).toList().toString(); }
private static class Collector implements AutoCloseable {
final List<Span> spans = new CopyOnWriteArrayList<>();
+ final List<String> metricNames = new CopyOnWriteArrayList<>();
final HttpServer server;
Collector() throws Exception {
server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
@@ -232,9 +235,19 @@ private static class Collector implements AutoCloseable {
exchange.sendResponseHeaders(200, 0);
} finally { exchange.close(); }
});
+ server.createContext("/v1/metrics", exchange -> {
+ try (var body = "gzip".equals(exchange.getRequestHeaders().getFirst("Content-Encoding"))
+ ? new GZIPInputStream(exchange.getRequestBody()) : exchange.getRequestBody()) {
+ var request = io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest.parseFrom(body);
+ request.getResourceMetricsList().forEach(resource -> resource.getScopeMetricsList().forEach(scope ->
+ scope.getMetricsList().forEach(metric -> metricNames.add(metric.getName()))));
+ exchange.sendResponseHeaders(200, 0);
+ } finally { exchange.close(); }
+ });
server.start();
}
String endpoint() { return "http://127.0.0.1:" + server.getAddress().getPort() + "/v1/traces"; }
+ String metricsEndpoint() { return "http://127.0.0.1:" + server.getAddress().getPort() + "/v1/metrics"; }
public void close() { server.stop(0); }
}
}
diff --git a/viewers/webcomponents/sample-htmx-petclinic/README.adoc b/viewers/webcomponents/sample-htmx-petclinic/README.adoc
index c5431752e41..5237d9f0d34 100644
--- a/viewers/webcomponents/sample-htmx-petclinic/README.adoc
+++ b/viewers/webcomponents/sample-htmx-petclinic/README.adoc
@@ -72,7 +72,7 @@ Stop Petclinic with Ctrl-C and switch tracing ownership by adding one flag:
----
The agent owns traces and automatic HTTP/JDBC instrumentation; Micrometer still owns metrics.
-The optional registry bridge joins Causeway observations to agent context and the agent profile excludes competing Boot tracing configuration.
+Causeway supplies the registry bridge automatically for `observation,agent` and excludes competing Boot tracing configuration. No application bridge code is required.
Agent metrics/log export is disabled; Micrometer metrics stay enabled.
Maven obtains agent 2.31.1 on first use, or set `OTEL_JAVAAGENT_PATH` to an existing JAR.
diff --git a/viewers/webcomponents/sample-htmx-petclinic/pom.xml b/viewers/webcomponents/sample-htmx-petclinic/pom.xml
index 4905dd1075b..e441f10e880 100644
--- a/viewers/webcomponents/sample-htmx-petclinic/pom.xml
+++ b/viewers/webcomponents/sample-htmx-petclinic/pom.xml
@@ -121,25 +121,6 @@
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
</dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <version>3.6.2</version>
- <executions>
- <execution>
- <id>add-observation-source</id>
- <phase>generate-sources</phase>
- <goals><goal>add-source</goal></goals>
- <configuration>
- <sources><source>src/observation/java</source></sources>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
</profile>
<profile>
<id>playwright</id>
diff --git a/viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties b/viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties
deleted file mode 100644
index 0c0be7c69f7..00000000000
--- a/viewers/webcomponents/sample-htmx-petclinic/src/main/resources/application-agent.properties
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-# The Java agent owns tracing. This profile requires its registry bridge and -javaagent.
-# Agent servlet instrumentation owns the HTTP entry; avoid a second Boot HTTP observation span.
-spring.autoconfigure.exclude=org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration,org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.OpenTelemetryTracingAutoConfiguration,org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp.OtlpTracingAutoConfiguration,org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration,org.springframework.boot.webmvc.autoconfigure.WebMvcObservationAuto [...]
-# Keep OTEL_* settings owned by the agent; do not map its metrics disable flag onto Micrometer.
-management.opentelemetry.map-environment-variables=false
-management.tracing.export.enabled=false
-# Micrometer metrics remain enabled by application-observation.properties.
diff --git a/viewers/webcomponents/sample-htmx-petclinic/src/observation/java/org/apache/causeway/viewer/webcomponents/sample/htmx/petclinic/AgentObservationConfiguration.java b/viewers/webcomponents/sample-htmx-petclinic/src/observation/java/org/apache/causeway/viewer/webcomponents/sample/htmx/petclinic/AgentObservationConfiguration.java
deleted file mode 100644
index 05530056067..00000000000
--- a/viewers/webcomponents/sample-htmx-petclinic/src/observation/java/org/apache/causeway/viewer/webcomponents/sample/htmx/petclinic/AgentObservationConfiguration.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.causeway.viewer.webcomponents.sample.htmx.petclinic;
-
-import java.util.List;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Profile;
-
-import io.micrometer.observation.ObservationRegistry;
-import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
-import io.micrometer.tracing.otel.bridge.OtelBaggageManager;
-import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext;
-import io.micrometer.tracing.otel.bridge.OtelTracer;
-import io.opentelemetry.api.GlobalOpenTelemetry;
-
-/** Bridges Causeway observations into the agent's HTTP/JDBC trace context. */
-@Configuration(proxyBeanMethods = false)
-@Profile("agent")
-public class AgentObservationConfiguration {
- @Bean
- io.micrometer.tracing.Tracer agentTracer() {
- // The attached agent owns the global SDK and exporter. Do not construct another SDK here.
- var currentContext = new OtelCurrentTraceContext();
- return new OtelTracer(GlobalOpenTelemetry.getTracer("org.apache.causeway"), currentContext,
- event -> {}, new OtelBaggageManager(currentContext, List.of(), List.of()));
- }
- @Bean
- ObservationRegistry agentObservationRegistry(io.micrometer.tracing.Tracer tracer) {
- var registry = ObservationRegistry.create();
- registry.observationConfig().observationHandler(new DefaultTracingObservationHandler(tracer));
- return registry;
- }
-}