slogdedup

package module
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2024 License: MIT Imports: 7 Imported by: 2

README

slog-dedup

tag Go Version GoDoc Build Status Go report Coverage Contributors License

Golang structured logging (slog) deduplication for use with json logging (or any other format where duplicates are not appreciated).

The slog handlers in this module are "middleware" handlers. When creating them, you must pass in another handler, which will be called after this handler has finished handling a log record. Because of this, these handlers can be chained with other middlewares, and can be used with many different final handlers, whether from the stdlib or third-party, such as json, protobuf, text, or data sinks.

The main impetus behind this package is because most JSON tools do not like duplicate keys for their member properties/fields. Some of them will give errors or fail to parse the log line, and some may even crash.

Unfortunately the default behavior of the stdlib slog handlers is to allow duplicate keys:

// This makes json tools unhappy    :(
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))
slog.Info("this is the stdlib json handler by itself",
    slog.String("duplicated", "zero"),
    slog.String("duplicated", "one"),
    slog.String("duplicated", "two"),
)

Outputs:

{
    "time": "2023-10-03T01:45:00Z",
    "level": "INFO",
    "msg": "this is the stdlib json handler by itself",
    "duplicated": "zero",
    "duplicated": "one",
    "duplicated": "two"
}

With this in mind, this repo was created with several different ways of deduplicating the keys: overwriting, ignoring, appending, incrementing. For example, incrementing:

{
    "time": "2024-03-21T09:33:25Z",
    "level": "INFO",
    "msg": "this is the dedup incrementer handler",
    "duplicated": "zero",
    "duplicated#01": "one",
    "duplicated#02": "two"
}

Additionally, this library includes convenience methods for formatting output to match what is expected for various log aggregation tools (such as Graylog), as well as cloud providers (such as Stackdriver / Google Cloud Operations / GCP Log Explorer).

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.
  • 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-dedup

import (
	slogdedup "github.com/veqryn/slog-dedup"
)

Usage

Overwrite Older Duplicates Handler
logger := slog.New(slogdedup.NewOverwriteHandler(slog.NewJSONHandler(os.Stdout, nil), nil))
logger.Info("this is the dedup overwrite handler",
    slog.String("duplicated", "zero"),
    slog.String("duplicated", "one"),
    slog.String("duplicated", "two"),
)

Outputs:

{
    "time": "2024-03-21T09:33:25Z",
    "level": "INFO",
    "msg": "this is the dedup overwrite handler",
    "duplicated": "two"
}
Ignore Newer Duplicates Handler
logger := slog.New(slogdedup.NewIgnoreHandler(slog.NewJSONHandler(os.Stdout, nil), nil))
logger.Info("this is the dedup ignore handler",
    slog.String("duplicated", "zero"),
    slog.String("duplicated", "one"),
    slog.String("duplicated", "two"),
)

Outputs:

{
    "time": "2024-03-21T09:33:25Z",
    "level": "INFO",
    "msg": "this is the dedup ignore handler",
    "duplicated": "zero"
}
Increment Newer Duplicate Key Names Handler
logger := slog.New(slogdedup.NewIncrementHandler(slog.NewJSONHandler(os.Stdout, nil), nil))
logger.Info("this is the dedup incrementer handler",
    slog.String("duplicated", "zero"),
    slog.String("duplicated", "one"),
    slog.String("duplicated", "two"),
)

Outputs:

{
    "time": "2024-03-21T09:33:25Z",
    "level": "INFO",
    "msg": "this is the dedup incrementer handler",
    "duplicated": "zero",
    "duplicated#01": "one",
    "duplicated#02": "two"
}
Append Duplicates Together in an Array Handler
logger := slog.New(slogdedup.NewAppendHandler(slog.NewJSONHandler(os.Stdout, nil), nil))
logger.Info("this is the dedup appender handler",
    slog.String("duplicated", "zero"),
    slog.String("duplicated", "one"),
    slog.String("duplicated", "two"),
)

