Skip to content

Commit

Permalink
Add slog-multi middleware methods
Browse files Browse the repository at this point in the history
  • Loading branch information
veqryn committed Nov 26, 2023
1 parent effa1cf commit b834b2b
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 7 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,17 @@ var overwriter = slogdedup.NewOverwriteHandler(slog.NewJSONHandler(os.Stdout, ni
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](https://github.com/samber/slog-multi),
in order to easily setup slog workflows such as pipelines, fanout, routing, failover, etc.
```go
slog.SetDefault(slog.New(slogmulti.
Pipe(slogcontext.NewMiddleware(&slogcontext.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.

Expand Down
26 changes: 22 additions & 4 deletions append_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ type AppendHandler struct {

var _ slog.Handler = &AppendHandler{} // Assert conformance with interface

// 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 NewAppendMiddleware(options *AppendHandlerOptions) func(slog.Handler) slog.Handler {
return func(next slog.Handler) slog.Handler {
return NewAppendHandler(
next,
options,
)
}
}

// 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.
Expand Down Expand Up @@ -110,7 +128,7 @@ func (h *AppendHandler) createAttrTree(uniq *b.Tree[string, any], goas []*groupO

// If a group is encountered, create a subtree for that group and all groupOrAttrs after it
if goas[0].group != "" {
if key, ok := h.getKey(goas[0].group, depth); ok {
if key, keep := h.getKey(goas[0].group, depth); keep {
uniqGroup := b.TreeNew[string, any](h.keyCompare)
h.createAttrTree(uniqGroup, goas[1:], depth+1)
// Ignore empty groups, otherwise put subtree into the map
Expand Down Expand Up @@ -142,16 +160,16 @@ func (h *AppendHandler) createAttrTree(uniq *b.Tree[string, any], goas []*groupO
// Since attributes are ordered from oldest to newest, it creates a slice whenever it detects the key already exists,
// appending the new attribute, then overwriting the key with that slice.
func (h *AppendHandler) resolveValues(uniq *b.Tree[string, any], attrs []slog.Attr, depth int) {
var ok bool
var keep bool
for _, a := range attrs {
a.Value = a.Value.Resolve()
if a.Equal(slog.Attr{}) {
continue // Ignore empty attributes, and keep iterating
}

// Default situation: resolve the key and put it into the map
a.Key, ok = h.getKey(a.Key, depth)
if !ok {
a.Key, keep = h.getKey(a.Key, depth)
if !keep {
continue
}

Expand Down
4 changes: 2 additions & 2 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ type appended []any
// buildAttrs converts the deduplicated map back into an attribute array,
// with any subtrees converted into slog.Group's
func buildAttrs(uniq *b.Tree[string, any]) []slog.Attr {
en, err := uniq.SeekFirst()
if err != nil {
en, emptyErr := uniq.SeekFirst()
if emptyErr != nil {
return nil // Empty (btree only returns an error when empty)
}
defer en.Close()
Expand Down
18 changes: 18 additions & 0 deletions ignore_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ type IgnoreHandler struct {

var _ slog.Handler = &IgnoreHandler{} // Assert conformance with interface

// 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 NewIgnoreMiddleware(options *IgnoreHandlerOptions) func(slog.Handler) slog.Handler {
return func(next slog.Handler) slog.Handler {
return NewIgnoreHandler(
next,
options,
)
}
}

// 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.
Expand Down
18 changes: 18 additions & 0 deletions increment_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ type IncrementHandler struct {

var _ slog.Handler = &IncrementHandler{} // Assert conformance with interface

// 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 NewIncrementMiddleware(options *IncrementHandlerOptions) func(slog.Handler) slog.Handler {
return func(next slog.Handler) slog.Handler {
return NewIncrementHandler(
next,
options,
)
}
}

// 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.
Expand Down
20 changes: 19 additions & 1 deletion overwrite_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,25 @@ type OverwriteHandler struct {

var _ slog.Handler = &OverwriteHandler{} // Assert conformance with interface

// NewOverwriteHandler creates a OverwriteHandler slog.Handler middleware that will deduplicate all attributes and
// 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 NewOverwriteMiddleware(options *OverwriteHandlerOptions) func(slog.Handler) slog.Handler {
return func(next slog.Handler) slog.Handler {
return NewOverwriteHandler(
next,
options,
)
}
}

// 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.
Expand Down

0 comments on commit b834b2b

Please sign in to comment.