Documentation
¶
Overview ¶
Package errors captures unhandled panics and 5xx-shaped failures from a nexus app, deduplicates them by fingerprint, surfaces the rolling history on the dashboard, and forwards each new occurrence to one or more configured transports (Sentry, generic webhook, stdout).
The framework's panic-recovery middleware + trace.Bus already captures error events with stacks attached. The errors plugin subscribes to that bus, classifies events as errors / not-errors, groups them by fingerprint into "issues", and publishes each new occurrence outward.
import "github.com/paulmanoni/nexus/extension/errors"
nexus.Run(
nexus.Config{Server: nexus.ServerConfig{Addr: ":8080"}},
errors.Plugin(errors.Config{
Environment: "production",
Release: gitSHA,
Capacity: 200, // ring buffer size
Transports: []errors.Transport{
errors.Sentry(os.Getenv("SENTRY_DSN")),
errors.Webhook("https://errors.internal/ingest"),
},
}),
// ... rest of the app
)
Manifest integration: the `errors:` block in nexus.toml drives Environment, Release, Capacity, SampleRate, and IgnorePaths per-environment, with environment_overrides letting production / staging / preview each set its own sample rate or switch to a different ingestion URL. Transports themselves stay in code (they carry Go-only types like http.Client).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Config ¶
type Config struct {
// Environment tags every reported event ("production", "staging",
// "preview"). Sentry's UI groups + filters by this; webhook
// receivers see it in the payload. Leave empty in tests.
Environment string
// Release tags every event with a version / commit hash so the
// receiver can group "errors introduced in v1.4.2" without
// guessing. Typical wiring is a Go ldflags-injected variable or
// the GIT_SHA env var.
Release string
// ServerName tags every event with the host that captured it.
// Useful when split deployments have multiple instances reporting
// the same fingerprint — the receiver sees which instance fired.
// Defaults to os.Hostname().
ServerName string
// Capacity is the in-memory ring-buffer size for the dashboard
// view. Old events evict when full. Defaults to 100.
Capacity int
// SampleRate is the fraction of captured events forwarded to
// transports. 1.0 = every error, 0.1 = 10%, 0.0 = none (still
// captured for the dashboard). Useful in preview environments
// where noisy errors aren't worth a Sentry quota burn.
// Defaults to 1.0.
SampleRate float64
// IgnorePaths is a list of request paths whose errors are NOT
// captured. Useful for health checks and admin surfaces that
// would otherwise fill the ring buffer with noise.
// Default: ["/__nexus/health", "/__nexus/ready"].
IgnorePaths []string
// Transports is the slice of receivers each captured error is
// forwarded to. Empty = dashboard-only (no external reporting).
// Transports run sequentially per event; one transport failing
// doesn't block the others.
Transports []Transport
// Disabled, when true, makes the plugin a no-op. Useful in
// environments where another error reporter is wired (or you
// just don't want external reporting from preview).
Disabled bool
}
Config controls the plugin's capture + reporting policy. Sensible defaults mean a bare Config{Transports: [...]} works in production; every field is independently tunable per environment via manifest.
type Event ¶
type Event struct {
// Fingerprint is the grouping key — same fingerprint = same
// "issue". Combines request method + path + the top stack
// frame so the dashboard can show "this 500 fired 47 times in
// the last hour" as a single row.
Fingerprint string `json:"fingerprint"`
// CapturedAt is the moment the plugin received the event.
// Distinct from the trace event's Timestamp (which is when
// the underlying span ended) by usually <1ms; tracked for
// completeness.
CapturedAt time.Time `json:"capturedAt"`
// Service / Endpoint / Method / Path / Status mirror what was
// in the trace event. Carried into the captured event so
// transports don't need a back-reference.
Service string `json:"service,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
Method string `json:"method,omitempty"`
Path string `json:"path,omitempty"`
Status int `json:"status,omitempty"`
// Error is the human-readable error message. Empty when the
// event was captured purely on Status >= 500 (server emitted
// 500 with no error in c.Errors).
Error string `json:"error,omitempty"`
// Stack is the cleaned panic stack (debug.Stack output after
// trace.CleanStack stripping). Empty when the error wasn't a
// panic (returned-error or manual 500).
Stack string `json:"stack,omitempty"`
// Environment / Release / ServerName are tagging fields the
// receiver uses to filter + group across deploys. Mirror the
// fields Sentry expects so the Sentry transport doesn't need a
// separate model.
Environment string `json:"environment,omitempty"`
Release string `json:"release,omitempty"`
ServerName string `json:"serverName,omitempty"`
// TraceID lets the receiver link back to the full request
// trace via the dashboard's /__nexus/traces/<id>. Surfaces in
// the Sentry / webhook payload as a tag.
TraceID string `json:"traceId,omitempty"`
}
Event is the plugin's captured-error shape — distinct from trace.Event (the framework's low-level wire format) so the dashboard + transports can reason about errors without leaking every span-level field a request carries.
Event is what Transports.Report() receives + what the dashboard serializes.
type Issue ¶
type Issue struct {
Fingerprint string `json:"fingerprint"`
Method string `json:"method,omitempty"`
Path string `json:"path,omitempty"`
Service string `json:"service,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
Error string `json:"error"`
Status int `json:"status,omitempty"`
Count int `json:"count"`
FirstSeen time.Time `json:"firstSeen"`
LastSeen time.Time `json:"lastSeen"`
Sample *Event `json:"sample,omitempty"`
}
Issue is the grouped view of a recurring error. One row per fingerprint; counts up on each new occurrence.
type Transport ¶
type Transport interface {
// Name is the human-readable label shown in the dashboard's
// errors status panel. Kept distinct from any internal type
// name so the wire payload can identify the transport without
// reflecting on the Go type.
Name() string
// Report ships one captured Event somewhere. Best-effort; an
// error return is logged at the call site but does not retry.
Report(ctx context.Context, e Event) error
}
Transport is the receiver an error report is forwarded to. Implementations can be built-in (Sentry, Webhook, Stdout) or user-supplied (any type satisfying this interface).
Report must NOT block — the caller already runs it in a goroutine with a short timeout, but implementations should respect ctx.Done to honor the deadline on their own network operations. Returning an error is informational only; the plugin does not retry. If you need retries, build them inside the transport.
func Sentry ¶
Sentry POSTs each captured Event to Sentry's store endpoint. The minimal direct-HTTP implementation avoids pulling in the sentry-go SDK as a hard dependency — operators wanting the full SDK (breadcrumbs, scope handling, performance traces) can wire their own Transport that wraps sentry.CaptureEvent.
errors.Sentry(os.Getenv("SENTRY_DSN"))
DSN format: https://<publicKey>@<host>/<projectId> The transport parses it once at construction. Invalid DSN yields a Sentry transport that errors every Report — the plugin logs each error but continues running (the dashboard view still works).
func Stdout ¶
func Stdout() Transport
Stdout writes each captured error to stderr as a one-line JSON object. Useful as a fallback transport during development or in environments where the platform's log collector is the receiver.
errors.Plugin(errors.Config{
Transports: []errors.Transport{errors.Stdout()},
})
func Webhook ¶
func Webhook(rawURL string, opts ...WebhookOption) Transport
Webhook POSTs each captured Event to an HTTP endpoint as JSON. Use for self-hosted error trackers or any internal endpoint that can accept the plugin's native Event shape.
errors.Webhook("https://errors.example.com/ingest")
errors.Webhook("https://...", errors.WithWebhookHeader("X-Token", "abc"))
type WebhookOption ¶
type WebhookOption func(*webhookTransport)
WebhookOption is the functional-option shape for Webhook customization. Headers and HTTP client are the typical knobs.
func WithWebhookClient ¶
func WithWebhookClient(c *http.Client) WebhookOption
WithWebhookClient overrides the default http.Client. Useful when you need a custom TLS config, a proxy, or shared connection pooling with the rest of the app.
func WithWebhookHeader ¶
func WithWebhookHeader(name, value string) WebhookOption
WithWebhookHeader adds a header to every request the transport makes. Use for auth (X-Api-Key, Bearer) or routing tags.