crashtracker

package
v2.12.0-dev.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: Apache-2.0, BSD-3-Clause, Apache-2.0 Imports: 28 Imported by: 0

Documentation

Overview

Package crashtracker monitors the application process for crashes and sends structured crash reports to Datadog Error Tracking.

It uses the monitor-process pattern: on Start, the application re-execs itself as a lightweight monitor child (identified by the DD_CRASHTRACKING_IS_MONITOR_PROCESS environment variable). The monitor child inherits a pipe fd registered via runtime/debug.SetCrashOutput; when the application crashes the Go runtime writes the crash dump to that pipe and the monitor child parses and uploads a structured report to the Error Tracking intake.

Requires runtime/debug.SetCrashOutput, added in Go 1.23; see this repository's go.mod for the minimum Go version this module actually builds with.

Lifecycle

Call Start as early as possible in main, before any goroutines are created:

func main() {
    if err := crashtracker.Start(); err != nil {
        log.Printf("crashtracker.Start: %v", err)
    }

    // ... application code
}

There is no corresponding Stop. Process exit alone closes the crash pipe, which is all the cleanup the monitor needs: it reads EOF and exits without filing a report. Do not add a deferred unregister step — deferred functions run during panic unwinding, before the runtime writes the crash dump, so a defer here would disable reporting for the most common crash: an unrecovered panic.

Start is idempotent: subsequent calls after the first are no-ops. A companion Orchestrion integration (not part of this package) can inject a Start call as the first statement of main using DD_* environment configuration; where that integration is built in, it wins the race to be the first Start call, so a later programmatic Start call with options in main is a no-op and those options are silently dropped — not applied, not merged, and not reported as ignored. Do not rely on programmatic options to control startup in that build; use the DD_* environment configuration the integration reads instead.

Configuration

