checkend

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jan 15, 2026 License: MIT Imports: 16 Imported by: 0

README

Checkend Go SDK

CI

Go SDK for Checkend error monitoring. Zero dependencies, async by default.

Features

  • Zero dependencies - Uses only Go standard library
  • Async by default - Non-blocking error sending via goroutine worker
  • Framework integrations - net/http, Gin, Echo middleware helpers
  • Job queue integrations - Asynq, River, Machinery support
  • Context-based - Request-scoped context using context.Context
  • Sensitive data filtering - Automatic scrubbing of passwords, tokens, etc.
  • Testing utilities - Capture errors in tests without sending

Installation

go get github.com/Checkend/checkend-go

Quick Start

package main

import (
    "errors"
    "github.com/Checkend/checkend-go"
)

func main() {
    // Configure the SDK
    checkend.Configure(checkend.Config{
        APIKey: "your-api-key",
    })
    defer checkend.Stop()

    // Report an error
    if err := doSomething(); err != nil {
        checkend.Notify(err)
    }
}

Configuration

import "github.com/Checkend/checkend-go"

enabled := true
sendEnv := false
checkend.Configure(checkend.Config{
    // Required
    APIKey: "your-api-key",

    // API Settings
    Endpoint:    "https://app.checkend.io",   // Custom endpoint
    Environment: "production",                 // Auto-detected from GO_ENV, etc.
    Enabled:     &enabled,                     // Enable/disable reporting

    // Application Metadata
    AppName:  "my-app",                        // Application identifier
    Revision: "abc123",                        // Git commit/revision
    RootPath: "/app",                          // Root path for backtrace cleaning

    // HTTP Settings
    Timeout:        15 * time.Second,          // Request timeout (default: 15s)
    ConnectTimeout: 5 * time.Second,           // Connection timeout (default: 5s)
    Proxy:          "http://proxy:8080",       // HTTP proxy URL
    SSLVerify:      &enabled,                  // TLS verification (default: true)

    // Async Settings
    AsyncSend:       true,                     // Async sending (default: true)
    MaxQueueSize:    1000,                     // Max queue size (default: 1000)
    ShutdownTimeout: 5 * time.Second,          // Graceful shutdown timeout (default: 5s)

    // Data Control
    SendRequestData: &enabled,                 // Include request data (default: true)
    SendUserData:    &enabled,                 // Include user data (default: true)
    SendEnvironment: &sendEnv,                 // Include env vars (default: false)
    SendSessionData: &enabled,                 // Include session data (default: true)

    // Filtering
    FilterKeys:    []string{"custom_secret"},  // Additional keys to filter
    IgnoredErrors: []interface{}{MyError{}},   // Errors to ignore

    // Callbacks
    BeforeNotify: []func(*checkend.Notice) bool{...},

    // Debug
    Debug: false,                              // Enable debug logging
})
Environment Variables
# Core Settings
CHECKEND_API_KEY=your-api-key
CHECKEND_ENDPOINT=https://your-server.com
CHECKEND_ENVIRONMENT=production
CHECKEND_DEBUG=true

# Application Metadata
CHECKEND_APP_NAME=my-app
CHECKEND_REVISION=abc123      # Also reads GIT_COMMIT
CHECKEND_ROOT_PATH=/app

# HTTP Settings
HTTPS_PROXY=http://proxy:8080
HTTP_PROXY=http://proxy:8080
CHECKEND_SSL_VERIFY=false

Manual Error Reporting

import "github.com/Checkend/checkend-go"

// Basic error reporting
if err := riskyOperation(); err != nil {
    checkend.Notify(err)
}

// With additional context
checkend.Notify(err,
    checkend.WithContext(map[string]interface{}{
        "order_id": orderID,
    }),
    checkend.WithUser(map[string]interface{}{
        "id":    user.ID,
        "email": user.Email,
    }),
    checkend.WithTags("orders", "critical"),
    checkend.WithFingerprint("order-processing-error"),
)