Outputs:

{
    "time": "2024-03-21T09:33:25Z",
    "level": "INFO",
    "msg": "this is the dedup appender handler",
    "duplicated": [
      "zero",
      "one",
      "two"
    ]
}
Using ResolveKey and ReplaceAttr
Stackdriver (GCP Cloud Operations / Google Log Explorer)
logger := slog.New(slogdedup.NewOverwriteHandler(
	slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		AddSource:   true,
		ReplaceAttr: slogdedup.ReplaceAttrStackdriver(nil), // Needed for builtin's
	}),
	&slogdedup.OverwriteHandlerOptions{ResolveKey: slogdedup.ResolveKeyStackdriver(nil)}, // Needed for everything else, and deduplication
))
logger.Warn("this is the main message", slog.String("duplicated", "zero"), slog.String("duplicated", "one"))

Outputs:

{
    "time": "2024-03-21T09:59:19.652284-06:00",
    "severity": "WARNING",
    "logging.googleapis.com/sourceLocation": {
        "function": "main.main",
        "file": "/go/src/github.com/veqryn/slog-dedup/cmd/replacers/cmd.go",
        "line": "19"
    },
    "message": "this is the main message",
    "duplicated": "one"
}

Full Example Main File

Basic Use of Each Handler
package main

import (
	"log/slog"
	"os"

	slogdedup "github.com/veqryn/slog-dedup"
)

func main() {
	// OverwriteHandler
	overwriter := slogdedup.NewOverwriteHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
	slog.SetDefault(slog.New(overwriter))

	/*
	  {
	    "time": "2024-03-21T09:33:25Z",
	    "level": "INFO",
	    "msg": "this is the dedup overwrite handler",
	    "duplicated": "two"
	  }
	*/
	slog.Info("this is the dedup overwrite handler",
		slog.String("duplicated", "zero"),
		slog.String("duplicated", "one"),
		slog.String("duplicated", "two"),
	)

	// IgnoreHandler
	ignorer := slogdedup.NewIgnoreHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
	slog.SetDefault(slog.New(ignorer))

	/*
	  {
	    "time": "2024-03-21T09:33:25Z",
	    "level": "INFO",
	    "msg": "this is the dedup ignore handler",
	    "duplicated": "zero"
	  }
	*/
	slog.Info("this is the dedup ignore handler",
		slog.String("duplicated", "zero"),
		slog.String("duplicated", "one"),
		slog.String("duplicated", "two"),
	)

	// IncrementHandler
	incrementer := slogdedup.NewIncrementHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
	slog.SetDefault(slog.New(incrementer))

	/*
	  {
	    "time": "2024-03-21T09:33:25Z",
	    "level": "INFO",
	    "msg": "this is the dedup incrementer handler",
	    "duplicated": "zero",
	    "duplicated#01": "one",
	    "duplicated#02": "two"
	  }
	*/
	slog.Info("this is the dedup incrementer handler",
		slog.String("duplicated", "zero"),
		slog.String("duplicated", "one"),
		slog.String("duplicated", "two"),
	)

	// AppendHandler
	appender := slogdedup.NewAppendHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
	slog.SetDefault(slog.New(appender))

	/*
	  {
	    "time": "2024-03-21T09:33:25Z",
	    "level": "INFO",
	    "msg": "this is the dedup appender handler",
	    "duplicated": [
	      "zero",
	      "one",
	      "two"
	    ]
	  }
	*/
	slog.Info("this is the dedup appender handler",
		slog.String("duplicated", "zero"),
		slog.String("duplicated", "one"),
		slog.String("duplicated", "two"),
	)
}
Using ResolveKey and ReplaceAttr
package main

import (
	"log/slog"
	"os"

	slogdedup "github.com/veqryn/slog-dedup"
)