The monitor process inherits all environment variables except GOMEMLIMIT and GOGC (the monitor sets its own memory ceiling instead of inheriting the application's). Options passed to Start (WithService, WithEnv, WithVersion, WithAPIKey, WithAgentURL, WithSite) are resolved in the application process and then forwarded to the monitor child across the process boundary, so they take effect end to end. WithHTTPClient is the one exception: an *http.Client cannot cross a process boundary, so it only affects direct calls to the package's internal upload path and has no effect via Start.

Goroutine stack completeness

By default Go uses GOTRACEBACK=single, which records only the crashing goroutine in the crash dump. Set GOTRACEBACK=all in the process environment to include all goroutines in the crash report's error.threads field.

Containers and PID 1

The monitor is a child of the application process, in the same PID namespace rather than a separate one. If the application is PID 1 of that namespace — the common case for a Go binary run directly as a container's ENTRYPOINT with no init process in front of it — the kernel terminates every other process in the namespace with an unblockable SIGKILL as soon as PID 1 fully exits (pid_namespaces(7)). That includes the monitor. Receiving the crash dump itself is not the risk: the runtime's write to the pipe happens synchronously before the application finishes exiting, with the monitor already started and reading. Uploading the parsed report is a network round trip, and it can still be in flight when the namespace teardown lands a moment later, silently losing the report. Run the application behind an init process (tini, dumb-init, or an orchestrator's own, e.g. Kubernetes' shareProcessNamespace) so it is not PID 1 itself — already common container practice for signal handling and zombie reaping, and required here for the same structural reason.

Init order note

The monitor child is intercepted from package init, which is the earliest hook available to a pure Go implementation. Go leaves cross-package init order unspecified beyond dependency constraints, and in practice this means any package linked into the binary — not just main's direct imports, but every transitive dependency — can run its init to full completion in the monitor role before crashtracker's own init detects that role and exits. This is not something reordering your own imports can influence: the ordering is decided by the toolchain across the whole import graph, not by import statement order in your source. Keep expensive or externally visible init work out of any package that might be imported when crashtracking is enabled, and call Start as the first statement of main for manual integrations.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Start

func Start(opts ...Option) error

Start initialises the crashtracker. It must be called as early as possible in main().

Start spawns a monitor child process, registers the crash pipe via runtime/debug.SetCrashOutput, and returns so the application continues normally. There is no corresponding Stop: process exit alone closes the pipe, which is all the cleanup the monitor needs (it reads EOF and exits without a report). An explicit unregister-and-close step is unnecessary and actively harmful if deferred, since deferred functions run during panic unwinding — before the runtime writes the crash dump — which would disable reporting for the most common crash: an unrecovered panic. See the package doc for the full example.

Start is idempotent: subsequent calls after the first are no-ops.

Types

type Error

type Error struct {
	Type       string      `json:"type,omitempty"`
	Message    string      `json:"message,omitempty"`
	Stack      *StackTrace `json:"stack,omitempty"`
	Threads    []Thread    `json:"threads,omitempty"`
	ThreadName string      `json:"thread_name,omitempty"`
	IsCrash    bool        `json:"is_crash"`
	SourceType string      `json:"source_type,omitempty"`
}

Error holds error details in the errorsintake model.

type Frame

type Frame struct {
	Function string `json:"function,omitempty"`
	File     string `json:"file,omitempty"`
	Line     int    `json:"line,omitempty"`
}

Frame is a single stack frame.

type OSInfo

type OSInfo struct {
	Architecture string `json:"architecture"`
	Bitness      string `json:"bitness"`
	OSType       string `json:"os_type"`
	Version      string `json:"version"`
}

OSInfo holds OS/platform details required by the Crashtracking error.source_type path. All four fields are always serialized: libdatadog's own OsInfo struct has no optional fields here (os_info.rs), so none of these should be omitempty.

type Option

type Option func(*config)

Option is a functional option for configuring the crashtracker.

func WithAPIKey

func WithAPIKey(apiKey string) Option

WithAPIKey sets the Datadog API key for agentless upload. The key reaches the monitor child process through its environment (like every other option; see buildChildEnv), so choosing WithAPIKey over DD_API_KEY does not keep the key out of a process environment — it only keeps it out of the application's own.

func WithAgentURL

func WithAgentURL(rawURL string) Option

WithAgentURL configures the Datadog Agent URL for report upload.

func WithEnabled

func WithEnabled(enabled bool) Option

WithEnabled explicitly enables or disables the crashtracker, overriding the DD_CRASHTRACKING_ENABLED environment gate. When disabled, Start does not spawn the monitor process and returns nil.

func WithEnv

func WithEnv(env string) Option

WithEnv sets the env tag on crash reports.

func WithHTTPClient

func WithHTTPClient(c *http.Client) Option

WithHTTPClient sets a custom HTTP client for report upload.

func WithService

func WithService(service string) Option

WithService sets the service name tag on crash reports.

func WithSite

func WithSite(site string) Option

WithSite sets the Datadog site for agentless intake (e.g. "datadoghq.com").

func WithVersion

func WithVersion(version string) Option

WithVersion sets the version tag on crash reports.

type Report

type Report struct {
	Timestamp int64    `json:"timestamp"` // unix ms when the monitor built the report (no crash time is available in the dump)
	DDSource  string   `json:"ddsource"`  // "crashtracker"
	DDTags    string   `json:"ddtags"`    // service,env,version,language_name:go,data_schema_version:...
	Error     Error    `json:"error"`
	OSInfo    OSInfo   `json:"os_info"`
	SigInfo   *SigInfo `json:"sig_info,omitempty"`
	TraceID   string   `json:"trace_id,omitempty"`
}

Report is the errorsintake payload sent to Datadog Error Tracking on a crash.

type SigInfo

type SigInfo struct {
	SiAddr string `json:"si_addr,omitempty"`
	SiCode int    `json:"si_code,omitempty"`
	// SiCodeHuman is declared to match libdatadog's schema, but parseSignal
	// does not currently populate it: the code-to-name mapping (SEGV_MAPERR,
	// BUS_ADRALN, FPE_INTDIV, ...) is both signal- and platform-specific,
	// needing the same kind of per-GOOS table as signalNumbers. Always empty
	// today; see TestParseSignalDoesNotPopulateSiCodeHuman.
	SiCodeHuman  string `json:"si_code_human_readable,omitempty"`
	SiSigno      int    `json:"si_signo,omitempty"`
	SiSignoHuman string `json:"si_signo_human_readable,omitempty"`
}

SigInfo holds UNIX signal details for signal-triggered crashes.

type StackTrace

type StackTrace struct {
	Format     string  `json:"format"`
	Frames     []Frame `json:"frames"`
	Incomplete bool    `json:"incomplete,omitempty"`
}

StackTrace is the Crashtracking-format structured stack (error.stack object).

type Thread

type Thread struct {
	Crashed bool       `json:"crashed"`
	Name    string     `json:"name"`
	Stack   StackTrace `json:"stack"`
	State   string     `json:"state,omitempty"`
}

Thread represents one goroutine in error.threads (flat []Thread per RFC 0011 L331-342).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL