Switch to slog, remove logrus (#1672)

This commit is contained in:
Nate Brown
2026-04-27 09:41:47 -05:00
committed by GitHub
parent 5f890dbc34
commit d0f02ba873
77 changed files with 2299 additions and 1338 deletions

View File

@@ -1,10 +1,10 @@
package util
import (
"context"
"errors"
"fmt"
"github.com/sirupsen/logrus"
"log/slog"
)
type ContextualError struct {
@@ -28,12 +28,12 @@ func ContextualizeIfNeeded(msg string, err error) error {
}
// LogWithContextIfNeeded is a helper function to log an error line for an error or ContextualError
func LogWithContextIfNeeded(msg string, err error, l *logrus.Logger) {
func LogWithContextIfNeeded(msg string, err error, l *slog.Logger) {
switch v := err.(type) {
case *ContextualError:
v.Log(l)
default:
l.WithError(err).Error(msg)
l.Error(msg, "error", err)
}
}
@@ -51,10 +51,19 @@ func (ce *ContextualError) Unwrap() error {
return ce.RealError
}
func (ce *ContextualError) Log(lr *logrus.Logger) {
if ce.RealError != nil {
lr.WithFields(ce.Fields).WithError(ce.RealError).Error(ce.Context)
} else {
lr.WithFields(ce.Fields).Error(ce.Context)
// Log emits ce as a single error-level log line with Fields and RealError
// promoted to top-level attributes, producing a flat shape callers can grep
// or parse without walking into a nested object.
func (ce *ContextualError) Log(l *slog.Logger) {
attrs := make([]slog.Attr, 0, len(ce.Fields)+1)
for k, v := range ce.Fields {
attrs = append(attrs, slog.Any(k, v))
}
if ce.RealError != nil {
attrs = append(attrs, slog.Any("error", ce.RealError))
}
// LogAttrs is intentional: attrs is built from a map[string]any so it has
// no pair-form equivalent.
//nolint:sloglint
l.LogAttrs(context.Background(), slog.LevelError, ce.Context, attrs...)
}

View File

@@ -1,95 +1,67 @@
package util
import (
"bytes"
"errors"
"fmt"
"testing"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula/test"
"github.com/stretchr/testify/assert"
)
type m = map[string]any
type TestLogWriter struct {
Logs []string
}
func NewTestLogWriter() *TestLogWriter {
return &TestLogWriter{Logs: make([]string, 0)}
}
func (tl *TestLogWriter) Write(p []byte) (n int, err error) {
tl.Logs = append(tl.Logs, string(p))
return len(p), nil
}
func (tl *TestLogWriter) Reset() {
tl.Logs = tl.Logs[:0]
}
func TestContextualError_Log(t *testing.T) {
l := logrus.New()
l.Formatter = &logrus.TextFormatter{
DisableTimestamp: true,
DisableColors: true,
}
tl := NewTestLogWriter()
l.Out = tl
buf := &bytes.Buffer{}
l := test.NewLoggerWithOutput(buf)
// Test a full context line
tl.Reset()
buf.Reset()
e := NewContextualError("test message", m{"field": "1"}, errors.New("error"))
e.Log(l)
assert.Equal(t, []string{"level=error msg=\"test message\" error=error field=1\n"}, tl.Logs)
assert.Equal(t, "level=ERROR msg=\"test message\" field=1 error=error\n", buf.String())
// Test a line with an error and msg but no fields
tl.Reset()
buf.Reset()
e = NewContextualError("test message", nil, errors.New("error"))
e.Log(l)
assert.Equal(t, []string{"level=error msg=\"test message\" error=error\n"}, tl.Logs)
assert.Equal(t, "level=ERROR msg=\"test message\" error=error\n", buf.String())
// Test just a context and fields
tl.Reset()
buf.Reset()
e = NewContextualError("test message", m{"field": "1"}, nil)
e.Log(l)
assert.Equal(t, []string{"level=error msg=\"test message\" field=1\n"}, tl.Logs)
assert.Equal(t, "level=ERROR msg=\"test message\" field=1\n", buf.String())
// Test just a context
tl.Reset()
buf.Reset()
e = NewContextualError("test message", nil, nil)
e.Log(l)
assert.Equal(t, []string{"level=error msg=\"test message\"\n"}, tl.Logs)
assert.Equal(t, "level=ERROR msg=\"test message\"\n", buf.String())
// Test just an error
tl.Reset()
buf.Reset()
e = NewContextualError("", nil, errors.New("error"))
e.Log(l)
assert.Equal(t, []string{"level=error error=error\n"}, tl.Logs)
assert.Equal(t, "level=ERROR msg=\"\" error=error\n", buf.String())
}
func TestLogWithContextIfNeeded(t *testing.T) {
l := logrus.New()
l.Formatter = &logrus.TextFormatter{
DisableTimestamp: true,
DisableColors: true,
}
tl := NewTestLogWriter()
l.Out = tl
buf := &bytes.Buffer{}
l := test.NewLoggerWithOutput(buf)
// Test ignoring fallback context
tl.Reset()
buf.Reset()
e := NewContextualError("test message", m{"field": "1"}, errors.New("error"))
LogWithContextIfNeeded("This should get thrown away", e, l)
assert.Equal(t, []string{"level=error msg=\"test message\" error=error field=1\n"}, tl.Logs)
assert.Equal(t, "level=ERROR msg=\"test message\" field=1 error=error\n", buf.String())
// Test using fallback context
tl.Reset()
buf.Reset()
err := fmt.Errorf("this is a normal error")
LogWithContextIfNeeded("Fallback context woo", err, l)
assert.Equal(t, []string{"level=error msg=\"Fallback context woo\" error=\"this is a normal error\"\n"}, tl.Logs)
assert.Equal(t, "level=ERROR msg=\"Fallback context woo\" error=\"this is a normal error\"\n", buf.String())
}
func TestContextualizeIfNeeded(t *testing.T) {