// Synchronous sending (blocks until sent)
response := checkend.NotifySync(err)
fmt.Printf("Notice ID: %d\n", response.ID)

Context & User Tracking

import (
    "context"
    "github.com/Checkend/checkend-go"
)

// Create context with Checkend data
ctx := context.Background()
ctx = checkend.SetContext(ctx, map[string]interface{}{
    "order_id":     12345,
    "feature_flag": "new-checkout",
})

ctx = checkend.SetUser(ctx, map[string]interface{}{
    "id":    user.ID,
    "email": user.Email,
    "name":  user.Name,
})

ctx = checkend.SetRequest(ctx, map[string]interface{}{
    "url":    request.URL.String(),
    "method": request.Method,
})

// Report error with context
checkend.NotifyWithContext(ctx, err)

Framework Integrations

net/http
import (
    "net/http"
    "github.com/Checkend/checkend-go"
    "github.com/Checkend/checkend-go/integrations"
)

func main() {
    checkend.Configure(checkend.Config{APIKey: "your-api-key"})
    defer checkend.Stop()

    handler := http.HandlerFunc(myHandler)
    http.Handle("/", integrations.HTTPMiddleware(handler))
    http.ListenAndServe(":8080", nil)
}
Gin
import (
    "github.com/gin-gonic/gin"
    "github.com/Checkend/checkend-go"
    "github.com/Checkend/checkend-go/integrations"
)

func main() {
    checkend.Configure(checkend.Config{APIKey: "your-api-key"})
    defer checkend.Stop()

    r := gin.New()

    // Use recovery middleware that reports to Checkend
    r.Use(func(c *gin.Context) {
        defer func() {
            if err := recover(); err != nil {
                integrations.GinPanicHandler(c.Request, err)
                panic(err) // Re-panic for Gin's default handler
            }
        }()
        c.Next()
    })

    // Or manually report errors in handlers
    r.GET("/api/users", func(c *gin.Context) {
        if err := doSomething(); err != nil {
            integrations.GinErrorHandler(c.Request, err)
            c.JSON(500, gin.H{"error": "internal error"})
            return
        }
    })
}
Echo
import (
    "github.com/labstack/echo/v4"
    "github.com/Checkend/checkend-go"
    "github.com/Checkend/checkend-go/integrations"
)

func main() {
    checkend.Configure(checkend.Config{APIKey: "your-api-key"})
    defer checkend.Stop()

    e := echo.New()

    // Report errors in handlers
    e.GET("/api/users", func(c echo.Context) error {
        if err := doSomething(); err != nil {
            integrations.EchoErrorHandler(c.Request(), err)
            return c.JSON(500, map[string]string{"error": "internal error"})
        }
        return nil
    })
}

Job Queue Integrations

Asynq (Redis-based)
import (
    "context"
    "github.com/hibiken/asynq"
    "github.com/Checkend/checkend-go"
    "github.com/Checkend/checkend-go/integrations"
)

func handleEmailTask(ctx context.Context, task *asynq.Task) error {
    // Use panic handler for unexpected errors
    defer integrations.AsynqPanicHandler(ctx, task)

    err := sendEmail(task.Payload())
    if err != nil {
        // Report the error to Checkend
        integrations.AsynqErrorHandler(ctx, task, err)
        return err
    }
    return nil
}

// With full task info
func handleWithInfo(ctx context.Context, task *asynq.Task, info *asynq.TaskInfo) error {
    err := doWork()
    if err != nil {
        integrations.AsynqErrorHandlerWithInfo(ctx, &integrations.AsynqTaskInfo{
            ID:       info.ID,
            Queue:    info.Queue,
            Type:     info.Type,
            Retried:  info.Retried,
            MaxRetry: info.MaxRetry,
        }, err)
        return err
    }
    return nil
}
River (Postgres-based)
import (
    "context"
    "github.com/riverqueue/river"
    "github.com/Checkend/checkend-go"
    "github.com/Checkend/checkend-go/integrations"
)

type EmailWorker struct {
    river.WorkerDefaults[EmailArgs]
}

