clog

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 24, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

clog

clog sends structured log events to an OpenTracing-compatible backend (e.g., Jaeger) by attaching them to active spans. It wraps jaeger-client-go with sensible defaults so you can start tracing with minimal setup.

Install

go get github.com/nanjj/clog

Quick Start

1. Initialise the tracer (usually in main)
tracer, err := clog.NewTracer("my-service")
if err != nil {
    panic(err)
}
clog.SetGlobalTracer(tracer)
defer tracer.Close() // flush pending spans before exit
2. Create spans and attach logs
span, ctx := clog.StartSpanFromContext(ctx, "CreateOrder")
defer span.Finish()

// Log structured key-value pairs on the span
span.LogKV("event", "order_created", "order_id", "12345")

// Or use the more type-safe log.Field API
span.LogFields(
    log.String("event", "payment_processed"),
    log.Int("amount_cents", 2999),
)

Environment Variables

clog reads standard Jaeger environment variables via config.FromEnv(). Values set before importing clog take priority over the built-in defaults.

Variable Default Description
JAEGER_SERVICE_NAME (set via NewTracer(name)) Service name shown in traces
JAEGER_SAMPLER_TYPE const Sampler type (const, probabilistic, ratelimiting, remote)
JAEGER_SAMPLER_PARAM 1 Sampler parameter (e.g. 1 = sample all)
JAEGER_REPORTER_MAX_QUEUE_SIZE 64 Max spans in the send queue
JAEGER_REPORTER_FLUSH_INTERVAL 10s How often to flush spans to the agent
JAEGER_TRACEID_128BIT true 128-bit trace IDs (W3C Trace Context standard)
JAEGER_AGENT_HOST localhost Jaeger agent UDP host
JAEGER_AGENT_PORT 6831 Jaeger agent UDP port
JAEGER_REPORTER_LOG_SPANS (not set) Log reporter activity when set to true
JAEGER_DISABLED (not set) Disable tracing when set to true
JAEGER_TAGS (not set) Comma-separated key=value tags applied to all spans
Customising the agent endpoint

By default spans are sent to localhost:6831 (Jaeger agent UDP socket). To change:

export JAEGER_AGENT_HOST=jaeger.example.com
export JAEGER_AGENT_PORT=6831
Additional tags at construction time
tracer, err := clog.NewTracer("my-service",
    config.Tag("runner", "127.0.0.1:54321"),
    config.Tag("leader", "127.0.0.1:54312"),
)

API

clog.NewTracer(name string, opts ...config.Option) (*Tracer, error)

Creates a new Jaeger tracer. The name is used as the JAEGER_SERVICE_NAME. Additional config.Option values (e.g., config.Tag(...)) can be passed to customise the tracer further.

clog.NewTracerWithOptions(name string, opts ...Option) (*Tracer, error)

Creates a new Jaeger tracer with programmatic option overrides. Options are applied before reading environment variables, so they take precedence over env vars.

tracer, err := clog.NewTracerWithOptions("my-service",
    clog.With128Bit(true),
)
clog.With128Bit(enabled bool) Option

Enables or disables 128-bit trace IDs. Enabled by default — pass With128Bit(false) to use legacy 64-bit trace IDs.

clog.CloseTracer(tracer *Tracer) error

Closes a tracer, flushing any pending spans. Safe to call with nil.

defer clog.CloseTracer(tracer)
clog.SetGlobalTracer(tracer *Tracer)

Registers the tracer as the OpenTracing global tracer. Required before calling StartSpanFromContext without a parent span in context.

clog.GlobalTracer() *Tracer

Returns the previously registered global tracer, or nil if none was set.

clog.StartSpanFromContext(ctx, name, opts...) (opentracing.Span, context.Context)

Starts a new span. If ctx already contains a parent span, the new span becomes a child of it; otherwise a root span is created via the global tracer. Returns the span and an updated context that carries it.

clog.ExtractFromEnv(ctx) opentracing.SpanContext

Extracts a parent span context from environment variables (CLOG_TRACEPARENT—W3C Trace Context, fallback UBER_TRACE_ID—Jaeger legacy). Used together with InjectToEnv or TraceEnv to propagate traces across process boundaries.

clog.InjectToEnv(span opentracing.Span) func()

Serialises a span's context into environment variables (CLOG_TRACEPARENT, CLOG_BAGGAGE_*) so a child process can pick it up via ExtractFromEnv. Returns a restore function that reverts the environment to its previous state.

// Parent process
span, ctx := clog.StartSpanFromContext(ctx, "parent")
restore := clog.InjectToEnv(span)
defer restore()

cmd := exec.Command("child-binary")
cmd.Env = os.Environ() // carries CLOG_TRACEPARENT
cmd.Run()
// Child process (child-binary)
ctx := context.Background()
parentCtx := clog.ExtractFromEnv(ctx) // reads CLOG_TRACEPARENT
span, ctx := clog.StartSpanFromContext(ctx, "child")
// span is a child of the parent
defer span.Finish()
clog.TraceEnv(span opentracing.Span) []string

