Skip to content

Commit dce65a3

Browse files
author
Russ Egan
committed
Updated docs and tests
1 parent 8ad5204 commit dce65a3

File tree

4 files changed

+133
-13
lines changed

4 files changed

+133
-13
lines changed

v2/config_from_env_test.go

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ package flume
22

33
import (
44
"bytes"
5+
"context"
56
"io"
67
"log/slog"
8+
"path"
9+
"runtime"
10+
"strings"
711
"testing"
12+
"time"
813

14+
"github.com/ansel1/console-slog"
915
"github.com/stretchr/testify/assert"
1016
"github.com/stretchr/testify/require"
1117
)
@@ -257,23 +263,60 @@ func TestRegisterHandlerFn(t *testing.T) {
257263
}.Run(t)
258264
})
259265
}
266+
}
260267

268+
func TestBuiltInHandlers(t *testing.T) {
269+
theme := console.NewDefaultTheme()
261270
builtIns := map[string]string{
262-
TermHandler: "blue INF | hi\n",
263-
TermColorHandler: "\x1b[1;90mblue \x1b[0m \x1b[32mINF\x1b[0m \x1b[1;90m|\x1b[0m \x1b[1mhi\x1b[0m\n",
264-
TextHandler: "level=INFO msg=hi logger=blue\n",
265-
JSONHandler: `{"level":"INFO","msg":"hi","logger":"blue"}` + "\n",
266-
ConsoleHandler: "level=INFO msg=hi logger=blue\n",
267-
NoopHandler: "",
271+
TermHandler: "blue |INF| hi\n",
272+
TermColorHandler: styled("blue ", theme.Header) + " " +
273+
styled("|", theme.Header) + styled("INF", theme.LevelInfo) + styled("|", theme.Header) + " " +
274+
styled("hi", theme.Message) + "\n",
275+
TextHandler: "level=INFO msg=hi logger=blue\n",
276+
JSONHandler: `{"level":"INFO","msg":"hi","logger":"blue"}` + "\n",
277+
ConsoleHandler: "level=INFO msg=hi logger=blue\n",
278+
NoopHandler: "",
268279
}
269280
for name, want := range builtIns {
281+
handlerFn := LookupHandlerFn(name)
282+
270283
t.Run("builtin "+name, func(t *testing.T) {
271284
handlerTest{
272285
want: want,
273286
handlerFn: func(buf *bytes.Buffer) slog.Handler {
274-
return LookupHandlerFn(name)("", buf, nil).WithAttrs([]slog.Attr{slog.String(LoggerKey, "blue")})
287+
return handlerFn("", buf, nil).WithAttrs([]slog.Attr{slog.String(LoggerKey, "blue")})
275288
},
276289
}.Run(t)
277290
})
291+
292+
if name == NoopHandler {
293+
// noop handler doesn't honor any of the other options
294+
continue
295+
}
296+
297+
t.Run("builtin "+name+" with options", func(t *testing.T) {
298+
pc, file, _, _ := runtime.Caller(0)
299+
file = path.Base(file)
300+
301+
buf := bytes.NewBuffer(nil)
302+
h := handlerFn("", buf, &slog.HandlerOptions{Level: slog.LevelWarn, AddSource: true, ReplaceAttr: replaceKey(LoggerKey, slog.String(LoggerKey, "cerulean"))})
303+
h = h.WithAttrs([]slog.Attr{slog.String(LoggerKey, "blue")})
304+
305+
assert.True(t, h.Enabled(context.Background(), slog.LevelWarn))
306+
assert.False(t, h.Enabled(context.Background(), slog.LevelInfo))
307+
308+
err := h.Handle(context.Background(), slog.NewRecord(time.Now(), slog.LevelWarn, "hi", pc))
309+
require.NoError(t, err)
310+
311+
assert.Contains(t, buf.String(), "cerulean", "ReplaceAttr function should have replaced logger key")
312+
assert.Contains(t, buf.String(), file, "AddSource should have been true")
313+
})
314+
}
315+
}
316+
317+
func styled(s string, c console.ANSIMod) string {
318+
if c == "" {
319+
return s
278320
}
321+
return strings.Join([]string{string(c), s, string(console.ResetMod)}, "")
279322
}

v2/example_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ func ExampleTermHandler() {
4848
l.With(LoggerKey, "server").Info("Request received", "method", "GET", "url", "/users/123")
4949

5050
// Output:
51-
// 20:41:28.515 main INF | Hello, World!
52-
// 20:41:28.515 main WRN | High temp temp=30
53-
// 20:41:28.515 main ERR | Failed to read file error=file not found
54-
// 20:41:28.515 server INF | Request received method=GET url=/users/123
51+
// 20:41:28.515 main |INF| Hello, World!
52+
// 20:41:28.515 main |WRN| High temp temp=30
53+
// 20:41:28.515 main |ERR| Failed to read file error=file not found
54+
// 20:41:28.515 server |INF| Request received method=GET url=/users/123
5555
}

v2/handler_options_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ func TestLevels_UnmarshalText(t *testing.T) {
209209
}
210210

