mirror of
https://github.com/slackhq/nebula.git
synced 2026-05-15 20:37:36 +02:00
Switch to slog, remove logrus (#1672)
This commit is contained in:
@@ -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...)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user