logging

package
v0.0.0-...-19169da Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2019 License: MIT Imports: 12 Imported by: 1

README

Nacelle Logging

Nacelle provides rather opinionated structured logging following a few short principles.

(1) Logs must be structured. It is absolutely essential to be able to correlate and aggregate log messages to form a view of a running system. (2) Applications should always log to a standard error. In order to redirect logs to a secondary target (such as an ELK stack), the application's output should simply be redirected. This keeps the application simple and allows redirection of logs to any source without requiring an application update. For an example of redirection when run in a Docker container, see nacelle's fluentd wrapper.

The interfaces provided here are backed by gomol.

Interface

The logging interface is simple. Each log method (each one named after the level at which it will log) provides a printf-like interface. Logging at the fatal level will abort the application after the log has been flushed.

logger.Debug("A debug message (%#v)", args...)
logger.Info("A info message (%#v)", args...)
logger.Warning("A warning message (%#v)", args...)
logger.Error("A error message (%#v)", args...)
logger.Fatal("A fatal message (%#v)", args...)

Each log method also has a WithFields variant, which takes a Fields object as its first parameter. Fields are a map from strings to interfaces. Each field provided to the logger will be output with (but separately from) the formatted message.

fields:=nacelle.Fields{
    "foo": "bar",
    "baz": 12345,
}

logger.DebugWithFields(fields, "A debug message (%#v)", args...)
logger.InfoWithFields(fields, "A info message (%#v)", args...)
logger.WarningWithFields(fields, "A warning message (%#v)", args...)
logger.ErrorWithFields(fields, "A error message (%#v)", args...)
logger.FatalWithFields(fields, "A fatal message (%#v)", args...)

A logger can also be decorated with fields and used later so that multiple log messages share the same set of fields. This is useful for request correlation in servers where a logger instance can be given a unique request identifier.

requestLogger := logger.WithFields(fields)

// Same as above
requestLogger.Info("A debug message (%#v)", args...)

A logger should not be constructed, but should be injected via a service container. See the service package documentation for additional formation.

Configuration

The default logging behavior can be configured by the following environment variables.

Environment Variable Default Description
LOG_LEVEL info The highest level that will be emitted.
LOG_ENCODING console console for human-readable output and json for JSON-formatted output.
LOG_FIELDS A JSON-encoded map of fields to include in every log.
LOG_FIELD_BLACKLIST A JSON-encoded list of fields to omit from logs. Works with console encoding only.
LOG_COLORIZE true Colorize log messages by level when true. Works with console encoding only.
LOG_SHORT_TIME false Omit date from timestamp when true. Works with console encoding only.
LOG_DISPLAY_FIELDS true Omit log fields from output when false. Works with console encoding only.
LOG_DISPLAY_MULTILINE_FIELDS false Print fields on one line when true, one field per line when false. Works with console encoding only.

Adapters

Nacelle ships with a handful of logging adapters - objects which wrap a logger instance and decorates it with some additional behavior or data. A custom adapter can be created for behavior which is not provided here.

Replay

The ReplayAdapter supports replaying a sequence of log messages but at a higher log level.

Example use case: Each request in an HTTP server has a unique log adapter which traces the request. This adapter generally logs at the DEBUG level. When a request encounters an error or is being served slowly, the entire trace can be replayed at a higher level so the entire context is available for analysis.

adapter := NewReplayAdapter(
    logger,          // base logger
    log.LevelDebug,  // track debug messages for replay
    log.LevelInfo,   // also track info messages
)

// ...

if requestIsTakingLong() {
    // Re-log journaled messages at warning level
    adapter.Replay(log.LevelWarning)
}

Messages which are replayed at a higher level will keep the original message timestamp (if supplied), or use the time the Log message was invoked (if not supplied). Each message will also be sent with an additional field called replayed-from-level with a value equal to the original level of the message.

Rollup

The RollupAdapter supports collapsing similar log messages into a multiplicity. This is intended to be used with a chatty subsystem that only logs a handful of messages for which a higher frequency does not provide a benefit (for example, failure to connect to a Redis cache during a network partition).

adapter := NewRollupAdapter(
    logger,       // base logger
    time.Second,  // rollup window
)

for i:=0; i < 10000; i++ {
    adapter.Debug("Some problem here!")
}

A rollup begins once two messages with the same format string are seen within the rollup window period. During a rollup, all log messages (except for the first in the window) are discarded but counted, and the first log message in that window will be sent at the end of the window period with an additional field called rollup-multiplicity with a value equal to the number of logs in that window.

Documentation

Index

Constants

View Source
const FieldReplay = "replayed-from-level"

FieldReplay is a field assigned to a message that has been replayed at a different log level. Its value is equal to the original log level.

