Skip to content

Commit 5ec57db

Browse files
authored
feat: adding some router predicates (#37)
* feat: adding some router predicates * Update router.go
1 parent 2722063 commit 5ec57db

File tree

3 files changed

+168
-25
lines changed

3 files changed

+168
-25
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func main() {
191191
Add(slackChannelUS, recordMatchRegion("us")).
192192
Add(slackChannelEU, recordMatchRegion("eu")).
193193
Add(slackChannelAPAC, recordMatchRegion("apac")).
194-
Add(consoleHandler, slogmulti.Level(slog.LevelInfo)).
194+
Add(consoleHandler, slogmulti.LevelIs(slog.LevelInfo, slog.LevelDebug)).
195195
Handler(),
196196
)
197197

router.go

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type router struct {
1919
// Example usage:
2020
//
2121
// r := slogmulti.Router().
22-
// Add(consoleHandler, slogmulti.Level(slog.LevelInfo)).
23-
// Add(fileHandler, slogmulti.Level(slog.LevelError)).
22+
// Add(consoleHandler, slogmulti.LevelIs(slog.LevelInfo)).
23+
// Add(fileHandler, slogmulti.LevelIs(slog.LevelError)).
2424
// Handler()
2525
//
2626
// Returns:
@@ -32,26 +32,26 @@ func Router() *router {
3232
}
3333
}
3434

35-
// Add registers a new handler with optional matchers to the router.
36-
// The handler will only process records if all provided matchers return true.
35+
// Add registers a new handler with optional predicates to the router.
36+
// The handler will only process records if all provided predicates return true.
3737
//
3838
// Args:
3939
//
4040
// handler: The slog.Handler to register
41-
// matchers: Optional functions that determine if a record should be routed to this handler
41+
// predicates: Optional functions that determine if a record should be routed to this handler
4242
//
4343
// Returns:
4444
//
4545
// The router instance for method chaining
46-
func (h *router) Add(handler slog.Handler, matchers ...func(ctx context.Context, r slog.Record) bool) *router {
46+
func (h *router) Add(handler slog.Handler, predicates ...func(ctx context.Context, r slog.Record) bool) *router {
4747
return &router{
4848
handlers: append(
4949
h.handlers,
5050
&RoutableHandler{
51-
matchers: matchers,
52-
handler: handler,
53-
groups: []string{},
54-
attrs: []slog.Attr{},
51+
predicates: predicates,
52+
handler: handler,
53+
groups: []string{},
54+
attrs: []slog.Attr{},
5555
},
5656
),
5757
}
@@ -72,13 +72,13 @@ func (h *router) Handler() slog.Handler {
7272
var _ slog.Handler = (*RoutableHandler)(nil)
7373

7474
// RoutableHandler wraps a slog.Handler with conditional matching logic.
75-
// It only forwards records to the underlying handler if all matchers return true.
75+
// It only forwards records to the underlying handler if all predicates return true.
7676
// This enables sophisticated routing scenarios like level-based or attribute-based routing.
7777
//
7878
// @TODO: implement round robin strategy for load balancing across multiple handlers
7979
type RoutableHandler struct {
80-
// matchers contains functions that determine if a record should be processed
81-
matchers []func(ctx context.Context, r slog.Record) bool
80+
// predicates contains functions that determine if a record should be processed
81+
predicates []func(ctx context.Context, r slog.Record) bool
8282
// handler is the underlying slog.Handler that processes matching records
8383
handler slog.Handler
8484
// groups tracks the current group hierarchy for proper attribute handling
@@ -102,7 +102,7 @@ func (h *RoutableHandler) Enabled(ctx context.Context, l slog.Level) bool {
102102
return h.handler.Enabled(ctx, l)
103103
}
104104

105-
// Handle processes a log record if all matchers return true.
105+
// Handle processes a log record if all predicates return true.
106106
// This method implements the slog.Handler interface requirement.
107107
//
108108
// Args:
@@ -119,8 +119,8 @@ func (h *RoutableHandler) Handle(ctx context.Context, r slog.Record) error {
119119
slogcommon.AppendRecordAttrsToAttrs(h.attrs, h.groups, &r)...,
120120
)
121121

122-
for _, matcher := range h.matchers {
123-
if !matcher(ctx, clone) {
122+
for _, predicate := range h.predicates {
123+
if !predicate(ctx, clone) {
124124
return nil
125125
}
126126
}
@@ -143,10 +143,10 @@ func (h *RoutableHandler) Handle(ctx context.Context, r slog.Record) error {
143143
// A new RoutableHandler with the additional attributes
144144
func (h *RoutableHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
145145
return &RoutableHandler{
146-
matchers: h.matchers,
147-
handler: h.handler.WithAttrs(attrs),
148-
groups: slices.Clone(h.groups),
149-
attrs: slogcommon.AppendAttrsToGroup(h.groups, h.attrs, attrs...),
146+
predicates: h.predicates,
147+
handler: h.handler.WithAttrs(attrs),
148+
groups: slices.Clone(h.groups),
149+
attrs: slogcommon.AppendAttrsToGroup(h.groups, h.attrs, attrs...),
150150
}
151151
}
152152

@@ -171,9 +171,9 @@ func (h *RoutableHandler) WithGroup(name string) slog.Handler {
171171
}
172172

173173
return &RoutableHandler{
174-
matchers: h.matchers,
175-
handler: h.handler.WithGroup(name),
176-
groups: append(slices.Clone(h.groups), name),
177-
attrs: h.attrs,
174+
predicates: h.predicates,
175+
handler: h.handler.WithGroup(name),
176+
groups: append(slices.Clone(h.groups), name),
177+
attrs: h.attrs,
178178
}
179179
}

router_predicate.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package slogmulti
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
"strings"
7+
)
8+
9+
// LevelIs returns a function that checks if the record level is in the given levels.
10+
// Example usage:
11+
//
12+
// r := slogmulti.Router().
13+
// Add(consoleHandler, slogmulti.LevelIs(slog.LevelInfo)).
14+
// Add(fileHandler, slogmulti.LevelIs(slog.LevelError)).
15+
// Handler()
16+
//
17+
// Args:
18+
//
19+
// levels: The levels to match
20+
//
21+
// Returns:
22+
//
23+
// A function that checks if the record level is in the given levels
24+
func LevelIs(levels ...slog.Level) func(ctx context.Context, r slog.Record) bool {
25+
return func(ctx context.Context, r slog.Record) bool {
26+
for _, level := range levels {
27+
if r.Level == level {
28+
return true
29+
}
30+
}
31+
return false
32+
}
33+
}
34+
35+
// LevelIsNot returns a function that checks if the record level is not in the given levels.
36+
// Example usage:
37+
//
38+
// r := slogmulti.Router().
39+
// Add(consoleHandler, slogmulti.LevelIsNot(slog.LevelInfo)).
40+
// Add(fileHandler, slogmulti.LevelIsNot(slog.LevelError)).
41+
// Handler()
42+
//
43+
// Args:
44+
//
45+
// levels: The levels to check
46+
//
47+
// Returns:
48+
//
49+
// A function that checks if the record level is not in the given levels
50+
func LevelIsNot(levels ...slog.Level) func(ctx context.Context, r slog.Record) bool {
51+
return func(ctx context.Context, r slog.Record) bool {
52+
for _, level := range levels {
53+
if r.Level == level {
54+
return false
55+
}
56+
}
57+
return true
58+
}
59+
}
60+
61+
// MessageIs returns a function that checks if the record message is equal to the given message.
62+
// Example usage:
63+
//
64+
// r := slogmulti.Router().
65+
// Add(consoleHandler, slogmulti.MessageIs("database error")).
66+
// Add(fileHandler, slogmulti.MessageIs("database error")).
67+
// Handler()
68+
//
69+
// Args:
70+
//
71+
// msg: The message to check
72+
//
73+
// Returns:
74+
//
75+
// A function that checks if the record message is equal to the given message
76+
func MessageIs(msg string) func(ctx context.Context, r slog.Record) bool {
77+
return func(ctx context.Context, r slog.Record) bool {
78+
return r.Message == msg
79+
}
80+
}
81+
82+
// MessageIsNot returns a function that checks if the record message is not equal to the given message.
83+
// Example usage:
84+
//
85+
// r := slogmulti.Router().
86+
// Add(consoleHandler, slogmulti.MessageIsNot("database error")).
87+
// Add(fileHandler, slogmulti.MessageIsNot("database error")).
88+
// Handler()
89+
//
90+
// Args:
91+
//
92+
// msg: The message to check
93+
//
94+
// Returns:
95+
//
96+
// A function that checks if the record message is not equal to the given message
97+
func MessageIsNot(msg string) func(ctx context.Context, r slog.Record) bool {
98+
return func(ctx context.Context, r slog.Record) bool {
99+
return r.Message != msg
100+
}
101+
}
102+
103+
// MessageContains returns a function that checks if the record message contains the given part.
104+
// Example usage:
105+
//
106+
// r := slogmulti.Router().
107+
// Add(consoleHandler, slogmulti.MessageContains("database error")).
108+
// Add(fileHandler, slogmulti.MessageContains("database error")).
109+
// Handler()
110+
//
111+
// Args:
112+
//
113+
// part: The part to check
114+
//
115+
// Returns:
116+
//
117+
// A function that checks if the record message contains the given part
118+
func MessageContains(part string) func(ctx context.Context, r slog.Record) bool {
119+
return func(ctx context.Context, r slog.Record) bool {
120+
return strings.Contains(r.Message, part)
121+
}
122+
}
123+
124+
// MessageNotContains returns a function that checks if the record message does not contain the given part.
125+
// Example usage:
126+
//
127+
// r := slogmulti.Router().
128+
// Add(consoleHandler, slogmulti.MessageNotContains("database error")).
129+
// Add(fileHandler, slogmulti.MessageNotContains("database error")).
130+
// Handler()
131+
//
132+
// Args:
133+
//
134+
// part: The part to check
135+
//
136+
// Returns:
137+
//
138+
// A function that checks if the record message does not contain the given part
139+
func MessageNotContains(part string) func(ctx context.Context, r slog.Record) bool {
140+
return func(ctx context.Context, r slog.Record) bool {
141+
return !strings.Contains(r.Message, part)
142+
}
143+
}

0 commit comments

Comments
 (0)