Go SDK for Checkend error monitoring. Zero dependencies, async by default.
- 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
go get github.com/Checkend/checkend-gopackage 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)
}
}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
})# 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=falseimport "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)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)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)
}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
}
})
}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
})
}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
}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
}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())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)
}
}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.
checkend.Configure(checkend.Config{
APIKey: "your-api-key",
IgnoredErrors: []interface{}{
&MyCustomError{}, // By error instance type
"context.Canceled", // By string pattern
".*timeout.*", // By regex
},
})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
},
},
})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()- Go 1.21+
- No external dependencies
# Run tests
make test
# Run linter
make lint
# Format code
make fmt
# Build
make build
# Install git hooks
make install-hooksOr using Go directly:
# Run tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run specific test
go test -run TestNotifyMIT License - see LICENSE for details.