| Copyright | (c) Ian Duncan 2021-2026 |
|---|---|
| License | BSD-3 |
| Stability | experimental |
| Safe Haskell | None |
| Language | Haskell2010 |
OpenTelemetry.Context.ThreadLocal
Description
Overview
Manages the implicit Context that flows through your application. The
inSpan functions automatically push/pop context here, so you rarely
need to use this module directly.
Common operations
import OpenTelemetry.Context.ThreadLocal import OpenTelemetry.Context (insertBaggage, lookupSpan) -- Read the current context (contains active span + baggage): ctx <- getContext -- Modify the current context (e.g. attach baggage): adjustContext (insertBaggage myBaggage) -- Attach context extracted from incoming headers: tok <- attachContext extractedCtx -- ... later ... detachContext tok
Token semantics
Per the OpenTelemetry specification, attachContext returns an opaque Token
that must be passed to detachContext to restore the previous context. Tokens
enforce LIFO ordering: if you attach contexts A then B, you must detach B
before A. Detaching out of order logs a diagnostic error.
When to use this module
- Reading the current span:
lookupSpan <$> getContext - Setting/reading baggage on the current thread
- Manually attaching context after propagator extraction
- Implementing custom instrumentation middleware
Synopsis
- data Token
- getContext :: MonadIO m => m Context
- lookupContext :: MonadIO m => m (Maybe Context)
- attachContext :: MonadIO m => Context -> m Token
- detachContext :: MonadIO m => Token -> m ()
- adjustContext :: MonadIO m => (Context -> Context) -> m ()
- getAndAdjustContext :: MonadIO m => (Context -> Context) -> m Context
- getActiveBaggage :: MonadIO m => m (Maybe Baggage)
- setActiveBaggage :: MonadIO m => Baggage -> m ()
- clearActiveBaggage :: MonadIO m => m ()
- data ContextEntry
- emptyEntry :: ContextEntry
- ensureContextRef :: ThreadId -> Int -> IO (IORef ContextEntry)
- ensureContextRefFast :: IO (Int, IORef ContextEntry)
- lookupContextRefFast :: IO (Int, Maybe (IORef ContextEntry))
- readContextRef :: IORef ContextEntry -> IO Context
- writeContextRef :: IORef ContextEntry -> Context -> IO ()
- lookupContextOnThread :: MonadIO m => ThreadId -> m (Maybe Context)
- attachContextOnThread :: MonadIO m => ThreadId -> Context -> m Token
- detachContextFromThread :: MonadIO m => ThreadId -> Token -> m ()
- adjustContextOnThread :: MonadIO m => ThreadId -> (Context -> Context) -> m ()
- threadContextMap :: ThreadContextMap
Thread-local context
Opaque token (spec-mandated attach/detach handle)
Opaque token returned by attachContext.
Pass this to detachContext to restore the context that was active before
the attach. The implementation validates LIFO ordering: detaching out of
order (i.e. with a token that doesn't match the most recent attach) logs
a diagnostic error per the OpenTelemetry specification.
Tokens are lightweight (three words) and do not hold resources that need
explicit cleanup. If a token is simply dropped without calling
detachContext, nothing leaks — but you lose the LIFO validation for
that attach point.
Since: 0.5.0.0
getContext :: MonadIO m => m Context Source #
Retrieve a stored Context for the current thread, or an empty context if none exists.
Warning: this can easily cause disconnected traces if libraries don't explicitly set the context on forked threads.
Since: 0.0.1.0
lookupContext :: MonadIO m => m (Maybe Context) Source #
Retrieve a stored Context for the current thread, if it exists.
Since: 0.0.1.0
attachContext :: MonadIO m => Context -> m Token Source #
Attach a Context to the current thread, returning an opaque Token.
Pass the token to detachContext to restore the previous context. Tokens
enforce LIFO ordering per the OpenTelemetry specification.
Since: 0.5.0.0
detachContext :: MonadIO m => Token -> m () Source #
Restore the context that was active before the corresponding
attachContext call, using the provided Token.
If the token does not match the most recently attached context on this thread (LIFO violation), the implementation logs a diagnostic error per the OpenTelemetry specification. The previous context is still restored regardless.
Since: 0.5.0.0
adjustContext :: MonadIO m => (Context -> Context) -> m () Source #
Alter the context on the current thread using the provided function.
If there is not a context associated with the current thread, the function will be applied to an empty context and the result will be stored.
This does not affect the active token — it modifies the context in place.
Since: 0.0.1.0
getAndAdjustContext :: MonadIO m => (Context -> Context) -> m Context Source #
Atomically read the current context and replace it via f in a single
operation, returning the old context.
Since: 0.4.0.0
Active baggage (implicit context)
getActiveBaggage :: MonadIO m => m (Maybe Baggage) Source #
Get the currently active Baggage from the implicit thread-local context.
Since: 0.4.0.0
setActiveBaggage :: MonadIO m => Baggage -> m () Source #
Set the currently active Baggage in the implicit thread-local context.
Since: 0.4.0.0
clearActiveBaggage :: MonadIO m => m () Source #
Clear the active Baggage from the implicit thread-local context.
Since: 0.4.0.0
Fused ref-based operations (zero CAS on hot path)
data ContextEntry Source #
Per-thread context entry: the context plus the active token ID. A token ID of 0 means no token is active (initial state).
ensureContextRef :: ThreadId -> Int -> IO (IORef ContextEntry) Source #
Get or create the per-thread context IORef. On the first call per thread, this inserts a new entry (flat-table CAS, once per thread lifetime). On all subsequent calls the IORef is found via a single flat-table probe (no CAS).
Use readIORef and writeIORef on the returned IORef for zero-overhead
context access on the hot path.
Since: 0.5.0.0
ensureContextRefFast :: IO (Int, IORef ContextEntry) Source #
Fused CMM fast path: reads CurrentTSO.id and probes the flat table
entirely in CMM, returning the per-thread context IORef. No ThreadId
allocation, no FFI call, no Maybe wrapper on the steady-state path.
Returns (threadId, contextRef). The thread ID is returned for reuse
as the thread.id span attribute.
First call per thread: falls back to myThreadId + insert (one CAS).
Since: 0.5.0.0
lookupContextRefFast :: IO (Int, Maybe (IORef ContextEntry)) Source #
Fused CMM probe: reads CurrentTSO.id and probes the flat table
entirely in CMM. Returns (threadId, Maybe (IORef ContextEntry)).
The thread ID is returned for reuse on the slow path.
Since: 0.5.0.0
readContextRef :: IORef ContextEntry -> IO Context Source #
writeContextRef :: IORef ContextEntry -> Context -> IO () Source #
Write a Context to a per-thread IORef, preserving the active token ID.
Only the owning thread should call this.
For the hot path (e.g. inSpanInternal), prefer writing the full
ContextEntry via writeIORef to avoid the read-modify-write.
Since: 0.5.0.0
Generalized thread-local context functions
adjustContextOnThread :: MonadIO m => ThreadId -> (Context -> Context) -> m () Source #
Alter the context on the provided thread.
If there is not a context associated with the provided thread, the function will be applied to an empty context and the result will be stored.
Since: 0.0.1.0
Debugging tools
threadContextMap :: ThreadContextMap Source #
This is a global variable that is used to store the thread-local context map. It is not intended to be used directly for production purposes, but is exposed for debugging purposes.