func (w *EmailWorker) Work(ctx context.Context, job *river.Job[EmailArgs]) error {
    // Use panic handler for unexpected errors
    defer integrations.RiverPanicHandler(ctx, job)

    err := sendEmail(job.Args.To, job.Args.Subject)
    if err != nil {
        // Report the error to Checkend
        integrations.RiverErrorHandler(ctx, job, err)
        return err
    }
    return nil
}
Machinery (Distributed)
import (
    "github.com/RichardKnop/machinery/v2"
    "github.com/Checkend/checkend-go"
    "github.com/Checkend/checkend-go/integrations"
)

func sendEmailTask(to, subject string) error {
    // Use panic handler for unexpected errors
    defer integrations.MachineryPanicHandler("send_email")

    err := sendEmail(to, subject)
    if err != nil {
        // Report the error to Checkend
        integrations.MachineryErrorHandler(context.Background(), "send_email", err)
        return err
    }
    return nil
}

// Configure error handler on server
server.SetErrorHandler(integrations.MachineryOnTaskFailure())

Testing

Use the testing functions to capture errors without sending them:

import (
    "testing"
    "errors"
    "github.com/Checkend/checkend-go"
)

func TestErrorReporting(t *testing.T) {
    // Enable testing mode
    checkend.SetupTesting()
    defer checkend.Reset()

    enabled := true
    checkend.Configure(checkend.Config{
        APIKey:  "test-key",
        Enabled: &enabled,
    })

    // Trigger an error
    checkend.Notify(errors.New("test error"))

    // Assert on captured notices
    if !checkend.TestingHasNotices() {
        t.Error("Expected notices to be captured")
    }

    if checkend.TestingNoticeCount() != 1 {
        t.Errorf("Expected 1 notice, got %d", checkend.TestingNoticeCount())
    }

    notice := checkend.TestingLastNotice()
    if notice.Message != "test error" {
        t.Errorf("Expected message 'test error', got '%s'", notice.Message)
    }
}

Filtering Sensitive Data

By default, these keys are filtered: password, secret, token, api_key, authorization, credit_card, cvv, ssn, etc.

Add custom keys:

checkend.Configure(checkend.Config{
    APIKey:     "your-api-key",
    FilterKeys: []string{"custom_secret", "internal_token"},
})

Filtered values appear as [FILTERED] in the dashboard.

Ignoring Errors

checkend.Configure(checkend.Config{
    APIKey: "your-api-key",
    IgnoredErrors: []interface{}{
        &MyCustomError{},     // By error instance type
        "context.Canceled",   // By string pattern
        ".*timeout.*",        // By regex
    },
})

Before Notify Callbacks

checkend.Configure(checkend.Config{
    APIKey: "your-api-key",
    BeforeNotify: []func(*checkend.Notice) bool{
        func(notice *checkend.Notice) bool {
            notice.Context["server"] = "web-1"
            return true // Continue sending
        },
        func(notice *checkend.Notice) bool {
            if strings.Contains(notice.Message, "ignore-me") {
                return false // Skip sending
            }
            return true
        },
    },
})

Graceful Shutdown

The SDK automatically flushes pending notices when Stop() is called with a configurable timeout. Always defer Stop():

func main() {
    checkend.Configure(checkend.Config{
        APIKey:          "your-api-key",
        ShutdownTimeout: 10 * time.Second, // Wait up to 10s for pending notices
    })
    defer checkend.Stop()

    // Your application code...
}

For manual control:

// Wait for pending notices to send
checkend.Flush()

// Stop the worker
checkend.Stop()

Requirements

  • Go 1.21+
  • No external dependencies

Development

# Run tests
make test

# Run linter
make lint

# Format code
make fmt

# Build
make build

# Install git hooks
make install-hooks

Or using Go directly:

# Run tests
go test ./...

# Run tests with coverage
go test -cover ./...

# Run specific test
go test -run TestNotify

License

MIT License - see LICENSE for details.

Documentation

Overview

Package checkend provides error monitoring for Go applications.

