# Go

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

Canonical page: https://anectico.com/docs/instrument/go/


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

## Install and start

During early access, install from the authorized repository checkout:

```bash
go get github.com/anectico/anectico/sdks/go@main
```

Use the commit or version supplied during onboarding for a reproducible production build. Do not
leave a production dependency on the moving `main` branch.

```go
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

```go
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

```go
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.

```go
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, for both unary and streaming calls. A streamed call is recorded as one span when the stream is
exhausted, fails, or is abandoned; closing before exhaustion is abandonment, not an error, since the
call was made and billed. 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.

- [Identify customers](/docs/instrument/identity)
- [Capture errors and releases](/docs/instrument/errors)
- [Record application metrics](/docs/instrument/metrics)
- [Record LLM calls](/docs/instrument/llm-calls)
- [Go SDK API reference](/docs/reference/go-sdk)
- [Configuration reference](/docs/reference/configuration)
