Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tracelit.io/llms.txt

Use this file to discover all available pages before exploring further.

Requirements: Go ≥ 1.21

Installation

go get github.com/tracelit-ai/tracelit-go

Quick start

1

Initialise the SDK

main.go
package main

import (
    "context"
    "log"

    "github.com/tracelit-ai/tracelit-go"
)

func main() {
    sdk, err := tracelit.New(
        tracelit.WithAPIKey(os.Getenv("TRACELIT_API_KEY")),
        tracelit.WithServiceName("payments-api"),
        tracelit.WithEnvironment("production"),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer sdk.Shutdown(context.Background())

    // Your application code here
}
2

Or use environment variables (zero code changes)

export TRACELIT_API_KEY=your-api-key
export TRACELIT_SERVICE_NAME=payments-api
export TRACELIT_ENVIRONMENT=production
if err := tracelit.Configure(); err != nil {
    log.Fatal(err)
}
defer tracelit.Shutdown(context.Background())

Configuration reference

OptionEnv variableDefaultDescription
WithAPIKey(key)TRACELIT_API_KEYRequired. Your Tracelit API key
WithServiceName(name)TRACELIT_SERVICE_NAMERequired. Service name shown in Tracelit
WithEnvironment(env)TRACELIT_ENVIRONMENTproductionDeployment environment tag
WithEndpoint(url)TRACELIT_ENDPOINThttps://ingest.tracelit.appOverride only when self-hosting
WithSampleRate(rate)TRACELIT_SAMPLE_RATE1.0Head-based sampling ratio 0.01.0
WithEnabled(bool)TRACELIT_ENABLEDtrueSet false to disable all telemetry
WithResourceAttributes(map)Extra resource attributes on every signal

Custom resource attributes

sdk, err := tracelit.New(
    tracelit.WithAPIKey(os.Getenv("TRACELIT_API_KEY")),
    tracelit.WithServiceName("orders-api"),
    tracelit.WithResourceAttributes(map[string]string{
        "deployment.region": "us-east-1",
        "team":              "platform",
    }),
)

Tracing

Automatic instrumentation

Once tracelit.New (or tracelit.Configure) is called, the global OTel TracerProvider is set. Any library that uses the OTel API (otelhttp, otelgrpc, otelsql, etc.) will automatically send spans to Tracelit without further configuration.

Manual spans

import "github.com/tracelit-ai/tracelit-go"

func processOrder(ctx context.Context, order Order) error {
    ctx, span := tracelit.StartSpan(ctx, "process-order",
        tracelit.WithSpanAttributes(map[string]any{
            "order.id":       order.ID,
            "order.currency": order.Currency,
        }),
    )
    defer span.End()

    if err := validateOrder(ctx, order); err != nil {
        span.RecordError(err) // sets codes.Error + attaches exception.stacktrace
        return err
    }

    // Child span — automatically a child of the span above
    ctx, paySpan := tracelit.StartClientSpan(ctx, "charge-payment-provider")
    defer paySpan.End()

    if err := chargeCard(ctx, order); err != nil {
        paySpan.RecordError(err,
            tracelit.WithErrorAttributes(map[string]any{
                "payment.gateway": "stripe",
                "payment.amount":  order.TotalCents,
            }),
        )
        return err
    }

    span.AddEvent("payment-captured", map[string]any{"amount": order.TotalCents})
    return nil
}

Span kinds

// SpanKindInternal (default) — internal operation
ctx, span := tracelit.StartSpan(ctx, "validate-input")

// SpanKindServer — inbound request handler
ctx, span := tracelit.StartServerSpan(ctx, "POST /orders")

// SpanKindClient — outbound call (HTTP, gRPC, DB)
ctx, span := tracelit.StartClientSpan(ctx, "stripe.charge")

// SpanKindProducer / Consumer — message queues
ctx, span := tracelit.StartProducerSpan(ctx, "orders.created publish")
ctx, span := tracelit.StartConsumerSpan(ctx, "orders.created consume")

Stack traces on errors

RecordError captures a full goroutine stack trace automatically:
ctx, span := tracelit.StartSpan(ctx, "risky-op")
defer span.End()

if err := riskyOperation(); err != nil {
    span.RecordError(err) // sets codes.Error + attaches exception.stacktrace
    return err
}

Panic recovery

ctx, span := tracelit.StartSpan(ctx, "risky-op")
defer func() {
    if r := recover(); r != nil {
        span.RecordPanic(r)           // records panic + re-panics by default
        // span.RecordPanic(r, tracelit.WithSwallowPanic()) // absorb the panic
    }
    span.End()
}()

Trace and span IDs (for log correlation)

traceID := tracelit.TraceID(ctx) // "4bf92f3577b34da6a3ce929d0e0e4736"
spanID  := tracelit.SpanID(ctx)  // "00f067aa0ba902b7"

Passing spans across goroutines

ctx, span := tracelit.StartSpan(ctx, "fan-out")
defer span.End()

for _, item := range items {
    go func(item Item) {
        childCtx, child := tracelit.StartSpan(ctx, "process-item")
        defer child.End()
        processItem(childCtx, item)
    }(item)
}

HTTP and gRPC instrumentation

HTTP server and client

import (
    "github.com/tracelit-ai/tracelit-go/middleware"
    "net/http"
)

// Server — wrap entire mux or individual handlers
mux := http.NewServeMux()
mux.HandleFunc("/api/orders", handleOrders)
http.ListenAndServe(":8080", middleware.NewHTTPHandler(mux))

// Client — wrap transport for outgoing requests
client := &http.Client{
    Transport: middleware.NewHTTPTransport(nil),
}
resp, err := client.Get("https://api.stripe.com/v1/charges")

gRPC

import "github.com/tracelit-ai/tracelit-go/middleware"

// Server
grpcServer := grpc.NewServer(
    grpc.StatsHandler(middleware.NewGRPCServerHandler()),
)

// Client
conn, err := grpc.NewClient("localhost:50051",
    grpc.WithStatsHandler(middleware.NewGRPCClientHandler()),
)

Metrics

import (
    "go.opentelemetry.io/otel/metric"
    "go.opentelemetry.io/otel/attribute"
)

// Counter
requests, err := tracelit.Counter("http.server.requests",
    metric.WithDescription("Total HTTP requests"),
    metric.WithUnit("{request}"),
)
requests.Add(ctx, 1, metric.WithAttributes(
    attribute.String("method", r.Method),
    attribute.Int("status", code),
))

// Histogram
latency, err := tracelit.Histogram("http.server.duration",
    metric.WithUnit("ms"),
)
latency.Record(ctx, durationMs)
Runtime and process metrics (goroutines, GC, memory) are exported automatically every 60 seconds — no extra code required.

Logger bridges

All bridges route log records to the Tracelit log pipeline and automatically inject trace_id and span_id from the context.

slog (standard library)

import (
    "log/slog"
    "github.com/tracelit-ai/tracelit-go/bridge"
)

// Replace the default global slog logger
slog.SetDefault(slog.New(bridge.NewSlogHandler()))

// Logs are correlated with the active span when ctx is passed
slog.InfoContext(ctx, "order created", "order_id", order.ID)
import (
    "github.com/tracelit-ai/tracelit-go/bridge"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

// Option 1: OTel-only logger
logger := bridge.NewZapLogger("payments-api")

// Option 2: tee to both stdout and OTel
stdoutLogger, _ := zap.NewProduction()
logger := zap.New(zapcore.NewTee(
    stdoutLogger.Core(),
    bridge.NewZapCore("payments-api"),
), zap.AddCaller())

logger.Info("order created", zap.String("order_id", order.ID))
import (
    "github.com/sirupsen/logrus"
    "github.com/tracelit-ai/tracelit-go/bridge"
)

logrus.AddHook(bridge.NewLogrusHook())

// Pass ctx to get trace correlation
logrus.WithContext(ctx).WithField("order_id", order.ID).Info("order created")
import (
    "os"
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
    "github.com/tracelit-ai/tracelit-go/bridge"
)

// Replace the global zerolog logger
log.Logger = zerolog.New(bridge.NewZerologWriter(os.Stderr)).
    With().Timestamp().Logger()

// Use log.Ctx(ctx) to attach trace correlation
log.Ctx(ctx).Info().Str("order_id", order.ID).Msg("order created")

Sampling and error guarantee

sdk, err := tracelit.New(
    tracelit.WithAPIKey(os.Getenv("TRACELIT_API_KEY")),
    tracelit.WithServiceName("high-traffic-api"),
    tracelit.WithSampleRate(0.1), // keep 10% of traces
)
Error spans are always exported, even when the parent trace falls outside the sample ratio. The SDK uses a custom ErrorAlwaysOnSampler + ErrorSpanProcessor pair — no configuration required.

Disabling in tests

sdk, err := tracelit.New(
    tracelit.WithAPIKey(os.Getenv("TRACELIT_API_KEY")),
    tracelit.WithServiceName("my-service"),
    tracelit.WithEnabled(false), // or TRACELIT_ENABLED=false
)

Complete example

package main

import (
    "context"
    "log/slog"
    "net/http"
    "os"
    "os/signal"
    "syscall"

    "github.com/tracelit-ai/tracelit-go"
    "github.com/tracelit-ai/tracelit-go/bridge"
    "github.com/tracelit-ai/tracelit-go/middleware"
)

func main() {
    sdk, err := tracelit.New(
        tracelit.WithAPIKey(os.Getenv("TRACELIT_API_KEY")),
        tracelit.WithServiceName("my-api"),
        tracelit.WithEnvironment("production"),
        tracelit.WithSampleRate(0.2),
    )
    if err != nil {
        slog.Error("tracelit init failed", "error", err)
        os.Exit(1)
    }
    defer sdk.Shutdown(context.Background())

    // Route all slog output through Tracelit
    slog.SetDefault(slog.New(bridge.NewSlogHandler()))

    mux := http.NewServeMux()
    mux.HandleFunc("POST /orders", func(w http.ResponseWriter, r *http.Request) {
        ctx, span := tracelit.StartServerSpan(r.Context(), "create-order")
        defer span.End()

        slog.InfoContext(ctx, "creating order")

        if err := processOrder(ctx); err != nil {
            span.RecordError(err)
            http.Error(w, "internal error", http.StatusInternalServerError)
            return
        }
        w.WriteHeader(http.StatusCreated)
    })

    srv := &http.Server{
        Addr:    ":8080",
        Handler: middleware.NewHTTPHandler(mux),
    }

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
    go func() { _ = srv.ListenAndServe() }()
    <-quit
    _ = srv.Shutdown(context.Background())
}

GitHub

Source code and issue tracker: github.com/Tracelit-AI/tracelit-go