Checkend is a lightweight, zero-dependency error monitoring SDK that sends errors to the Checkend service for tracking and analysis.

Basic usage:

import "github.com/Checkend/checkend-go"

func main() {
    checkend.Configure(checkend.Config{
        APIKey: "your-api-key",
    })
    defer checkend.Stop()

    // Your application code...
    if err != nil {
        checkend.Notify(err)
    }
}

Index

Constants

View Source
const DefaultConnectTimeout = 5 * time.Second

DefaultConnectTimeout is the default connection establishment timeout.

View Source
const DefaultEndpoint = "https://app.checkend.io"

DefaultEndpoint is the default Checkend API endpoint.

View Source
const DefaultMaxQueueSize = 1000

DefaultMaxQueueSize is the default maximum queue size for async sending.

View Source
const DefaultShutdownTimeout = 5 * time.Second

DefaultShutdownTimeout is the default graceful shutdown timeout.

View Source
const DefaultTimeout = 15 * time.Second

DefaultTimeout is the default HTTP request timeout.

View Source
const Version = "0.1.0"

Version is the SDK version.

Variables

View Source
var DefaultFilterKeys = []string{
	"password",
	"password_confirmation",
	"secret",
	"secret_key",
	"api_key",
	"apikey",
	"access_token",
	"auth_token",
	"authorization",
	"token",
	"credit_card",
	"card_number",
	"cvv",
	"cvc",
	"ssn",
	"social_security",
}

DefaultFilterKeys are the default keys to filter from payloads.

Functions

func ClearTesting

func ClearTesting()

ClearTesting clears testing state.

func Flush

func Flush()

Flush waits for all queued notices to be sent.

func Notify

func Notify(err error, opts ...NotifyOption)

Notify sends an error to Checkend asynchronously.

func NotifyWithContext

func NotifyWithContext(ctx context.Context, err error, opts ...NotifyOption)

NotifyWithContext sends an error to Checkend asynchronously with context.

func Reset

func Reset()

Reset resets all state (useful for testing).

func SetContext

func SetContext(ctx context.Context, data map[string]interface{}) context.Context

SetContext adds context data to the given context.

func SetRequest

func SetRequest(ctx context.Context, request map[string]interface{}) context.Context

SetRequest sets request information in the given context.

func SetUser

func SetUser(ctx context.Context, user map[string]interface{}) context.Context

SetUser sets user information in the given context.

func SetupTesting

func SetupTesting()

SetupTesting enables testing mode.

func Stop

func Stop()

Stop stops the worker and waits for pending notices.

func TeardownTesting

func TeardownTesting()

TeardownTesting disables testing mode.

func TestingClearNotices

func TestingClearNotices()

TestingClearNotices clears all captured notices.

func TestingHasNotices

func TestingHasNotices() bool

TestingHasNotices returns true if any notices have been captured.

func TestingNoticeCount

func TestingNoticeCount() int

TestingNoticeCount returns the number of captured notices.

func WithContextData

func WithContextData(ctx context.Context, data *ContextData) context.Context

WithContextData returns a new context with Checkend data.

Types

type APIResponse

type APIResponse struct {
	ID        int `json:"id"`
	ProblemID int `json:"problem_id"`
}

APIResponse represents the response from the Checkend API.

func NotifySync

func NotifySync(err error, opts ...NotifyOption) *APIResponse

NotifySync sends an error to Checkend synchronously and returns the response.

func NotifySyncWithContext

func NotifySyncWithContext(ctx context.Context, err error, opts ...NotifyOption) *APIResponse

NotifySyncWithContext sends an error to Checkend synchronously with context.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the HTTP client for the Checkend API.

func NewClient

func NewClient(config *Configuration) *Client

NewClient creates a new API client.

func (*Client) Send

func (c *Client) Send(notice *Notice) *APIResponse

Send sends a notice to Checkend.

type Config

