GitHub - samber/slog-loki: 🚨 slog: Loki handler (original) (raw)

slog: Loki handler

tag Go Version GoDoc Build Status Go report Coverage Contributors License

A Loki Handler for slog Go library.

See also:

HTTP middlewares:

Loggers:

Log sinks:

🚀 Install

go get github.com/samber/slog-loki/v3

Compatibility: go >= 1.21

No breaking changes will be made to exported APIs before v4.0.0.

💡 Usage

GoDoc: https://pkg.go.dev/github.com/samber/slog-loki/v3

Handler options

type Option struct { // log level (default: debug) Level slog.Leveler

// loki
Client *loki.Client

// optional: customize webhook event builder
Converter Converter
// optional: fetch attributes from context
AttrFromContext []func(ctx context.Context) []slog.Attr

// optional: see slog.HandlerOptions
AddSource   bool
ReplaceAttr func(groups []string, a slog.Attr) slog.Attr

// By default, LokiHandler.Handle sends record attributes as labels to Loki.
// When set to true, this handler sends record attributes as structured metadata.
// 
// Combine with RemoveAttrsConverter to avoid sending attributes as labels.
HandleRecordsWithMetadata bool

}

Attributes will be injected in log payload.

Other global parameters:

slogloki.SourceKey = "source" slogloki.ErrorKeys = []string{"error", "err"} slogloki.SubAttributeSeparator = "_" slogloki.AttributeKeyInvalidCharReplacement = ""

Example

Default behaviour (all attributes are treated as labels):

import ( "github.com/grafana/loki-client-go/loki" slogloki "github.com/samber/slog-loki/v3" "log/slog" )

func main() { // setup loki client config, _ := loki.NewDefaultConfig("http://localhost:3100/loki/api/v1/push") config.TenantID = "xyz" client, _ := loki.New(config)

logger := slog.New(slogloki.Option{Level: slog.LevelDebug, Client: client}.NewLokiHandler())
logger = logger.
    With("environment", "dev").
    With("release", "v1.0.0")

// log error
logger.Error("caramba!")

// log user signup
logger.Info("user registration")

// stop loki client and purge buffers
client.Stop()

}

To send record attributes as structured metadata (instead of labels), use the HandleRecordsWithMetadata option along with the RemoveAttrsConverter converter:

import ( "github.com/grafana/loki-client-go/loki" slogloki "github.com/samber/slog-loki/v3" "log/slog" )

func main() { // setup loki client config, _ := loki.NewDefaultConfig("http://localhost:3100/loki/api/v1/push") config.TenantID = "xyz" client, _ := loki.New(config)

// With slogloki.RemoveAttrsConverter and HandleRecordsWithMetadata enabled, attributes are not sent as labels, thus
// allowing to log high-cardinality metadata without impacting performance.
o := slogloki.Option{
    HandleRecordsWithMetadata: true,
    Converter:                 slogloki.RemoveAttrsConverter,
    Level:                     slog.LevelDebug,
    Client:                    client,
}
logger := slog.New(o.NewLokiHandler())

// Attributes added via WithAttrs are always sent as labels to Loki.
logger = logger.With("release", "v1.0.0")
// This will send the "span_id", a high cardinality value, as structured metadata, not as a label.
//
// More about structured metadata in Loki:
// https://grafana.com/docs/loki/latest/get-started/labels/structured-metadata/
logger.Error("A message with structured metadata", slog.String("span_id", "1234567"))

client.Stop()

}

Note: Attributes added via WithAttrs are always sent as labels to Loki.

Tracing

Import the samber/slog-otel library.

import ( slogloki "github.com/samber/slog-loki" slogotel "github.com/samber/slog-otel" "go.opentelemetry.io/otel/sdk/trace" )

func main() { tp := trace.NewTracerProvider( trace.WithSampler(trace.AlwaysSample()), ) tracer := tp.Tracer("hello/world")

ctx, span := tracer.Start(context.Background(), "foo")
defer span.End()

span.AddEvent("bar")

logger := slog.New(
    slogloki.Option{
        // ...
        AttrFromContext: []func(ctx context.Context) []slog.Attr{
            slogotel.ExtractOtelAttrFromContext([]string{"tracing"}, "trace_id", "span_id"),
        },
    }.NewLokiHandler(),
)

logger.ErrorContext(ctx, "a message")

}

🤝 Contributing

Don't hesitate ;)

Install some dev dependencies

make tools

Run tests

make test

or

make watch-test

👤 Contributors

Contributors

💫 Show your support

Give a ⭐️ if this project helped you!

GitHub Sponsors

📝 License

Copyright © 2023 Samuel Berthe.

This project is MIT licensed.