func main() {
	// Example sending logs of Stackdriver (GCP Cloud Operations / Google Log Explorer)
	// which then pipes the data to Graylog.

	// First, create a function to resolve/replace attribute keys before deduplication,
	// which will also ensure the builtin keys are unused by non-builtin attributes:
	resolveKey := slogdedup.JoinResolveKey(
		slogdedup.ResolveKeyStackdriver(nil),
		slogdedup.ResolveKeyGraylog(nil),
	)

	// Second, create a function to replace the builtin record attributes
	// (time, level, msg, source) with the appropriate keys and values for
	// Stackdriver and Graylog:
	replaceAttr := slogdedup.JoinReplaceAttr(
		slogdedup.ReplaceAttrStackdriver(nil),
		slogdedup.ReplaceAttrGraylog(nil),
	)

	// Next create the final handler (the sink), which is a json handler,
	// using the replaceAttr function we just made:
	jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		AddSource:   true,
		Level:       slog.LevelDebug,
		ReplaceAttr: replaceAttr,
	})

	// Now create any one of the dedup middleware handlers, using the
	// resolveKey function we just made, then set it as the default logger:
	dedupHandler := slogdedup.NewOverwriteHandler(
		jsonHandler,
		&slogdedup.OverwriteHandlerOptions{
			KeyCompare: slogdedup.CaseSensitiveCmp, // this is the default
			ResolveKey: resolveKey,                 // use the key resolver for stackdriver/graylog
		},
	)
	slog.SetDefault(slog.New(dedupHandler))

	/*
		{
			"time": "2024-03-21T09:53:43Z",
			"severity": "WARNING",
			"logging.googleapis.com/sourceLocation": {
				"function": "main.main",
				"file": "/go/src/github.com/veqryn/slog-dedup/cmd/replacers/cmd.go",
				"line": "56"
			},
			"message": "this is the main message",
			"duplicated": "one"
		}
	*/
	slog.Warn("this is the main message", slog.String("duplicated", "zero"), slog.String("duplicated", "one"))
}

Breaking Changes

O.4.x -> 0.5.0

Resolvers and Replacers (such as ResolveKeyStackdriver and ReplaceAttrStackdriver) now take an argument of *ResolveReplaceOptions, which can be nil for the default behavior.

O.3.x -> 0.4.0

ResolveBuiltinKeyConflict,DoesBuiltinKeyConflict, and IncrementKeyName have all been unified into a single function: ResolveKey.

O.1.x -> 0.2.0

Package renamed from dedup to slogdedup. To fix, change this:

import "github.com/veqryn/slog-dedup"
var overwriter = dedup.NewOverwriteHandler(slog.NewJSONHandler(os.Stdout, nil), nil)

To this:

import "github.com/veqryn/slog-dedup"
var overwriter = slogdedup.NewOverwriteHandler(slog.NewJSONHandler(os.Stdout, nil), nil)

Named imports are unaffected.

Other Details

slog-multi Middleware

This library has convenience methods 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.

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

Using an overwrite handler allows a slightly different style of logging that is less verbose. As an application moves deeper into domain functions, it is common that additional details or knowledge is uncovered. By overwriting keys with better and more explanatory values as you go, the final log lines are often easier to read and more informative.

WithAttrs, WithGroup, and slog.Group()

These handlers will correctly deal with sub-loggers, whether using WithAttrs() or WithGroup(). It will even handle groups injected as attributes using slog.Group(). Due to the lack of a slog.Slice type/kind, the AppendHandler has a special case where groups that are inside of slices/arrays are turned into a map[string]any{} slog attribute before being passed to the final handler.

The Built-In Fields (time, level, msg, source)

Because this handler is a middleware, it must pass a slog.Record to the final handler. The built-in attributes for time, level, msg, and source are treated separately, and have their own fields on the slog.Record struct. It would therefore be impossible to deduplicate these, if we didn't handle these as a special case. The increment handler considers that these four keys are always taken at the root level, and any attributes using those keys will start with the #01 increment on their key name. The other handlers can be customized using their options struct to either increment the name (default), drop old attributes using those keys (overwrite with the final slog.Record builtins), or allow the duplicates for the builtin keys. You can also customize this behavior by passing your own functions to the options struct (same for log handlers that use different keys for the built-in fields).

