structlog

package module
v0.7.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2021 License: MIT Imports: 11 Imported by: 88

README

structlog

GoDoc Go Report Card CircleCI Coverage Status

Features

  • log only key/value pairs
  • output both as Text and JSON
  • log level support
  • compatible enough with log.Logger to use as drop-in replacement
  • short names for service keys (like log level, time, etc.)
  • support default values for keys
  • service keys with caller's function name, file and line
  • fixed log level width
  • service key for high-level source name (like package or subsystem), caller's package name by default
  • when creating new logger instance:
    • inherit default logger settings (configured in main())
    • add new default key/values
    • disable inherited default keys
  • warn about imbalanced key/value pairs
  • first parameter to log functions should be value for "message" service key
  • able to output stack trace
  • level-guards like IsDebug()
  • output complex struct as key values (using "%v" like formatting)
  • Error returns message as error (auto-convert from string, if needed)
    • actually it returns first .(error) arg if any or message otherwise
  • convenient helpers IfFail and Recover for use with defer
  • output can be redirected/intercepted
  • when output as JSON:
    • add service field time by default
  • when output as Text:
    • do not add service field time by default
    • order of keys in output is fixed, same as order of log function parameters
    • it is possible to set minimal width for some keys (both user and service ones)
    • it is possible to setup order for service keys, including this keys should be output before and after user keys
    • it is possible to choose output style for some keys: "key: ", "key=", do not output key name
    • it is possible to choose how to escape values by default and for selected keys: no escaping, use ``, use ""
    • do not support colored output - this can be added by external tools like grc and we anyway won't have colors in log files

Documentation

Overview

