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 ¶
- func CaseInsensitiveCmp(a, b string) int
- func CaseSensitiveCmp(a, b string) int
- func DropIfBuiltinKeyConflict(groups []string, key string, index int) (string, bool)
- func IncrementIfBuiltinKeyConflict(groups []string, key string, index int) (string, bool)
- func JoinReplaceAttr(replaceAttrFunctions ...func(groups []string, a slog.Attr) slog.Attr) func(groups []string, a slog.Attr) slog.Attr
- func JoinResolveKey(...) func(groups []string, key string, index int) (string, bool)
- func KeepIfBuiltinKeyConflict(_ []string, key string, index int) (string, bool)
- func NewAppendMiddleware(options *AppendHandlerOptions) func(slog.Handler) slog.Handler
- func NewIgnoreMiddleware(options *IgnoreHandlerOptions) func(slog.Handler) slog.Handler
- func NewIncrementMiddleware(options *IncrementHandlerOptions) func(slog.Handler) slog.Handler
- func NewOverwriteMiddleware(options *OverwriteHandlerOptions) func(slog.Handler) slog.Handler
- func ReplaceAttrGraylog(options *ResolveReplaceOptions) func(groups []string, a slog.Attr) slog.Attr
- func ReplaceAttrStackdriver(options *ResolveReplaceOptions) func(groups []string, a slog.Attr) slog.Attr
- func ResolveKeyGraylog(options *ResolveReplaceOptions) func(groups []string, key string, index int) (string, bool)
- func ResolveKeyStackdriver(options *ResolveReplaceOptions) func(groups []string, key string, index int) (string, bool)
- type AppendHandler
- type AppendHandlerOptions
- type IgnoreHandler
- type IgnoreHandlerOptions
- type IncrementHandler
- type IncrementHandlerOptions
- type OverwriteHandler
- type OverwriteHandlerOptions
- type ResolveReplaceOptions
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CaseInsensitiveCmp ¶
CaseInsensitiveCmp is a case-insensitive comparison and ordering function that orders by byte values
func CaseSensitiveCmp ¶
CaseSensitiveCmp is a case-sensitive comparison and ordering function that orders by byte values
func DropIfBuiltinKeyConflict ¶
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 ¶
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 ¶
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 ¶
Enabled reports whether the next handler handles records at the given level. The handler ignores records whose level is lower.
func (*AppendHandler) Handle ¶
Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.
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 ¶
Enabled reports whether the next handler handles records at the given level. The handler ignores records whose level is lower.
func (*IgnoreHandler) Handle ¶
Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.
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 ¶
Enabled reports whether the next handler handles records at the given level. The handler ignores records whose level is lower.
func (*IncrementHandler) Handle ¶
Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.
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 ¶
Enabled reports whether the next handler handles records at the given level. The handler ignores records whose level is lower.
func (*OverwriteHandler) Handle ¶
Handle de-duplicates all attributes and groups, then passes the new set of attributes to the next handler.
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.