slogbugsnag

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2024 License: MIT Imports: 13 Imported by: 0

README

slog-bugsnag

tag Go Version GoDoc Build Status Go report Coverage Contributors License

Golang structured logging (slog) handler middleware for Bugsnag. Automatically send all Error level logs to Bugsnag, along with all attributes and context. Never forget to snag another bug again.

Other Great SLOG Utilities
  • slogctx: Add attributes to context and have them automatically added to all log lines. Work with a logger stored in context.
  • slogotel: Automatically extract and add OpenTelemetry TraceID's to all log lines.
  • slogdedup: Middleware that deduplicates and sorts attributes. Particularly useful for JSON logging. Format logs for aggregators (Graylog, GCP/Stackdriver, etc).
  • slogbugsnag: Middleware that pipes Errors to Bugsnag.
  • slogjson: Formatter that uses the JSON v2 library, with optional single-line pretty-printing.

Install

go get github.com/veqryn/slog-bugsnag

import (
	slogbugsnag "github.com/veqryn/slog-bugsnag"
)

Usage

package main

import (
	"context"
	"log/slog"
	"net/http"
	"os"

	"github.com/bugsnag/bugsnag-go/v2"
	"github.com/pkg/errors"
	slogbugsnag "github.com/veqryn/slog-bugsnag"
)

func main() {
	// Configure bugsnag
	bugsnag.Configure(bugsnag.Configuration{
		APIKey:     os.Getenv("BUGSNAG_API_KEY"),
		AppVersion: "0.1.0",
	})

	// Setup slog handlers
	h := slogbugsnag.NewHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
	slog.SetDefault(slog.New(h))

	// Optional: closing the handler will flush all bugs in the queue to bugsnag
	defer h.Close()

	slog.Info("starting up...") // Not sent to bugsnag

	err := errors.New("my horrible error")
	slog.Error("oh no...", "err", err) // Sent to bugsnag, with original err's stack trace

	// Can be used with context too
	ctx := bugsnag.StartSession(context.Background())
	defer bugsnag.AutoNotify()

	// Bugsnag can capture basic http.Request data, if added to the ctx
	req, _ := http.NewRequest("GET", "https://www.github.com/veqryn/slog-bugsnag", nil)
	ctx = bugsnag.AttachRequestData(ctx, req)

	// User information can be sent as a bugsnag.User
	user := bugsnag.User{Id: "1234", Name: "joe", Email: "none"}

	// Or using these string types, added to the log as attribute values (any key is fine)
	id := slogbugsnag.ID("1234")
	name := slogbugsnag.Name("joe")
	email := slogbugsnag.Email("none")

	// Tabs will be created in bugsnag for each group,
	// with the root level attributes going into a "log" tab.
	log := slog.With(slog.Any("id", id), slog.Any("name", name), slog.Any("email", email))
	log = log.WithGroup("MyTab")

	// If no "error" type is found among log attribute values,
	// then an error will be created using the log message, with a stack trace at the log call.
	log.ErrorContext(ctx, "more bad things", slog.Any("user", user), slog.String("foo", "bar"))

	// All of the above will log out 3 lines and send 2 bugs reports to bugsnag.
	// The second bug will have tabs for Stacktrace, App, Device, Request, User, Log, and MyTab,
	// containing all the data in the log record.
}
slog-multi Middleware

This library has a convenience method that allow it to interoperate with github.com/samber/slog-multi, in order to easily setup slog workflows such as pipelines, fanout, routing, failover, etc.

notifiers := slogbugsnag.NewNotifierWorkers(&slogbugsnag.NotifierOptions{})
defer notifiers.Close()

slog.SetDefault(slog.New(slogmulti.
	Pipe(slogcontext.NewMiddleware(&slogcontext.HandlerOptions{})).
	Pipe(slogdedup.NewOverwriteMiddleware(&slogdedup.OverwriteHandlerOptions{})).
	Pipe(slogbugsnag.NewMiddleware(&slogbugsnag.HandlerOptions{Notifiers: notifiers})).
	Handler(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})),
))

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewMiddleware

func NewMiddleware(options *HandlerOptions) func(slog.Handler) slog.Handler

NewMiddleware creates a slogbugsnag.Handler slog.Handler middleware that conforms to github.com/samber/slog-multi.Middleware interface. It can be used with slogmulti methods such as Pipe to easily setup a pipeline of slog handlers:

slog.SetDefault(slog.New(slogmulti.
	Pipe(slogcontext.NewMiddleware(&slogcontext.HandlerOptions{})).
	Pipe(slogdedup.NewOverwriteMiddleware(&slogdedup.OverwriteHandlerOptions{})).
	Pipe(slogbugsnag.NewMiddleware(&slogbugsnag.HandlerOptions{})).
	Handler(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})),
))

Types

type Email

type Email string

Email is a string that, if used as a log attribute value, will be filled in on the bugsnag.User struct.

func (Email) BugsnagUserEmail

func (email Email) BugsnagUserEmail() string

