Skip to main content
The Tracer trait is the main entry point for creating and managing spans in otel4s. It provides methods for creating spans, managing span context, and controlling trace propagation.

Type Signature

sealed trait Tracer[F[_]]

Core Methods

meta

def meta: InstrumentMeta[F]
The instrument’s metadata. Indicates whether instrumentation is enabled or not.

spanBuilder

def spanBuilder(name: String): SpanBuilder[F]
Creates a new SpanBuilder for building a fully customized span.
name
String
required
The name of the span
Returns: A SpanBuilder[F] for creating a span

currentSpanContext

def currentSpanContext: F[Option[SpanContext]]
Returns the context of the current span when a span that is not no-op exists in the local scope. Returns: F[Option[SpanContext]] - Some(SpanContext) if a non-noop span exists, None otherwise

currentSpanOrNoop

def currentSpanOrNoop: F[Span[F]]
Returns the current span if one exists in the local scope, or a no-op span otherwise. Returns: F[Span[F]] - The current span or a no-op span

currentSpanOrThrow

def currentSpanOrThrow: F[Span[F]]
Returns the current span if one exists in the local scope (even if it’s a no-op span), or raises an error in F otherwise. Returns: F[Span[F]] - The current span Throws: IllegalStateException when called while not inside a span, indicating programmer error

withCurrentSpanOrNoop

def withCurrentSpanOrNoop[A](f: Span[F] => F[A]): F[A]
Applies f to the current span if one exists in the local scope, or a no-op span otherwise. This is a convenience for currentSpanOrNoop.flatMap(f).
f
Span[F] => F[A]
required
Function to apply to the current span
Returns: F[A] - The result of applying f

Scope Management

childScope

def childScope[A](parent: SpanContext)(fa: F[A]): F[A]
Creates a new tracing scope with a custom parent. A newly created non-root span will be a child of the given parent.
parent
SpanContext
required
The span context to use as a parent
fa
F[A]
required
The effect to run within the child scope
Example:
val tracer: Tracer[F] = ???
val span: Span[F] = ???
val customChild: F[A] =
  tracer.childScope(span.context) {
    tracer.span("custom-parent").use { span => ??? }
  }

childOrContinue

def childOrContinue[A](parent: Option[SpanContext])(fa: F[A]): F[A]
Creates a new tracing scope if the given parent is defined. A newly created non-root span will be a child of the given parent. If parent is None, the effect executes in the current scope.
parent
Option[SpanContext]
required
Optional span context to use as a parent
fa
F[A]
required
The effect to run

rootScope

def rootScope[A](fa: F[A]): F[A]
Creates a new root tracing scope. The parent span will not be available inside. Thus, a span created inside of the scope will be a root one. Useful when an effect needs to be executed in the background and the parent tracing info is not needed.
fa
F[A]
required
The effect to run within the root scope
Example:
val tracer: Tracer[F] = ???
tracer.span("root-span").use { _ =>
  for {
    _ <- tracer.span("child-1").use(_ => ???) // a child of 'root-span'
    _ <- tracer.rootScope {
      tracer.span("child-2").use(_ => ???) // a root span not associated with 'root-span'
    }
  } yield ()
}

noopScope

def noopScope[A](fa: F[A]): F[A]
Creates a no-op tracing scope. The tracing operations inside of the scope are no-op.
fa
F[A]
required
The effect to run within the no-op scope
Example:
val tracer: Tracer[F] = ???
tracer.span("root-span").use { _ =>
  for {
    _ <- tracer.span("child-1").use(_ => ???) // a child of 'root-span'
    _ <- tracer.noopScope {
      tracer.span("child-2").use(_ => ???) // 'child-2' is not created at all
    }
  } yield ()
}

Context Propagation

joinOrRoot

def joinOrRoot[A, C: TextMapGetter](carrier: C)(fa: F[A]): F[A]
Creates a new tracing scope if a parent can be extracted from the given carrier. A newly created non-root span will be a child of the extracted parent. If the context cannot be extracted from the carrier, the given effect fa will be executed within the root span.
carrier
C
required
The carrier to extract the context from (e.g., HTTP headers)
fa
F[A]
required
The effect to run
Example - W3C headers:
val w3cHeaders: Map[String, String] =
  Map("traceparent" -> "00-80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-01")

Tracer[F].joinOrRoot(w3cHeaders) {
  Tracer[F].span("child").use { span => ??? } // a child of the external span
}
Example - Fallback to root:
Tracer[F].span("process").surround {
  Tracer[F].joinOrRoot(Map.empty) { // cannot extract context from empty map
    Tracer[F].span("child").use { span => ??? } // a child of the new root span
  }
}

propagate

def propagate[C: TextMapUpdater](carrier: C): F[C]
Propagates this tracer’s context into an immutable carrier.
carrier
C
required
The immutable carrier to append the context to
Returns: F[C] - A copy of the immutable carrier with this tracer’s context appended to it

Transformation

liftTo

def liftTo[G[_]: MonadCancelThrow](implicit
    F: MonadCancelThrow[F],
    kt: KindTransformer[F, G]
): Tracer[G]
Modifies the context F using an implicit KindTransformer from F to G.
G
Type Parameter
required
The target effect type
Returns: Tracer[G] - A tracer with the transformed effect type

Creating a Tracer

No-op Tracer

import Tracer.Implicits.noop
Creates a no-op implementation where all tracing operations are no-op.

From TracerProvider

val tracerProvider: TracerProvider[IO] = ???
tracerProvider
  .get("com.service.runtime")
  .flatMap { implicit tracer: Tracer[IO] => ??? }

See Also

Build docs developers (and LLMs) love