Skip to content

Commit f261e6d

Browse files
authored
logflags: replace logrus with log/slog (#3918)
We weren't using most features of logrus and doing this allows us to drop one external dependency. The package log/slog was added in Go 1.21 which is already the minimum version we can build with.
1 parent 7ea6d8f commit f261e6d

34 files changed

+198
-3229
lines changed

cmd/dlv/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"os"
55

66
"github.com/go-delve/delve/cmd/dlv/cmds"
7+
"github.com/go-delve/delve/pkg/logflags"
78
"github.com/go-delve/delve/pkg/version"
8-
"github.com/sirupsen/logrus"
99
"golang.org/x/telemetry"
1010
)
1111

@@ -25,7 +25,7 @@ func main() {
2525
if os.Getenv(cgoCflagsEnv) == "" {
2626
os.Setenv(cgoCflagsEnv, "-O0 -g")
2727
} else {
28-
logrus.WithFields(logrus.Fields{"layer": "dlv"}).Warnln("CGO_CFLAGS already set, Cgo code could be optimized.")
28+
logflags.WriteCgoFlagsWarning()
2929
}
3030

3131
cmds.New(false).Execute()

go.mod

-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ require (
1212
github.com/hashicorp/golang-lru v1.0.2
1313
github.com/mattn/go-colorable v0.1.13
1414
github.com/mattn/go-isatty v0.0.20
15-
github.com/sirupsen/logrus v1.9.3
1615
github.com/spf13/cobra v1.9.1
1716
github.com/spf13/pflag v1.0.6
1817
go.starlark.net v0.0.0-20231101134539-556fd59b42f6
@@ -29,7 +28,6 @@ require (
2928
github.com/mattn/go-runewidth v0.0.13 // indirect
3029
github.com/rivo/uniseg v0.2.0 // indirect
3130
github.com/russross/blackfriday/v2 v2.1.0 // indirect
32-
github.com/stretchr/testify v1.8.4 // indirect
3331
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect
3432
golang.org/x/mod v0.20.0 // indirect
3533
golang.org/x/sync v0.8.0 // indirect

go.sum

-13
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo
66
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
77
github.com/creack/pty v1.1.20 h1:VIPb/a2s17qNeQgDnkfZC35RScx+blkKF8GV68n80J4=
88
github.com/creack/pty v1.1.20/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
9-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10-
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
11-
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
129
github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d h1:hUWoLdw5kvo2xCsqlsIBMvWUc1QCSsCYD2J2+Fg6YoU=
1310
github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d/go.mod h1:C7Es+DLenIpPc9J6IYw4jrK0h7S9bKj4DNl8+KxGEXU=
1411
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
@@ -35,24 +32,16 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
3532
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
3633
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
3734
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
38-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
39-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4035
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
4136
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
4237
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
4338
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
4439
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
4540
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
46-
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
47-
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
4841
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
4942
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
5043
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
5144
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
52-
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
53-
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
54-
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
55-
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
5645
go.starlark.net v0.0.0-20231101134539-556fd59b42f6 h1:+eC0F/k4aBLC4szgOcjd7bDTEnpxADJyWJE0yowgM3E=
5746
go.starlark.net v0.0.0-20231101134539-556fd59b42f6/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM=
5847
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
@@ -64,7 +53,6 @@ golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
6453
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
6554
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
6655
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
67-
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
6856
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
6957
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7058
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
@@ -77,6 +65,5 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
7765
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
7866
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
7967
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
80-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
8168
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
8269
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

pkg/logflags/logflags.go

+94-53
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ package logflags
22

33
import (
44
"bytes"
5+
"context"
56
"errors"
67
"fmt"
78
"io"
89
"log"
10+
"log/slog"
911
"net"
1012
"os"
1113
"sort"
1214
"strconv"
1315
"strings"
1416
"time"
15-
16-
"github.com/sirupsen/logrus"
1717
)
1818

1919
var any = false
@@ -29,20 +29,25 @@ var stack = false
2929

3030
var logOut io.WriteCloser
3131

32-
func makeLogger(flag bool, fields Fields) Logger {
32+
func makeLogger(flag bool, attrs ...interface{}) Logger {
3333
if lf := loggerFactory; lf != nil {
34+
fields := make(Fields)
35+
for i := 0; i < len(attrs); i += 2 {
36+
fields[attrs[i].(string)] = attrs[i+1]
37+
}
3438
return lf(flag, fields, logOut)
3539
}
36-
logger := logrus.New().WithFields(logrus.Fields(fields))
37-
logger.Logger.Formatter = DefaultFormatter()
40+
41+
var out io.WriteCloser = os.Stderr
3842
if logOut != nil {
39-
logger.Logger.Out = logOut
43+
out = logOut
4044
}
41-
logger.Logger.Level = logrus.ErrorLevel
45+
level := slog.LevelError
4246
if flag {
43-
logger.Logger.Level = logrus.DebugLevel
47+
level = slog.LevelDebug
4448
}
45-
return &logrusLogger{logger}
49+
logger := slog.New(newTextHandler(out, &slog.HandlerOptions{Level: level})).With(attrs...)
50+
return slogLogger{logger}
4651
}
4752

4853
// Any returns true if any logging is enabled.
@@ -58,7 +63,7 @@ func GdbWire() bool {
5863

5964
// GdbWireLogger returns a configured logger for the gdbserial wire protocol.
6065
func GdbWireLogger() Logger {
61-
return makeLogger(gdbWire, Fields{"layer": "gdbconn"})
66+
return makeLogger(gdbWire, "layer", "gdbconn")
6267
}
6368

6469
// Debugger returns true if the debugger package should log.
@@ -68,7 +73,7 @@ func Debugger() bool {
6873

6974
// DebuggerLogger returns a logger for the debugger package.
7075
func DebuggerLogger() Logger {
71-
return makeLogger(debugger, Fields{"layer": "debugger"})
76+
return makeLogger(debugger, "layer", "debugger")
7277
}
7378

7479
// LLDBServerOutput returns true if the output of the LLDB server should be
@@ -85,7 +90,7 @@ func DebugLineErrors() bool {
8590

8691
// DebugLineLogger returns a logger for the dwarf/line package.
8792
func DebugLineLogger() Logger {
88-
return makeLogger(debugLineErrors, Fields{"layer": "dwarf-line"})
93+
return makeLogger(debugLineErrors, "layer", "dwarf-line")
8994
}
9095

9196
// RPC returns true if RPC messages should be logged.
@@ -100,7 +105,7 @@ func RPCLogger() Logger {
100105

101106
// rpcLogger returns a logger for RPC messages set to a specific minimal log level.
102107
func rpcLogger(flag bool) Logger {
103-
return makeLogger(flag, Fields{"layer": "rpc"})
108+
return makeLogger(flag, "layer", "rpc")
104109
}
105110

106111
// DAP returns true if dap package should log.
@@ -110,7 +115,7 @@ func DAP() bool {
110115

111116
// DAPLogger returns a logger for dap package.
112117
func DAPLogger() Logger {
113-
return makeLogger(dap, Fields{"layer": "dap"})
118+
return makeLogger(dap, "layer", "dap")
114119
}
115120

116121
// FnCall returns true if the function call protocol should be logged.
@@ -119,7 +124,7 @@ func FnCall() bool {
119124
}
120125

121126
func FnCallLogger() Logger {
122-
return makeLogger(fnCall, Fields{"layer": "proc", "kind": "fncall"})
127+
return makeLogger(fnCall, "layer", "proc", "kind", "fncall")
123128
}
124129

125130
// Minidump returns true if the minidump loader should be logged.
@@ -128,7 +133,7 @@ func Minidump() bool {
128133
}
129134

130135
func MinidumpLogger() Logger {
131-
return makeLogger(minidump, Fields{"layer": "core", "kind": "minidump"})
136+
return makeLogger(minidump, "layer", "core", "kind", "minidump")
132137
}
133138

134139
// Stack returns true if the stacktracer should be logged.
@@ -137,7 +142,7 @@ func Stack() bool {
137142
}
138143

139144
func StackLogger() Logger {
140-
return makeLogger(stack, Fields{"layer": "core", "kind": "stack"})
145+
return makeLogger(stack, "layer", "core", "kind", "stack")
141146
}
142147

143148
// WriteDAPListeningMessage writes the "DAP server listening" message in dap mode.
@@ -162,7 +167,7 @@ func writeListeningMessage(server string, addr net.Addr) {
162167
return
163168
}
164169
logger := rpcLogger(true)
165-
logger.Warn("Listening for remote connections (connections are not authenticated nor encrypted)")
170+
logger.Warnf("Listening for remote connections (connections are not authenticated nor encrypted)")
166171
}
167172

168173
func WriteError(msg string) {
@@ -173,6 +178,10 @@ func WriteError(msg string) {
173178
}
174179
}
175180

181+
func WriteCgoFlagsWarning() {
182+
makeLogger(true, "layer", "dlv").Warn("CGO_CFLAGS already set, Cgo code could be optimized.")
183+
}
184+
176185
var errLogstrWithoutLog = errors.New("--log-output specified without --log")
177186

178187
// Setup sets debugger flags based on the contents of logstr.
@@ -240,59 +249,91 @@ func Close() {
240249
}
241250
}
242251

243-
// DefaultFormatter provides a simplified version of logrus.TextFormatter that
244-
// doesn't make logs unreadable when they are output to a text file or to a
245-
// terminal that doesn't support colors.
246-
func DefaultFormatter() logrus.Formatter {
247-
return textFormatterInstance
252+
type textHandler struct {
253+
out io.WriteCloser
254+
opts slog.HandlerOptions
255+
attrs []slog.Attr
256+
preformattedAttrs string // preformatted version of attrs for optimization purposes
248257
}
249258

250-
type textFormatter struct{}
259+
func newTextHandler(out io.WriteCloser, opts *slog.HandlerOptions) *textHandler {
260+
return &textHandler{
261+
out: out,
262+
opts: *opts,
263+
}
264+
}
251265

252-
var textFormatterInstance = &textFormatter{}
266+
func (h *textHandler) Enabled(_ context.Context, l slog.Level) bool {
267+
return l >= h.opts.Level.Level()
268+
}
253269

254-
func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
255-
var b *bytes.Buffer
256-
if entry.Buffer != nil {
257-
b = entry.Buffer
258-
} else {
259-
b = &bytes.Buffer{}
270+
func (h *textHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
271+
h2 := *h
272+
h2.attrs = append(h2.attrs, attrs...)
273+
m := map[string]slog.Value{}
274+
keys := []string{}
275+
for i := range attrs {
276+
m[attrs[i].Key] = attrs[i].Value
277+
keys = append(keys, attrs[i].Key)
278+
}
279+
sort.Strings(keys)
280+
b := new(bytes.Buffer)
281+
for _, key := range keys {
282+
appendAttr(b, key, m[key])
260283
}
284+
b.Truncate(b.Len() - 1)
285+
h2.preformattedAttrs = b.String()
286+
return &h2
287+
}
261288

262-
keys := make([]string, 0, len(entry.Data))
263-
for k := range entry.Data {
264-
keys = append(keys, k)
289+
func appendAttr(b *bytes.Buffer, key string, val slog.Value) {
290+
b.WriteString(key)
291+
b.WriteByte('=')
292+
stringVal := val.String()
293+
if needsQuoting(stringVal) {
294+
fmt.Fprintf(b, "%q", stringVal)
295+
} else {
296+
b.WriteString(stringVal)
265297
}
266-
sort.Strings(keys)
298+
b.WriteByte(',')
299+
}
300+
301+
func (h *textHandler) WithGroup(group string) slog.Handler {
302+
// group not handled
303+
return h
304+
}
305+
306+
func (h *textHandler) Handle(_ context.Context, entry slog.Record) error {
307+
b := &bytes.Buffer{}
267308

268309
b.WriteString(entry.Time.Format(time.RFC3339))
269310
b.WriteByte(' ')
270-
b.WriteString(entry.Level.String())
311+
b.WriteString(strings.ToLower(entry.Level.String()))
271312
b.WriteByte(' ')
272-
for i, key := range keys {
273-
b.WriteString(key)
274-
b.WriteByte('=')
275-
stringVal, ok := entry.Data[key].(string)
276-
if !ok {
277-
stringVal = fmt.Sprint(entry.Data[key])
278-
}
279-
if f.needsQuoting(stringVal) {
280-
fmt.Fprintf(b, "%q", stringVal)
281-
} else {
282-
b.WriteString(stringVal)
283-
}
284-
if i != len(keys)-1 {
285-
b.WriteByte(',')
286-
} else {
313+
b.WriteString(h.preformattedAttrs)
314+
315+
if entry.NumAttrs() > 0 {
316+
if len(h.preformattedAttrs) > 0 {
287317
b.WriteByte(' ')
288318
}
319+
entry.Attrs(func(attr slog.Attr) bool {
320+
appendAttr(b, attr.Key, attr.Value)
321+
return true
322+
})
323+
b.Truncate(b.Len() - 1)
289324
}
325+
326+
if len(h.preformattedAttrs) > 0 || entry.NumAttrs() > 0 {
327+
b.WriteByte(' ')
328+
}
329+
290330
b.WriteString(entry.Message)
291331
b.WriteByte('\n')
292-
return b.Bytes(), nil
332+
_, err := h.out.Write(b.Bytes())
333+
return err
293334
}
294335

295-
func (f *textFormatter) needsQuoting(text string) bool {
336+
func needsQuoting(text string) bool {
296337
for _, ch := range text {
297338
if !((ch >= 'a' && ch <= 'z') ||
298339
(ch >= 'A' && ch <= 'Z') ||

0 commit comments

Comments
 (0)