Skip to content

Commit

Permalink
Remove logrus dependency
Browse files Browse the repository at this point in the history
This is the _last_ step required to migrate from logrus to slog.
All components in the repository have been migrated to use slog
allowing the logrus formatter to be deleted.

The slog handler tests that validate the output have been updated
to assert the format directly instead of comparing it to the output
from the logrus formatter.
  • Loading branch information
rosstimothy committed Jan 9, 2025
1 parent 37c43cb commit b1498d5
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 939 deletions.
8 changes: 0 additions & 8 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,6 @@ linters-settings:
desc: 'use "crypto" or "x/crypto" instead'
# Prevent importing any additional logging libraries.
logging:
files:
# Integrations are still allowed to use logrus becuase they haven't
# been converted to slog yet. Once they use slog, remove this exception.
- '!**/integrations/**'
# The log package still contains the logrus formatter consumed by the integrations.
# Remove this exception when said formatter is deleted.
- '!**/lib/utils/log/**'
- '!**/lib/utils/cli.go'
deny:
- pkg: github.com/sirupsen/logrus
desc: 'use "log/slog" instead'
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ require (
github.com/sigstore/cosign/v2 v2.4.1
github.com/sigstore/sigstore v1.8.11
github.com/sijms/go-ora/v2 v2.8.22
github.com/sirupsen/logrus v1.9.3
github.com/snowflakedb/gosnowflake v1.12.1
github.com/spf13/cobra v1.8.1
github.com/spiffe/go-spiffe/v2 v2.4.0
Expand Down Expand Up @@ -501,6 +500,7 @@ require (
github.com/sigstore/protobuf-specs v0.3.2 // indirect
github.com/sigstore/rekor v1.3.6 // indirect
github.com/sigstore/timestamp-authority v1.2.2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
Expand Down
2 changes: 1 addition & 1 deletion integrations/terraform/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1
github.com/jonboulle/clockwork v0.4.0
github.com/sirupsen/logrus v1.9.3
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stretchr/testify v1.10.0
google.golang.org/grpc v1.69.2
google.golang.org/protobuf v1.36.2
Expand Down
2 changes: 1 addition & 1 deletion lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2853,7 +2853,7 @@ type execResult struct {

// sharedWriter is an [io.Writer] implementation that protects
// writes with a mutex. This allows a single [io.Writer] to be shared
// by both logrus and slog without their output clobbering each other.
// by multiple command runners.
type sharedWriter struct {
mu sync.Mutex
io.Writer
Expand Down
2 changes: 1 addition & 1 deletion lib/srv/desktop/rdp/rdpclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func init() {
var rustLogLevel string

// initialize the Rust logger by setting $RUST_LOG based
// on the logrus log level
// on the slog log level
// (unless RUST_LOG is already explicitly set, then we
// assume the user knows what they want)
rl := os.Getenv("RUST_LOG")
Expand Down
152 changes: 18 additions & 134 deletions lib/utils/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"flag"
"fmt"
"io"
stdlog "log"
"log/slog"
"os"
"runtime"
Expand All @@ -38,7 +37,6 @@ import (

"github.com/alecthomas/kingpin/v2"
"github.com/gravitational/trace"
"github.com/sirupsen/logrus"
"golang.org/x/term"

"github.com/gravitational/teleport"
Expand Down Expand Up @@ -100,59 +98,18 @@ func InitLogger(purpose LoggingPurpose, level slog.Level, opts ...LoggerOption)
opt(&o)
}

logrus.StandardLogger().ReplaceHooks(make(logrus.LevelHooks))
logrus.SetLevel(logutils.SlogLevelToLogrusLevel(level))

var (
w io.Writer
enableColors bool
)
switch purpose {
case LoggingForCLI:
// If debug logging was asked for on the CLI, then write logs to stderr.
// Otherwise, discard all logs.
if level == slog.LevelDebug {
enableColors = IsTerminal(os.Stderr)
w = logutils.NewSharedWriter(os.Stderr)
} else {
w = io.Discard
enableColors = false
}
case LoggingForDaemon:
enableColors = IsTerminal(os.Stderr)
w = logutils.NewSharedWriter(os.Stderr)
}

var (
formatter logrus.Formatter
handler slog.Handler
)
switch o.format {
case LogFormatText, "":
textFormatter := logutils.NewDefaultTextFormatter(enableColors)

// Calling CheckAndSetDefaults enables the timestamp field to
// be included in the output. The error returned is ignored
// because the default formatter cannot be invalid.
if purpose == LoggingForCLI && level == slog.LevelDebug {
_ = textFormatter.CheckAndSetDefaults()
}

formatter = textFormatter
handler = logutils.NewSlogTextHandler(w, logutils.SlogTextHandlerConfig{
Level: level,
EnableColors: enableColors,
})
case LogFormatJSON:
formatter = &logutils.JSONFormatter{}
handler = logutils.NewSlogJSONHandler(w, logutils.SlogJSONHandlerConfig{
Level: level,
})
// If debug or trace logging is not enabled for CLIs,
// then discard all log output.
if purpose == LoggingForCLI && level > slog.LevelDebug {
slog.SetDefault(slog.New(logutils.DiscardHandler{}))
return
}

logrus.SetFormatter(formatter)
logrus.SetOutput(w)
slog.SetDefault(slog.New(handler))
logutils.Initialize(logutils.Config{
Severity: level.String(),
Format: o.format,
EnableColors: IsTerminal(os.Stderr),
})
}

var initTestLoggerOnce = sync.Once{}
Expand All @@ -163,56 +120,24 @@ func InitLoggerForTests() {
// Parse flags to check testing.Verbose().
flag.Parse()

level := slog.LevelWarn
w := io.Discard
if testing.Verbose() {
level = slog.LevelDebug
w = os.Stderr
if !testing.Verbose() {
slog.SetDefault(slog.New(logutils.DiscardHandler{}))
return
}

logger := logrus.StandardLogger()
logger.SetFormatter(logutils.NewTestJSONFormatter())
logger.SetLevel(logutils.SlogLevelToLogrusLevel(level))

output := logutils.NewSharedWriter(w)
logger.SetOutput(output)
slog.SetDefault(slog.New(logutils.NewSlogJSONHandler(output, logutils.SlogJSONHandlerConfig{Level: level})))
logutils.Initialize(logutils.Config{
Severity: slog.LevelDebug.String(),
Format: LogFormatJSON,
})
})
}

// NewLoggerForTests creates a new logrus logger for test environments.
func NewLoggerForTests() *logrus.Logger {
InitLoggerForTests()
return logrus.StandardLogger()
}

// NewSlogLoggerForTests creates a new slog logger for test environments.
func NewSlogLoggerForTests() *slog.Logger {
InitLoggerForTests()
return slog.Default()
}

// WrapLogger wraps an existing logger entry and returns
// a value satisfying the Logger interface
func WrapLogger(logger *logrus.Entry) Logger {
return &logWrapper{Entry: logger}
}

// NewLogger creates a new empty logrus logger.
func NewLogger() *logrus.Logger {
return logrus.StandardLogger()
}

// Logger describes a logger value
type Logger interface {
logrus.FieldLogger
// GetLevel specifies the level at which this logger
// value is logging
GetLevel() logrus.Level
// SetLevel sets the logger's level to the specified value
SetLevel(level logrus.Level)
}

// FatalError is for CLI front-ends: it detects gravitational/trace debugging
// information, sends it to the logger, strips it off and prints a clean message to stderr
func FatalError(err error) {
Expand All @@ -231,7 +156,7 @@ func GetIterations() int {
if err != nil {
panic(err)
}
logrus.Debugf("Starting tests with %v iterations.", iter)
slog.DebugContext(context.Background(), "Running tests multiple times due to presence of ITERATIONS environment variable", "iterations", iter)
return iter
}

Expand Down Expand Up @@ -484,47 +409,6 @@ func AllowWhitespace(s string) string {
return sb.String()
}

// NewStdlogger creates a new stdlib logger that uses the specified leveled logger
// for output and the given component as a logging prefix.
func NewStdlogger(logger LeveledOutputFunc, component string) *stdlog.Logger {
return stdlog.New(&stdlogAdapter{
log: logger,
}, component, stdlog.LstdFlags)
}

// Write writes the specified buffer p to the underlying leveled logger.
// Implements io.Writer
func (r *stdlogAdapter) Write(p []byte) (n int, err error) {
r.log(string(p))
return len(p), nil
}

// stdlogAdapter is an io.Writer that writes into an instance
// of logrus.Logger
type stdlogAdapter struct {
log LeveledOutputFunc
}

// LeveledOutputFunc describes a function that emits given
// arguments at a specific level to an underlying logger
type LeveledOutputFunc func(args ...interface{})

// GetLevel returns the level of the underlying logger
func (r *logWrapper) GetLevel() logrus.Level {
return r.Entry.Logger.GetLevel()
}

// SetLevel sets the logging level to the given value
func (r *logWrapper) SetLevel(level logrus.Level) {
r.Entry.Logger.SetLevel(level)
}

// logWrapper wraps a log entry.
// Implements Logger
type logWrapper struct {
*logrus.Entry
}

// needsQuoting returns true if any non-printable characters are found.
func needsQuoting(text string) bool {
for _, r := range text {
Expand Down
Loading

0 comments on commit b1498d5

Please sign in to comment.