Documentation
¶
Overview ¶
Package apmcore is the framework-agnostic Elastic APM instrumentation engine behind the apmfiber (Fiber v2) and apmfiberv3 (Fiber v3) adapters.
It exposes:
- SetupOTelSDK: bootstrap the Elastic APM Go agent together with an OpenTelemetry TracerProvider/MeterProvider bridge so OTel-aware libraries (e.g. redisotel) export spans/metrics through the APM agent's transport.
- WrapHTTPTransport: wrap an http.RoundTripper so outgoing requests produce APM spans and inject W3C traceparent headers.
- NewGormPlugin / RegisterDriver: GORM callback plugin + database/sql driver wrapper that emit foldable spans (logical gorm op → underlying prepare/exec/query/close roundtrips) for any database/sql driver.
- DBPoolMetrics: an apm.MetricsGatherer that publishes *sql.DB pool stats on the agent's metrics tick.
- WrapZapCore / LogCtxFields: helpers to correlate zap logs with the current APM trace via trace.id / transaction.id / span.id fields.
The package is intentionally Fiber-agnostic. The HTTP middleware lives in the apmfiber/apmfiberv3 adapter packages.
Index ¶
- Constants
- Variables
- func BatchSpanEmitter() func(table string, ops int, elapsed time.Duration)
- func CaptureError(ctx context.Context, err error)
- func InjectFastHTTPTraceContext(ctx context.Context, req *fasthttp.Request)
- func InstrumentCacher(inner caches.Cacher) caches.Cacher
- func InstrumentRedis(client redis.UniversalClient) error
- func InstrumentRueidis(client rueidis.Client) rueidis.Client
- func LogCtxFields(ctx context.Context) []zap.Field
- func NewGormPlugin() gorm.Plugin
- func PluginNameOf(p gorm.Plugin) string
- func RegisterDBPoolMetrics(db *sql.DB) func()
- func RegisterDBPoolMetricsWithManager(db *sql.DB, mgr CloserRegistrar, phase int, timeout time.Duration)
- func RegisterDriver(name string, base driver.Driver)
- func RegisterStartupMetricsWithManager(processStart time.Time, mgr *gscore.Manager)
- func RegisterWithManager(fn ShutdownFunc, mgr CloserRegistrar, phase int, timeout time.Duration)
- func SetLabel(ctx context.Context, key, value string)
- func SetLabels(ctx context.Context, m map[string]string)
- func TraceFastHTTPCall(ctx context.Context, req *fasthttp.Request, resp *fasthttp.Response, ...) error
- func WrapHTTPTransport(base http.RoundTripper) http.RoundTripper
- func WrapZapCore(c zapcore.Core) zapcore.Core
- type CloserRegistrar
- type ShutdownFunc
- type StartupMetricsGatherer
Constants ¶
const GormPluginName = "apmcore:dbtrace"
GormPluginName is the name registered with gorm.Use.
Variables ¶
var ErrNoBaseDriver = fmt.Errorf("apmcore: base driver is nil")
ErrNoBaseDriver is returned when RegisterDriver is called with a nil base.
Functions ¶
func BatchSpanEmitter ¶ added in v0.7.0
BatchSpanEmitter returns a SpanEmitter compatible with gormautobatch.Config.SpanEmitter. It emits a background OTel span for each batch flush so batched writes are visible in APM even though they belong to no specific request.
func CaptureError ¶
CaptureError records err on the APM transaction in ctx and sends it to the agent. Safe to call with nil err (no-op) or with ctx that has no active transaction (drops the event).
Use this from handlers that map errors inline (return ctx.Status(...). JSON(...)) so the error still appears in Kibana → APM → Errors with the request's trace.id attached.
func InjectFastHTTPTraceContext ¶ added in v0.5.0
InjectFastHTTPTraceContext writes the W3C traceparent header on req from the currently active APM span (or transaction) in ctx. Use this when you must hand the *fasthttp.Request off to a library you do not control — so you can't wrap the call site with TraceFastHTTPCall.
No-op when ctx has neither span nor transaction.
func InstrumentCacher ¶ added in v0.8.0
InstrumentCacher wraps inner with OTel spans for every cache operation. Spans are emitted on the tracer "gormcache" and capture hit/miss for Get, tag counts for Store, and affected tables for Invalidate. Errors are recorded and the span status is set to Error so that APM tools can surface them via error-rate metrics.
func InstrumentRedis ¶
func InstrumentRedis(client redis.UniversalClient) error
InstrumentRedis attaches the redisotel tracing + metrics hooks to client. Spans/metrics flow through the OTel global providers configured by SetupOTelSDK and end up in the APM agent's transport.
Pass any redis.UniversalClient (Client, ClusterClient, Ring, etc.).
func InstrumentRueidis ¶ added in v0.7.0
InstrumentRueidis wraps client with OpenTelemetry tracing and metrics via rueidisotel and returns the instrumented client. It mirrors InstrumentRedis for the rueidis client used by gsrueidis.
Pass the client returned by rueidis.NewClient — the returned client is a drop-in replacement that emits OTel spans and metrics through the global providers configured by SetupOTelSDK.
Example:
client, err := rueidis.NewClient(rueidis.ClientOption{...})
if err != nil { ... }
client = apmcore.InstrumentRueidis(client)
func LogCtxFields ¶
LogCtxFields returns the trace.id / transaction.id / span.id zap fields for the active APM transaction (or nil if ctx has none).
Usage in request handlers:
logger.With(apmcore.LogCtxFields(ctx)...).Info("processed request")
Returns an empty slice when ctx is nil or has no active transaction.
func NewGormPlugin ¶
NewGormPlugin returns a gorm.Plugin that wraps each gorm operation (Create/Query/Update/Delete/Row/Raw) in a parent APM span named "<Op> <table>" of type "db.gorm.<op>". The driver-level spans then nest underneath, producing a foldable hierarchy in Kibana.
The plugin assigns the new ctx back into tx.Statement.Context so the driver-level spans inherit the gorm span as parent. This is the foldable-spans invariant — without it, driver spans parent directly to the transaction and the gorm span looks empty.
func PluginNameOf ¶
PluginNameOf is exported for callers who want to assert plugin registration.
func RegisterDBPoolMetrics ¶
RegisterDBPoolMetrics registers an apm.MetricsGatherer that publishes *sql.DB pool statistics on the agent's metrics tick (default 30s, configurable via ELASTIC_APM_METRICS_INTERVAL).
Emitted metrics:
db.pool.max_open db.pool.open db.pool.in_use db.pool.idle db.pool.wait_count db.pool.wait_duration_ms db.pool.max_idle_closed db.pool.max_idle_time_closed db.pool.max_lifetime_closed
They land in the metrics-apm.app.<service>-default data stream and are chartable from Kibana → Observability → Infrastructure → Metrics Explorer.
The returned function deregisters the gatherer; call it when the pool is closed (e.g. from a graceful-shutdown PhasePostDB hook).
func RegisterDBPoolMetricsWithManager ¶ added in v0.7.0
func RegisterDBPoolMetricsWithManager(db *sql.DB, mgr CloserRegistrar, phase int, timeout time.Duration)
RegisterDBPoolMetricsWithManager registers the APM pool metrics gatherer and wires its deregister function as a PhasePostDB closer. Without deregistering, the APM agent collects zeroed metrics from the closed pool for one extra tick.
phase must be gscore.PhasePostDB (value 4). Pass 0 to use PhasePostDB. timeout=0 defaults to 5s.
apmcore.RegisterDBPoolMetricsWithManager(sqlDB, mgr, gscore.PhasePostDB, 0)
func RegisterDriver ¶
RegisterDriver wraps an existing database/sql driver with APM instrumentation and registers it under name so it can be opened via sql.Open(name, dsn) or referenced as gorm.Config.DriverName.
The wrapper emits spans for Prepare/Query/Exec/Begin/Commit/Rollback and, crucially, for prepared-statement Close roundtrips — which dominate transaction-commit cost when gorm.PrepareStmt is enabled.
Span names use apmsql.QuerySignature so they are readable in the Kibana waterfall (e.g. "SELECT FROM users (query)" instead of the raw SQL).
Registration is idempotent: calling twice with the same name is a no-op.
func RegisterStartupMetricsWithManager ¶ added in v0.8.3
RegisterStartupMetricsWithManager registers a StartupMetricsGatherer with the APM default tracer and wires its deregister function as a PhasePostDB closer so the gatherer is cleanly removed before the APM agent shuts down.
- processStart is the wall-clock time the process was launched. Pass time.Now() from main before any other setup runs so the duration covers the full boot sequence including dependency wiring.
- mgr is the gscore.Manager whose probe state is reflected in the metrics.
The deregister closer uses a 5 s timeout, matching RegisterDBPoolMetricsWithManager.
func RegisterWithManager ¶ added in v0.7.0
func RegisterWithManager(fn ShutdownFunc, mgr CloserRegistrar, phase int, timeout time.Duration)
RegisterWithManager registers the OTel SDK shutdown function as a PhasePostDB closer. Call this immediately after SetupOTelSDK so that in-flight spans and metrics are flushed before the process exits.
phase must be gscore.PhasePostDB (value 4). Pass 0 to use PhasePostDB. timeout=0 defaults to 15s.
shutdown, err := apmcore.SetupOTelSDK(ctx) apmcore.RegisterWithManager(shutdown, mgr, gscore.PhasePostDB, 0)
func SetLabel ¶
SetLabel attaches a business identifier to the APM transaction in ctx. In Kibana the value appears under `labels.<key>` and can be filtered on. No-op if ctx has no active transaction or value is empty.
Useful for late-binding identifiers that only become available after the handler ran (e.g. a generated transaction ID returned from a domain call).
func SetLabels ¶
SetLabels applies SetLabel for every non-empty entry in m. Convenience for middlewares that decode known identifiers from the request body and publish them in one pass.
func TraceFastHTTPCall ¶ added in v0.5.0
func TraceFastHTTPCall(ctx context.Context, req *fasthttp.Request, resp *fasthttp.Response, do func() error) error
TraceFastHTTPCall wraps a single fasthttp client call with an APM exit span. It injects the W3C traceparent header on req, runs do, records the HTTP status code, captures errors, and ends the span.
Use it with any *fasthttp.Client / *fasthttp.HostClient / *PipelineClient method that accepts (*Request, *Response):
err := apmcore.TraceFastHTTPCall(ctx, req, resp, func() error {
return client.Do(req, resp)
})
For DoTimeout / DoDeadline / DoRedirects, pass the matching closure. Inside a retry loop, call TraceFastHTTPCall on the *innermost* attempt so each retry produces its own sibling span (matching the apmhttp retry-pattern guidance).
If ctx has no active APM transaction the call still runs; the span is silently dropped.
func WrapHTTPTransport ¶
func WrapHTTPTransport(base http.RoundTripper) http.RoundTripper
WrapHTTPTransport wraps base with apmhttp.WrapRoundTripper so every outgoing request built with http.NewRequestWithContext gets its own APM span and the W3C traceparent header injected.
If base is nil, http.DefaultTransport is wrapped.
func WrapZapCore ¶
WrapZapCore decorates a zapcore.Core with apmzap.Core so that:
- Any Error/Fatal log line is auto-emitted as an APM error event, visible in Kibana → APM → Errors.
- When used together with LogCtxFields, every log line carries the current request's trace.id / transaction.id / span.id.
Wire it into your zap logger with zap.WrapCore:
logger := zap.NewProductionConfig().Build(
zap.WrapCore(func(c zapcore.Core) zapcore.Core { return apmcore.WrapZapCore(c) }),
)
Note: at the central error-handler site, log at Warn level (not Error) to avoid double-reporting: apm.CaptureError already records the exception; an Error log here would create a second "log" error doc in Kibana with empty error.exception.type.
Types ¶
type CloserRegistrar ¶ added in v0.7.0
type CloserRegistrar = gscore.CloserRegistrar
CloserRegistrar is the subset of gscore.Manager used by apmcore helpers. It is a type alias for gscore.CloserRegistrar; *gscore.Manager satisfies it directly.
type ShutdownFunc ¶
ShutdownFunc flushes and closes APM/OTel pipelines. It is safe to call multiple times; subsequent calls are no-ops.
func SetupOTelSDK ¶
func SetupOTelSDK(ctx context.Context) (ShutdownFunc, error)
SetupOTelSDK wires the Elastic APM Go agent into the OpenTelemetry global providers and registers the apmotel metrics gatherer with the APM agent.
After this returns:
- otel.GetTracerProvider() routes spans through the APM agent.
- otel.GetMeterProvider() routes metrics through the APM agent.
- otel.GetTextMapPropagator() propagates W3C TraceContext + Baggage so traces stitch across services.
The returned ShutdownFunc closes the APM tracer (flushing buffered events) and shuts down the OTel MeterProvider. Call it from main after the server has finished draining.
type StartupMetricsGatherer ¶ added in v0.8.3
type StartupMetricsGatherer struct {
// contains filtered or unexported fields
}
StartupMetricsGatherer implements apm.MetricsGatherer and emits four metrics on every APM metrics tick (default 30 s):
- app.startup.duration_ms — milliseconds from processStart to MarkStarted. Emits 0 until the startup probe has flipped; once started, the value is stable. Use this to tune Kubernetes initialDelaySeconds / failureThreshold.
- app.probe.live — always 1 (process is alive and responding).
- app.probe.ready — 1 while accepting traffic, 0 during shutdown.
- app.probe.started — 1 after MarkStarted, 0 during boot.
All four metrics land in the metrics-apm.app.<service>-default data stream and are chartable from Kibana → Observability → Infrastructure → Metrics.
func NewStartupMetricsGatherer ¶ added in v0.8.3
func NewStartupMetricsGatherer(processStart time.Time, mgr *gscore.Manager) *StartupMetricsGatherer
NewStartupMetricsGatherer constructs a StartupMetricsGatherer.
- processStart is the wall-clock time the process was launched (passed by the caller so the gatherer does not call time.Now() internally, keeping it deterministic and test-friendly).
- mgr is the gscore.Manager whose probe state is reflected in the metrics.
func (*StartupMetricsGatherer) GatherMetrics ¶ added in v0.8.3
GatherMetrics implements apm.MetricsGatherer.