Skip to content

Commit 7a9bd83

Browse files
committed
handler: add styled output option
1 parent 4a8712c commit 7a9bd83

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

handler.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,22 @@ import (
66
"io"
77
"log/slog"
88
"strconv"
9+
"strings"
910
"sync"
1011
"sync/atomic"
1112
"unicode"
1213
"unicode/utf8"
1314
)
1415

16+
const (
17+
resetSeq = "0"
18+
boldSeq = "1"
19+
faintSeq = "2"
20+
21+
esc = '\x1b'
22+
csi = string(esc) + "["
23+
)
24+
1525
// HandlerOption is the signature of a functional option that can be used to
1626
// modify the behaviour of the DefaultHandler.
1727
type HandlerOption func(*handlerOpts)
@@ -39,6 +49,15 @@ func WithCallerFlags(flags uint32) HandlerOption {
3949
}
4050
}
4151

52+
// WithStyledOutput can be used to add additional styling to the logs. This
53+
// currently includes colored & bold tags and faint fonts for attribute keys and
54+
// callsites.
55+
func WithStyledOutput() HandlerOption {
56+
return func(opts *handlerOpts) {
57+
opts.styled = true
58+
}
59+
}
60+
4261
// WithNoTimestamp is an option that can be used to omit timestamps from the log
4362
// lines.
4463
func WithNoTimestamp() HandlerOption {
@@ -213,6 +232,23 @@ func (d *DefaultHandler) with(tag string, withCallstackOffset bool,
213232
return &sl
214233
}
215234

235+
func (d *DefaultHandler) styleString(s string, styles ...string) string {
236+
if !d.opts.styled {
237+
return s
238+
}
239+
240+
if len(styles) == 0 {
241+
return s
242+
}
243+
244+
seq := strings.Join(styles, ";")
245+
if seq == "" {
246+
return s
247+
}
248+
249+
return fmt.Sprintf("%s%sm%s%sm", csi, seq, s, csi+resetSeq)
250+
}
251+
216252
func (d *DefaultHandler) appendAttr(buf *buffer, a slog.Attr) {
217253
// Resolve the Attr's value before doing anything else.
218254
a.Value = a.Value.Resolve()
@@ -229,15 +265,19 @@ func (d *DefaultHandler) appendAttr(buf *buffer, a slog.Attr) {
229265
func (d *DefaultHandler) writeLevel(buf *buffer, level Level) {
230266
str := fmt.Sprintf("[%s] ", level)
231267

232-
buf.writeString(str)
268+
buf.writeString(d.styleString(
269+
str, boldSeq, string(level.ansiColoSeq())),
270+
)
233271
}
234272

235273
func (d *DefaultHandler) writeCallSite(buf *buffer, file string, line int) {
236274
if file == "" {
237275
return
238276
}
239277

240-
buf.writeString(fmt.Sprintf(" %s:%d", file, line))
278+
buf.writeString(
279+
d.styleString(fmt.Sprintf(" %s:%d", file, line), faintSeq),
280+
)
241281
}
242282

243283
func appendString(buf *buffer, str string) {
@@ -255,7 +295,7 @@ func (d *DefaultHandler) appendKey(buf *buffer, key string) {
255295
}
256296
key += "="
257297

258-
buf.writeString(key)
298+
buf.writeString(d.styleString(key, faintSeq))
259299
}
260300

261301
func appendValue(buf *buffer, v slog.Value) {

level.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,31 @@ func (l Level) String() string {
8080
return str("CRT", l-LevelCritical)
8181
}
8282
}
83+
84+
type ansiColorSeq string
85+
86+
const (
87+
ansiColorSeqDarkTeal ansiColorSeq = "38;5;30"
88+
ansiColorSeqDarkBlue ansiColorSeq = "38;5;63"
89+
ansiColorSeqLightBlue ansiColorSeq = "38;5;86"
90+
ansiColorSeqYellow ansiColorSeq = "38;5;192"
91+
ansiColorSeqRed ansiColorSeq = "38;5;204"
92+
ansiColorSeqPink ansiColorSeq = "38;5;134"
93+
)
94+
95+
func (l Level) ansiColoSeq() ansiColorSeq {
96+
switch l {
97+
case LevelTrace:
98+
return ansiColorSeqDarkTeal
99+
case LevelDebug:
100+
return ansiColorSeqDarkBlue
101+
case LevelWarn:
102+
return ansiColorSeqYellow
103+
case LevelError:
104+
return ansiColorSeqRed
105+
case LevelCritical:
106+
return ansiColorSeqPink
107+
default:
108+
return ansiColorSeqLightBlue
109+
}
110+
}

0 commit comments

Comments
 (0)