Returns the span's trace context as KEY=VALUE strings suitable for appending to os.Environ() or cmd.Env. Unlike InjectToEnv, does not modify the process environment — ideal for constructing explicit env slices without global side effects.

// Using TraceEnv with cmd.Env (no global side effects)
span, ctx := clog.StartSpanFromContext(ctx, "parent")
cmd := exec.Command("child-binary")
cmd.Env = append(os.Environ(), clog.TraceEnv(span)...)
cmd.Run()
// Using TraceEnv with mvdan/sh (explicit env slice)
envVars := append(config.EnvVars, clog.TraceEnv(span)...)
r := interp.New(interp.Env(expand.ListEnviron(envVars...)))

Important Notes

  1. 128-bit trace IDs: clog enables 128-bit trace IDs by default (JAEGER_TRACEID_128BIT=true). This aligns with the W3C Trace Context and OpenTelemetry standards. To use legacy 64-bit trace IDs, set JAEGER_TRACEID_128BIT=false before importing clog, or pass clog.With128Bit(false) to NewTracerWithOptions.

  2. UDP packet limits: Jaeger's agent accepts spans over UDP. If you log many events in a single span, the combined payload may exceed the ~65 KB UDP packet limit. Use JAEGER_REPORTER_MAX_QUEUE_SIZE and JAEGER_REPORTER_FLUSH_INTERVAL to tune batching.

  3. Always close: defer tracer.Close() in main and defer span.Finish() in each traced operation are required. Without them, buffered spans may never reach the backend. clog.CloseTracer(tracer) is a nil-safe convenience wrapper.

  4. Child span lifecycle: A child span must be finished before its parent. The parent span only sends its logs and timing after Finish() is called.

  5. Lazy defaults: Jaeger environment variable defaults (see table above) are only set on the first call to NewTracer or NewTracerWithOptions, and only when the corresponding env var is unset. Set any of these before calling NewTracer to override. No env vars are modified at import time.

  6. Cross-process propagation: Use InjectToEnv/ExtractFromEnv or TraceEnv to propagate traces across process boundaries (e.g., exec.Command). ExtractFromEnv is also called internally by StartSpanFromContext as a fallback when no parent span exists in context, so child processes can create child spans without calling ExtractFromEnv explicitly. Prefer TraceEnv when you control the child's env slice (no global side effects); use InjectToEnv with defer restore() when modifying os.Environ() is more convenient.

Documentation

Overview

Package clog provides structured log events via OpenTracing/Jaeger.

Basic usage:

tracer, err := clog.NewTracer("my-service")
if err != nil {
    log.Fatal(err)
}
defer tracer.Close()
clog.SetGlobalTracer(tracer)

span, ctx := clog.StartSpanFromContext(context.Background(), "operation")
defer span.Finish()
span.LogKV("event", "work-done", "count", 42)

Environment variables set by applyJaegerDefaults at first NewTracer call (only when unset):

JAEGER_SAMPLER_TYPE    const
JAEGER_SAMPLER_PARAM   1
JAEGER_TRACEID_128BIT  true

Reporter env vars MaxQueueSize and FlushInterval are NOT set by applyJaegerDefaults; the Jaeger client's built-in defaults (100, 1s) are used instead.

Set any of these before calling NewTracer to override the default. Use NewTracerWithOptions for programmatic control.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CloseTracer added in v0.1.1

func CloseTracer(tracer *Tracer) error

CloseTracer closes a *Tracer, flushing any pending spans. Safe to call with nil — returns nil immediately.

func Debug added in v0.3.0

func Debug(ctx context.Context, msg string, args ...any)

Debug logs at slog.LevelDebug via LogAttrs. The args are key-value pairs, following slog's convention:

clog.Debug(ctx, "processing order", "order_id", id, "amount", amt)

func Error added in v0.3.0

func Error(ctx context.Context, msg string, args ...any)

Error logs at slog.LevelError via LogAttrs.

func ExtractFromEnv added in v0.1.3

func ExtractFromEnv(_ context.Context) opentracing.SpanContext

Supported env vars (checked in order):

  • CLOG_TRACEPARENT (W3C Trace Context, preferred)
  • UBER_TRACE_ID (Jaeger legacy, backward-compatible)

CLOG_TRACEPARENT format (W3C):

00-{trace-id-32hex}-{span-id-16hex}-{flags-2hex}

Example:

00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

UBER_TRACE_ID format (Jaeger legacy):

{trace-id}:{span-id}:{parent-span-id}:{flags}

func Info added in v0.3.0

func Info(ctx context.Context, msg string, args ...any)

Info logs at slog.LevelInfo via LogAttrs.

func InjectToEnv added in v0.1.3

func InjectToEnv(span opentracing.Span) (restore func())

InjectToEnv injects the span's context into environment variables so that child processes can continue the trace via StartSpanFromContext / ExtractFromEnv.

Returns a restore function that reverts the environment to its previous state. Typical usage:

span, _ := clog.StartSpanFromContext(ctx, "parent-op")
defer span.Finish()
restore := clog.InjectToEnv(span)
defer restore()
// exec child ...

Sets CLOG_TRACEPARENT (W3C format) and CLOG_BAGGAGE_* for baggage items.

For scenarios where modifying the global process environment is undesirable (e.g., constructing cmd.Env), use TraceEnv instead.

func LogAttrs added in v0.3.0

func LogAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr)

LogAttrs writes a log record at the given level via slog, and also writes the message and attributes to the active OpenTracing span in ctx (if any).

The span event uses the convention event=<msg> (see package doc). Attributes are preserved with their slog types mapped to the closest OpenTracing log.Field type.

Example:

clog.LogAttrs(ctx, slog.LevelInfo, "request handled",
    slog.String("method", r.Method),
    slog.Int("status", 200),
    slog.Duration("latency", dur),
)

func LogKV added in v0.2.1

func LogKV(ctx context.Context, keyValues ...interface{})

LogKV writes key-value pairs to the active span in context, if any. Safe to call anywhere — silently no-ops when there is no active span.

Example:

clog.LogKV(ctx, "event", "cache_miss", "key", req.Path)

func NewSpanHandler added in v0.3.0

func NewSpanHandler() slog.Handler

NewSpanHandler returns a slog.Handler that writes log records to the active OpenTracing span found in the context passed to Handle.

The handler does not write to any output sink (stdout, stderr, file). To write to both the span and a sink, combine handlers via slog.NewMultiHandler:

slog.SetDefault(slog.New(
    slog.NewMultiHandler(
        clog.NewSpanHandler(),
        slog.NewTextHandler(os.Stderr, nil),
    ),
))

WithAttrs and WithGroup are supported. Grouped attributes are flattened with dot-separated keys (e.g. "http.method") for OpenTracing span events.

func SetGlobalTracer

func SetGlobalTracer(tracer *Tracer)

SetGlobalTracer registers the tracer as the OpenTracing global tracer. Must be called before StartSpanFromContext when there is no parent span.

func SpanFromContext added in v0.1.3

func SpanFromContext(ctx context.Context) opentracing.Span

SpanFromContext returns a span from context if one exists.

func StartSpanFromContext

func StartSpanFromContext(ctx context.Context,
	name string,
	opts ...opentracing.StartSpanOption) (opentracing.Span, context.Context)

StartSpanFromContext starts a span from context and returns the span and new context with the span. If a parent span exists in the context, the new span is created as a child of it. Otherwise, if a remote parent SpanContext has been propagated via environment variables (see ExtractFromEnv), the new span is created as a child of that remote parent. Otherwise a root span is started via the global tracer.

func TraceEnv added in v0.1.4

func TraceEnv(span opentracing.Span) []string

TraceEnv returns environment variable KEY=VALUE strings representing the span's trace context, suitable for propagating to child processes.

Unlike InjectToEnv, TraceEnv does not modify the process environment. The caller controls how the variables reach the child process:

cmd.Env = append(os.Environ(), clog.TraceEnv(span)...)

Currently returns CLOG_TRACEPARENT and CLOG_BAGGAGE_* entries for baggage items. Returns nil if span is nil or context injection fails.

func TraceparentOf added in v0.2.0

func TraceparentOf(span opentracing.Span) string

TraceparentOf returns the W3C traceparent string for the given span. Returns "" if the span is nil or context injection fails.

Unlike TraceEnv, which returns KEY=VALUE pairs for process propagation, TraceparentOf returns only the traceparent value. This is useful when you need the raw traceparent string — for example, injecting into MCP _meta fields or HTTP headers:

if tp := clog.TraceparentOf(span); tp != "" {
    params.SetMeta(map[string]any{"traceparent": tp})
}

func Warn added in v0.3.0

func Warn(ctx context.Context, msg string, args ...any)

Warn logs at slog.LevelWarn via LogAttrs.

Types

type Option added in v0.1.1

type Option func()

Option programmatically overrides Jaeger tracer configuration. Options are applied before config.FromEnv(), so they take precedence over environment variables (but not over values set explicitly before calling NewTracerWithOptions).

func With128Bit added in v0.1.1

func With128Bit(enabled bool) Option

With128Bit enables or disables 128-bit trace IDs. Enabled by default. Pass With128Bit(false) to use legacy 64-bit trace IDs.

type Tracer

type Tracer struct {
	opentracing.Tracer
	io.Closer
}

Tracer wraps an OpenTracing tracer with its io.Closer. Call Close to flush pending spans before the process exits.

func GlobalTracer

func GlobalTracer() (tracer *Tracer)

GlobalTracer returns the previously registered global tracer, or nil if none was set or if the global tracer is not a clog.Tracer.

func NewTracer

func NewTracer(name string, opts ...config.Option) (tracer *Tracer, err error)

NewTracer creates a new Jaeger tracer with the given service name. The name is used as JAEGER_SERVICE_NAME. Additional config.Option values (e.g. config.Tag) can be passed to customise the tracer.

func NewTracerWithOptions added in v0.1.1

func NewTracerWithOptions(name string, opts ...Option) (*Tracer, error)

NewTracerWithOptions creates a new Jaeger tracer with programmatic option overrides. Options are applied before reading environment variables, so they take precedence over env vars.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL