Class Tracing

java.lang.Object
com.babelqueue.otel.Tracing

public final class Tracing extends Object
Optional OpenTelemetry tracing for a babelqueue producer or consumer — the Java mirror of the Go babelqueue-go/otel, Python babelqueue.otel and Node @babelqueue/core/otel helpers.

Cross-hop trace propagation works at two layered levels:

  • trace_id ↔ OTel trace id (ADR-0025, v0.1): the envelope's trace_id — a UUID — maps 1:1 to a 32-hex OpenTelemetry trace id (traceIdOf(java.lang.String) / uuidOf(java.lang.String); a non-UUID trace_id is hashed deterministically, SHA-256). Every hop that shares a trace_id shares one OTel trace — correlation and per-hop timing with zero wire/transport change.
  • W3C traceparent transport header (ADR-0028, v0.2): the producer also injects the active span context as a traceparent (and tracestate) onto an out-of-band header map that rides beside the frozen envelope, never inside it (GR-1). The consumer reads it and starts its span as a true child of the producer span — real cross-hop parent→child linkage and per-hop span timing, not just a shared trace. With no traceparent present it falls back to the v0.1 trace_id behaviour, so enabling propagation is a strict, backward-compatible upgrade — never a regression.
  • Consumer: wrapHandler(Tracer, Handler) emits a CONSUMER span process <urn> parented on the trace_id-derived trace (v0.1). The headers overload additionally reads the delivered message's out-of-band headers and, when they carry a valid traceparent, parents the span as a true child of the producer span (v0.2). It reuses the shared Handler, so it composes with Idempotent.wrap / SchemaValidation.wrap.
  • Producer: publish(Tracer, String, Map, String, Sender) emits a PRODUCER span publish <urn> that carries the active trace's id into the message's trace_id (v0.1), then writes it via a Sender. The HeaderSender overload additionally injects the active span's traceparent onto an out-of-band header map handed to the transport (v0.2).

The wire envelope is untouched (GR-1) and the core never imports OpenTelemetry on a non-opt-in path: io.opentelemetry:opentelemetry-api (which itself ships OTel's W3C propagator) is an optional dependency, so the core stays zero-dependency for users who do not opt in (GR-7).

Transport wiring is a documented follow-up. The Java transports live in separate artifacts (babelqueue-java-sqs, babelqueue-java-redis, babelqueue-spring). This core delivers the v0.2 mechanism — the out-of-band header seam (HeaderSender produce-side, a delivered-headers Supplier consume-side) plus the traceparent inject/extract. Carrying the header map on each transport's native per-message metadata channel (AMQP headers, SQS MessageAttributes, a Redis transport-owned frame), beside the contract bq-*/x-* headers, is the per-transport rollout — the same seam ADR-0027 and the broker bindings roll out per SDK. Until a transport wires it, propagation degrades to the v0.1 trace_id correlation with no error.

  • Method Summary

    Modifier and Type
    Method
    Description
    static String
    publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, HeaderSender send)
    Publishes (urn, data) to the "default" queue under a PRODUCER span, propagating a W3C traceparent on the out-of-band header map (ADR-0028).
    static String
    publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, Sender send)
    Publishes (urn, data) to the "default" queue under a PRODUCER span.
    static String
    publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, String queue, HeaderSender send)
    Emits a PRODUCER span publish <urn> and propagates the trace downstream two ways (ADR-0028): It injects the active span context as a W3C traceparent (and tracestate) onto a fresh out-of-band header map, so a consumer can start its span as a true child of this producer span — real cross-hop parent→child linkage (v0.2).
    static String
    publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, String queue, Sender send)
    Emits a PRODUCER span publish <urn>, carrying the active trace's id into the built envelope's trace_id so the downstream consumer recovers the same trace, then writes the envelope via send.
    static String
    traceIdOf(String traceId)
    Maps an envelope trace_id to a deterministic 32-hex OTel trace id: a UUID maps to its hex bytes; any other string is hashed (SHA-256, first 16 bytes).
    static String
    uuidOf(String traceIdHex)
    Formats a 32-hex OTel trace id as a canonical UUID string — the form a producer stamps into the message's trace_id so a consumer can recover the same trace id via traceIdOf(java.lang.String).
    static Handler
    wrapHandler(io.opentelemetry.api.trace.Tracer tracer, Handler handler)
    Wraps a consume handler to emit a CONSUMER span per message, in the OTel trace derived from the envelope's trace_id (ADR-0025 Option 1), recording the handler's error/status.
    static Handler
    wrapHandler(io.opentelemetry.api.trace.Tracer tracer, Handler handler, Supplier<Map<String,String>> headers)
    Wraps a consume handler to emit a CONSUMER span per message, recording the handler's error/status.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • traceIdOf

      public static String traceIdOf(String traceId)
      Maps an envelope trace_id to a deterministic 32-hex OTel trace id: a UUID maps to its hex bytes; any other string is hashed (SHA-256, first 16 bytes). Never the all-zero (invalid) trace id. The inverse of uuidOf(java.lang.String) for the UUID case.
      Parameters:
      traceId - the envelope trace_id
      Returns:
      a valid 32-hex OTel trace id
    • uuidOf

      public static String uuidOf(String traceIdHex)
      Formats a 32-hex OTel trace id as a canonical UUID string — the form a producer stamps into the message's trace_id so a consumer can recover the same trace id via traceIdOf(java.lang.String).
      Parameters:
      traceIdHex - a 32-hex OTel trace id
      Returns:
      the canonical UUID string
    • wrapHandler

      public static Handler wrapHandler(io.opentelemetry.api.trace.Tracer tracer, Handler handler)
      Wraps a consume handler to emit a CONSUMER span per message, in the OTel trace derived from the envelope's trace_id (ADR-0025 Option 1), recording the handler's error/status. The handler receives the full Envelope as before.

      This is the v0.1 shape: it parents the span on the trace_id-derived trace only. To additionally start the span as a true child of the producer span when a W3C traceparent was carried beside the envelope, use wrapHandler(Tracer, Handler, Supplier).

      Parameters:
      tracer - the OTel tracer
      handler - the handler to instrument
      Returns:
      the wrapped handler
    • wrapHandler

      public static Handler wrapHandler(io.opentelemetry.api.trace.Tracer tracer, Handler handler, Supplier<Map<String,String>> headers)
      Wraps a consume handler to emit a CONSUMER span per message, recording the handler's error/status. The handler receives the full Envelope as before.

      Parent selection (ADR-0028): when headers supplies the delivered message's out-of-band headers and they carry a valid W3C traceparent, the span is started as a true child of the producer span — real cross-hop parent→child linkage with per-hop span timing. When no traceparent is present (a null supplier, a null or empty map, or a malformed value) it falls back to the v0.1 behaviour: a remote parent derived from the envelope's trace_id (ADR-0025 Option 1), which shares the trace but not the exact span edge. So enabling traceparent propagation is a strict, backward-compatible upgrade — no regression for messages produced without it.

      headers is a Supplier a transport adapter wires to a reserved message's out-of-band headers (e.g. Redrive.Reserved.headers()); it is read once per delivery, after the envelope is in hand. The Java transports live in separate artifacts, so wiring this supplier to each broker's per-message metadata channel is a documented follow-up.

      Parameters:
      tracer - the OTel tracer
      handler - the handler to instrument
      headers - a supplier of the delivered message's out-of-band headers, or null to always use the v0.1 trace_id-derived parent
      Returns:
      the wrapped handler
    • publish

      public static String publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, Sender send) throws Exception
      Publishes (urn, data) to the "default" queue under a PRODUCER span. See publish(Tracer, String, Map, String, Sender).
      Parameters:
      tracer - the OTel tracer
      urn - the message URN
      data - the message data
      send - the transport write
      Returns:
      the published message id (meta.id)
      Throws:
      Exception - if send fails (recorded on the span and re-thrown)
    • publish

      public static String publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, String queue, Sender send) throws Exception
      Emits a PRODUCER span publish <urn>, carrying the active trace's id into the built envelope's trace_id so the downstream consumer recovers the same trace, then writes the envelope via send.
      Parameters:
      tracer - the OTel tracer
      urn - the message URN
      data - the message data
      queue - the destination queue
      send - the transport write
      Returns:
      the published message id (meta.id)
      Throws:
      Exception - if send fails (recorded on the span and re-thrown)
    • publish

      public static String publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, HeaderSender send) throws Exception
      Publishes (urn, data) to the "default" queue under a PRODUCER span, propagating a W3C traceparent on the out-of-band header map (ADR-0028). See publish(Tracer, String, Map, String, HeaderSender).
      Parameters:
      tracer - the OTel tracer
      urn - the message URN
      data - the message data
      send - the transport write, receiving the envelope and its out-of-band headers
      Returns:
      the published message id (meta.id)
      Throws:
      Exception - if send fails (recorded on the span and re-thrown)
    • publish

      public static String publish(io.opentelemetry.api.trace.Tracer tracer, String urn, Map<String,Object> data, String queue, HeaderSender send) throws Exception
      Emits a PRODUCER span publish <urn> and propagates the trace downstream two ways (ADR-0028):
      • It injects the active span context as a W3C traceparent (and tracestate) onto a fresh out-of-band header map, so a consumer can start its span as a true child of this producer span — real cross-hop parent→child linkage (v0.2). The header rides beside the frozen envelope, never in it (GR-1); a transport that cannot carry headers may simply ignore the map (the traceparent is then not propagated — no error).
      • It also carries the active trace's id into the built envelope's trace_id (the v0.1 behaviour), so even a consumer that ignores the header — or a transport that drops it — still recovers the same trace (correlation without exact span linkage).

      The header map is handed to send alongside the built envelope.

      Parameters:
      tracer - the OTel tracer
      urn - the message URN
      data - the message data
      queue - the destination queue
      send - the transport write, receiving the envelope and its out-of-band headers
      Returns:
      the published message id (meta.id)
      Throws:
      Exception - if send fails (recorded on the span and re-thrown)