Documentation

Overview

Package slogdedup provides structured logging (slog) deduplication for use with json logging (or any other format where duplicates are not appreciated). The main impetus behind this package is because most JSON tools do not like duplicate keys for their member properties/fields. Some of them will give errors or fail to parse the log line, and some may even crash. Unfortunately the default behavior of the stdlib slog handlers is to allow duplicate keys.

Additionally, this library includes convenience methods for formatting output to match what is expected for various log aggregation tools (such as Graylog), as well as cloud providers (such as Stackdriver / Google Cloud Operations / GCP Log Explorer).

Usage:

// OverwriteHandler
overwriter := slogdedup.NewOverwriteHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
slog.SetDefault(slog.New(overwriter))

// {
//   "time": "2024-03-21T09:33:25Z",
//   "level": "INFO",
//   "msg": "this is the dedup overwrite handler",
//   "duplicated": "two"
// }
slog.Info("this is the dedup overwrite handler",
	slog.String("duplicated", "zero"),
	slog.String("duplicated", "one"),
	slog.String("duplicated", "two"),
)

// IgnoreHandler
ignorer := slogdedup.NewIgnoreHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
slog.SetDefault(slog.New(ignorer))

// {
//   "time": "2024-03-21T09:33:25Z",
//   "level": "INFO",
//   "msg": "this is the dedup ignore handler",
//   "duplicated": "zero"
// }
slog.Info("this is the dedup ignore handler",
	slog.String("duplicated", "zero"),
	slog.String("duplicated", "one"),
	slog.String("duplicated", "two"),
)

// IncrementHandler
incrementer := slogdedup.NewIncrementHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
slog.SetDefault(slog.New(incrementer))

// {
//   "time": "2024-03-21T09:33:25Z",
//   "level": "INFO",
//   "msg": "this is the dedup incrementer handler",
//   "duplicated": "zero",
//   "duplicated#01": "one",
//   "duplicated#02": "two"
// }
slog.Info("this is the dedup incrementer handler",
	slog.String("duplicated", "zero"),
	slog.String("duplicated", "one"),
	slog.String("duplicated", "two"),
)

// AppendHandler
appender := slogdedup.NewAppendHandler(slog.NewJSONHandler(os.Stdout, nil), nil)
slog.SetDefault(slog.New(appender))

// {
//   "time": "2024-03-21T09:33:25Z",
//   "level": "INFO",
//   "msg": "this is the dedup appender handler",
//   "duplicated": [
//     "zero",
//     "one",
//     "two"
//   ]
// }
slog.Info("this is the dedup appender handler",
	slog.String("duplicated", "zero"),
	slog.String("duplicated", "one"),
	slog.String("duplicated", "two"),
)

logger := slog.New(slogdedup.NewOverwriteHandler(
	slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
		AddSource:   true,
		ReplaceAttr: slogdedup.ReplaceAttrStackdriver(nil), // Needed for builtin's
	}),
	&slogdedup.OverwriteHandlerOptions{ResolveKey: slogdedup.ResolveKeyStackdriver(nil)}, // Needed for everything else, and deduplication
))

// {
//   "time": "2024-03-21T09:59:19.652284-06:00",
//   "severity": "WARNING",
//   "logging.googleapis.com/sourceLocation": {
//     "function": "main.main",
//     "file": "/go/src/github.com/veqryn/slog-dedup/cmd/replacers/cmd.go",
//     "line": "19"
//   },
//   "message": "this is the main message",
//   "duplicated": "one"
// }
logger.Warn("this is the main message", slog.String("duplicated", "zero"), slog.String("duplicated", "one"))

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CaseInsensitiveCmp

func CaseInsensitiveCmp(a, b string) int

CaseInsensitiveCmp is a case-insensitive comparison and ordering function that orders by byte values

