Skip to content

logrus Transport

Go ReferenceVersionSourceChangelog

Wraps an existing *logrus.Logger. Metadata nests under a single configurable key.

sh
go get go.loglayer.dev/transports/logrus/v3
go get github.com/sirupsen/logrus

Basic Usage

go
import (
    "os"

    "github.com/sirupsen/logrus"

    "go.loglayer.dev/v3"
    lllogrus "go.loglayer.dev/transports/logrus/v3"
)

l := logrus.New()
l.Out = os.Stderr
l.Formatter = &logrus.JSONFormatter{}

log := loglayer.New(loglayer.Config{
    Transport: lllogrus.New(lllogrus.Config{Logger: l}),
})

log.Info("hello")
// {"level":"info","msg":"hello","time":"..."}

If you don't pass a Logger, the transport constructs one writing to Writer (default os.Stderr) at TraceLevel.

Config

go
type Config struct {
    transport.BaseConfig

    Logger *logrus.Logger // wrap an existing logger
    Writer io.Writer      // used only when Logger is nil
}

Fatal Behavior

logrus.Logger.Fatal and Logger.Log(FatalLevel, ...) call the logger's ExitFunc, which by default is os.Exit(1). To prevent logrus from exiting before the core's DisableFatalExit check can run, this transport always builds a fresh *logrus.Logger that copies the supplied logger's settings (Out, Hooks, Formatter, ReportCaller, Level, BufferPool) but with ExitFunc set to a no-op.

Two consequences:

  • The user's original *logrus.Logger is never mutated; its ExitFunc is preserved.
  • The wrapped copy returned by GetLoggerInstance is the no-op-ExitFunc version, not the original. For most operations this doesn't matter; configurations set on the original (formatter, hooks, etc.) are all preserved.

The core then decides whether os.Exit(1) is called after dispatch. See Fatal Exits the Process.

Metadata Handling

The core nests the entry's metadata under MetadataFieldName (default "metadata"; set Config.FlattenMetadata: true to restore per-transport v2 placement). This transport honors that key.

Metadata nests under the metadata key

go
log.WithMetadata(loglayer.Metadata{"requestId": "xyz", "n": 42}).Info("served")
// {"level":"info","msg":"served","metadata":{"requestId":"xyz","n":42},"time":"..."}

log.WithMetadata(User{ID: 7, Name: "Alice"}).Info("user")
// {"level":"info","metadata":{"id":7,"name":"Alice"},"msg":"user",...}

With Config.FlattenMetadata: true, map metadata instead becomes keys in a logrus.Fields bag passed to WithFields.

Change the nesting key on the core's config:

go
loglayer.New(loglayer.Config{
    Transport:         lllogrus.New(lllogrus.Config{Logger: l}),
    MetadataFieldName: "payload",
})

Reaching the Underlying Logger

GetLoggerInstance returns the wrapped (no-op-ExitFunc) *logrus.Logger:

go
l := log.GetLoggerInstance("logrus").(*logrus.Logger)
l.AddHook(myHook)

("logrus" is whatever you set as BaseConfig.ID; defaults to an auto-generated ID when unset.)

Level Mapping

LogLayer Levellogrus Level
LogLevelTraceTraceLevel
LogLevelDebugDebugLevel
LogLevelInfoInfoLevel
LogLevelWarnWarnLevel
LogLevelErrorErrorLevel
LogLevelFatalFatalLevel
LogLevelPanicPanicLevel