Package structlog provides structured logger which looks friendly as plain text (more like handcrafted vertical-aligned log lines which doesn't feels like key/value pairs) but also really useful as JSON (each important value in log line became separate key/value pair).

In short, with structlog you will have to write just this:

log := structlog.New()
log.Warn("something goes wrong", "somevar", var, "err", err)

and structlog will take care of the rest (output plain text or JSON, format plain text output to make it easy to read, include in output extra key/value pairs - both added by you before that call and calculated automatically like caller's details or stack trace).

Well, not really. First you'll need to configure logger, to define what "easy to read" means in your case - but this has to be done just once, usually in main(), and then you'll get simple and powerful logging as promised above.

Overview

It was designed to produce easy to read, vertically-aligned log lines while your project is small, and later, when your project will grow to use something like ELK Stack, switch by changing one option to produce easy to parse JSON log records.

You can log key/value pairs without bothering about output format (plain text or JSON) and then fine-tune it plain text output by:

  • Re-ordering output values by choosing which keys should be output as prefix (before log message) and as suffix (after other keys).
  • Defining internal order of prefix and suffix keys.
  • Defining key/value output style using fmt.Sprintf 'verbs', including ability to output only value, without key name.
  • Choosing which ones of pre-defined key/values to include in output:
  • caller's package
  • caller's function name
  • caller's file and line
  • multiline stack trace (a-la panic output)

Supported log levels: Err, Warn, Info and Debug.

On import it calls stdlib's log.SetFlags(0) and by default will use stdlib's log.Print() to output log lines - this is to make sure structlog's output goes at same place as logging from other packages (which often use stdlib's log).

Inheritance

For convenience you may have multiple loggers, and create new logger in such a way to inherit all settings from existing logger. These settings often include predefined key/value pairs to be automatically output in each logged line in addition to logger formatting setting.

All loggers created using structlog.New() will inherit settings from logger in global variable structlog.DefaultLogger. So, usually you will configure structlog.DefaultLogger in your main(), to apply this configuration to each and every logger created in any other package of your application.

Then you can do some extra setup on your logger (usually - just add some default key/value pair which should be output by each log call), and call log.New() method to get new logger which will inherit this extra setup.

E.g. imagine HTTP middlewares: first will detect IP of connected client and store it in logger, second will check authentication and optionally add key/value pair with user ID - as result everything logged by your HTTP handler later using logger preconfigured by these middlewares will include remote IP and user ID in each log record - without needs to manually include it in each line where you log something.

Contents

★ Creating a new logger:

New (function)  - will inherit from structlog.DefaultLogger
New (method)    - will inherit from it's object
NewZeroLogger   - new empty logger (usually you won't need this)

★ Passing logger inside context.Context:

NewContext
FromContext

★ Normal logging:

Debug
Info
Warn
Err
PrintErr        - like Err, but don't return error (usually you won't need this)

★ Logging useful with defer:

DebugIfFail
InfoIfFail
WarnIfFail
ErrIfFail
Recover

★ Delayed logging:

WrapErr

★ Configuring structlog.DefaultLogger in your main():

AppendPrefixKeys
PrependSuffixKeys
SetKeyValFormat
SetKeysFormat
SetLogFormat
SetPrefixKeys
SetSuffixKeys
SetTimeFormat
SetTimeValFormat

★ Configuring current logger:

SetDefaultKeyvals
AddCallDepth

★ Handling log levels:

IsDebug
IsInfo
ParseLevel
SetLogLevel

★ Passing this logger to 3rd-party packages which expects interface of stdlib's log.Logger:

Fatal
Fatalf
Fatalln
Panic
Panicf
Panicln
Print
Printf
Println

★ Redirecting log output (useful to redirect to ioutil.Discard in tests):

SetOutput
SetPrinter

Index

Examples

Constants

View Source
const (
	Text logFormat = iota
	JSON
)

Log formats.

View Source
const (
	DBG logLevel = iota
	INF
	WRN
	ERR
)

Log levels.

View Source
const (
	DefaultLogFormat     = Text
	DefaultLogLevel      = DBG
	DefaultKeyValFormat  = ` %s=%v`
	DefaultTimeFormat    = time.StampMicro
	DefaultTimeValFormat = time.RFC3339Nano
	MissingValue         = "(MISSING)"
)

Defaults.

View Source
const (
	KeyTime    = "_t" // Key name used to output current time.
	KeyApp     = "_a" // Key name used to output app name.
	KeyPID     = "_p" // Key name used to output PID.
	KeyLevel   = "_l" // Key name used to output log level.
	KeyUnit    = "_u" // Key name used to output unit/module/package name.
	KeyMessage = "_m" // Key name used to output log message.
	KeyFunc    = "_f" // Key name used to output caller's function name.
	KeySource  = "_s" // Key name used to output caller's file and line.
	KeyStack   = "__" // Key name used to output multiline stack trace.
)

Predefined key names.

View Source
const Auto = "\x00"

Auto can be used as value for KeyTime, KeyUnit and KeyStack to automatically generate their values: current time, caller package's directory name and full stack of the current goroutine.

Variables

View Source
var DefaultLogger = NewZeroLogger(
	KeyApp, path.Base(os.Args[0]),
	KeyPID, os.Getpid(),
).SetPrefixKeys(
	KeyTime, KeyApp, KeyPID, KeyLevel, KeyUnit,
).SetSuffixKeys(
	KeyFunc, KeySource, KeyStack,
).SetKeysFormat(map[string]string{
	KeyTime:    "%[2]s ",
	KeyApp:     "%[2]s",
	KeyPID:     "[%[2]d]",
	KeyLevel:   " %[2]s",
	KeyUnit:    " %[2]s:",
	KeyMessage: " %#[2]q",
	KeyFunc:    " \t@ %[2]s",
	KeySource:  "(%[2]s)",
	KeyStack:   "\n%[2]s",
})

DefaultLogger provides sane defaults inherited by new logger objects created with New(). Feel free to change it settings when your app start.

Functions

func NewContext added in v0.3.0

func NewContext(ctx context.Context, log *Logger) context.Context

NewContext returns a new Context that carries value log.

func ParseLevel

func ParseLevel(levelName string) logLevel

ParseLevel convert levelName from flag or config file into logLevel.

Types

type Logger

type Logger struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Logger implements structured logger.

func FromContext added in v0.3.0

func FromContext(ctx context.Context, defaultLog *Logger) *Logger

FromContext returns the Logger value stored in ctx or defaultLog or New() if defaultLog is nil.

func New

func New(defaultKeyvals ...interface{}) *Logger

New creates and returns a new logger which inherits all settings from DefaultLogger.

func NewZeroLogger

func NewZeroLogger(defaultKeyvals ...interface{}) *Logger

NewZeroLogger creates and returns a new logger with empty settings.

func (*Logger) AddCallDepth

func (l *Logger) AddCallDepth(depth int) *Logger

AddCallDepth will add depth to amount of skipped stack frames while calculating default values for KeyUnit, KeyFunc and KeySource.

Use it if you want to report from perspective of your caller.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) AppendPrefixKeys

func (l *Logger) AppendPrefixKeys(keys ...string) *Logger

AppendPrefixKeys appends keys to current prefixKeys for l.

XXX Panics if will be called after using l (or logger created using l.New()) to log anything.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) Debug

func (l *Logger) Debug(msg interface{}, keyvals ...interface{})

Debug log defaultKeyvals, msg and keyvals with level DBG.

func (*Logger) DebugIfFail

func (l *Logger) DebugIfFail(f func() error, keyvals ...interface{})

DebugIfFail will run f and log defaultKeyvals, returned error and keyvals with level DBG if returned error is not nil.