View Source
const FieldRollup = "rollup-multiplicity"

FieldRollup is a field assigned to the last message in a window. Its value is equal to the number of messages in the window before it was flushed.

Variables

View Source
var (
	ErrIllegalLevel    = fmt.Errorf("illegal log level")
	ErrIllegalEncoding = fmt.Errorf("illegal log encoding")
)

Functions

func LogEmergencyError

func LogEmergencyError(message string, err error)

func LogEmergencyErrors

func LogEmergencyErrors(message string, errs []error)

Types

type Config

type Config struct {
	LogLevel                  string            `env:"log_level" file:"log_level" default:"info"`
	LogEncoding               string            `env:"log_encoding" file:"log_encoding" default:"console"`
	LogColorize               bool              `env:"log_colorize" file:"log_colorize" default:"true"`
	LogJSONFieldNames         map[string]string `env:"log_json_field_names" file:"log_json_field_names"`
	LogInitialFields          Fields            `env:"log_fields" file:"log_fields"`
	LogShortTime              bool              `env:"log_short_time" file:"log_short_time" default:"false"`
	LogDisplayFields          bool              `env:"log_display_fields" file:"log_display_fields" default:"true"`
	LogDisplayMultilineFields bool              `env:"log_display_multiline_fields" file:"log_display_multiline_fields" default:"false"`
	RawLogFieldBlacklist      string            `env:"log_field_blacklist" file:"log_field_blacklist" mask:"true"`
	LogFieldBlacklist         []string
}

func (*Config) PostLoad

func (c *Config) PostLoad() error

type Fields

type Fields map[string]interface{}

type GomolShim

type GomolShim struct {
	// contains filtered or unexported fields
}

func (*GomolShim) LogWithFields

func (g *GomolShim) LogWithFields(level LogLevel, fields Fields, format string, args ...interface{})

func (*GomolShim) Sync

func (g *GomolShim) Sync() error

func (*GomolShim) WithFields

func (g *GomolShim) WithFields(fields Fields) logShim

type LogLevel

type LogLevel int
const (
	LevelFatal LogLevel = iota
	LevelError
	LevelWarning
	LevelInfo
	LevelDebug

	ConsoleTimeFormat = "2006-01-02 15:04:05.000"
	JSONTimeFormat    = "2006-01-02T15:04:05.000-0700"
)

func (LogLevel) String

func (l LogLevel) String() string

type Logger

type Logger interface {
	WithFields(Fields) Logger
	LogWithFields(LogLevel, Fields, string, ...interface{})
	Sync() error

	// Convenience Methods
	Debug(string, ...interface{})
	Info(string, ...interface{})
	Warning(string, ...interface{})
	Error(string, ...interface{})
	Fatal(string, ...interface{})
	DebugWithFields(Fields, string, ...interface{})
	InfoWithFields(Fields, string, ...interface{})
	WarningWithFields(Fields, string, ...interface{})
	ErrorWithFields(Fields, string, ...interface{})
	FatalWithFields(Fields, string, ...interface{})
}

func EmergencyLogger

func EmergencyLogger() Logger

func InitGomolShim

func InitGomolShim(c *Config) (Logger, error)

func NewGomolLogger

func NewGomolLogger(logger *gomol.LogAdapter, initialFields Fields) Logger

func NewNilLogger

func NewNilLogger() Logger

func NewRollupAdapter

func NewRollupAdapter(logger Logger, windowDuration time.Duration) Logger

NewRollupAdapter returns a logger with functionality to throttle similar messages. Messages begin a roll-up when a second messages with an identical format string is seen in the same window period. All remaining messages logged within that period are captured and emitted as a single message at the end of the window period. The fields and args are equal to the first rolled-up message.

type NilShim

type NilShim struct{}

func (*NilShim) LogWithFields

func (n *NilShim) LogWithFields(LogLevel, Fields, string, ...interface{})

func (*NilShim) Sync

func (n *NilShim) Sync() error

func (*NilShim) WithFields

func (n *NilShim) WithFields(Fields) logShim

type ReplayLogger

type ReplayLogger interface {
	Logger

	// Replay will cause all of the messages previously logged at one of the
	// journaled levels to be re-set at the given level. All future messages
	// logged at one of the journaled levels will be replayed immediately.
	Replay(LogLevel)
}

ReplayLogger is a Logger that provides a way to replay a sequence of message in the order they were logged, at a higher log level.

func NewReplayAdapter

func NewReplayAdapter(logger Logger, levels ...LogLevel) ReplayLogger

NewReplayAdapter creates a ReplayLogger wrapping the given logger.

Jump to

Keyboard shortcuts

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