// Package flume is a logging package, build on top of zap. It's structured and leveled logs, like zap/logrus/etc. // It adds global, runtime re-configuration of all loggers, via an internal logger registry. // // There are two interaction points with flume: code that generates logs, and code that configures logging output. // Code which generates logs needs to create named logger instances, and call log functions on it, like Info() // and Debug(). But by default, all these logs will be silently discarded. Flume does not output // log entries unless explicitly told to do so. This ensures libraries can freely use flume internally, without // polluting the stdout of the programs importing the library. // // The Logger type is a small interface. Libraries should allow replacement of their Logger instances so // importers can entirely replace flume if they wish. Alternately, importers can use flume to configure // the library's log output, and/or redirect it into the overall program's log stream. // // Logging // // This package does not offer package level log functions, so you need to create a logger instance first: // A common pattern is to create a single, package-wide logger, named after the package: // // var log = flume.New("mypkg") // // Then, write some logs: // // log.Debug("created user", "username", "frank", "role", "admin") // // Logs have a message, then matched pairs of key/value properties. Child loggers can be created // and pre-seeded with a set of properties: // // reqLogger := log.With("remoteAddr", req.RemoteAddr) // // Expensive log events can be avoid by explicitly checking level: // // if log.IsDebug() { // log.Debug("created resource", "resource", resource.ExpensiveToString()) // } // // Loggers can be bound to context.Context, which is convenient for carrying // per-transaction loggers (pre-seeded with transaction specific context) through layers of request // processing code: // // ctx = flume.WithLogger(ctx, log.With("transactionID", tid)) // // ...later... // flume.FromContext(ctx).Info("Request handled.") // // The standard Logger interface only supports 3 levels of log, DBG, INF, and ERR. This is inspired by // this article: https://dave.cheney.net/2015/11/05/lets-talk-about-logging. However, you can create // instances of DeprecatedLogger instead, which support more levels. // // Configuration // // There are several package level functions which reconfigure logging output. They control which // levels are discarded, which fields are included in each log entry, and how those fields are rendered, // and how the overall log entry is rendered (JSON, LTSV, colorized, etc). // // To configure logging settings from environment variables, call the configuration function from main(): // // flume.ConfigFromEnv() // // This reads the log configuration from the environment variable "FLUME" (the default, which can be // overridden). The value is JSON, e.g.: // // {"level":"INF","levels":"http=DBG","development"="true"} // // The properties of the config string: // // - "level": ERR, INF, or DBG. The default level for all loggers. // - "levels": A string configuring log levels for specific loggers, overriding the default level. // See note below for syntax. // - "development": true or false. In development mode, the defaults for the other // settings change to be more suitable for developers at a terminal (colorized, multiline, human // readable, etc). See note below for exact defaults. // - "addCaller": true or false. Adds call site information to log entries (file and line). // - "encoding": json, ltsv, term, or term-color. Configures how log entries are encoded in the output. // "term" and "term-color" are multi-line, human-friendly // formats, intended for terminal output. // - "encoderConfig": a JSON object which configures advanced encoding settings, like how timestamps // are formatted. See docs for go.uber.org/zap/zapcore/EncoderConfig // // - "messageKey": the label of the message property of the log entry. If empty, message is omitted. // - "levelKey": the label of the level property of the log entry. If empty, level is omitted. // - "timeKey": the label of the timestamp of the log entry. If empty, timestamp is omitted. // - "nameKey": the label of the logger name in the log entry. If empty, logger name is omitted. // - "callerKey": the label of the logger name in the log entry. If empty, logger name is omitted. // - "lineEnding": the end of each log output line. // - "levelEncoder": capital, capitalColor, color, lower, or abbr. Controls how the log entry level // is rendered. "abbr" renders 3-letter abbreviations, like ERR and INF. // - "timeEncoder": iso8601, millis, nanos, unix, or justtime. Controls how timestamps are rendered. // "millis", "nanos", and "unix" are since UNIX epoch. "unix" is in floating point seconds. // "justtime" omits the date, and just prints the time in the format "15:04:05.000". // - "durationEncoder": string, nanos, or seconds. Controls how time.Duration values are rendered. // - "callerEncoder": full or short. Controls how the call site is rendered. // "full" includes the entire package path, "short" only includes the last folder of the package. // // Defaults: // // { // "level":"INF", // "levels":"", // "development":false, // "addCaller":false, // "encoding":"term-color", // "encoderConfig":{ // "messageKey":"msg", // "levelKey":"level", // "timeKey":"time", // "nameKey":"name", // "callerKey":"caller", // "lineEnding":"\n", // "levelEncoder":"abbr", // "timeEncoder":"iso8601", // "durationEncoder":"seconds", // "callerEncoder":"short", // } // } // // These defaults are only applied if one of the configuration functions is called, like ConfigFromEnv(), ConfigString(), // Configure(), or LevelsString(). Initially, all loggers are configured to discard everything, following // flume's opinion that log packages should be silent unless spoken too. Ancillary to this: library packages // should *not* call these functions, or configure logging levels or output in anyway. Only program entry points, // like main() or test code, should configure logging. Libraries should just create loggers and log to them. // // Development mode: if "development"=true, the defaults for the rest of the settings change, equivalent to: // // { // "addCaller":true, // "encoding":"term-color", // "encodingConfig": { // "timeEncoder":"justtime", // "durationEncoder":"string", // } // } // // The "levels" value is a list of key=value pairs, configuring the level of individual named loggers. // If the key is "*", it sets the default level. If "level" and "levels" both configure the default // level, "levels" wins. // Examples: // // * // set the default level to ALL, equivalent to {"level"="ALL"} // *=INF // same, but set default level to INF // *,sql=WRN // set default to ALL, set "sql" logger to WRN // *=INF,http=ALL // set default to INF, set "http" to ALL // *=INF,http // same as above. If name has no level, level is set to ALL // *=INF,-http // set default to INF, set "http" to OFF // http=INF // leave default setting unchanged. // // Factories // // Most usages of flume will use its package functions. The package functions delegate to an internal // instance of Factory, which a the logger registry. You can create and manage your own instance of // Factory, which will be an isolated set of Loggers. // // tl;dr // // The implementation is a wrapper around zap. zap does levels, structured logs, and is very fast. // zap doesn't do centralized, global configuration, so this package // adds that by maintaining an internal registry of all loggers, and using the sync.atomic stuff to swap out // levels and writers in a thread safe way. package flume