defer log.DebugIfFail(file.Close)

func (*Logger) Err

func (l *Logger) Err(msg interface{}, keyvals ...interface{}) error

Err log defaultKeyvals, msg and keyvals with level ERR and returns first arg of error type or msg if there are no errors in args.

return log.Err("message to log", "error to log and return", err)
return log.Err(errors.New("error to log and return"), "error to log", err)

func (*Logger) ErrIfFail

func (l *Logger) ErrIfFail(f func() error, keyvals ...interface{})

ErrIfFail will run f and log defaultKeyvals, returned error and keyvals with level ERR if returned error is not nil.

defer log.ErrIfFail(file.Close)

func (*Logger) Fatal

func (l *Logger) Fatal(v ...interface{})

Fatal works like log.Fatal. Use level ERR. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) Fatalf

func (l *Logger) Fatalf(format string, v ...interface{})

Fatalf works like log.Fatalf. Use level ERR. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) Fatalln

func (l *Logger) Fatalln(v ...interface{})

Fatalln works like log.Fatalln. Use level ERR. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) Info

func (l *Logger) Info(msg interface{}, keyvals ...interface{})

Info log defaultKeyvals, msg and keyvals with level INF.

func (*Logger) InfoIfFail

func (l *Logger) InfoIfFail(f func() error, keyvals ...interface{})

InfoIfFail will run f and log defaultKeyvals, returned error and keyvals with level INF if returned error is not nil.

defer log.InfoIfFail(file.Close)

func (*Logger) IsDebug

func (l *Logger) IsDebug() bool

IsDebug returns true if l's log level DBG.

func (*Logger) IsInfo

func (l *Logger) IsInfo() bool

IsInfo returns true if l's log level DBG or INF.

func (*Logger) New

func (l *Logger) New(defaultKeyvals ...interface{}) *Logger

New creates and returns a new logger which inherits all settings from l.

func (*Logger) Panic

func (l *Logger) Panic(v ...interface{})

Panic works like log.Panic. Use level ERR. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) Panicf

func (l *Logger) Panicf(format string, v ...interface{})

Panicf works like log.Panicf. Use level ERR. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) Panicln

func (l *Logger) Panicln(v ...interface{})

Panicln works like log.Panicln. Use level ERR. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) PrependSuffixKeys

func (l *Logger) PrependSuffixKeys(keys ...string) *Logger

PrependSuffixKeys prepend keys to current suffixKeys for l.

XXX Panics if will be called after using l (or logger created using l.New()) to log anything.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) Print

func (l *Logger) Print(v ...interface{})

Print works like log.Print. Use level INF. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) PrintErr

func (l *Logger) PrintErr(msg interface{}, keyvals ...interface{})

PrintErr log defaultKeyvals, msg and keyvals with level ERR.

In most cases you should use Err instead, to both log and handle error.

func (*Logger) Printf

func (l *Logger) Printf(format string, v ...interface{})

Printf works like log.Printf. Use level INF. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) Println

func (l *Logger) Println(v ...interface{})

Println works like log.Println. Use level INF. Also output defaultKeyvals for prefixKeys/suffixKeys.

func (*Logger) Recover

func (l *Logger) Recover(err *error, keyvals ...interface{})

Recover calls recover(), and if it returns non-nil, then log defaultKeyvals, value returned by recover() and keyvals with stack trace and level ERR plus stores value returned by recover() into err if err is not nil.

defer log.Recover(nil)
func PanicToErr() (err error) { defer log.Recover(&err); ... }

func (*Logger) SetDefaultKeyvals

func (l *Logger) SetDefaultKeyvals(keyvals ...interface{}) *Logger

SetDefaultKeyvals add/replace values for keys in defaultKeyvals.

The keyvals must be a list of key/value pairs, keys must be a string. In case of odd amount of elements in keyvals it'll log error and use MissingValue as value for last key. In case of non-string keys it'll log error and convert key to string.

Keys in defaultKeyvals will provide default values for prefixKeys/suffixKeys, but these values will be used only if their key is included in prefixKeys or suffixKeys and same key won't be included within keyvals provided with log message.

To delete keys from defaultKeyvals set their value to nil. This is very useful if unwanted key was inherited from parent logger.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetKeyValFormat

func (l *Logger) SetKeyValFormat(format string) *Logger

SetKeyValFormat changes fmt format string used to output key/value pair for keys which doesn't have custom format set by SetKeysFormat (default value is DefaultKeyValFormat).

See SetKeysFormat for more details.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetKeysFormat

func (l *Logger) SetKeysFormat(keysFormat map[string]string) *Logger