func CaseSensitiveCmp

func CaseSensitiveCmp(a, b string) int

CaseSensitiveCmp is a case-sensitive comparison and ordering function that orders by byte values

func DropIfBuiltinKeyConflict

func DropIfBuiltinKeyConflict(groups []string, key string, index int) (string, bool)

DropIfBuiltinKeyConflict is a ResolveKey function that will, if there is a conflict/duplication at the root level (not in a group) with one of the built-in keys, drop the whole attribute

func IncrementIfBuiltinKeyConflict

func IncrementIfBuiltinKeyConflict(groups []string, key string, index int) (string, bool)

IncrementIfBuiltinKeyConflict is a ResolveKey function that will, if there is a conflict/duplication at the root level (not in a group) with one of the built-in keys, add "#01" to the end of the key.

func JoinReplaceAttr added in v0.4.0

func JoinReplaceAttr(replaceAttrFunctions ...func(groups []string, a slog.Attr) slog.Attr) func(groups []string, a slog.Attr) slog.Attr

JoinReplaceAttr can be used to join together many slog.HandlerOptions.ReplaceAttr into a single one that applies all rules in order.

func JoinResolveKey added in v0.4.0

func JoinResolveKey(resolveKeyFunctions ...func(groups []string, key string, index int) (string, bool)) func(groups []string, key string, index int) (string, bool)

JoinResolveKey can be used to join together many slogdedup middlewares xHandlerOptions.ResolveKey functions into a single one that applies all the rules in order.

func KeepIfBuiltinKeyConflict

func KeepIfBuiltinKeyConflict(_ []string, key string, index int) (string, bool)

KeepIfBuiltinKeyConflict is a ResolveKey function that will keep all keys even if there would be a conflict/duplication at the root level (not in a group) with one of the built-in keys

func NewAppendMiddleware added in v0.3.0

func NewAppendMiddleware(options *AppendHandlerOptions) func(slog.Handler) slog.Handler

NewAppendMiddleware creates an AppendHandler 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.NewAppendMiddleware(&slogdedup.AppendHandlerOptions{})).
	Handler(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})),
))

func NewIgnoreMiddleware added in v0.3.0

func NewIgnoreMiddleware(options *IgnoreHandlerOptions) func(slog.Handler) slog.Handler

NewIgnoreMiddleware creates an IgnoreHandler 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.NewIgnoreMiddleware(&slogdedup.IgnoreHandlerOptions{})).
	Handler(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})),
))

func NewIncrementMiddleware added in v0.3.0

func NewIncrementMiddleware(options *IncrementHandlerOptions) func(slog.Handler) slog.Handler

NewIncrementMiddleware creates an IncrementHandler 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.NewIncrementMiddleware(&slogdedup.IncrementHandlerOptions{})).
	Handler(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})),
))

func NewOverwriteMiddleware added in v0.3.0

func NewOverwriteMiddleware(options *OverwriteHandlerOptions) func(slog.Handler) slog.Handler

NewOverwriteMiddleware creates an OverwriteHandler 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{})).
	Handler(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})),
))

func ReplaceAttrGraylog added in v0.4.0

func ReplaceAttrGraylog(options *ResolveReplaceOptions) func(groups []string, a slog.Attr) slog.Attr

ReplaceAttrGraylog returns a ReplaceAttr function works for Graylog. If OverwriteSummary is true, the slog.Record "msg" key will be changed to "message", causing it to show up as the main log line when skimming.

func ReplaceAttrStackdriver added in v0.4.0

func ReplaceAttrStackdriver(options *ResolveReplaceOptions) func(groups []string, a slog.Attr) slog.Attr

ReplaceAttrStackdriver returns a ReplaceAttr function works for Stackdriver (aka Google Cloud Operations, aka GCP Log Explorer). If OverwriteSummary is true, the slog.Record "msg" key will be changed to "message", causing it to show up as the main log line when skimming.

func ResolveKeyGraylog added in v0.4.0

