Thanks to visit codestin.com
Credit goes to github.com

Skip to content

samber/slog-loki

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

84 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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.