211211
func TestHandlerOptions_UnmarshalJSON(t *testing.T) {
212+
theme := console.NewDefaultTheme()
212213
tests := []struct {
213214
name string
214215
confJSON string
@@ -234,8 +235,9 @@ func TestHandlerOptions_UnmarshalJSON(t *testing.T) {
234235
{
235236
name: "dev defaults",
236237
confJSON: `{"development":true}`,
237-
expected: DevDefaults(),
238-
want: " \x1b[32mINF\x1b[0m \x1b[1;90m|\x1b[0m \x1b[1mhi\x1b[0m\n",
238+
expected: *DevDefaults(),
239+
want: styled("|", theme.Header) + styled("INF", theme.LevelInfo) + styled("|", theme.Header) + " " +
240+
styled("hi", theme.Message) + "\n",
239241
},
240242
{
241243
name: "int level",

v2/middleware.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,27 @@ import (
5656
// return err, ok
5757
// }
5858

59+
// AbbreviateLevel is a ReplaceAttr function that abbreviates log level names.
60+
//
61+
// It modifies the attribute if it's a log level (slog.Level) and changes the level name to its abbreviation.
62+
// The abbreviations are:
63+
//
64+
// - "DEBUG" becomes "DBG"
65+
// - "INFO" becomes "INF"
66+
// - "WARN" becomes "WRN"
67+
// - "ERROR" becomes "ERR"
68+
//
69+
// If the attribute's value is not a slog.Level, it is returned unchanged.
70+
//
71+
// Example:
72+
//
73+
// // Create a logger with the ReplaceAttr function.
74+
// logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
75+
// ReplaceAttr: AbbreviateLevel,
76+
// }))
77+
//
78+
// // Log a message.
79+
// logger.Debug("This is a debug message.") // Output will be: level=DBG msg="This is a debug message."
5980
func AbbreviateLevel(_ []string, attr slog.Attr) slog.Attr {
6081
if attr.Value.Kind() != slog.KindAny {
6182
return attr
@@ -79,6 +100,46 @@ func AbbreviateLevel(_ []string, attr slog.Attr) slog.Attr {
79100
return attr
80101
}
81102

103+
// FormatTimes returns a ReplaceAttr function that formats time values according to the specified format.
104+
//
105+
// It modifies an attribute if its value is a time.Time. The time is formatted using the provided format string,
106+
// and the attribute's value is updated to the formatted time string.
107+
//
108+
// If the attribute's value is not a time.Time, it is returned unchanged.
109+
//
110+
// Args:
111+
//
112+
// format string: The format string to use for formatting time values (e.g., time.DateTime, "2006-01-02", "15:04:05").
113+
//
114+
// Returns:
115+
//
116+
// func([]string, slog.Attr) slog.Attr: A ReplaceAttr function that formats time values.
117+
//
118+
// Example:
119+
//
120+
// // Create a ReplaceAttr function that formats times using a custom format.
121+
// customTimeFormat := FormatTimes("2006-01-02 15:04:05")
122+
//
123+
// // Create a logger with the ReplaceAttr function.
124+
// logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
125+
// ReplaceAttr: customTimeFormat,
126+
// }))
127+
//
128+
// // Log a message with a time attribute.
129+
// logger.Info("Time example", slog.Time("now", time.Now()))
130+
// // Output might be: level=INFO msg="Time example" now="2023-10-27 10:30:00"
131+
//
132+
// // Create a ReplaceAttr function that formats times using a predefined format.
133+
// dateTimeFormat := FormatTimes(time.DateTime)
134+
//
135+
// // Create a logger with the ReplaceAttr function.
136+
// logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
137+
// ReplaceAttr: dateTimeFormat,
138+
// }))
139+
//
140+
// // Log a message with a time attribute.
141+
// logger.Info("Time example", slog.Time("now", time.Now()))
142+
// // Output might be: level=INFO msg="Time example" now="2023-10-27 10:30:00.000"
82143
func FormatTimes(format string) func([]string, slog.Attr) slog.Attr {
83144
return func(_ []string, a slog.Attr) slog.Attr {
84145
if a.Value.Kind() == slog.KindTime {
@@ -88,6 +149,20 @@ func FormatTimes(format string) func([]string, slog.Attr) slog.Attr {
88149
}
89150
}
90151

152+
// SimpleTime returns a ReplaceAttr function that formats time values to a simple time format: "15:04:05.000".
153+
//
154+
// It's a convenience function that uses FormatTimes internally with a predefined format.
155+
//
156+
// Example:
157+
//
158+
// // Create a logger with the ReplaceAttr function.
159+
// logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
160+
// ReplaceAttr: SimpleTime(),
161+
// }))
162+
//
163+
// // Log a message with a time attribute.
164+
// logger.Info("Time example", slog.Time("now", time.Now()))
165+
// // Output might be: level=INFO msg="Time example" now="10:30:00.000"
91166
func SimpleTime() func([]string, slog.Attr) slog.Attr {
92167
return FormatTimes("15:04:05.000")
93168
}

0 commit comments

Comments
 (0)