BugsnagUserEmail returns the bugsnag user email

type Handler

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

Handler is a slog.Handler middleware that will automatically send log lines to Bugsnag (https://www.bugsnag.com/) if they are at least a certain level (Error by default). The latest error in the log line is sent as the primary error, and all log attributes and the context are put into metadata and user tabs and sent with the bug. It passes the final record and attributes off to the next handler when finished. The Bugsnag V2 library should be configured before any logging is done.

bugsnag.Configure(bugsnag.Configuration{APIKey: ...})

func NewHandler

func NewHandler(next slog.Handler, opts *HandlerOptions) *Handler

NewHandler creates a slog.Handler middleware that will automatically send log lines to Bugsnag (https://www.bugsnag.com/) if they are at least a certain level (Error by default). The latest error in the log line is sent as the primary error, and all log attributes and the context are put into metadata and user tabs and sent with the bug. It passes the final record and attributes off to the next handler when finished. The Bugsnag V2 library should be configured before any logging is done.

bugsnag.Configure(bugsnag.Configuration{APIKey: ...})

If opts is nil, the default options are used.

func (*Handler) Close

func (h *Handler) Close()

Close stops the handler from sending any new bugs after this point to bugsnag, but it will continue to pass the log records to the next handler. This call will block until all bugs currently queued have been sent.

func (*Handler) Enabled

func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool

Enabled reports whether the next handler handles records at the given level. The handler ignores records whose level is lower.

func (*Handler) Handle

func (h *Handler) Handle(ctx context.Context, r slog.Record) error

Handle collects all attributes and groups, then passes the record and its attributes to the next handler.

func (*Handler) WithAttrs

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a new AppendHandler whose attributes consists of h's attributes followed by attrs.

func (*Handler) WithGroup

func (h *Handler) WithGroup(name string) slog.Handler

WithGroup returns a new AppendHandler that still has h's attributes, but any future attributes added will be namespaced.

type HandlerOptions

type HandlerOptions struct {

	// Level reports the minimum record level that will be sent to bugsnag.
	// The handler ignores but still passes along records with lower levels
	// to the next handler.
	// If NotifyLevel is nil, the handler assumes LevelError.
	// The handler calls NotifyLevel.Level() for each record processed;
	// to adjust the minimum level dynamically, use a LevelVar.
	NotifyLevel slog.Leveler

	// UnhandledLevel reports the minimum record level that will be sent to
	// bugsnag as an unhandled error.
	// If UnhandledLevel is nil, the handler assumes slog.LevelError + 4.
	UnhandledLevel slog.Leveler

	// Notifiers is a worker pool, where each worker synchronously sends
	// bugs to bugsnag. This gives us the ability to flush all bugs before
	// terminating an application, by calling Close on the pool or the handler.
	// If nil, a default notifier worker pool will be started.
	Notifiers *NotifierWorkers
}

HandlerOptions are options for a Handler

type ID

type ID string

ID is a string that, if used as a log attribute value, will be filled in on the bugsnag.User struct. The ID is also used to determine the number of users affected by the bug, in bugsnag's system.

func (ID) BugsnagUserID

func (id ID) BugsnagUserID() string

BugsnagUserID returns the bugsnag user id

type Name

type Name string

Name is a string that, if used as a log attribute value, will be filled in on the bugsnag.User struct.

func (Name) BugsnagUserName

func (name Name) BugsnagUserName() string

BugsnagUserName returns the bugsnag user name

type NotifierOptions added in v0.2.0

type NotifierOptions struct {
	// Notifier is the bugsnag notifier that will be used. It should be
	// configured, and may contain custom rawData added to all events.
	// If nil, a default one will be created.
	Notifier *bugsnag.Notifier

	// MaxNotifierConcurrency sets the maximum number of bugs that can be sent
	// to bugsnag in parallel. It defaults to the number of CPU's.
	// Bugs are placed on a buffered channel to be sent to bugsnag, in order
	// to not block or delay the log call from returning. The bugs are then
	// sent to bugsnag synchronously by a number of workers equal to this int.
	MaxNotifierConcurrency int
}

NotifierOptions are options for NotifierWorkers

type NotifierWorkers added in v0.2.0

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

NotifierWorkers can run a worker pool, where each worker synchronously sends bugs to bugsnag. This gives us the ability to flush all bugs before terminating an application, by calling NotifierWorkers.Close

func NewNotifierWorkers added in v0.2.0

func NewNotifierWorkers(opts *NotifierOptions) *NotifierWorkers

NewNotifierWorkers creates and starts a worker pool, where each worker synchronously sends bugs to bugsnag. This gives us the ability to flush all bugs before terminating an application, by calling NotifierWorkers.Close

func (*NotifierWorkers) Close added in v0.2.0

func (nw *NotifierWorkers) Close()

Close stops the NotifierWorkers from accepting any new bugs to its queue. This call will block until all bugs currently queued have been sent.

Jump to

Keyboard shortcuts

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