Skip to content

Commit 4e45b38

Browse files
committed
handler: add styled output option
1 parent 13b1a59 commit 4e45b38

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,13 +6,23 @@ import (
66
"io"
77
"log/slog"
88
"strconv"
9+
"strings"
910
"sync"
1011
"sync/atomic"
1112
"time"
1213
"unicode"
1314
"unicode/utf8"
1415
)
1516

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

62+
// WithStyledOutput can be used to add additional styling to the logs. This
63+
// currently includes colored & bold tags and faint fonts for attribute keys and
64+
// callsites.
65+
func WithStyledOutput() HandlerOption {
66+
return func(opts *handlerOpts) {
67+
opts.styled = true
68+
}
69+
}
70+
5271
// WithNoTimestamp is an option that can be used to omit timestamps from the log
5372
// lines.
5473
func WithNoTimestamp() HandlerOption {
@@ -228,6 +247,23 @@ func (d *DefaultHandler) with(tag string, withCallstackOffset bool,
228247
return &sl
229248
}
230249

250+
func (d *DefaultHandler) styleString(s string, styles ...string) string {
251+
if !d.opts.styled {
252+
return s
253+
}
254+
255+
if len(styles) == 0 {
256+
return s
257+
}
258+
259+
seq := strings.Join(styles, ";")
260+
if seq == "" {
261+
return s
262+
}
263+
264+
return fmt.Sprintf("%s%sm%s%sm", csi, seq, s, csi+resetSeq)
265+
}
266+
231267
func (d *DefaultHandler) appendAttr(buf *buffer, a slog.Attr) {
232268
// Resolve the Attr's value before doing anything else.
233269
a.Value = a.Value.Resolve()
@@ -244,15 +280,19 @@ func (d *DefaultHandler) appendAttr(buf *buffer, a slog.Attr) {
244280
func (d *DefaultHandler) writeLevel(buf *buffer, level Level) {
245281
str := fmt.Sprintf("[%s]", level)
246282

247-
buf.writeString(str)
283+
buf.writeString(d.styleString(
284+
str, boldSeq, string(level.ansiColoSeq())),
285+
)
248286
}
249287

250288
func (d *DefaultHandler) writeCallSite(buf *buffer, file string, line int) {
251289
if file == "" {
252290
return
253291
}
254292

255-
buf.writeString(fmt.Sprintf(" %s:%d", file, line))
293+
buf.writeString(
294+
d.styleString(fmt.Sprintf(" %s:%d", file, line), faintSeq),
295+
)
256296
}
257297

258298
func appendString(buf *buffer, str string) {
@@ -270,7 +310,7 @@ func (d *DefaultHandler) appendKey(buf *buffer, key string) {
270310
}
271311
key += "="
272312

273-
buf.writeString(key)
313+
buf.writeString(d.styleString(key, faintSeq))
274314
}
275315

276316
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)