func ResolveKeyGraylog(options *ResolveReplaceOptions) func(groups []string, key string, index int) (string, bool)

ResolveKeyGraylog returns a ResolveKey function works for Graylog. If OverwriteSummary is true, the slog.Record "msg" key will be changed to "message", causing it to show up as the main log line when skimming.

func ResolveKeyStackdriver added in v0.4.0

func ResolveKeyStackdriver(options *ResolveReplaceOptions) func(groups []string, key string, index int) (string, bool)

ResolveKeyStackdriver returns a ResolveKey function works for Stackdriver (aka Google Cloud Operations, aka GCP Log Explorer). If OverwriteSummary is true, the slog.Record "msg" key will be changed to "message", causing it to show up as the main log line when skimming.

Types

type AppendHandler

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

AppendHandler is a slog.Handler middleware that will deduplicate all attributes and groups by creating a slice/array whenever there is more than one attribute with the same key. It passes the final record and attributes off to the next handler when finished.

func NewAppendHandler

func NewAppendHandler(next slog.Handler, opts *AppendHandlerOptions) *AppendHandler

NewAppendHandler creates a AppendHandler slog.Handler middleware that will deduplicate all attributes and groups by creating a slice/array whenever there is more than one attribute with the same key. It passes the final record and attributes off to the next handler when finished. If opts is nil, the default options are used.

func (*AppendHandler) Enabled

func (h *AppendHandler) 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 (*AppendHandler) Handle

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

Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.

func (*AppendHandler) WithAttrs

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

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

func (*AppendHandler) WithGroup

func (h *AppendHandler) 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 AppendHandlerOptions

type AppendHandlerOptions struct {
	// Comparison function to determine if two keys are equal
	KeyCompare func(a, b string) int

	// Function that will be called on each attribute and group, to determine
	// the key to use. Returns the new key value to use, and true to keep the
	// attribute or false to drop it. Can be used to drop, keep, or rename any
	// attributes matching the builtin attributes.
	//
	// The first argument is a list of currently open groups that contain the
	// Attr. It must not be retained or modified.
	//
	// ResolveKey will not be called for the built-in fields on slog.Record
	// (ie: time, level, msg, and source).
	ResolveKey func(groups []string, key string, _ int) (string, bool)
}

AppendHandlerOptions are options for a AppendHandler

type IgnoreHandler

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

IgnoreHandler is a slog.Handler middleware that will deduplicate all attributes and groups by ignoring any newer attributes or groups with the same string key as an older attribute. It passes the final record and attributes off to the next handler when finished.

func NewIgnoreHandler

func NewIgnoreHandler(next slog.Handler, opts *IgnoreHandlerOptions) *IgnoreHandler

NewIgnoreHandler creates a IgnoreHandler slog.Handler middleware that will deduplicate all attributes and groups by ignoring any newer attributes or groups with the same string key as an older attribute. It passes the final record and attributes off to the next handler when finished. If opts is nil, the default options are used.

func (*IgnoreHandler) Enabled

func (h *IgnoreHandler) 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 (*IgnoreHandler) Handle

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

Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.

func (*IgnoreHandler) WithAttrs

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

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

func (*IgnoreHandler) WithGroup

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

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

type IgnoreHandlerOptions

type IgnoreHandlerOptions struct {
	// Comparison function to determine if two keys are equal
	KeyCompare func(a, b string) int

	// Function that will be called on each attribute and group, to determine
	// the key to use. Returns the new key value to use, and true to keep the
	// attribute or false to drop it. Can be used to drop, keep, or rename any
	// attributes matching the builtin attributes.
	//
	// The first argument is a list of currently open groups that contain the
	// Attr. It must not be retained or modified.
	//
	// ResolveKey will not be called for the built-in fields on slog.Record
	// (ie: time, level, msg, and source).
	ResolveKey func(groups []string, key string, _ int) (string, bool)
}

IgnoreHandlerOptions are options for a IgnoreHandler