type Config struct {
	// APIKey is your Checkend ingestion API key (required).
	APIKey string

	// Endpoint is the API endpoint URL.
	Endpoint string

	// Environment is the environment name (e.g., "production", "staging").
	Environment string

	// Enabled controls whether error reporting is active.
	Enabled *bool

	// AsyncSend controls whether errors are sent asynchronously.
	AsyncSend bool

	// MaxQueueSize is the maximum queue size for async sending.
	MaxQueueSize int

	// Timeout is the HTTP request timeout.
	Timeout time.Duration

	// ConnectTimeout is the connection establishment timeout.
	ConnectTimeout time.Duration

	// ShutdownTimeout is the graceful shutdown timeout.
	ShutdownTimeout time.Duration

	// FilterKeys are additional keys to filter from payloads.
	FilterKeys []string

	// IgnoredErrors are error types or patterns to ignore.
	IgnoredErrors []interface{}

	// BeforeNotify are callbacks to run before sending a notice.
	// Return false to skip sending.
	BeforeNotify []func(*Notice) bool

	// Debug enables debug logging.
	Debug bool

	// AppName is the application identifier.
	AppName string

	// Revision is the code revision or commit hash.
	Revision string

	// RootPath is the application root path for cleaning backtraces.
	RootPath string

	// SendRequestData controls whether request data is included in notices.
	SendRequestData *bool

	// SendSessionData controls whether session data is included in notices.
	SendSessionData *bool

	// SendEnvironment controls whether environment variables are included in notices.
	SendEnvironment *bool

	// SendUserData controls whether user data is included in notices.
	SendUserData *bool

	// Proxy is the HTTP proxy URL.
	Proxy string

	// SSLVerify controls TLS certificate verification.
	SSLVerify *bool
}

Config holds the configuration options for Checkend.

type Configuration

type Configuration struct {
	APIKey          string
	Endpoint        string
	Environment     string
	Enabled         bool
	AsyncSend       bool
	MaxQueueSize    int
	Timeout         time.Duration
	ConnectTimeout  time.Duration
	ShutdownTimeout time.Duration
	FilterKeys      []string
	IgnoredErrors   []interface{}
	BeforeNotify    []func(*Notice) bool
	Debug           bool
	AppName         string
	Revision        string
	RootPath        string
	SendRequestData bool
	SendSessionData bool
	SendEnvironment bool
	SendUserData    bool
	Proxy           string
	SSLVerify       bool
}

Configuration is the resolved configuration for the SDK.

func Configure

func Configure(cfg Config) *Configuration

Configure initializes the Checkend SDK with the given configuration.

func GetConfiguration

func GetConfiguration() *Configuration

GetConfiguration returns the current configuration.

func NewConfiguration

func NewConfiguration(cfg Config) *Configuration

NewConfiguration creates a new Configuration from Config.

type ContextData

type ContextData struct {
	Context map[string]interface{}
	User    map[string]interface{}
	Request map[string]interface{}
}

ContextData holds request-scoped Checkend data.

func GetContextData

func GetContextData(ctx context.Context) *ContextData

GetContextData retrieves Checkend data from the context.

type ErrorPayload

type ErrorPayload struct {
	Class       string   `json:"class"`
	Message     string   `json:"message"`
	Backtrace   []string `json:"backtrace"`
	Fingerprint string   `json:"fingerprint,omitempty"`
	Tags        []string `json:"tags,omitempty"`
	OccurredAt  string   `json:"occurred_at"`
}

ErrorPayload represents the error portion of the payload.

type IgnoreFilter

type IgnoreFilter = filters.IgnoreFilter

IgnoreFilter wraps the filters package IgnoreFilter for internal use.

func NewIgnoreFilter

func NewIgnoreFilter(patterns []interface{}) *IgnoreFilter

NewIgnoreFilter creates a new IgnoreFilter.

type Notice

