diff --git a/common/pom.xml b/common/pom.xml
new file mode 100644
index 00000000..44254ea8
--- /dev/null
+++ b/common/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+
+ org.commonjava.util
+ o11yphant
+ 1.9.2-SNAPSHOT
+
+
+ o11yphant-common
+
+
\ No newline at end of file
diff --git a/common/src/main/java/org/commonjava/o11yphant/common/thread/ThreadContext.java b/common/src/main/java/org/commonjava/o11yphant/common/thread/ThreadContext.java
new file mode 100644
index 00000000..48881b41
--- /dev/null
+++ b/common/src/main/java/org/commonjava/o11yphant/common/thread/ThreadContext.java
@@ -0,0 +1,275 @@
+/**
+ * Copyright (C) 2013-2022 Red Hat, Inc. (https://github.com/Commonjava/weft)
+ *
+ * Licensed 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.commonjava.o11yphant.common.thread;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * (Copied from weft: https://github.com/Commonjava/weft)
+ *
+ * This {@link ThreadContext} keeps a count of the number of threads referencing it, and can run finalization logic
+ * when that number hits 0.
+ *
+ * Created by jdcasey on 1/3/17.
+ */
+public class ThreadContext
+ implements Map
+{
+ private static ThreadLocal THREAD_LOCAL = new ThreadLocal<>();
+
+ private final Map contextMap = new ConcurrentHashMap<>();
+
+ private Map mdcMap; // mapped diagnostic context
+
+ private int refs = 1;
+
+ private List> finalizers = new ArrayList<>();
+
+ public static ThreadContext getContext( boolean create )
+ {
+ ThreadContext threadContext = THREAD_LOCAL.get();
+ if ( threadContext == null && create )
+ {
+ threadContext = new ThreadContext();
+ THREAD_LOCAL.set( threadContext );
+ }
+
+ if ( threadContext != null )
+ {
+ threadContext.mdcMap = MDC.getCopyOfContextMap();
+ }
+
+ return threadContext;
+ }
+
+ public static ThreadContext setContext( ThreadContext ctx )
+ {
+ ThreadContext oldCtx = swapContext( ctx );
+ if ( ctx != null && ctx.mdcMap != null )
+ {
+ MDC.setContextMap(ctx.mdcMap);
+ }
+ return oldCtx;
+ }
+
+ private static ThreadContext swapContext( final ThreadContext ctx )
+ {
+ ThreadContext oldCtx = THREAD_LOCAL.get();
+ if ( oldCtx != null )
+ {
+ Logger logger = LoggerFactory.getLogger( ThreadContext.class );
+ oldCtx.refs--;
+ logger.trace( "context refs: {}", oldCtx.refs );
+ oldCtx.runFinalizersIfDone();
+ }
+
+ if ( ctx != null )
+ {
+ THREAD_LOCAL.set( ctx );
+ ctx.refs++;
+ }
+ else
+ {
+ THREAD_LOCAL.remove();
+ }
+
+ return oldCtx;
+ }
+
+ /**
+ * Provide some finalizer logic to handle the scenario where the number of "live" threads referencing this context
+ * drops to 0. Before this happens, any contextual information in this ThreadContext may be needed by running threads,
+ * and it's not safe to clean up. However, since the context may contain {@link java.io.Closeable} instances and
+ * the like, it's important to have some point where they will be cleaned up.
+ * @since 1.5
+ * @param finalizer
+ */
+ public synchronized void registerFinalizer( Consumer finalizer )
+ {
+ if ( !this.finalizers.contains( finalizer ) )
+ {
+ Logger logger = LoggerFactory.getLogger( getClass() );
+ logger.debug( "Registering finalizer: {} on ThreadContext: {}", finalizer, this );
+ this.finalizers.add( finalizer );
+ }
+ }
+
+ /**
+ * If the thread reference count on this context drops to zero, run any finalization logic that might be registered.
+ */
+ private synchronized void runFinalizersIfDone()
+ {
+ Logger logger = LoggerFactory.getLogger( getClass() );
+ if ( refs < 1 && finalizers != null )
+ {
+ logger.debug( "Running finalizers for ThreadContext: {}", this );
+ finalizers.forEach( c->{
+ if ( c != null )
+ {
+ logger.debug( "Running finalizer: {} for ThreadContext: {}", c, this );
+
+ try
+ {
+ c.accept( this );
+ }
+ catch ( Throwable t )
+ {
+ logger.error( "Caught error while running finalizer: " + c + " on ThreadContext: " + this, t );
+ }
+
+ logger.trace( "Finalizer: {} done for ThreadContext: {}", c, this );
+ }
+ } );
+ }
+ }
+
+ public static void clearContext()
+ {
+ swapContext( null );
+ MDC.clear();
+ }
+
+ private ThreadContext(){}
+
+ public int size()
+ {
+ return contextMap.size();
+ }
+
+ public boolean isEmpty()
+ {
+ return contextMap.isEmpty();
+ }
+
+ public void putAll( Map extends String, ?> map )
+ {
+ contextMap.putAll( map );
+ }
+
+ public Collection