Official Go SDK for LogTide — structured logging with automatic batching, retry, circuit breaker, and OpenTelemetry integration.
- Leveled logging — Debug, Info, Warn, Error, Critical, CaptureError
- Hub / Scope model — per-request context isolation with breadcrumbs, tags, and user metadata
- Automatic batching — configurable batch size and flush interval
- Retry with backoff — exponential backoff with jitter
- Circuit breaker — prevents cascading failures
- OpenTelemetry — trace/span IDs extracted automatically; span and metric exporters included
- log/slog handler — route existing
sloglogging to LogTide with no code changes - net/http middleware — per-request scope isolation out of the box
- Thread-safe — safe for concurrent use
- Go 1.23 or later
- A LogTide account and DSN
go get github.com/logtide-dev/logtide-sdk-gopackage main
import (
"context"
logtide "github.com/logtide-dev/logtide-sdk-go"
)
func main() {
flush := logtide.Init(logtide.ClientOptions{
DSN: "https://lp_your_api_key@api.logtide.dev",
Service: "my-service",
Environment: "production",
Release: "v1.2.3",
})
defer flush()
logtide.Info(context.Background(), "Hello LogTide!", nil)
logtide.Error(context.Background(), "Something went wrong", map[string]any{
"user_id": 42,
})
}opts := logtide.NewClientOptions()
opts.DSN = "https://lp_your_api_key@api.logtide.dev"
opts.Service = "my-service"
client, err := logtide.NewClient(opts)
if err != nil {
log.Fatal(err)
}
defer client.Close()
id := client.Info(context.Background(), "Hello", nil)
fmt.Println("event id:", id)https://{api_key}@{host}
Example: https://lp_abc123@api.logtide.dev
opts := logtide.NewClientOptions()
opts.DSN = "https://lp_abc@api.logtide.dev"
opts.Service = "my-service" // required
opts.Release = "v1.2.3"
opts.Environment = "production"
opts.Tags = map[string]string{"region": "eu-west-1"}
opts.BatchSize = 100 // entries per HTTP batch
opts.FlushInterval = 5 * time.Second
opts.FlushTimeout = 10 * time.Second
opts.MaxRetries = 3
opts.RetryMinBackoff = 1 * time.Second
opts.RetryMaxBackoff = 60 * time.Second
opts.CircuitBreakerThreshold = 5 // consecutive failures before open
opts.CircuitBreakerTimeout = 30 * time.Second
opts.AttachStacktrace = logtide.Bool(true)All log methods return the EventID assigned to the entry, or "" if the entry was dropped.
ctx := context.Background()
client.Debug(ctx, "cache miss", map[string]any{"key": "user:42"})
client.Info(ctx, "request handled", map[string]any{"status": 200, "ms": 12})
client.Warn(ctx, "rate limit approaching", nil)
client.Error(ctx, "db query failed", map[string]any{"query": "SELECT ..."})
client.Critical(ctx, "out of memory", nil)
// Capture an error with full stack trace
if err := doSomething(); err != nil {
client.CaptureError(ctx, err, map[string]any{"op": "doSomething"})
}The Hub/Scope model lets you attach contextual data (tags, breadcrumbs, user info, trace context) to all log entries within a logical unit of work.
// Configure the global scope
logtide.ConfigureScope(func(s *logtide.Scope) {
s.SetTag("region", "eu-west-1")
s.SetUser(logtide.User{ID: "u123", Email: "alice@example.com"})
})
// Per-request isolation via PushScope / PopScope
logtide.PushScope()
defer logtide.PopScope()
logtide.ConfigureScope(func(s *logtide.Scope) {
s.SetTag("request_id", requestID)
s.AddBreadcrumb(&logtide.Breadcrumb{
Category: "auth",
Message: "user authenticated",
Level: logtide.LevelInfo,
Timestamp: time.Now(),
}, nil)
})
logtide.Info(ctx, "processing order", nil) // includes request_id tag + breadcrumbimport lnethttp "github.com/logtide-dev/logtide-sdk-go/integrations/nethttp"
http.Handle("/", lnethttp.Middleware(myHandler))The middleware automatically:
- clones the Hub for each request (scope isolation)
- sets
http.method,http.url,http.host,http.client_iptags - parses the
Traceparentheader and stores trace/span IDs on the scope - adds request and response breadcrumbs
Trace and span IDs are extracted automatically from any active OTel span in the context:
ctx, span := tracer.Start(ctx, "process-order")
defer span.End()
// trace_id and span_id are included automatically
client.Info(ctx, "order processed", map[string]any{"order_id": 99})Export completed spans to LogTide:
import "github.com/logtide-dev/logtide-sdk-go/integrations/otelexport"
integration := otelexport.New()
flush := logtide.Init(logtide.ClientOptions{
DSN: "https://lp_abc@api.logtide.dev",
Service: "my-service",
Integrations: func(defaults []logtide.Integration) []logtide.Integration {
return append(defaults, integration)
},
})
defer flush()
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(integration.Exporter()),
)Export OpenTelemetry metrics (counters, gauges, histograms) to LogTide. Each data point becomes a log entry carrying the same service name, environment, tags and resource attributes as your logs and traces. It is opt-in — register it only if you need metrics.
import (
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"github.com/logtide-dev/logtide-sdk-go/integrations/otelmetric"
)
integration := otelmetric.New()
flush := logtide.Init(logtide.ClientOptions{
DSN: "https://lp_abc@api.logtide.dev",
Service: "my-service",
Integrations: func(defaults []logtide.Integration) []logtide.Integration {
return append(defaults, integration)
},
})
defer flush()
mp := sdkmetric.NewMeterProvider(
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(integration.Exporter())),
)
otel.SetMeterProvider(mp)When metric exemplars are enabled (an exemplar filter is configured and a sampled
span is active during measurement), each linked data point's entry inherits the
exemplar's trace_id/span_id, and the full exemplar list is recorded under
metadata.metric.exemplars — correlating metrics with the traces that produced
them.
Route existing log/slog logging through LogTide without changing call sites.
Records honour the full client pipeline (scope merge, processors, BeforeSend,
sampling, batching). Attributes become entry metadata, slog groups become nested
metadata objects, and attribute values implementing error are promoted to
structured exceptions for server-side error grouping.
import (
"log/slog"
"github.com/logtide-dev/logtide-sdk-go/integrations/logtideslog"
)
client, _ := logtide.NewClient(logtide.ClientOptions{
DSN: "https://lp_abc@api.logtide.dev",
Service: "my-service",
})
logger := slog.New(logtideslog.New(client, nil))
slog.SetDefault(logger)
slog.Info("user signed up", "user_id", 42) // flows to LogTidePass &logtideslog.Options{Level: slog.LevelDebug} to change the minimum level
(defaults to slog.LevelInfo).
// Flush with deadline
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client.Flush(ctx)
// Close flushes and releases all resources
client.Close()Inspect or drop entries before they are sent:
opts.BeforeSend = func(entry *logtide.LogEntry, hint *logtide.EventHint) *logtide.LogEntry {
// drop health-check noise
if entry.Message == "health check" {
return nil
}
return entry
}Use NoopTransport to silence all output in tests:
client, _ := logtide.NewClient(logtide.ClientOptions{
Service: "test",
Transport: logtide.NoopTransport{},
})| Example | Description |
|---|---|
| examples/basic | All log levels, metadata, CaptureError |
| examples/gin | Gin framework integration |
| examples/echo | Echo framework integration |
| examples/stdlib | Standard library net/http |
| examples/otel | OpenTelemetry distributed tracing |
| examples/otelmetric | OpenTelemetry metrics export |
- Online: pkg.go.dev/github.com/logtide-dev/logtide-sdk-go
- Local:
godoc -http=:6060
See CONTRIBUTING.md.
MIT — see LICENSE.