type Notice struct {
	ErrorClass  string                 `json:"error_class"`
	Message     string                 `json:"message"`
	Backtrace   []string               `json:"backtrace"`
	Fingerprint string                 `json:"fingerprint,omitempty"`
	Tags        []string               `json:"tags,omitempty"`
	Context     map[string]interface{} `json:"context,omitempty"`
	Request     map[string]interface{} `json:"request,omitempty"`
	User        map[string]interface{} `json:"user,omitempty"`
	Environment string                 `json:"environment"`
	OccurredAt  time.Time              `json:"occurred_at"`
	Notifier    NotifierInfo           `json:"notifier"`
	AppName     string                 `json:"app_name,omitempty"`
	Revision    string                 `json:"revision,omitempty"`
	Hostname    string                 `json:"hostname,omitempty"`
}

Notice represents an error notice to be sent to Checkend.

func TestingFirstNotice

func TestingFirstNotice() *Notice

TestingFirstNotice returns the first captured notice.

func TestingLastNotice

func TestingLastNotice() *Notice

TestingLastNotice returns the last captured notice.

func TestingNotices

func TestingNotices() []*Notice

TestingNotices returns all captured notices.

func (*Notice) ToPayload

func (n *Notice) ToPayload() *Payload

ToPayload converts the Notice to an API payload.

type NoticeBuilder

type NoticeBuilder struct {
	// contains filtered or unexported fields
}

NoticeBuilder builds Notice objects from errors.

func NewNoticeBuilder

func NewNoticeBuilder(config *Configuration) *NoticeBuilder

NewNoticeBuilder creates a new NoticeBuilder.

func (*NoticeBuilder) Build

func (b *NoticeBuilder) Build(
	err error,
	context map[string]interface{},
	user map[string]interface{},
	request map[string]interface{},
	fingerprint string,
	tags []string,
) *Notice

Build creates a Notice from an error.

type NotifierInfo

type NotifierInfo struct {
	Name            string `json:"name"`
	Version         string `json:"version"`
	Language        string `json:"language"`
	LanguageVersion string `json:"language_version"`
}

NotifierInfo contains SDK metadata.

type NotifyOption

type NotifyOption func(*notifyOptions)

NotifyOption is a functional option for Notify.

func WithContext

func WithContext(ctx map[string]interface{}) NotifyOption

WithContext sets additional context data.

func WithFingerprint

func WithFingerprint(fingerprint string) NotifyOption

WithFingerprint sets a custom fingerprint for error grouping.

func WithRequest

func WithRequest(request map[string]interface{}) NotifyOption

WithRequest sets request information.

func WithTags

func WithTags(tags ...string) NotifyOption

WithTags sets tags for the error.

func WithUser

func WithUser(user map[string]interface{}) NotifyOption

WithUser sets user information.

type Payload

type Payload struct {
	Error    ErrorPayload           `json:"error"`
	Context  map[string]interface{} `json:"context"`
	Request  map[string]interface{} `json:"request,omitempty"`
	User     map[string]interface{} `json:"user,omitempty"`
	Notifier NotifierInfo           `json:"notifier"`
	Server   *ServerInfo            `json:"server,omitempty"`
}

Payload represents the API request payload.

type SanitizeFilter

type SanitizeFilter = filters.SanitizeFilter

SanitizeFilter wraps the filters package SanitizeFilter for internal use.

func NewSanitizeFilter

func NewSanitizeFilter(filterKeys []string) *SanitizeFilter

NewSanitizeFilter creates a new SanitizeFilter.

type ServerInfo

type ServerInfo struct {
	AppName  string `json:"app_name,omitempty"`
	Revision string `json:"revision,omitempty"`
	Hostname string `json:"hostname,omitempty"`
}

ServerInfo contains server/application metadata.

type Worker

type Worker struct {
	// contains filtered or unexported fields
}

Worker handles asynchronous sending of notices.

func NewWorker

func NewWorker(config *Configuration) *Worker

NewWorker creates a new Worker.

func (*Worker) Flush

func (w *Worker) Flush()

Flush waits for all queued notices to be sent.

func (*Worker) Push

func (w *Worker) Push(notice *Notice) bool

Push adds a notice to the queue.

func (*Worker) Start

func (w *Worker) Start()

Start starts the worker goroutine.

func (*Worker) Stop

func (w *Worker) Stop()

Stop stops the worker and waits for pending notices with timeout.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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