SetKeysFormat add/replace custom fmt format string for keys. If key doesn't have custom format string then it will use format set using SetKeyValFormat (default value is DefaultKeyValFormat).

These format strings will be used as fmt.Sprintf(format,key,val), so you can refer to key name and it value as %[1] and %[2] - this is very useful in case you wanna output only key value, without name.

No extra spaces will be output between key/value pairs, so if you need some delimiters then include them inside format strings.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetLogFormat

func (l *Logger) SetLogFormat(format logFormat) *Logger

SetLogFormat changes log output format (default value is DefaultLogFormat).

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetLogLevel

func (l *Logger) SetLogLevel(level logLevel) *Logger

SetLogLevel changes minimum required log level to output log (default value is DefaultLogLevel).

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetOutput added in v0.2.0

func (l *Logger) SetOutput(w io.Writer) *Logger

SetOutput is a convenience wrapper for SetPrinter.

func (*Logger) SetPrefixKeys

func (l *Logger) SetPrefixKeys(keys ...string) *Logger

SetPrefixKeys replace current prefixKeys for l.

These keys will be output right after l's parent prefixKeys, if any.

XXX Panics if will be called after using l (or logger created using l.New()) to log anything.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetPrinter added in v0.2.0

func (l *Logger) SetPrinter(printer Printer) *Logger

SetPrinter changes log output destination (default value is PrinterFunc(log.Print), i.e. use standard logger, which will be configured using log.SetFlags(0) while importing this package).

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetSuffixKeys

func (l *Logger) SetSuffixKeys(keys ...string) *Logger

SetSuffixKeys replace current suffixKeys for l.

These keys will be output just before l's parent suffixKeys, if any.

XXX Panics if will be called after using l (or logger created using l.New()) to log anything.

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetTimeFormat

func (l *Logger) SetTimeFormat(format string) *Logger

SetTimeFormat changes format for time.Time.Format used when output log time (default value is DefaultTimeFormat).

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) SetTimeValFormat

func (l *Logger) SetTimeValFormat(format string) *Logger

SetTimeValFormat changes format for time.Time.Format used when output time.Time values (default value is DefaultTimeValFormat).

It doesn't creates a new logger, it returns l just for convenience.

func (*Logger) Warn

func (l *Logger) Warn(msg interface{}, keyvals ...interface{})

Warn log defaultKeyvals, msg and keyvals with level WRN.

func (*Logger) WarnIfFail

func (l *Logger) WarnIfFail(f func() error, keyvals ...interface{})

WarnIfFail will run f and log defaultKeyvals, returned error and keyvals with level WRN if returned error is not nil.

defer log.WarnIfFail(file.Close)

func (*Logger) WrapErr added in v0.4.0

func (l *Logger) WrapErr(err error, keyvals ...interface{}) error

WrapErr returns given err wrapped with keyvals. If returned err will be logged later these keyvals will be included in output.

If called with nil error it'll return nil.

Example
package main

import (
	"fmt"
	"io"
	"os"

	"github.com/powerman/structlog"
)

func main() {
	// Use NewZeroLogger to avoid reconfiguring
	// structlog.DefaultLogger in example, but in real code usually
	// reconfiguring DefaultLogger is better than using NewZeroLogger.
	log := structlog.NewZeroLogger().
		SetOutput(os.Stdout).
		SetPrefixKeys(structlog.KeyLevel).
		SetKeysFormat(map[string]string{
			structlog.KeyLevel:   "%[2]s",
			structlog.KeyMessage: " %#[2]q",
		})

	lowLevelFunc := func() error {
		return log.WrapErr(io.EOF, "details", "about error")
	}
	middleLevelFunc := func(action string) error {
		if err := lowLevelFunc(); err != nil {
			err = fmt.Errorf("lowLevelFunc: %w", err)
			return log.WrapErr(err, "action", action)
		}
		return nil
	}
	topLevelFunc := func() {
		if err := middleLevelFunc("doit"); err != nil {
			log.Warn("log only at top level", "err", err)
		}
	}
	topLevelFunc()
}
Output:

WRN `log only at top level` details=about error action=doit err=lowLevelFunc: EOF

type Printer added in v0.2.0

type Printer interface {
	// Print outputs v plus \n. Arguments are handled in the manner of fmt.Print.
	Print(v ...interface{})
}

Printer is an interface used to output log.

type PrinterFunc added in v0.2.0

type PrinterFunc func(v ...interface{})

The PrinterFunc type is an adapter to allow the use of ordinary functions as Printer.

func (PrinterFunc) Print added in v0.2.0

func (f PrinterFunc) Print(v ...interface{})

Print outputs v plus \n. Arguments are handled in the manner of fmt.Print.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL