Documentation ¶
Overview ¶
Package log provides a structured logger.
Structured logging produces logs easily consumed later by humans or machines. Humans might be interested in debugging errors, or tracing specific requests. Machines might be interested in counting interesting events, or aggregating information for off-line processing. In both cases, it is important that the log messages are structured and actionable. Package log is designed to encourage both of these best practices.
Basic Usage ¶
The fundamental interface is Logger. Loggers create log events from key/value data. The Logger interface has a single method, Log, which accepts a sequence of alternating key/value pairs, which this package names keyvals.
type Logger interface { Log(keyvals ...interface{}) error }
Here is an example of a function using a Logger to create log events.
func RunTask(task Task, logger log.Logger) string { logger.Log("taskID", task.ID, "event", "starting task") ... logger.Log("taskID", task.ID, "event", "task complete") }
The keys in the above example are "taskID" and "event". The values are task.ID, "starting task", and "task complete". Every key is followed immediately by its value.
Keys are usually plain strings. Values may be any type that has a sensible encoding in the chosen log format. With structured logging it is a good idea to log simple values without formatting them. This practice allows the chosen logger to encode values in the most appropriate way.
Log Context ¶
A log context stores keyvals that it includes in all log events. Building appropriate log contexts reduces repetition and aids consistency in the resulting log output. We can use a context to improve the RunTask example.
func RunTask(task Task, logger log.Logger) string { logger = log.NewContext(logger).With("taskID", task.ID) logger.Log("event", "starting task") ... taskHelper(task.Cmd, logger) ... logger.Log("event", "task complete") }
The improved version emits the same log events as the original for the first and last calls to Log. The call to taskHelper highlights that a context may be passed as a logger to other functions. Each log event created by the called function will include the task.ID even though the function does not have access to that value. Using log contexts this way simplifies producing log output that enables tracing the life cycle of individual tasks. (See the Context example for the full code of the above snippet.)
Dynamic Context Values ¶
A Valuer function stored in a log context generates a new value each time the context logs an event. The Valuer example demonstrates how this feature works.
Valuers provide the basis for consistently logging timestamps and source code location. The log package defines several valuers for that purpose. See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and DefaultCaller. A common logger initialization sequence that ensures all log entries contain a timestamp and source location looks like this:
logger := log.NewLogfmtLogger(log.SyncWriter(os.Stdout)) logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
Concurrent Safety ¶
Applications with multiple goroutines want each log event written to the same logger to remain separate from other log events. Package log provides two simple solutions for concurrent safe logging.
NewSyncWriter wraps an io.Writer and serializes each call to its Write method. Using a SyncWriter has the benefit that the smallest practical portion of the logging logic is performed within a mutex, but it requires the formatting Logger to make only one call to Write per log event.
NewSyncLogger wraps any Logger and serializes each call to its Log method. Using a SyncLogger has the benefit that it guarantees each log event is handled atomically within the wrapped logger, but it typically serializes both the formatting and output logic. Use a SyncLogger if the formatting logger may perform multiple writes per log event.
Example (Basic) ¶
package main import ( "os" "github.com/go-kit/kit/log" ) func main() { logger := log.NewLogfmtLogger(os.Stdout) type Task struct { ID int } RunTask := func(task Task, logger log.Logger) { logger.Log("taskID", task.ID, "event", "starting task") logger.Log("taskID", task.ID, "event", "task complete") } RunTask(Task{ID: 1}, logger) }
Output: taskID=1 event="starting task" taskID=1 event="task complete"
Example (Context) ¶
package main import ( "os" "time" "github.com/go-kit/kit/log" ) func main() { logger := log.NewLogfmtLogger(os.Stdout) type Task struct { ID int Cmd string } taskHelper := func(cmd string, logger log.Logger) { // execute(cmd) logger.Log("cmd", cmd, "dur", 42*time.Millisecond) } RunTask := func(task Task, logger log.Logger) { logger = log.NewContext(logger).With("taskID", task.ID) logger.Log("event", "starting task") taskHelper(task.Cmd, logger) logger.Log("event", "task complete") } RunTask(Task{ID: 1, Cmd: "echo Hello, world!"}, logger) }
Output: taskID=1 event="starting task" taskID=1 cmd="echo Hello, world!" dur=42ms taskID=1 event="task complete"
Example (DebugInfo) ¶
package main import ( "os" "time" "github.com/go-kit/kit/log" ) func main() { logger := log.NewLogfmtLogger(os.Stdout) // make time predictable for this test baseTime := time.Date(2015, time.February, 3, 10, 0, 0, 0, time.UTC) mockTime := func() time.Time { baseTime = baseTime.Add(time.Second) return baseTime } logger = log.NewContext(logger).With("time", log.Timestamp(mockTime), "caller", log.DefaultCaller) logger.Log("call", "first") logger.Log("call", "second") // ... logger.Log("call", "third") }
Output: time=2015-02-03T10:00:01Z caller=example_test.go:91 call=first time=2015-02-03T10:00:02Z caller=example_test.go:92 call=second time=2015-02-03T10:00:03Z caller=example_test.go:96 call=third
Example (Valuer) ¶
package main import ( "os" "github.com/go-kit/kit/log" ) func main() { logger := log.NewLogfmtLogger(os.Stdout) count := 0 counter := func() interface{} { count++ return count } logger = log.NewContext(logger).With("count", log.Valuer(counter)) logger.Log("call", "first") logger.Log("call", "second") }
Output: count=1 call=first count=2 call=second
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // DefaultCaller is a Valuer that returns the file and line where the Log // method was invoked. It can only be used with log.With. DefaultCaller = Caller(3) )
var ErrMissingValue = errors.New("(MISSING)")
ErrMissingValue is appended to keyvals slices with odd length to substitute the missing value.
Functions ¶
func NewStdlibAdapter ¶
func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer
NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed logger. It's designed to be passed to log.SetOutput.
Types ¶
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
A Context wraps a Logger and holds keyvals that it includes in all log events. When logging, a Context replaces all value elements (odd indexes) containing a Valuer with their generated value for each call to its Log method.
func NewContext ¶
NewContext returns a new Context that logs to logger.
func (*Context) Log ¶
Log replaces all value elements (odd indexes) containing a Valuer in the stored context with their generated value, appends keyvals, and passes the result to the wrapped Logger.
func (*Context) WithPrefix ¶
WithPrefix returns a new Context with keyvals prepended to those of the receiver.
type Logger ¶
type Logger interface {
Log(keyvals ...interface{}) error
}
Logger is the fundamental interface for all log operations. Log creates a log event from keyvals, a variadic sequence of alternating keys and values. Implementations must be safe for concurrent use by multiple goroutines. In particular, any implementation of Logger that appends to keyvals or modifies any of its elements must make a copy first.
func NewJSONLogger ¶
NewJSONLogger returns a Logger that encodes keyvals to the Writer as a single JSON object. Each log event produces no more than one call to w.Write. The passed Writer must be safe for concurrent use by multiple goroutines if the returned Logger will be used concurrently.
func NewLogfmtLogger ¶
NewLogfmtLogger returns a logger that encodes keyvals to the Writer in logfmt format. Each log event produces no more than one call to w.Write. The passed Writer must be safe for concurrent use by multiple goroutines if the returned Logger will be used concurrently.
func NewNopLogger ¶
func NewNopLogger() Logger
NewNopLogger returns a logger that doesn't do anything.
func NewSyncLogger ¶ added in v0.2.0
NewSyncLogger returns a logger that synchronizes concurrent use of the wrapped logger. When multiple goroutines use the SyncLogger concurrently only one goroutine will be allowed to log to the wrapped logger at a time. The other goroutines will block until the logger is available.
type LoggerFunc ¶
type LoggerFunc func(...interface{}) error
LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If f is a function with the appropriate signature, LoggerFunc(f) is a Logger object that calls f.
func (LoggerFunc) Log ¶
func (f LoggerFunc) Log(keyvals ...interface{}) error
Log implements Logger by calling f(keyvals...).
type StdlibAdapter ¶
type StdlibAdapter struct { Logger // contains filtered or unexported fields }
StdlibAdapter wraps a Logger and allows it to be passed to the stdlib logger's SetOutput. It will extract date/timestamps, filenames, and messages, and place them under relevant keys.
type StdlibAdapterOption ¶
type StdlibAdapterOption func(*StdlibAdapter)
StdlibAdapterOption sets a parameter for the StdlibAdapter.
func FileKey ¶
func FileKey(key string) StdlibAdapterOption
FileKey sets the key for the file and line field. By default, it's "file".
func MessageKey ¶
func MessageKey(key string) StdlibAdapterOption
MessageKey sets the key for the actual log message. By default, it's "msg".
func TimestampKey ¶
func TimestampKey(key string) StdlibAdapterOption
TimestampKey sets the key for the timestamp field. By default, it's "ts".
type StdlibWriter ¶
type StdlibWriter struct{}
StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's designed to be passed to a Go kit logger as the writer, for cases where it's necessary to redirect all Go kit log output to the stdlib logger.
If you have any choice in the matter, you shouldn't use this. Prefer to redirect the stdlib log to the Go kit logger via NewStdlibAdapter.
type SwapLogger ¶
type SwapLogger struct {
// contains filtered or unexported fields
}
SwapLogger wraps another logger that may be safely replaced while other goroutines use the SwapLogger concurrently. The zero value for a SwapLogger will discard all log events without error.
SwapLogger serves well as a package global logger that can be changed by importers.
func (*SwapLogger) Log ¶
func (l *SwapLogger) Log(keyvals ...interface{}) error
Log implements the Logger interface by forwarding keyvals to the currently wrapped logger. It does not log anything if the wrapped logger is nil.
func (*SwapLogger) Swap ¶
func (l *SwapLogger) Swap(logger Logger)
Swap replaces the currently wrapped logger with logger. Swap may be called concurrently with calls to Log from other goroutines.
type SyncWriter ¶ added in v0.2.0
type SyncWriter struct {
// contains filtered or unexported fields
}
SyncWriter synchronizes concurrent writes to an io.Writer.
func NewSyncWriter ¶ added in v0.2.0
func NewSyncWriter(w io.Writer) *SyncWriter
NewSyncWriter returns a new SyncWriter. The returned writer is safe for concurrent use by multiple goroutines.
type Valuer ¶
type Valuer func() interface{}
A Valuer generates a log value. When passed to Context.With in a value element (odd indexes), it represents a dynamic value which is re-evaluated with each log event.
var ( // DefaultTimestamp is a Valuer that returns the current wallclock time, // respecting time zones, when bound. DefaultTimestamp Valuer = func() interface{} { return time.Now().Format(time.RFC3339) } // DefaultTimestampUTC is a Valuer that returns the current time in UTC // when bound. DefaultTimestampUTC Valuer = func() interface{} { return time.Now().UTC().Format(time.RFC3339) } )