You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2019/10/19 16:08:07 UTC

[isis] branch ISIS-2164 updated: ISIS-2164: traces action and pages

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

danhaywood pushed a commit to branch ISIS-2164
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/ISIS-2164 by this push:
     new 40e773f  ISIS-2164: traces action and pages
40e773f is described below

commit 40e773fe0c4d8e85b8541e8e93e51213622be702
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sat Oct 19 17:07:52 2019 +0100

    ISIS-2164: traces action and pages
    
    working reasonably well, I think
---
 .../isis/core/webapp/OpenTracingBootstrapper.java  |   9 +-
 .../core/webapp/OpenTracingFilterForceClose.java   |  55 ++++++++
 .../apache/isis/core/tracing/Configuration2.java   |  15 ++
 .../isis/core/tracing/JaegerObjectFactory2.java    |  26 ++++
 .../org/apache/isis/core/tracing/JaegerSpan2.java  |  57 ++++++++
 .../isis/core/tracing/JaegerTracerBuilder2.java    |  10 ++
 .../java/org/apache/isis/core/tracing/Scope2.java  |  12 ++
 .../apache/isis/core/tracing/ScopeManager2.java    |  18 +++
 .../java/org/apache/isis/core/tracing/Span2.java   |  24 ++++
 .../isis/core/tracing/ThreadLocalScope2.java       |  24 ++--
 .../core/tracing/ThreadLocalScopeManager2.java     |  90 ------------
 .../isis/core/tracing/TraceScopeManager.java       | 157 +++++++++++++++++++++
 .../apache/isis/core/tracing/TracingDemoMain.java  |  12 +-
 ...rgetRespondListenerToResetQueryResultCache.java |   6 +
 .../integration/wicket/WebRequestCycleForIsis.java | 143 +++++++++++--------
 .../viewer/wicket/model/models/ActionModel.java    |  39 ++++-
 .../ui/pages/actionprompt/ActionPromptPage.java    |  24 +++-
 .../viewer/wicket/ui/pages/entity/EntityPage.java  |  15 +-
 .../viewer/wicket/ui/pages/error/ErrorPage.java    |  60 +++++---
 .../StandaloneCollectionPage.java                  |  15 +-
 .../java/domainapp/dom/impl/HelloWorldObjects.java |   4 +-
 .../helloworld/src/main/webapp/WEB-INF/web.xml     |  10 ++
 22 files changed, 620 insertions(+), 205 deletions(-)

diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/OpenTracingBootstrapper.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/OpenTracingBootstrapper.java
index a78814d..2bf8b8b 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/webapp/OpenTracingBootstrapper.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/OpenTracingBootstrapper.java
@@ -24,7 +24,8 @@ import java.util.concurrent.Callable;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
 
-import org.apache.isis.core.tracing.ThreadLocalScopeManager2;
+import org.apache.isis.core.tracing.Configuration2;
+import org.apache.isis.core.tracing.TraceScopeManager;
 
 import io.jaegertracing.Configuration;
 import io.jaegertracing.internal.samplers.ConstSampler;
@@ -41,7 +42,7 @@ import lombok.val;
  * accessible using {@link GlobalTracer#get()}. This is then picked up by the filter.
  *
  * It also installs a custom variant of {@link io.opentracing.util.ThreadLocalScopeManager}, namely
- * {@link ThreadLocalScopeManager2}.  Together with {@link org.apache.isis.core.tracing.ThreadLocalScope2}, this provides some convenience APIs and makes it possible to close a scope
+ * {@link TraceScopeManager}.  Together with {@link org.apache.isis.core.tracing.ThreadLocalScope2}, this provides some convenience APIs and makes it possible to close a scope
  * in a different method (though must be in the same thread) as the method that opened the scope.
  *
  * See the <code>TracingDemoMain</code> class (in <code>isis-core-tracing</code> module) for
@@ -79,7 +80,7 @@ public class OpenTracingBootstrapper implements ServletContextListener {
                         .withSender(Configuration.SenderConfiguration.fromEnv());
 
 
-                val config = new Configuration(openTracingServiceName)
+                val config = new Configuration2(openTracingServiceName)
                         .withSampler(samplerConfig)
                         .withReporter(reporterConfig)
                         ;
@@ -89,7 +90,7 @@ public class OpenTracingBootstrapper implements ServletContextListener {
 
                 return config
                         .getTracerBuilder()
-                        .withScopeManager(new ThreadLocalScopeManager2())
+                        .withScopeManager(new TraceScopeManager())
 
                         .withMetricsFactory(metricsReporter)
                         .build();
diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/OpenTracingFilterForceClose.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/OpenTracingFilterForceClose.java
new file mode 100644
index 0000000..2e79e90
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/OpenTracingFilterForceClose.java
@@ -0,0 +1,55 @@
+package org.apache.isis.core.webapp;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.isis.core.tracing.Scope2;
+import org.apache.isis.core.tracing.Span2;
+import org.apache.isis.core.tracing.TraceScopeManager;
+
+public class OpenTracingFilterForceClose implements Filter {
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+    }
+
+    /**
+     * To avoid infinite loops, this is the maximum number of scopes that can be forcibly closed
+     */
+    private final static int MAX_NESTING_LEVEL = 100;
+
+    @Override
+    public void doFilter(
+            final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain)
+            throws IOException, ServletException {
+
+        filterChain.doFilter(servletRequest, servletResponse);
+
+        forceCloseAnyUnclosedScopes();
+    }
+
+    private static void forceCloseAnyUnclosedScopes() {
+        Scope2 scope2 = TraceScopeManager.get().activeScope();
+        int count = 0;
+        while(scope2 != null && !scope2.isClosed() && count++ < MAX_NESTING_LEVEL) {
+
+            final Span2 span2 = TraceScopeManager.get().activeSpan();
+            if(span2 != null && !span2.isFinished()) {
+                span2.log("TracingFilterForceClose")
+                        .finish();
+            }
+            scope2.close();
+
+            scope2 = TraceScopeManager.get().activeScope();
+        }
+    }
+
+    @Override public void destroy() {
+    }
+}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/Configuration2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/Configuration2.java
new file mode 100644
index 0000000..1e96b08
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/Configuration2.java
@@ -0,0 +1,15 @@
+package org.apache.isis.core.tracing;
+
+import io.jaegertracing.Configuration;
+import io.jaegertracing.internal.JaegerTracer;
+
+public class Configuration2 extends Configuration {
+    public Configuration2(final String serviceName) {
+        super(serviceName);
+    }
+
+    @Override protected JaegerTracer.Builder createTracerBuilder(final String serviceName) {
+        return new JaegerTracerBuilder2(serviceName);
+    }
+
+}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerObjectFactory2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerObjectFactory2.java
new file mode 100644
index 0000000..455d177
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerObjectFactory2.java
@@ -0,0 +1,26 @@
+package org.apache.isis.core.tracing;
+
+import java.util.List;
+import java.util.Map;
+
+import io.jaegertracing.internal.JaegerObjectFactory;
+import io.jaegertracing.internal.JaegerSpan;
+import io.jaegertracing.internal.JaegerSpanContext;
+import io.jaegertracing.internal.JaegerTracer;
+import io.jaegertracing.internal.Reference;
+
+class JaegerObjectFactory2 extends JaegerObjectFactory {
+    @Override public JaegerSpan createSpan(
+            final JaegerTracer tracer,
+            final String operationName,
+            final JaegerSpanContext context,
+            final long startTimeMicroseconds,
+            final long startTimeNanoTicks,
+            final boolean computeDurationViaNanoTicks,
+            final Map<String, Object> tags,
+            final List<Reference> references) {
+        return new JaegerSpan2(tracer, operationName, context, startTimeMicroseconds, startTimeNanoTicks,
+                computeDurationViaNanoTicks, tags, references);
+    }
+
+}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerSpan2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerSpan2.java
new file mode 100644
index 0000000..1416270
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerSpan2.java
@@ -0,0 +1,57 @@
+package org.apache.isis.core.tracing;
+
+import java.util.List;
+import java.util.Map;
+
+import io.jaegertracing.internal.JaegerSpan;
+import io.jaegertracing.internal.JaegerSpanContext;
+import io.jaegertracing.internal.JaegerTracer;
+import io.jaegertracing.internal.Reference;
+
+class JaegerSpan2 extends JaegerSpan implements Span2 {
+
+    private boolean finished;
+    private Scope2 scope2;
+
+    public JaegerSpan2(
+            final JaegerTracer tracer,
+            final String operationName,
+            final JaegerSpanContext context,
+            final long startTimeMicroseconds,
+            final long startTimeNanoTicks,
+            final boolean computeDurationViaNanoTicks,
+            final Map<String, Object> tags,
+            final List<Reference> references) {
+        super(tracer, operationName, context, startTimeMicroseconds, startTimeNanoTicks, computeDurationViaNanoTicks, tags, references);
+    }
+
+    @Override
+    public void finish() {
+        super.finish();
+        finished = true;
+    }
+
+    @Override
+    public void finish(final long finishMicros) {
+        super.finish(finishMicros);
+        finished = true;
+    }
+
+    public boolean isFinished() {
+        return finished;
+    }
+
+    @Override
+    public Scope2 scope() {
+        return scope2;
+    }
+
+    @Override public boolean hasTag(final String tag) {
+        return getTags().keySet().contains(tag);
+    }
+
+    void setScope(final Scope2 scope2) {
+        this.scope2 = scope2;
+    }
+
+}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerTracerBuilder2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerTracerBuilder2.java
new file mode 100644
index 0000000..cb89e81
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/JaegerTracerBuilder2.java
@@ -0,0 +1,10 @@
+package org.apache.isis.core.tracing;
+
+import io.jaegertracing.internal.JaegerTracer;
+
+class JaegerTracerBuilder2 extends JaegerTracer.Builder {
+    JaegerTracerBuilder2(final String serviceName) {
+        super(serviceName, new JaegerObjectFactory2());
+    }
+
+}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/Scope2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/Scope2.java
new file mode 100644
index 0000000..e52d9a0
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/Scope2.java
@@ -0,0 +1,12 @@
+package org.apache.isis.core.tracing;
+
+import io.opentracing.Scope;
+
+public interface Scope2 extends Scope {
+
+    Span2 span();
+
+    void closeAndFinish();
+
+    boolean isClosed();
+}
\ No newline at end of file
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/ScopeManager2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/ScopeManager2.java
new file mode 100644
index 0000000..4db8c9e
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/ScopeManager2.java
@@ -0,0 +1,18 @@
+package org.apache.isis.core.tracing;
+
+import io.opentracing.ScopeManager;
+
+public interface ScopeManager2 extends ScopeManager {
+
+    Scope2 activeScope();
+
+    Scope2 startActive(final String name);
+
+    interface Executable {
+        void exec(Scope2 scope2);
+    }
+
+    interface ExecutableT<T> {
+        T exec(Scope2 scope2);
+    }
+}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/Span2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/Span2.java
new file mode 100644
index 0000000..9517487
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/Span2.java
@@ -0,0 +1,24 @@
+package org.apache.isis.core.tracing;
+
+import io.opentracing.Span;
+
+public interface Span2 extends Span {
+
+    String START_TAG = "_in";
+    String FINISH_TAG = "_out";
+
+    /**
+     * Indicates whether a nested scope/span has overridden the operation of a higher level (usually root) scope/span.
+     * 
+     * If so, this tag is set and the operation cannot be specified again.
+     * 
+     * @see TraceScopeManager#rootOperation(String).
+     */
+    String ROOT_OPERATION_TAG = "isis.root.operation";
+
+    boolean isFinished();
+
+    Scope2 scope();
+
+    boolean hasTag(String tag);
+}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/ThreadLocalScope2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/ThreadLocalScope2.java
index 2fe76e7..249919c 100644
--- a/core/tracing/src/main/java/org/apache/isis/core/tracing/ThreadLocalScope2.java
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/ThreadLocalScope2.java
@@ -1,14 +1,12 @@
 package org.apache.isis.core.tracing;
 
-import io.opentracing.Scope;
-import io.opentracing.Span;
+class ThreadLocalScope2 implements Scope2 {
 
-public class ThreadLocalScope2 implements Scope {
-    private final ThreadLocalScopeManager2 scopeManager;
-    private final Span wrapped;
-    private final ThreadLocalScope2 toRestore;
+    private final TraceScopeManager scopeManager;
+    private final Span2 wrapped;
+    final ThreadLocalScope2 toRestore;
 
-    ThreadLocalScope2(ThreadLocalScopeManager2 scopeManager, Span wrapped) {
+    ThreadLocalScope2(TraceScopeManager scopeManager, Span2 wrapped) {
         this.scopeManager = scopeManager;
         this.wrapped = wrapped;
         this.toRestore = scopeManager.tlsScope.get();
@@ -18,7 +16,7 @@ public class ThreadLocalScope2 implements Scope {
 
     @Override
     public void close() {
-        if (scopeManager.tlsScope.get() != this) {
+        if (!isActive()) {
             // This shouldn't happen if users call methods in the expected order. Bail out.
             return;
         }
@@ -26,7 +24,7 @@ public class ThreadLocalScope2 implements Scope {
         scopeManager.tlsScope.set(toRestore);
     }
 
-    public Span span() {
+    public Span2 span() {
         return wrapped;
     }
 
@@ -34,4 +32,12 @@ public class ThreadLocalScope2 implements Scope {
         close();
         wrapped.finish();
     }
+
+    @Override public boolean isClosed() {
+        return !isActive();
+    }
+
+    private boolean isActive() {
+        return scopeManager.tlsScope.get() == this;
+    }
 }
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/ThreadLocalScopeManager2.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/ThreadLocalScopeManager2.java
deleted file mode 100644
index 57b194e..0000000
--- a/core/tracing/src/main/java/org/apache/isis/core/tracing/ThreadLocalScopeManager2.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.apache.isis.core.tracing;
-
-import java.util.concurrent.Callable;
-
-import com.google.common.base.Throwables;
-
-import io.opentracing.Scope;
-import io.opentracing.ScopeManager;
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.util.GlobalTracer;
-
-/**
- * This allows the closing of the scope to be in a different method to the one that activated it
- * (though it must of course be in the same thread).
- */
-public class ThreadLocalScopeManager2 implements ScopeManager {
-
-    final ThreadLocal<ThreadLocalScope2> tlsScope = new ThreadLocal<>();
-
-    @Override
-    public Scope activate(Span span) {
-        return new ThreadLocalScope2(this, span);
-    }
-
-    @Override
-    public Span activeSpan() {
-        ThreadLocalScope2 scope = activeScope();
-        return scope == null ? null : scope.span();
-    }
-
-    public ThreadLocalScope2 activeScope() {
-        return tlsScope.get();
-    }
-
-    public static ThreadLocalScopeManager2 get() {
-        return (ThreadLocalScopeManager2) GlobalTracer.get().scopeManager();
-    }
-
-    public ThreadLocalScope2 startAndActivateChildSpan(final String name) {
-
-        final Span outer = this.activeSpan();
-        final Tracer tracer = GlobalTracer.get();
-        final Span innerSpan = tracer.buildSpan(name).asChildOf(outer).start();
-
-        return (ThreadLocalScope2) tracer.activateSpan(innerSpan);
-    }
-
-    private static ThreadLocalScope2 closeSpanIfAny() {
-
-        final ThreadLocalScopeManager2 scopeManager = (ThreadLocalScopeManager2) GlobalTracer.get().scopeManager();
-        final ThreadLocalScope2 scope = scopeManager.activeScope();
-
-        if(scope != null) {
-            scope.closeAndFinish();
-        }
-
-        return scope;
-    }
-
-    public void runInSpan(final String name, final Runnable runnable) {
-        ThreadLocalScope2 tracingScope = ThreadLocalScopeManager2.get().startAndActivateChildSpan(name);
-        try {
-            runnable.run();
-        } finally {
-            tracingScope.closeAndFinish();
-        }
-    }
-
-    public <T> T callInSpan(final String name, final Callable<T> callable) throws Exception {
-        ThreadLocalScope2 tracingScope = ThreadLocalScopeManager2.get().startAndActivateChildSpan(name);
-        try {
-            return callable.call();
-        } finally {
-            tracingScope.closeAndFinish();
-        }
-    }
-
-    public <T> T callInSpanEx(final String name, final Callable<T> callable) {
-        ThreadLocalScope2 tracingScope = ThreadLocalScopeManager2.get().startAndActivateChildSpan(name);
-        try {
-            return callable.call();
-        } catch (Exception e) {
-            tracingScope.span().setTag("exception", Throwables.getStackTraceAsString(e));
-            return null;
-        } finally {
-            tracingScope.closeAndFinish();
-        }
-    }
-}
diff --git a/core/tracing/src/main/java/org/apache/isis/core/tracing/TraceScopeManager.java b/core/tracing/src/main/java/org/apache/isis/core/tracing/TraceScopeManager.java
new file mode 100644
index 0000000..d61ce5c
--- /dev/null
+++ b/core/tracing/src/main/java/org/apache/isis/core/tracing/TraceScopeManager.java
@@ -0,0 +1,157 @@
+package org.apache.isis.core.tracing;
+
+import java.util.concurrent.Callable;
+
+import com.google.common.base.Throwables;
+
+import io.opentracing.Scope;
+import io.opentracing.Span;
+import io.opentracing.Tracer;
+import io.opentracing.util.GlobalTracer;
+
+/**
+ * This allows the closing of the scope to be in a different method to the one that activated it
+ * (though it must of course be in the same thread).
+ */
+public class TraceScopeManager implements ScopeManager2 {
+
+    final ThreadLocal<ThreadLocalScope2> tlsScope = new ThreadLocal<>();
+
+    public static TraceScopeManager get() {
+        return (TraceScopeManager) GlobalTracer.get().scopeManager();
+    }
+
+    @Override
+    public Scope activate(Span span) {
+        return new ThreadLocalScope2(this, (Span2)span);
+    }
+
+    @Override
+    public Span2 activeSpan() {
+        Scope2 scope = activeScope();
+        return scope == null ? null : scope.span();
+    }
+
+    @Override
+    public Scope2 activeScope() {
+        return tlsScope.get();
+    }
+
+    /**
+     * To avoid infinite loops, this is the maximum number of scopes ot search to root.
+     */
+    private final static int MAX_NESTING_LEVEL = 100;
+
+    /**
+     * Searches for the scope with no parent.
+     */
+    public Scope2 rootScope() {
+        ThreadLocalScope2 scope2 = tlsScope.get();
+        if(scope2 == null) {
+            return null;
+        }
+
+        int count = 0;
+        while(scope2.toRestore != null && count++ < MAX_NESTING_LEVEL) {
+            scope2 = scope2.toRestore;
+        }
+        return scope2;
+    }
+
+    public Scope2 startActive(final String name) {
+
+        final Span2 outerSpan = this.activeSpan();
+
+        final Tracer tracer = GlobalTracer.get();
+        final JaegerSpan2 innerSpan =
+                (JaegerSpan2) tracer.buildSpan(name).asChildOf(outerSpan).start();
+        final ThreadLocalScope2 innerScope = (ThreadLocalScope2) tracer.activateSpan(innerSpan);
+        innerSpan.setScope(innerScope);
+
+        return innerScope;
+    }
+
+
+    public void execInScope(final String operationName, final Executable withinScope) {
+        final Scope2 newScope = TraceScopeManager.get().startActive(operationName);
+        final Span2 span = newScope.span();
+        span.setTag(Span2.START_TAG, operationName)
+                .log(operationName);
+        try {
+            withinScope.exec(newScope);
+        } finally {
+            span.setTag(Span2.FINISH_TAG, operationName).finish();
+            newScope.close();
+        }
+    }
+
+    public <T> T execInScope(final String operationName, final ExecutableT<T> withinScope) {
+        final Scope2 newScope = TraceScopeManager.get().startActive(operationName);
+        final Span2 span = newScope.span();
+        span.setTag(Span2.START_TAG, operationName)
+                .log(operationName);
+        try {
+            return withinScope.exec(newScope);
+        } finally {
+            span.setTag(Span2.FINISH_TAG, operationName).finish();
+            newScope.close();
+        }
+    }
+
+    public void runInScope(final String operationName, final Runnable runnable) {
+        final Scope2 newScope = TraceScopeManager.get().startActive(operationName);
+        final Span2 span = newScope.span();
+        span.setTag(Span2.START_TAG, operationName)
+                .log(operationName);
+        try {
+            runnable.run();
+        } finally {
+            span.setTag(Span2.FINISH_TAG, operationName).finish();
+            newScope.close();
+        }
+    }
+
+    public <T> T callInScope(final String operationName, final Callable<T> callable) throws Exception {
+        final Scope2 tracingScope = TraceScopeManager.get()
+                .startActive(operationName);
+        final Span2 span = tracingScope.span();
+        span.setTag(Span2.START_TAG, operationName)
+                .log(operationName);
+        try {
+            return callable.call();
+        } finally {
+            span.setTag(Span2.FINISH_TAG, operationName).finish();
+            tracingScope.close();
+        }
+    }
+
+    public <T> T callInScopeX(final String operationName, final Callable<T> callable) {
+        final Scope2 tracingScope = TraceScopeManager.get()
+                .startActive(operationName);
+        final Span2 span = tracingScope.span();
+        span.setTag(Span2.START_TAG, operationName)
+            .log(operationName);
+        try {
+            return callable.call();
+        } catch (Exception e) {
+            span.setTag("exception", Throwables.getStackTraceAsString(e));
+            return null;
+        } finally {
+            span.setTag(Span2.FINISH_TAG, operationName).finish();
+            tracingScope.close();
+        }
+    }
+
+    public void rootOperation(final String operationName) {
+        final Scope2 rootScope = rootScope();
+        if(rootScope == null || rootScope.span() == null) {
+            return;
+        }
+        final Span2 span = rootScope.span();
+        if(span.hasTag(Span2.ROOT_OPERATION_TAG)) {
+            return;
+        }
+        span.setOperationName(operationName);
+        span.setTag(Span2.ROOT_OPERATION_TAG, true);
+    }
+}
diff --git a/core/tracing/src/test/java/org/apache/isis/core/tracing/TracingDemoMain.java b/core/tracing/src/test/java/org/apache/isis/core/tracing/TracingDemoMain.java
index 1b1e586..ac6f265 100644
--- a/core/tracing/src/test/java/org/apache/isis/core/tracing/TracingDemoMain.java
+++ b/core/tracing/src/test/java/org/apache/isis/core/tracing/TracingDemoMain.java
@@ -59,7 +59,7 @@ public class TracingDemoMain {
 
                 return config
                         .getTracerBuilder()
-                        .withScopeManager(new ThreadLocalScopeManager2())
+                        .withScopeManager(new TraceScopeManager())
                         .withMetricsFactory(metricsReporter)
                         .build();
             }
@@ -70,20 +70,20 @@ public class TracingDemoMain {
 
         // creates a child (but top-level if no existing parent)
         // we don't need to hold onto the scope created; we can look it up later (see end of this method)
-        final ThreadLocalScope2 unused =
-                ThreadLocalScopeManager2.get().startAndActivateChildSpan("outer-Z");
+        final Scope2 unused =
+                TraceScopeManager.get().startActive("outer-Z");
 
         Thread.sleep(300);
 
 
         // for nested scope, option (1) is handle scope and close
-        ThreadLocalScope2 tracingScope = ThreadLocalScopeManager2.get().startAndActivateChildSpan("inner-1");
+        Scope2 tracingScope = TraceScopeManager.get().startActive("inner-1");
         try {
             Thread.sleep(500);
 
 
             // for nested scope, option (2) is to use a callable or runnable
-            ThreadLocalScopeManager2.get().callInSpanEx("inner-2", new Callable<Object>() {
+            TraceScopeManager.get().callInScopeX("inner-2", new Callable<Object>() {
                 @Override public Object call() throws Exception {
                     Thread.sleep(500);
                     return null;
@@ -98,7 +98,7 @@ public class TracingDemoMain {
         Thread.sleep(200);
 
         // can look up the existing scope, don't need to pass through
-        final ThreadLocalScope2 scope = ThreadLocalScopeManager2.get().activeScope();
+        final Scope2 scope = TraceScopeManager.get().activeScope();
         if(scope != null) {
             scope.closeAndFinish();
         }
diff --git a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/TargetRespondListenerToResetQueryResultCache.java b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/TargetRespondListenerToResetQueryResultCache.java
index a3e9bd1..1e3a51a 100644
--- a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/TargetRespondListenerToResetQueryResultCache.java
+++ b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/TargetRespondListenerToResetQueryResultCache.java
@@ -24,6 +24,7 @@ import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 import org.apache.isis.core.metamodel.services.ServicesInjector;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
+import org.apache.isis.core.tracing.TraceScopeManager;
 
 class TargetRespondListenerToResetQueryResultCache implements AjaxRequestTarget.ITargetRespondListener {
 
@@ -32,6 +33,11 @@ class TargetRespondListenerToResetQueryResultCache implements AjaxRequestTarget.
     @Override
     public void onTargetRespond(final AjaxRequestTarget target) {
 
+        TraceScopeManager.get()
+                .activeSpan()
+                .log("onTargetResponse : RESPOND PHASE STARTED, resetting cache")
+                ;
+
         if(LOG.isDebugEnabled()) {
             LOG.debug("RESPOND PHASE STARTED: resetting cache");
         }
diff --git a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
index ebd7520..e52e588 100644
--- a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
+++ b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
@@ -65,7 +65,9 @@ import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.session.IsisSession;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
-import org.apache.isis.core.tracing.ThreadLocalScopeManager2;
+import org.apache.isis.core.tracing.Scope2;
+import org.apache.isis.core.tracing.Span2;
+import org.apache.isis.core.tracing.TraceScopeManager;
 import org.apache.isis.viewer.wicket.model.models.PageType;
 import org.apache.isis.viewer.wicket.ui.errors.ExceptionModel;
 import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry;
@@ -87,12 +89,11 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
     private PageClassRegistry pageClassRegistry;
 
     @Override
-    public synchronized void onBeginRequest(RequestCycle requestCycle) {
+    public synchronized void onBeginRequest(final RequestCycle requestCycle) {
 
         if (!Session.exists()) {
             return;
         }
-        LOG.debug("onBeginRequest");
 
 
         final AuthenticatedWebSessionForIsis wicketSession = AuthenticatedWebSessionForIsis.get();
@@ -101,9 +102,12 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
             return;
         }
 
-        ThreadLocalScopeManager2.get().startAndActivateChildSpan("wicket-request-cycle")
-                .span()
-                .setOperationName("onBeginRequest")
+        final Scope2 currScope = TraceScopeManager.get().activeScope();
+        final Scope2 newScope = TraceScopeManager.get()
+                .startActive("web-request-cycle-for-isis");
+        newScope.span()
+                .setTag(Span2.START_TAG, "onBeginRequest")
+                .log("onBeginRequest")
                 .setTag("requestCycle.request.url", requestCycle.getRequest().getUrl().toString());
 
         getIsisSessionFactory().openSession(authenticationSession);
@@ -112,9 +116,13 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
     }
 
     @Override
-    public void onRequestHandlerResolved(final RequestCycle cycle, final IRequestHandler handler) {
+    public void onRequestHandlerResolved(
+            final RequestCycle requestCycle,
+            final IRequestHandler handler) {
 
-        LOG.debug("onRequestHandlerResolved");
+        TraceScopeManager.get().activeSpan()
+                .log("onRequestHandlerResolved")
+        ;
 
         if(handler instanceof RenderPageRequestHandler) {
             AdapterManager.ConcurrencyChecking.disable();
@@ -131,7 +139,6 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
                 throw mmie;
             }
         }
-
     }
 
 
@@ -140,8 +147,23 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
      * throw an exception.
      */
     @Override
-    public void onRequestHandlerExecuted(RequestCycle cycle, IRequestHandler handler) {
-        LOG.debug("onRequestHandlerExecuted: handler: {}", handler);
+    public void onRequestHandlerExecuted(
+            final RequestCycle requestCycle,
+            final IRequestHandler handler) {
+
+        TraceScopeManager.get()
+                .activeSpan()
+                .log("onRequestHandlerExecuted")
+                .setTag(Span2.FINISH_TAG, "onRequestHandlerExecuted")
+                .finish();
+
+        TraceScopeManager.get()
+                .startActive("continue")
+                .span()
+                .setTag(Span2.START_TAG, "onRequestHandlerExecuted")
+                .log("onRequestHandlerExecuted")
+                .setTag("requestCycle.request.url", requestCycle.getRequest().getUrl().toString())
+                .setTag("handler.class.simpleName", handler.getClass().getSimpleName());
 
         if(handler instanceof RenderPageRequestHandler) {
             AdapterManager.ConcurrencyChecking.reset(AdapterManager.ConcurrencyChecking.CHECK);
@@ -153,24 +175,24 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
                 // an abort will cause the exception to be thrown.
                 getTransactionManager().endTransaction();
 
-                ThreadLocalScopeManager2.get()
-                        .activeSpan().setTag("finished-by", "onRequestHandlerExecuted");
-                ThreadLocalScopeManager2.get()
-                        .activeScope().closeAndFinish();
+                TraceScopeManager.get()
+                        .activeSpan()
+                        .log("onRequestHandlerExecuted: endTransaction OK");
             } catch(Exception ex) {
 
-                ThreadLocalScopeManager2.get()
+                TraceScopeManager.get()
                         .activeSpan()
-                        .setTag("errored-by", "onRequestHandlerExecuted")
-                        .setTag("exception", Throwables.getStackTraceAsString(ex));
-                ThreadLocalScopeManager2.get()
-                        .activeScope().closeAndFinish();
-
-                ThreadLocalScopeManager2.get()
-                        .startAndActivateChildSpan("error-response")
+                        .log("onRequestHandlerExecuted: endTransaction exception")
+                        .setTag("exception-location", "onRequestHandlerExecuted")
+                        .setTag("exception", Throwables.getStackTraceAsString(ex))
+                        .setTag(Span2.FINISH_TAG, "onRequestHandlerExecuted")
+                        .finish();
+
+                TraceScopeManager.get()
+                        .startActive("error-response")
                         .span()
-                        .setOperationName("onRequestHandlerExecuted")
-                        .setTag("purpose", "restart response after fail to commit xactn");
+                        .setTag(Span2.START_TAG, "onRequestHandlerExecuted")
+                        .log("restart response after fail to commit xactn");
 
                 // will redirect to error page after this,
                 // so make sure there is a new transaction ready to go.
@@ -184,7 +206,7 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
                         return;
                     }
                 }
-                
+
                 // shouldn't return null given that we're in a session ...
                 PageProvider errorPageProvider = errorPageProviderFor(ex);
                 throw new RestartResponseException(errorPageProvider, RedirectPolicy.ALWAYS_REDIRECT);
@@ -196,25 +218,33 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
      * It is not possible to throw exceptions here, hence use of {@link #onRequestHandlerExecuted(RequestCycle, IRequestHandler)}.
      */
     @Override
-    public synchronized void onEndRequest(RequestCycle cycle) {
+    public synchronized void onEndRequest(final RequestCycle requestCycle) {
 
-        LOG.debug("onEndRequest");
+        TraceScopeManager.get()
+                .activeSpan()
+                .log("onEndRequest")
+        ;
 
-        if (getIsisSessionFactory().inSession()) {
-            try {
-                // belt and braces
-                getTransactionManager().endTransaction();
-            } finally {
-                getIsisSessionFactory().closeSession();
+        try {
+            if (getIsisSessionFactory().inSession()) {
+                try {
+                    // belt and braces
+                    getTransactionManager().endTransaction();
+                } finally {
+                    getIsisSessionFactory().closeSession();
+                }
             }
+
+        } finally {
+            TraceScopeManager.get()
+                    .activeSpan()
+                    .setTag(Span2.FINISH_TAG, "onEndRequest")
+                    .finish();
+            TraceScopeManager.get()
+                    .activeScope()
+                    .close();
         }
 
-        ThreadLocalScopeManager2.get()
-                .activeSpan()
-                .setTag("finished-by", "onEndRequest");
-        ThreadLocalScopeManager2.get()
-                .activeScope()
-                .closeAndFinish();
 
     }
 
@@ -222,13 +252,12 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
     @Override
     public IRequestHandler onException(RequestCycle cycle, Exception ex) {
 
-        LOG.debug("onException");
-
-        ThreadLocalScopeManager2.get()
+        TraceScopeManager.get()
                 .activeSpan()
-                .setTag("errored-by", "onException")
+                .log("onException")
+                .setTag("exception-location", "onException")
                 .setTag("exception", Throwables.getStackTraceAsString(ex));
-        ThreadLocalScopeManager2.get()
+        TraceScopeManager.get()
                 .activeScope()
                 .closeAndFinish();
 
@@ -238,8 +267,8 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
             final Set<String> validationErrors = mmie.getValidationErrors();
             final MmvErrorPage mmvErrorPage = new MmvErrorPage(validationErrors);
 
-            ThreadLocalScopeManager2.get()
-                    .startAndActivateChildSpan("error-response")
+            TraceScopeManager.get()
+                    .startActive("error-response")
                     .span()
                     .setOperationName("onException")
                     .setTag("purpose", "redirect to error page on MetaModelInvalidException");
@@ -260,8 +289,8 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
 
                 }
 
-                ThreadLocalScopeManager2.get()
-                        .startAndActivateChildSpan("error-response")
+                TraceScopeManager.get()
+                        .startActive("error-response")
                         .span()
                         .setOperationName("onException")
                         .setTag("purpose", "respond gracefully after ListenerInvocationNotAllowedException");
@@ -276,8 +305,8 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
             String recognizedMessageIfAny = new ExceptionRecognizerComposite(exceptionRecognizers).recognize(ex);
             if(recognizedMessageIfAny != null) {
 
-                ThreadLocalScopeManager2.get()
-                        .startAndActivateChildSpan("error-response")
+                TraceScopeManager.get()
+                        .startActive("error-response")
                         .span()
                         .setOperationName("onException")
                         .setTag("purpose", "respond gracefully after recognised exception");
@@ -291,8 +320,8 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
             if(hiddenIfAny.isPresent()) {
                 addMessage("hidden");
 
-                ThreadLocalScopeManager2.get()
-                        .startAndActivateChildSpan("error-response")
+                TraceScopeManager.get()
+                        .startActive("error-response")
                         .span()
                         .setOperationName("onException")
                         .setTag("purpose", "respond gracefully if hidden");
@@ -304,8 +333,8 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
             if(disabledIfAny.isPresent()) {
                 addTranslatedMessage(disabledIfAny.get().getMessage());
 
-                ThreadLocalScopeManager2.get()
-                        .startAndActivateChildSpan("error-response")
+                TraceScopeManager.get()
+                        .startActive("error-response")
                         .span()
                         .setOperationName("onException")
                         .setTag("purpose", "respond gracefully if disabled");
@@ -323,8 +352,8 @@ public class WebRequestCycleForIsis extends AbstractRequestCycleListener {
                 ? RedirectPolicy.NEVER_REDIRECT
                 : RedirectPolicy.ALWAYS_REDIRECT;
 
-        ThreadLocalScopeManager2.get()
-                .startAndActivateChildSpan("error-response")
+        TraceScopeManager.get()
+                .startActive("error-response")
                 .span()
                 .setOperationName("onException")
                 .setTag("purpose", "redirect to error page on " + ex.getClass().getSimpleName());
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
index 9b04433..a7773f5 100644
--- a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
@@ -67,12 +67,18 @@ import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.tracing.Scope2;
+import org.apache.isis.core.tracing.Span2;
+import org.apache.isis.core.tracing.TraceScopeManager;
 import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
 import org.apache.isis.viewer.wicket.model.mementos.ActionMemento;
 import org.apache.isis.viewer.wicket.model.mementos.ActionParameterMemento;
 import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
 import org.apache.isis.viewer.wicket.model.mementos.PageParameterNames;
 
+import io.opentracing.Span;
+
 public class ActionModel extends BookmarkableModel<ObjectAdapter> implements FormExecutorContext {
 
     private static final long serialVersionUID = 1L;
@@ -456,11 +462,34 @@ public class ActionModel extends BookmarkableModel<ObjectAdapter> implements For
 
         // if this action is a mixin, then it will fill in the details automatically.
         final ObjectAdapter mixedInAdapter = null;
-        final ObjectAdapter resultAdapter =
-                action.executeWithRuleChecking(
-                        targetAdapter, mixedInAdapter, arguments,
-                        InteractionInitiatedBy.USER,
-                        WHERE_FOR_ACTION_INVOCATION);
+
+        final Scope2 newScope = TraceScopeManager.get().startActive("executeAction");
+        final Span span = newScope.span()
+                .setTag(Span2.START_TAG, "executeAction")
+                .setTag("user", IsisContext.getSessionFactory().getCurrentSession().getAuthenticationSession().getUserName())
+                .setTag("actionId", action.getIdentifier().toClassAndNameIdentityString())
+                .setTag("target", targetAdapter.titleString(null));
+        TraceScopeManager.get().rootOperation(action.getIdentifier().toClassAndNameIdentityString());
+
+
+        final List<ObjectActionParameter> parameters = action.getParameters();
+        for (int i = 0; i < parameters.size(); i++) {
+            final ObjectActionParameter parameter = parameters.get(i);
+            final ObjectAdapter argument = arguments[i];
+            span.setTag("action param: " + parameter.getId(), argument.titleString(targetAdapter));
+        }
+
+        final ObjectAdapter resultAdapter;
+        try {
+            resultAdapter = action.executeWithRuleChecking(
+                    targetAdapter, mixedInAdapter, arguments,
+                    InteractionInitiatedBy.USER,
+                    WHERE_FOR_ACTION_INVOCATION);
+        } finally {
+            span.setTag(Span2.FINISH_TAG, "executeAction")
+                .finish();
+            newScope.close();
+        }
 
         final List<RoutingService> routingServices = getServicesInjector().lookupServices(RoutingService.class);
         final Object result = resultAdapter != null ? resultAdapter.getObject() : null;
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/actionprompt/ActionPromptPage.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/actionprompt/ActionPromptPage.java
index b03d48b..dca8c7d 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/actionprompt/ActionPromptPage.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/actionprompt/ActionPromptPage.java
@@ -24,6 +24,9 @@ import org.apache.wicket.request.mapper.parameter.PageParameters;
 
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.tracing.Scope2;
+import org.apache.isis.core.tracing.ScopeManager2;
+import org.apache.isis.core.tracing.TraceScopeManager;
 import org.apache.isis.viewer.wicket.model.models.ActionModel;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
@@ -38,12 +41,23 @@ public class ActionPromptPage extends PageAbstract {
 
     public ActionPromptPage(final ActionModel model) {
         super(new PageParameters(), model.getActionMemento().getAction(model.getSpecificationLoader()).getName(), ComponentType.ACTION_PROMPT);
-        addChildComponents(themeDiv, model);
 
-        if(model.isBookmarkable()) {
-            bookmarkPageIfShown(model);
-        }
-        addBookmarkedPages(themeDiv);
+        TraceScopeManager.get()
+                .execInScope("ActionPromptPage#<init>", new ScopeManager2.Executable() {
+                    @Override
+                    public void exec(Scope2 scope2) {
+                        scope2.span().setTag("user", IsisContext.getSessionFactory().getCurrentSession().getAuthenticationSession().getUserName());
+
+                        addChildComponents(themeDiv, model);
+
+                        if(model.isBookmarkable()) {
+                            bookmarkPageIfShown(model);
+                        }
+                        addBookmarkedPages(themeDiv);
+
+                    }
+                });
+
     }
 
     /**
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java
index 6e52696..6946521 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java
@@ -36,6 +36,10 @@ import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.tracing.Scope2;
+import org.apache.isis.core.tracing.ScopeManager2;
+import org.apache.isis.core.tracing.TraceScopeManager;
 import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
@@ -112,7 +116,16 @@ public class EntityPage extends PageAbstract {
         this.model = entityModel;
         this.titleString = titleString;
 
-        buildPage();
+        TraceScopeManager.get()
+                .execInScope("EntityPage#<init>", new ScopeManager2.Executable() {
+                    @Override
+                    public void exec(Scope2 scope2) {
+                        scope2.span().setTag("user", IsisContext.getSessionFactory().getCurrentSession().getAuthenticationSession().getUserName());
+                        buildPage();
+                        TraceScopeManager.get().rootOperation(entityModel.getObjectAdapterMemento().asBookmark().toString());
+
+                    }
+                });
     }
 
     private void addBreadcrumbIfShown(final EntityModel entityModel) {
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
index e5c2973..4191b47 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
@@ -31,6 +31,10 @@ import org.apache.wicket.authroles.authorization.strategies.role.annotations.Aut
 import org.apache.isis.applib.services.error.ErrorDetails;
 import org.apache.isis.applib.services.error.ErrorReportingService;
 import org.apache.isis.applib.services.error.Ticket;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.tracing.Scope2;
+import org.apache.isis.core.tracing.ScopeManager2;
+import org.apache.isis.core.tracing.TraceScopeManager;
 import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
 import org.apache.isis.viewer.wicket.ui.errors.ExceptionModel;
 import org.apache.isis.viewer.wicket.ui.errors.ExceptionStackTracePanel;
@@ -48,40 +52,50 @@ public class ErrorPage extends PageAbstract {
     private static final String ID_EXCEPTION_STACK_TRACE = "exceptionStackTrace";
 
 
-    public ErrorPage(ExceptionModel exceptionModel) {
+    public ErrorPage(final ExceptionModel exceptionModel) {
         super(PageParametersUtils.newPageParameters(), null);
 
-        addBookmarkedPages(themeDiv);
+        TraceScopeManager.get()
+                .execInScope("ErrorPage#<init>", new ScopeManager2.Executable() {
+                    @Override
+                    public void exec(Scope2 scope2) {
+                        scope2.span().setTag("user", IsisContext.getSessionFactory().getCurrentSession().getAuthenticationSession().getUserName());
 
-        final ErrorReportingService errorReportingService = getServicesInjector()
-                .lookupService(ErrorReportingService.class);
-        if(errorReportingService != null) {
+                        addBookmarkedPages(themeDiv);
 
-            final String mainMessage = exceptionModel.getMainMessage();
-            final boolean recognized = exceptionModel.isRecognized();
-            final boolean authorizationException = exceptionModel.isAuthorizationException();
+                        final ErrorReportingService errorReportingService = getServicesInjector()
+                                .lookupService(ErrorReportingService.class);
+                        if(errorReportingService != null) {
 
-            final List<StackTraceDetail> stackTrace = exceptionModel.getStackTrace();
-            final List<String> stackDetailList = transform(stackTrace);
+                            final String mainMessage = exceptionModel.getMainMessage();
+                            final boolean recognized = exceptionModel.isRecognized();
+                            final boolean authorizationException = exceptionModel.isAuthorizationException();
 
-            final List<List<StackTraceDetail>> stackTraces = exceptionModel.getStackTraces();
-            final List<List<String>> stackDetailLists = Lists.newArrayList();
-            for (List<StackTraceDetail> trace : stackTraces) {
-                stackDetailLists.add(transform(trace));
-            }
+                            final List<StackTraceDetail> stackTrace = exceptionModel.getStackTrace();
+                            final List<String> stackDetailList = transform(stackTrace);
 
-            final ErrorDetails errorDetails =
-                    new ErrorDetails(mainMessage, recognized, authorizationException, stackDetailList, stackDetailLists);
+                            final List<List<StackTraceDetail>> stackTraces = exceptionModel.getStackTraces();
+                            final List<List<String>> stackDetailLists = Lists.newArrayList();
+                            for (List<StackTraceDetail> trace : stackTraces) {
+                                stackDetailLists.add(transform(trace));
+                            }
 
-            final Ticket ticket = errorReportingService.reportError(errorDetails);
+                            final ErrorDetails errorDetails =
+                                    new ErrorDetails(mainMessage, recognized, authorizationException, stackDetailList, stackDetailLists);
 
-            if (ticket != null) {
-                exceptionModel.setTicket(ticket);
-            }
+                            final Ticket ticket = errorReportingService.reportError(errorDetails);
 
-        }
+                            if (ticket != null) {
+                                exceptionModel.setTicket(ticket);
+                            }
+
+                        }
+
+                        themeDiv.add(new ExceptionStackTracePanel(ID_EXCEPTION_STACK_TRACE, exceptionModel));
+
+                    }
+                });
 
-        themeDiv.add(new ExceptionStackTracePanel(ID_EXCEPTION_STACK_TRACE, exceptionModel));
 
     }
 
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java
index 9c634c8..1ca9a2e 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/standalonecollection/StandaloneCollectionPage.java
@@ -22,6 +22,10 @@ package org.apache.isis.viewer.wicket.ui.pages.standalonecollection;
 import org.apache.wicket.Component;
 import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
 
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.core.tracing.Scope2;
+import org.apache.isis.core.tracing.ScopeManager2;
+import org.apache.isis.core.tracing.TraceScopeManager;
 import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
 import org.apache.isis.viewer.wicket.model.models.ActionModel;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
@@ -41,9 +45,16 @@ public class StandaloneCollectionPage extends PageAbstract {
      */
     public StandaloneCollectionPage(final EntityCollectionModel model) {
         super(PageParametersUtils.newPageParameters(), actionNameFrom(model), ComponentType.STANDALONE_COLLECTION);
-        addChildComponents(themeDiv, model);
+        TraceScopeManager.get()
+                .execInScope("StandaloneCollectionPage#<init>", new ScopeManager2.Executable() {
+                    @Override
+                    public void exec(Scope2 scope2) {
+                        scope2.span().setTag("user", IsisContext.getSessionFactory().getCurrentSession().getAuthenticationSession().getUserName());
+                        addChildComponents(themeDiv, model);
+                        addBookmarkedPages(themeDiv);
+                    }
+                });
 
-        addBookmarkedPages(themeDiv);
     }
 
     private static String actionNameFrom(final EntityCollectionModel model) {
diff --git a/example/application/helloworld/src/main/java/domainapp/dom/impl/HelloWorldObjects.java b/example/application/helloworld/src/main/java/domainapp/dom/impl/HelloWorldObjects.java
index 11c5435..4e5824a 100644
--- a/example/application/helloworld/src/main/java/domainapp/dom/impl/HelloWorldObjects.java
+++ b/example/application/helloworld/src/main/java/domainapp/dom/impl/HelloWorldObjects.java
@@ -33,7 +33,6 @@ import org.apache.isis.applib.annotation.RestrictTo;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.jdosupport.IsisJdoSupport;
 import org.apache.isis.applib.services.repository.RepositoryService;
-import org.apache.isis.core.tracing.ThreadLocalScopeManager2;
 
 @DomainService(
         nature = NatureOfService.VIEW_MENU_ONLY,
@@ -67,8 +66,7 @@ public class HelloWorldObjects {
 
     @Action(semantics = SemanticsOf.SAFE, restrictTo = RestrictTo.PROTOTYPING)
     public List<HelloWorldObject> listAll() {
-        return ThreadLocalScopeManager2.get().callInSpanEx(
-                "listAll", () -> repositoryService.allInstances(HelloWorldObject.class));
+        return repositoryService.allInstances(HelloWorldObject.class);
     }
 
     @javax.inject.Inject
diff --git a/example/application/helloworld/src/main/webapp/WEB-INF/web.xml b/example/application/helloworld/src/main/webapp/WEB-INF/web.xml
index f012b11..9be6c18 100644
--- a/example/application/helloworld/src/main/webapp/WEB-INF/web.xml
+++ b/example/application/helloworld/src/main/webapp/WEB-INF/web.xml
@@ -37,6 +37,16 @@
     </listener>
 
     <filter>
+        <filter-name>OpenTracingFilterForceClose</filter-name>
+        <filter-class>org.apache.isis.core.webapp.OpenTracingFilterForceClose</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>OpenTracingFilterForceClose</filter-name>
+        <url-pattern>/wicket/*</url-pattern>
+    </filter-mapping>
+
+    <filter>
         <filter-name>OpenTracingFilter</filter-name>
         <filter-class>io.opentracing.contrib.web.servlet.filter.TracingFilter</filter-class>
     </filter>