This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3975-telemetry
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/3975-telemetry by this push:
     new 2ab146d457b CAUSEWAY-3975: adds default 
OpenTelemetryServerRequestObservationConvention
2ab146d457b is described below

commit 2ab146d457b3125b2bb314b93bdeef188cdce693
Author: andi-huber <[email protected]>
AuthorDate: Sun Mar 22 10:36:56 2026 +0100

    CAUSEWAY-3975: adds default
    OpenTelemetryServerRequestObservationConvention
---
 commons/src/main/java/module-info.java             |  1 +
 .../observation/CausewayObservationInternal.java   | 11 ++++++--
 .../session/InteractionServiceDefault.java         |  9 +++++--
 .../core/webapp/CausewayModuleCoreWebapp.java      | 30 +++++++++++++++++++---
 .../viewer/CausewayModuleViewerWicketViewer.java   |  4 +--
 .../viewer/integration/TelemetryStartHandler.java  |  8 +++++-
 .../wicketapp/CausewayWicketApplication.java       |  4 +--
 7 files changed, 54 insertions(+), 13 deletions(-)

diff --git a/commons/src/main/java/module-info.java 
b/commons/src/main/java/module-info.java
index a5d7963e405..47a24ab13b0 100644
--- a/commons/src/main/java/module-info.java
+++ b/commons/src/main/java/module-info.java
@@ -68,6 +68,7 @@
     requires transitive tools.jackson.core;
     requires transitive tools.jackson.databind;
     requires transitive tools.jackson.module.jakarta.xmlbind;
+    requires transitive micrometer.commons;
     requires transitive micrometer.observation;
     requires transitive org.jdom2;
     requires transitive org.jspecify;
diff --git 
a/commons/src/main/java/org/apache/causeway/commons/internal/observation/CausewayObservationInternal.java
 
b/commons/src/main/java/org/apache/causeway/commons/internal/observation/CausewayObservationInternal.java
index 2d7ea230a6b..7248a116165 100644
--- 
a/commons/src/main/java/org/apache/causeway/commons/internal/observation/CausewayObservationInternal.java
+++ 
b/commons/src/main/java/org/apache/causeway/commons/internal/observation/CausewayObservationInternal.java
@@ -28,6 +28,7 @@
 import lombok.Data;
 import lombok.experimental.Accessors;
 
+import io.micrometer.common.KeyValue;
 import io.micrometer.observation.Observation;
 import io.micrometer.observation.Observation.Scope;
 import io.micrometer.observation.ObservationRegistry;
