Browse documentation

Instrument

Go

Instrument Go services with metrics, traces, logs, errors, and request-scoped customer identity.

Use the Go SDK for server-side traces, metrics, structured logs, errors, diagnostic events, and customer identity.

Install and start

go get github.com/anectico/anectico/sdks/go@main
ctx := context.Background()
client, err := anectico.New(
	anectico.WithAPIKey(os.Getenv("ANECTICO_API_KEY")),
	anectico.WithServiceName("orders-api"),
	anectico.WithEnvironment("production"),
)
if err != nil {
	return err
}
if err := client.Start(ctx); err != nil {
	return err
}

defer func() {
	shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	_ = client.Stop(shutdownCtx)
}()

Keep one client for the application. A client per request prevents batching and correlation.

Stop marks the client non-running and lets its first caller perform the final flush, bounded by both that caller’s context and ShutdownTimeout. Concurrent callers wait for that same flush and receive the same terminal result. A waiting caller whose own context expires returns its context error without canceling the shared shutdown; a later call after completion returns the completed result. Starting the client again waits for an in-flight stop and begins a fresh lifecycle.

Handle SIGTERM and os.Interrupt with a bounded shutdown context, and check the Stop error before the process exits. Do not rely on a deferred stop after os.Exit, because deferred functions do not run. SIGKILL cannot be caught, so no SDK can guarantee a final telemetry flush when the process is killed that way; leave enough orchestrator termination grace for the configured shutdown deadline.

Use a project-scoped key with ingest:write. Add analytics:write when calling Identify, Group, or diagnostic-event methods, as this guide does. Do not give an application key read or management scopes.

Trace work and capture errors

requestCtx := anectico.ContextWithDistinctID(ctx, userID)
traceCtx, span := client.StartSpan(requestCtx, "process-order")
defer span.End()

if err := processOrder(traceCtx); err != nil {
	client.CaptureError(traceCtx, err, anectico.WithTag("component", "checkout"))
	return err
}

Pass the returned context through downstream calls. Request-scoped identity is concurrency-safe and takes precedence over process identity. Use client.Identify only for a single-user process or an explicit identity merge, and reset identity on logout in stateful client applications. Reset also clears groups, global error-user context, and uncaptured breadcrumbs; telemetry captured before the boundary remains queued with its original attribution.

Record an application metric

client.RecordMetric("checkout.queue_depth", float64(queueDepth), map[string]string{
	"region":      "eu-west",
	"worker_pool": "payments",
})

RecordMetric records a gauge value and reuses the instrument by name. Use client.Meter() to create an explicit counter or histogram. Keep labels low-cardinality; do not add customer, order, request, trace, or session IDs.

HTTP and gRPC propagation

Use the SDK’s HTTP transport or gRPC interceptors with the request context so W3C traceparent and the anectico.distinct_id baggage member continue to trusted downstream services. At inbound public boundaries, do not accept customer identity baggage unless a trusted gateway has removed and recreated client-supplied headers.

The unary and streaming gRPC client interceptors preserve existing application metadata and inject the newly started client span plus identity baggage. Streaming spans remain active until EOF, the one response of a client-streaming call, a failed stream operation, or caller-context cancellation. A successful CloseSend only closes the send side; it does not end the span before the response status arrives.

Client gRPC transport failures remain error-status spans and do not create duplicate Issues. On instrumented internal RPCs, the server interceptor captures one Issue occurrence only for unexpected server failures such as ResourceExhausted, Internal, or DataLoss; cancellation, deadlines, and expected caller/control statuses remain spans only. The SDK marks non-canonical transport spans with anectico.issue.suppressed=true, which remains effective when exporter batches separate the transport span from its error.capture child. A successful server capture also correlates the parent with its error.id. If capture is disabled, sampled out, fails, or returns no ID, the actionable server transport span remains eligible as the fallback Issue. For external dependencies, capture one typed domain error after retries are exhausted when the failure should become an Issue.

Both the package-level anectico.WrapHTTPClient / anectico.WrapHTTPTransport helpers and the corresponding client-bound methods mark every generated outbound HTTP client span with anectico.issue.suppressed=true. Failed and canceled round trips remain visible with their original span status, propagation, and HTTP metrics, but these transport spans do not create stackless duplicate Issues. When an HTTP dependency failure is actionable, classify it at the application or domain boundary after retries are exhausted and call CaptureError once with an explicit typed error.

For net/http, derive the Anectico request context from r.Context() and pass it through handlers, database operations, and outbound requests. Equivalent instrumentation packages are available for gRPC and supported framework adapters.

For Gin, add instrumentation/gin.Middleware(client) after Gin’s recovery middleware (the order already provided by gin.Default()). Anectico captures c.Errors and recovered handler panics while Gin retains control of the recovery response. A recovered panic keeps an error-marked request span in the trace and creates one canonical, stacked error.capture Issue occurrence—not a duplicate Issue for each span. The opposite middleware order also captures panics, but Gin’s standard recovery discards the original panic value before Anectico can read it; the captured event therefore has a generic message and the original handler stack.

Structured logs

The client exposes an slog logger and the process-level LogEvent convenience method. Use the logger’s context-aware methods for request work so records inherit the active trace and customer identity.

client.Logger().LogAttrs(
	traceCtx,
	slog.LevelInfo,
	"order processed",
	slog.String("order_id", orderID),
)

LogEvent uses a background context and therefore cannot inherit request-scoped correlation.

Keep secrets, authorization headers, and unfiltered request bodies out of attributes.

Record AI calls

Optional OpenAI and Anthropic instrumentation packages record model, tokens, latency, and calculated cost. OpenAI supports unary and Chat Completion streaming calls; Anthropic supports non-streaming calls. OpenAI streams retain the provider’s Next/Current/Err/Close flow and request the final usage chunk for exact accounting. Content capture is off by default; enabling it can send prompt and completion text as span events.

Verify and recover

Send one request with ContextWithDistinctID, then open Customers and confirm the trace, log, and test error appear under the same customer. If they do not, enable SDK debug logging, check the key, endpoint, and project, and confirm Stop completes before the shutdown deadline.