Categorygithub.com/rs/xlog
modulepackage
0.0.0-20171227185259-131980fab91b
Repository: https://github.com/rs/xlog.git
Documentation: pkg.go.dev

# README

:warning: Check zerolog, the successor of xlog.

HTTP Handler Logger

godoc license Build Status Coverage

xlog is a logger for net/context aware HTTP applications.

Unlike most loggers, xlog will never block your application because one its outputs is lagging. The log commands are connected to their outputs through a buffered channel and will prefer to discard messages if the buffer get full. All message formatting, serialization and transport happen in a dedicated go routine.

Read more about xlog on Dailymotion engineering blog.

Features

  • Per request log context
  • Per request and/or per message key/value fields
  • Log levels (Debug, Info, Warn, Error)
  • Color output when terminal is detected
  • Custom output (JSON, logfmt, …)
  • Automatic gathering of request context like User-Agent, IP etc.
  • Drops message rather than blocking execution
  • Easy access logging thru github.com/rs/xaccess

Works with both Go 1.7+ (with net/context support) and Go 1.6 if used with github.com/rs/xhandler.

Install

go get github.com/rs/xlog

Usage

c := alice.New()

host, _ := os.Hostname()
conf := xlog.Config{
    // Log info level and higher
    Level: xlog.LevelInfo,
    // Set some global env fields
    Fields: xlog.F{
        "role": "my-service",
        "host": host,
    },
    // Output everything on console
    Output: xlog.NewOutputChannel(xlog.NewConsoleOutput()),
}

// Install the logger handler
c = c.Append(xlog.NewHandler(conf))

// Optionally plug the xlog handler's input to Go's default logger
log.SetFlags(0)
xlogger := xlog.New(conf)
log.SetOutput(xlogger)

// Install some provided extra handler to set some request's context fields.
// Thanks to those handler, all our logs will come with some pre-populated fields.
c = c.Append(xlog.MethodHandler("method"))
c = c.Append(xlog.URLHandler("url"))
c = c.Append(xlog.RemoteAddrHandler("ip"))
c = c.Append(xlog.UserAgentHandler("user_agent"))
c = c.Append(xlog.RefererHandler("referer"))
c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))

// Here is your final handler
h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Get the logger from the request's context. You can safely assume it
    // will be always there: if the handler is removed, xlog.FromContext
    // will return a NopLogger
    l := xlog.FromRequest(r)

    // Then log some errors
    if err := errors.New("some error from elsewhere"); err != nil {
        l.Errorf("Here is an error: %v", err)
    }

    // Or some info with fields
    l.Info("Something happend", xlog.F{
        "user":   "current user id",
        "status": "ok",
    })
    // Output:
    // {
    //   "message": "Something happend",
    //   "level": "info",
    //   "file": "main.go:34",
    //   "time": time.Time{...},
    //   "user": "current user id",
    //   "status": "ok",
    //   "ip": "1.2.3.4",
    //   "user-agent": "Mozilla/1.2.3...",
    //   "referer": "http://somewhere.com/path",
    //   "role": "my-service",
    //   "host": "somehost"
    // }
}))
http.Handle("/", h)

if err := http.ListenAndServe(":8080", nil); err != nil {
    xlogger.Fatal(err)
}

Copy Logger

You may want to get a copy of the current logger to pass a modified version to a function without touching the original:

l := xlog.FromContext(ctx)
l2 := xlog.Copy(l)
l2.SetField("foo", "bar")

Make sure you copy a request context logger if you plan to use it in a go routine that may still exist after the end of the current request. Contextual loggers are reused after each requests to lower the pressure on the garbage collector. If you would use such a logger in a go routine, you may end up using a logger from another request/context or worse, a nil pointer:

l := xlog.FromContext(ctx)
l2 := xlog.Copy(l)
go func() {
    // use the safe copy
    l2.Info("something")
}()

Global Logger

You may use the standard Go logger and plug xlog as it's output as xlog implements io.Writer:

xlogger := xlog.New(conf)
log.SetOutput(xlogger)

This has the advantage to make all your existing code or libraries already using Go's standard logger to use xlog with no change. The drawback though, is that you won't have control on the logging level and won't be able to add custom fields (other than ones set on the logger itself via configuration or SetFields()) for those messages.

Another option for code you manage but which is outside of a HTTP request handler is to use the xlog provided default logger:

xlog.Debugf("some message with %s", variable, xlog.F{"and": "field support"})

This way you have access to all the possibilities offered by xlog without having to carry the logger instance around. The default global logger has no fields set and has its output set to the console with no buffering channel. You may want to change that using the xlog.SetLogger() method:

xlog.SetLogger(xlog.New(xlog.Config{
    Level: xlog.LevelInfo,
    Output: xlog.NewConsoleOutput(),
    Fields: xlog.F{
        "role": "my-service",
    },
}))

Configure Output

By default, output is setup to output debug and info message on STDOUT and warning and errors to STDERR. You can easily change this setup.

XLog output can be customized using composable output handlers. Thanks to the LevelOutput, MultiOutput and FilterOutput, it is easy to route messages precisely.

conf := xlog.Config{
    Output: xlog.NewOutputChannel(xlog.MultiOutput{
        // Send all logs with field type=mymodule to a remote syslog
        0: xlog.FilterOutput{
            Cond: func(fields map[string]interface{}) bool {
                return fields["type"] == "mymodule"
            },
            Output: xlog.NewSyslogOutput("tcp", "1.2.3.4:1234", "mymodule"),
        },
        // Setup different output per log level
        1: xlog.LevelOutput{
            // Send errors to the console
            Error: xlog.NewConsoleOutput(),
            // Send syslog output for error level
            Info: xlog.NewSyslogOutput("", "", ""),
        },
    }),
})

h = xlog.NewHandler(conf)

Built-in Output Modules

NameDescription
OutputChannelBuffers messages before sending. This output should always be the output directly set to xlog's configuration.
MultiOutputRoutes the same message to several outputs. If one or more outputs return error, the last error is returned.
FilterOutputTests a condition on the message and forward it to the child output if true.
LevelOutputRoutes messages per level outputs.
ConsoleOutputPrints messages in a human readable form on the stdout with color when supported. Fallback to logfmt output if the stdout isn't a terminal.
JSONOutputSerialize messages in JSON.
LogfmtOutputSerialize messages using Heroku like logfmt.
LogstashOutputSerialize JSON message using Logstash 2.0 (schema v1) structured format.
SyslogOutputSend messages to syslog.
UIDOutputAppend a globally unique id to every message and forward it to the next output.

Third Party Extensions

ProjectAuthorDescription
gRPClogHugo González LabradorAn adapter to use xlog as the logger for grpclog.
xlog-nsqOlivier PoitreyAn xlog to NSQ output.
xlog-sentrytrongAn xlog to Sentry output.

Licenses

All source code is licensed under the MIT License.

# Functions

Copy returns a copy of the passed logger if the logger implements LoggerCopier or the NopLogger otherwise.
Debug calls the Debug() method on the default logger.
Debugf calls the Debugf() method on the default logger.
Error calls the Error() method on the default logger.
Errorf calls the Errorf() method on the default logger Go vet users: you may append %v at the end of you format when using xlog.F{} as a last argument to workaround go vet false alarm.
Fatal calls the Fatal() method on the default logger.
Fatalf calls the Fatalf() method on the default logger Go vet users: you may append %v at the end of you format when using xlog.F{} as a last argument to workaround go vet false alarm.
FromContext gets the logger out of the context.
FromRequest gets the logger in the request's context.
IDFromContext returns the unique id associated to the request if any.
IDFromRequest returns the unique id accociated to the request if any.
Info calls the Info() method on the default logger.
Infof calls the Infof() method on the default logger.
LevelFromString returns the level based on its string representation.
MethodHandler returns a handler setting the request's method as a field to the current context's logger using the passed name as field name.
New manually creates a logger.
NewConsoleOutput returns a Output printing message in a colored human readable form on the stderr.
NewConsoleOutputW returns a Output printing message in a colored human readable form with the provided writer.
NewContext returns a copy of the parent context and associates it with the provided logger.
NewHandler instanciates a new xlog HTTP handler.
NewJSONOutput returns a new JSON output with the given writer.
NewLogfmtOutput returns a new output using logstash JSON schema v1.
NewLogstashOutput returns an output to generate logstash friendly JSON format.
NewOutputChannel creates a consumer buffered channel for the given output with a default buffer of 100 messages.
NewOutputChannelBuffer creates a consumer buffered channel for the given output with a customizable buffer size.
NewSyslogOutput returns JSONOutputs in a LevelOutput with writers set to syslog with the proper priority added to a LOG_USER facility.
NewSyslogOutputFacility returns JSONOutputs in a LevelOutput with writers set to syslog with the proper priority added to the passed facility.
NewSyslogWriter returns a writer ready to be used with output modules.
NewTrimFieldsOutput trims listed field fields of type string with a value length greater than maxLen to maxLen.
NewTrimOutput trims any field of type string with a value length greater than maxLen to maxLen.
NewUIDOutput returns an output filter adding a globally unique id (using github.com/rs/xid) to all message going thru this output.
RefererHandler returns a handler setting the request's referer header as a field to the current context's logger using the passed name as field name.
RemoteAddrHandler returns a handler setting the request's remote address as a field to the current context's logger using the passed name as field name.
RequestHandler returns a handler setting the request's method and URL as a field to the current context's logger using the passed name as field name.
RequestIDHandler returns a handler setting a unique id to the request which can be gathered using IDFromContext(ctx).
SetLogger changes the global logger instance.
URLHandler returns a handler setting the request's URL as a field to the current context's logger using the passed name as field name.
UserAgentHandler returns a handler setting the request's client's user-agent as a field to the current context's logger using the passed name as field name.
Warn calls the Warn() method on the default logger.
Warnf calls the Warnf() method on the default logger.

# Constants

Log levels.
Log levels.
Log levels.
Log levels.
Log levels.

# Variables

Discard is an Output that discards all log message going thru it.
ErrBufferFull is returned when the output channel buffer is full and messages are discarded.
Common field names for log messages.
Common field names for log messages.
Common field names for log messages.
Common field names for log messages.
NopLogger is an no-op implementation of xlog.Logger.

# Structs

Config defines logger's configuration.
FilterOutput test a condition on the message and forward it to the child output if it returns true.
LevelOutput routes messages to different output based on the message's level.
OutputChannel is a send buffered channel between xlog and an Output.
RecorderOutput stores the raw messages in it's Messages field.

# Interfaces

Logger defines the interface for a xlog compatible logger.
LoggerCopier defines a logger with copy support.
Output sends a log message fields to a destination.

# Type aliases

F represents a set of log message fields.
Level defines log levels.
MultiOutput routes the same message to serveral outputs.
OutputFunc is an adapter to allow the use of ordinary functions as Output handlers.