@@ -68,7 +69,7 @@ public boolean isNoop() {
     public Observation createNotStarted(final Class<?> bean, final String 
name) {
         return Observation.createNotStarted(name, observationRegistry)
                 .lowCardinalityKeyValue("module", module)
-                .highCardinalityKeyValue("bean", bean.getSimpleName());
+                .lowCardinalityKeyValue("bean", bean.getSimpleName());
     }
 
     @FunctionalInterface
@@ -84,7 +85,7 @@ public ObservationProvider provider(final Class<?> bean) {
      * Helps if start and stop of an {@link Observation} happen in different 
code locations.
      */
     @Data @Accessors(fluent = true)
-    public static class ObservationClosure implements AutoCloseable {
+    public static final class ObservationClosure implements AutoCloseable {
 
         private Observation observation;
         private Scope scope;
@@ -123,4 +124,10 @@ public ObservationClosure tag(final String key, @Nullable 
final Supplier<Object>
 
     }
 
+    public static KeyValue currentThreadId() {
+        var ct = Thread.currentThread();
+        return KeyValue.of("threadId", "%d [%s]".formatted(ct.getId(), 
ct.getName()));
+
+    }
+
 }
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java
index 841f5f582db..ca90c91139a 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java
@@ -147,11 +147,16 @@ public InteractionLayer openInteraction(final @NonNull 
InteractionContext intera
             .map(it->(CausewayInteraction)it)
             .orElseGet(()->new 
CausewayInteraction(interactionIdGenerator.interactionId()));
 
-        var obs = observationProvider.get("Causeway Layered Interaction")
-                .highCardinalityKeyValue("stackSize", 
""+getInteractionLayerCount());
 
+        var obs = observationProvider.get(getInteractionLayerCount()==0
+                ? "Causeway Root Interaction"
+                : "Causeway Nested Interaction");
         var newInteractionLayer = layerStack.push(causewayInteraction, 
interactionContextToUse, obs);
 
+        if(getInteractionLayerCount()>0) {
+            obs.highCardinalityKeyValue("stackedLayers", 
""+getInteractionLayerCount());
+        }
+
         if(isAtTopLevel()) {
             transactionServiceSpring.onOpen(causewayInteraction);
             interactionScopeLifecycleHandler.onTopLevelInteractionOpened();
diff --git 
a/core/webapp/src/main/java/org/apache/causeway/core/webapp/CausewayModuleCoreWebapp.java
 
b/core/webapp/src/main/java/org/apache/causeway/core/webapp/CausewayModuleCoreWebapp.java
index fa99d8e5de6..6a16a0b7739 100644
--- 
a/core/webapp/src/main/java/org/apache/causeway/core/webapp/CausewayModuleCoreWebapp.java
+++ 
b/core/webapp/src/main/java/org/apache/causeway/core/webapp/CausewayModuleCoreWebapp.java
@@ -23,9 +23,12 @@
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Scope;
 import org.springframework.context.annotation.ScopedProxyMode;
+import 
org.springframework.http.server.observation.OpenTelemetryServerRequestObservationConvention;
+import 
org.springframework.http.server.observation.ServerRequestObservationContext;
 import org.springframework.web.context.WebApplicationContext;
 import org.springframework.web.context.request.RequestContextListener;
 
+import 
org.apache.causeway.commons.internal.observation.CausewayObservationInternal;
 import org.apache.causeway.core.interaction.session.MessageBrokerImpl;
 import org.apache.causeway.core.metamodel.services.message.MessageBroker;
 import org.apache.causeway.core.runtime.CausewayModuleCoreRuntime;
@@ -36,7 +39,9 @@
 import 
org.apache.causeway.core.webapp.modules.templresources.WebModuleTemplateResources;
 import 
org.apache.causeway.core.webapp.webappctx.CausewayWebAppContextInitializer;
 
-@Configuration
+import io.micrometer.common.KeyValues;
+
+@Configuration(proxyBeanMethods = false)
 @Import({
         // Modules
         CausewayModuleCoreRuntime.class,
@@ -61,7 +66,7 @@ public class CausewayModuleCoreWebapp {
     @Scope(
             value = WebApplicationContext.SCOPE_SESSION,
             proxyMode = ScopedProxyMode.TARGET_CLASS)
-    public MessageBroker sessionScopedMessageBroker() {
+    MessageBroker sessionScopedMessageBroker() {
         return new MessageBrokerImpl();
     }
 
@@ -73,8 +78,27 @@ public MessageBroker sessionScopedMessageBroker() {
      * @see <a 
href="https://stackoverflow.com/a/61431621/56880";>https://stackoverflow.com/a/61431621/56880</a>
      */
     @Bean
-    public RequestContextListener requestContextListener() {
+    RequestContextListener requestContextListener() {
         return new RequestContextListener();
     }
 
+    /**
+     * https://docs.spring.io/spring-boot/reference/actuator/observability.html
+     */
+    @Bean
+    OpenTelemetryServerRequestObservationConvention 
openTelemetryServerRequestObservationConvention() {
+        return new OpenTelemetryServerRequestObservationConvention() {
+            @Override
+            public String getContextualName(final 
ServerRequestObservationContext context) {
+                return super.getContextualName(context) + " {TODO}";
+            }
+            @Override
+            public KeyValues getHighCardinalityKeyValues(final 
ServerRequestObservationContext context) {
+                // Make sure that KeyValues entries are already sorted by name 
for better performance
+                return KeyValues.of(methodOriginal(context), httpUrl(context),
+                        CausewayObservationInternal.currentThreadId());
+            }
+        };
+    }
+
 }
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/CausewayModuleViewerWicketViewer.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/CausewayModuleViewerWicketViewer.java
index 8746f572e7c..b3482c5feb3 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/CausewayModuleViewerWicketViewer.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/CausewayModuleViewerWicketViewer.java
@@ -43,7 +43,7 @@
 /**
  * @since 1.x {@index}
  */
-@Configuration
+@Configuration(proxyBeanMethods = false)
 @Import({
         // Modules
         CausewayModuleViewerWicketUi.class,
@@ -69,9 +69,9 @@
         PageClassRegistryDefault.AutoConfiguration.class,
         PageNavigationServiceDefault.AutoConfiguration.class,
         HintStoreUsingWicketSession.AutoConfiguration.class,
-
 })
 public class CausewayModuleViewerWicketViewer {
 
     public static final String NAMESPACE = "causeway.viewer.wicket";
+
 }
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStartHandler.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStartHandler.java
index daeef157a43..41504896e40 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStartHandler.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/integration/TelemetryStartHandler.java
@@ -22,6 +22,7 @@
 import org.apache.wicket.request.cycle.IRequestCycleListener;
 import org.apache.wicket.request.cycle.RequestCycle;
 
+import 
org.apache.causeway.commons.internal.observation.CausewayObservationInternal;
 import 
org.apache.causeway.commons.internal.observation.CausewayObservationInternal.ObservationProvider;
 
 /**
@@ -31,11 +32,16 @@ public record TelemetryStartHandler(
         ObservationProvider observationProvider)
 implements IRequestCycleListener {
 
+    public TelemetryStartHandler(final CausewayObservationInternal 
observationInternal) {
+        this(observationInternal.provider(TelemetryStartHandler.class));
+    }
+
     @Override
     public synchronized void onBeginRequest(final RequestCycle requestCycle) {
         if (requestCycle instanceof RequestCycle2 requestCycle2) {
             requestCycle2.observationClosure.startAndOpenScope(
-                    observationProvider.get("Apache Wicket Request Cycle"));
+                    observationProvider.get("Apache Wicket Request Cycle")
+                .lowCardinalityKeyValue("ck2", "test2"));
         }
     }
 
diff --git 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
index 597e9548e82..36985ca8661 100644
--- 
a/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
+++ 
b/viewers/wicket/viewer/src/main/java/org/apache/causeway/viewer/wicket/viewer/wicketapp/CausewayWicketApplication.java
@@ -21,7 +21,6 @@
 import java.time.Duration;
 import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
 import java.util.UUID;
 import java.util.function.Function;
 
@@ -213,8 +212,7 @@ protected void init() {
             
getRequestCycleSettings().setRenderStrategy(RequestCycleSettings.RenderStrategy.REDIRECT_TO_RENDER);
             getResourceSettings().setParentFolderPlaceholder("$up$");
 
-            getRequestCycleListeners().add(new 
TelemetryStartHandler(Objects.requireNonNull(observationInternal)
-                    .provider(TelemetryStartHandler.class)));
+            getRequestCycleListeners().add(new 
TelemetryStartHandler(observationInternal));
             getRequestCycleListeners().add(new 
WebRequestCycleForCauseway(metaModelContext, getPageClassRegistry()));
             getRequestCycleListeners().add(new 
TelemetryStopHandler(metricService));
             getRequestCycleListeners().add(new RehydrationHandler());

Reply via email to