Slog

Slog is a structured logging library for Go, introduced in Go 1.21. This guide demonstrates how to integrate slog with Sentry to capture and send logs to Sentry.

For a quick reference, there is a complete example at the Go SDK source code repository.

Go API documentation for the sentryslog package is also available.

Slog structured logging is supported in Sentry Go SDK version 0.34.0 and above.

Copied
go get github.com/getsentry/sentry-go/slog

To integrate Sentry with slog, you need to set up a Sentry handler and configure the Sentry client.

Copied
import (
    "context"
	"fmt"
	"log"
	"time"

	"log/slog"

	"github.com/getsentry/sentry-go"
	sentryslog "github.com/getsentry/sentry-go/slog"
)

func main() {
	// Initialize Sentry
	err := sentry.Init(sentry.ClientOptions{
		Dsn:           "https://examplePublicKey@o0.ingest.sentry.io/0",
		EnableTracing: false,
		// Adds request headers and IP for users,
		// visit: https://docs.sentry.io/platforms/go/data-management/data-collected/ for more info
		SendDefaultPII: true,
		EnableLogs: true,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer sentry.Flush(2 * time.Second)

	// Configure `slog` to use Sentry as a handler
	ctx := context.Background()
	handler := sentryslog.Option{
		EventLevel: []slog.Level{slog.LevelError}, // Only Error level will be sent as events
		LogLevel:   []slog.Level{slog.LevelWarn, slog.LevelInfo},         // Only Warn and Info levels will be sent as logs
	}.NewSentryHandler(ctx)
	logger := slog.New(handler)
	logger = logger.With("release", "v1.0.0")

	// Log messages with various attributes
	logger.
		With(
			slog.Group("user",
				slog.String("id", "user-123"),
				slog.Time("created_at", time.Now()),
			),
		).
		With("environment", "dev").
		With("error", fmt.Errorf("an error")).
		ErrorContext(ctx, "a message")
}

sentryslog provides options to configure the integration with Sentry. It accepts a struct of sentryslog.Options that allows you to configure how the handler will behave. The options are:

Copied
// EventLevel specifies the exact log levels to capture and send to Sentry as Events.
// Only logs at these specific levels will be processed as events.
// Defaults to []slog.Level{slog.LevelError, LevelFatal}.
EventLevel slog.Leveler
// LogLevel specifies the exact log levels to capture and send to Sentry as Log entries
// Only logs at these specific levels will be processed as log entries.
// Defaults to []slog.Level{slog.LevelDebug, slog.LevelInfo, slog.LevelWarn, slog.LevelError, LevelFatal}.
LogLevel slog.Leveler
// Hub specifies the Sentry Hub to use for capturing events.
// If not provided, the current Hub is used by default.
Hub *sentry.Hub
// Converter is an optional function that customizes how log records
// are converted into Sentry events. By default, the DefaultConverter is used.
Converter Converter
// AttrFromContext is an optional slice of functions that extract attributes
// from the context. These functions can add additional metadata to the log entry.
AttrFromContext []func(ctx context.Context) []slog.Attr
// AddSource is an optional flag that, when set to true, includes the source
// information (such as file and line number) in the Sentry event.
// This can be useful for debugging purposes.
AddSource bool
// ReplaceAttr is an optional function that allows for the modification or
// replacement of attributes in the log record. This can be used to filter
// or transform attributes before they are sent to Sentry.
ReplaceAttr func(groups []string, a slog.Attr) slog.Attr

Sentry allows you to send logs either as log entries or as events. The minimum log level defaults to slog.LevelDebug and logs will be sent if the EnableLogs option is set. The minimum event level defaults to slog.LevelError.

Copied
ctx := context.Background()
logger := slog.New(sentryslog.Option{
    // specify all event levels
    EventLevel: []slog.Level{
        slog.LevelDebug,
        slog.LevelInfo,
        slog.LevelWarn,
        slog.LevelError,
	},
    LogLevel: []slog.Level{}, // disable log entries
	AttrFromContext: []func(ctx context.Context) []slog.Attr{
		func(ctx context.Context) []slog.Attr {
			return []slog.Attr{slog.String("request_id", "123")}
		},
	},
}.NewSentryHandler(ctx))

logger.Error("This log is sent as an event")

Copied
ctx := context.Background()
logger := slog.New(sentryslog.Option{
    EventLevel: []slog.Level{}, // disable events
    // specify all log levels
    LogLevel: []slog.Level{
        slog.LevelDebug,
        slog.LevelInfo,
        slog.LevelWarn,
        slog.LevelError,
    },
}.NewSentryHandler(ctx))

logger.Error("This log is sent as a log")

To correlate logs with transactions, you need to pass a context.Context that contains transaction information to your logger calls. The sentryhttp middleware automatically adds transaction information to the request's context. Here's an example of how to use InfoContext in an HTTP handler to ensure logs are associated with the correct trace.

Copied
// Assume logger is initialized with the Sentry handler as shown above.
// var logger *slog.Logger

func myAsyncHandler(w http.ResponseWriter, r *http.Request) {
	// The sentryhttp middleware adds a Hub with transaction information to the request context.
	ctx := r.Context()
	// By using InfoContext, the log entry will be associated with the transaction from the request.
	slog.InfoContext(ctx, "Log inside handler")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, "Handler finished, async task running in background.")
}

func main() {
	// Wrap your handler with sentryhttp to automatically start transactions for requests.
	sentryHandler := sentryhttp.New(sentryhttp.Options{})
	http.Handle("/async", sentryHandler.Handle(http.HandlerFunc(myAsyncHandler)))
}

Note: Ensure Sentry is flushed before the application exits to avoid losing any pending events.

For comprehensive logging setup with slog, including advanced configuration options and best practices, see the Go Logs documentation. The slog integration shown above provides seamless integration with Sentry's structured logging features.

Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").