Go SDK for DuckBug errors, logs, and transactions.
This module follows the DuckBug SDK architecture from duckbug-sdk-spec, while using the real ingest contract from duckbug/backend:
POST /ingest/{projectID}:{key}/errorsPOST /ingest/{projectID}:{key}/errors/batchPOST /ingest/{projectID}:{key}/logsPOST /ingest/{projectID}:{key}/logs/batchPOST /ingest/{projectID}:{key}/transactions
- Go
1.25.9+is the initial compatibility target. - CI validates the module on Go
1.25.9and1.26.1. - Releases are expected to be published via git tags in the
v*format.
go get github.com/duckbugio/duckbug-goTo install a tagged release:
go get github.com/duckbugio/duckbug-go@v0.2.1package main
import (
"context"
"log"
"os"
"strings"
duckbug "github.com/duckbugio/duckbug-go"
)
func main() {
dsn := strings.TrimSpace(os.Getenv("DUCKBUG_DSN"))
if dsn == "" {
log.Fatal("DUCKBUG_DSN is required")
}
duck := duckbug.New(
dsn,
duckbug.WithService("checkout"),
duckbug.WithEnvironment("production"),
)
defer duck.Flush(context.Background())
duck.Log("warning", "payment provider timeout", map[string]any{
"provider": "stripe",
})
if err := doWork(); err != nil {
duck.Quack(err)
}
}This is the smallest useful setup: the SDK only needs a DSN. Environment variable naming is up to your application; DUCKBUG_DSN is just the simplest convention for examples.
If you want richer context, you can optionally pass duckbug.WithService(...), duckbug.WithEnvironment(...), duckbug.WithRelease(...), duckbug.WithServerName(...), custom provider options, or a custom pond.Pond.
package main
import (
"context"
"log/slog"
"net/http"
duckbug "github.com/duckbugio/duckbug-go"
duckbughttp "github.com/duckbugio/duckbug-go/integrations/nethttp"
duckbugslog "github.com/duckbugio/duckbug-go/integrations/slog"
)
func main() {
duck := duckbug.New(
"https://duckbug.io/api/ingest/<projectID>:<publicKey>",
duckbug.WithSensitiveFields("password", "token"),
duckbug.WithEnvironment("production"),
duckbug.WithRelease("checkout@1.2.3"),
duckbug.WithService("checkout"),
)
logger := slog.New(duckbugslog.NewHandler(duck, slog.Default().Handler()))
logger.Warn("checkout degraded", "provider", "stripe")
handler := duckbughttp.Middleware(
duck,
duckbughttp.WithProductionDefaults(),
)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}))
_ = handler
duck.Flush(context.Background())
}Duckis the main runtime facade.New(dsn, ...)is the shortest setup path for the first-party provider.Quack(err)is the branded manual error capture API.pond.Ripple(...)builds the context/sanitization subsystem.Log(...)is the canonical log capture API.StartTransaction(...)andCaptureTransaction(...)provide the performance/trace ingest surface.
- Sensitive nested fields are masked with
***. - Sensitive header names are masked with
***. - Query/body/session/cookies/files/env are attached in the canonical event model and can be disabled in the DuckBug provider privacy options.
envcapture is disabled by default in the first-party provider.net/httpmiddleware does not read request bodies unless you explicitly enableWithReadBody(true).
- The first-party provider uses a background queue by default, so
Log,Quack,CaptureTransactionandslogbridge calls do not block the hot path on network I/O. - The default transport is tuned for application safety: short connection timeout and no retry storm on the request path.
Flush(...)waits for the provider queue and sends any buffered log/error batches, so it should be called on graceful shutdown.
logger := slog.New(duckbugslog.NewHandler(duck, slog.Default().Handler()))
logger.Info("checkout started", "requestId", "req-123")By default the SDK captures WARN+ from slog. To lower the threshold:
logger := slog.New(
duckbugslog.NewHandler(
duck,
slog.Default().Handler(),
duckbugslog.WithMinLevel(slog.LevelInfo),
),
)import (
"os"
duckbugzap "github.com/duckbugio/duckbug-go/integrations/zap"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
observerCore := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
zapcore.AddSync(os.Stdout),
zapcore.InfoLevel,
)
logger := zap.New(duckbugzap.NewCore(duck, observerCore)).With(zap.String("service", "api"))
logger.Warn("checkout degraded", zap.String("provider", "stripe"))import (
"os"
duckbugzerolog "github.com/duckbugio/duckbug-go/integrations/zerolog"
"github.com/rs/zerolog"
)
logger := zerolog.New(duckbugzerolog.NewWriter(duck, os.Stdout)).
With().
Timestamp().
Str("service", "api").
Logger()
logger.Warn().Str("provider", "stripe").Msg("checkout degraded")handler := duckbughttp.Middleware(
duck,
duckbughttp.WithCaptureTransactions(true),
duckbughttp.WithTransactionSampleRate(0.10),
)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
duck.LogContext(r.Context(), "info", "request received", map[string]any{
"path": r.URL.Path,
})
w.WriteHeader(http.StatusNoContent)
}))The first-party DuckBug provider supports:
- async non-blocking enqueue with bounded in-memory queue
- batching with explicit
Flush(...)and periodic background flush - single-shot
transactionsingest - safe-by-default transport timeouts and configurable retry/backoff
beforeSendmutation/drop hook- transport failure hook
- privacy controls for request sections and
env
Transactions mirror the current DuckBug backend and PHP SDK shape:
- required payload fields:
traceId,spanId,transaction,op,startTime,endTime,duration - optional fields:
eventId,parentSpanId,status,context,measurements,spans,release,environment,dist,platform,serverName,service,requestId,user,sdk,runtime,extra - duplicate transaction ingest is treated as idempotent success by the backend
Example:
tx := duck.StartTransaction("GET /checkout", "http.server")
span := tx.StartChild("db.query", "select order")
span.SetData(map[string]any{
"sql": "select * from orders where id = ?",
})
span.Finish("ok")
tx.AddMeasurement("db.rows", 1, "")
tx.Finish("ok")
duck.CaptureTransaction(tx)Implemented in this iteration:
- core runtime for
errors,logs, andtransactions - branded
Duck/Quack/pond.Ripple - first-party DuckBug provider
slog,zap, andzerologbridgesnet/httpmiddleware compatible withchi- schema copies and tests against the current payload contract for
errorsandlogs - transaction payload tests aligned with
duckbug/backendandduckbug-php
- Push a semver-style tag like
v0.2.0. - The release workflow re-runs module checks and creates a GitHub Release.
- Consumers install the module through the standard Go module mechanism via
go get.