otelsetup / test (push) Successful in 43s
otelsetup / vulnerabilities (push) Successful in 44s
Unbound Release / Check Preconditions (push) Successful in 1s
Unbound Release / Create Tag (push) Skipped
Unbound Release / Create Release (push) Successful in 4s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 9s
Release / release (push) Successful in 14s
pre-commit / pre-commit (push) Successful in 2m25s
## Summary gqlgen's `graphql.DefaultRecover` prints a recovered resolver panic and `debug.PrintStack()` to stderr, so the log collector stores every stack line as a separate, level-less entry (~60 entries per panic in production). Adds `otelsetup.Recover` (`func(ctx context.Context, err any) error`, a `graphql.RecoverFunc` for gqlgen v0.17.95): - logs the panic as **one** ERROR record via `slog.Default().ErrorContext` (the default logger services set up through `logging.SetupLogger`), with `panic`, `stack` (`debug.Stack()`) and `path` (GraphQL field path, when available) attributes - records the error on the active span (`RecordError` with stack trace, `SetStatus(codes.Error)`) - returns the generic `internal system error` gqlerror to the client, without the panic text Services install it with `srv.SetRecoverFunc(otelsetup.Recover)`. ## Test plan - `TestRecover`: error and non-error panic values; asserts the generic gqlerror, no panic text leak, exactly one ERROR log record with panic/stack attributes, and span status Error with an `exception` event carrying message and stacktrace (in-memory `tracetest.SpanRecorder`) - `go test -race ./...` passes, `prek run --all-files` passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01H6eVuR4Yt3m7TbDTDwyZJi Reviewed-on: #206
54 lines
2.0 KiB
Markdown
54 lines
2.0 KiB
Markdown
# otelsetup
|
|
|
|
Shared Go library for OpenTelemetry setup across all microservices.
|
|
|
|
## Shared Documentation
|
|
|
|
@../docs/claude/architecture.md
|
|
@../docs/claude/go-services.md
|
|
@../docs/claude/conventions.md
|
|
|
|
## Library Information
|
|
|
|
### Purpose
|
|
|
|
Provides standardized OpenTelemetry configuration for tracing, metrics, and logging. Used by all Go microservices to enable observability.
|
|
|
|
### Usage
|
|
|
|
```go
|
|
import "gitea.unbound.se/shiny/otelsetup"
|
|
|
|
// Bootstrap OpenTelemetry SDK
|
|
shutdown, err := otelsetup.SetupOTelSDK(ctx, enabled, serviceName, buildVersion, environment)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer shutdown(ctx)
|
|
|
|
// Wrap HTTP handlers with tracing
|
|
http.Handle("/", otelsetup.Handler(myHandler))
|
|
|
|
// gqlgen server hooks
|
|
srv.AroundOperations(otelsetup.AroundOperations)
|
|
srv.AroundRootFields(otelsetup.AroundRootFields)
|
|
srv.AroundFields(otelsetup.AroundFields)
|
|
srv.SetRecoverFunc(otelsetup.Recover)
|
|
```
|
|
|
|
### Features
|
|
|
|
- **Tracing** - OTLP HTTP exporter with batch processing
|
|
- **Metrics** - OTLP HTTP exporter with periodic reader
|
|
- **Logging** - Stdout log exporter
|
|
- **Propagation** - TraceContext and Baggage propagation
|
|
- **HTTP Handler** - Middleware for automatic span creation
|
|
- **gqlgen hooks** - Spans per operation, root field and resolver field
|
|
- **gqlgen recover** - `Recover` logs a resolver panic as one ERROR record via `slog.Default()` (attributes `panic`, `stack`, `path`), records it on the active span with status Error, and returns the generic "internal system error" to the client (gqlgen's default prints the stack to stderr line by line)
|
|
|
|
### Environment Variables
|
|
|
|
- `OTEL_RESOURCE_ATTRIBUTES` - Auto-set if not provided (service.name, service.version, service.environment)
|
|
- Standard OTLP environment variables for endpoint configuration
|
|
- The trace and metric exporters use our own HTTP client (idle connections closed after 30s, before the collector's 1m idle timeout), so `OTEL_EXPORTER_OTLP_*TIMEOUT` and the `OTEL_EXPORTER_OTLP_*CERTIFICATE` / `*CLIENT_*` TLS variables are ignored
|