type IncrementHandler

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

IncrementHandler is a slog.Handler middleware that will deduplicate all attributes and groups by incrementing/modifying their key names. It passes the final record and attributes off to the next handler when finished.

func NewIncrementHandler

func NewIncrementHandler(next slog.Handler, opts *IncrementHandlerOptions) *IncrementHandler

NewIncrementHandler creates a IncrementHandler slog.Handler middleware that will deduplicate all attributes and groups by incrementing/modifying their key names. It passes the final record and attributes off to the next handler when finished. If opts is nil, the default options are used.

func (*IncrementHandler) Enabled

func (h *IncrementHandler) 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 (*IncrementHandler) Handle

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

Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.

func (*IncrementHandler) WithAttrs

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

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

func (*IncrementHandler) WithGroup

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

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

type IncrementHandlerOptions

type IncrementHandlerOptions struct {
	// Comparison function to determine if two keys are equal
	KeyCompare func(a, b string) int

	// Function that will be called on each attribute and group, to determine
	// the key to use. Returns the new key value to use, and true to keep the
	// attribute or false to drop it. Can be used to drop, keep, or rename any
	// attributes matching the builtin attributes.
	//
	// For the IncrementHandler, it should return a modified key string based on
	// the index (first = 0, second = 1, third = 2, etc).
	// If the key is at the root level (groups is empty) and conflicts with a
	// builtin key on the slog.Record object (time, level, msg, source), the
	// index should be incremented before calculating the modified key string.
	//
	// The first argument is a list of currently open groups that contain the
	// Attr. It must not be retained or modified.
	//
	// ResolveKey will not be called for the built-in fields on slog.Record
	// (ie: time, level, msg, and source).
	ResolveKey func(groups []string, key string, index int) (string, bool)
}

IncrementHandlerOptions are options for a IncrementHandler

type OverwriteHandler

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

OverwriteHandler is a slog.Handler middleware that will deduplicate all attributes and groups by overwriting any older attributes or groups with the same string key. It passes the final record and attributes off to the next handler when finished.

func NewOverwriteHandler

func NewOverwriteHandler(next slog.Handler, opts *OverwriteHandlerOptions) *OverwriteHandler

NewOverwriteHandler creates an OverwriteHandler slog.Handler middleware that will deduplicate all attributes and groups by overwriting any older attributes or groups with the same string key. It passes the final record and attributes off to the next handler when finished. If opts is nil, the default options are used.

func (*OverwriteHandler) Enabled

func (h *OverwriteHandler) 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 (*OverwriteHandler) Handle

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

Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.

func (*OverwriteHandler) WithAttrs

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

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

func (*OverwriteHandler) WithGroup

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

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

type OverwriteHandlerOptions

type OverwriteHandlerOptions struct {
	// Comparison function to determine if two keys are equal
	KeyCompare func(a, b string) int

	// Function that will be called on each attribute and group, to determine
	// the key to use. Returns the new key value to use, and true to keep the
	// attribute or false to drop it. Can be used to drop, keep, or rename any
	// attributes matching the builtin attributes.
	//
	// The first argument is a list of currently open groups that contain the
	// Attr. It must not be retained or modified.
	//
	// ResolveKey will not be called for the built-in fields on slog.Record
	// (ie: time, level, msg, and source).
	ResolveKey func(groups []string, key string, _ int) (string, bool)
}

OverwriteHandlerOptions are options for a OverwriteHandler

type ResolveReplaceOptions added in v0.5.0

type ResolveReplaceOptions struct {
	// OverwriteSummary, if true and applicable to the log sink, will ensure the
	// builtin slog.Record "msg" key will be changed to the appropriate
	// "message" or "summary" key for that sink (usually causing the msg to show
	// up as the log line summary when skimming.
	OverwriteSummary bool
}

ResolveReplaceOptions is a struct of optional options that change the behavior of the ResolveKey and ReplaceAttr functions.

Jump to

Keyboard shortcuts

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