// Package logr defines abstract interfaces for logging. Packages can depend on // these interfaces and callers can implement logging in whatever way is // appropriate. // // This design derives from Dave Cheney's blog: // http://dave.cheney.net/2015/11/05/lets-talk-about-logging // // This is a BETA grade API. Until there is a significant 2nd implementation, // I don't really know how it will change. // // The logging specifically makes it non-trivial to use format strings, to encourage // attaching structured information instead of unstructured format strings. // // Usage // // Logging is done using a Logger. Loggers can have name prefixes and named values // attached, so that all log messages logged with that Logger have some base context // associated. // // The term "key" is used to refer to the name associated with a particular value, to // disambiguate it from the general Logger name. // // For instance, suppose we're trying to reconcile the state of an object, and we want // to log that we've made some decision. // // With the traditional log package, we might write // log.Printf( // "decided to set field foo to value %q for object %s/%s", // targetValue, object.Namespace, object.Name) // // With logr's structured logging, we'd write // // elsewhere in the file, set up the logger to log with the prefix of "reconcilers", // // and the named value target-type=Foo, for extra context. // log := mainLogger.WithName("reconcilers").WithValues("target-type", "Foo") // // // later on... // log.Info("setting field foo on object", "value", targetValue, "object", object) // // Depending on our logging implementation, we could then make logging decisions based on field values // (like only logging such events for objects in a certain namespace), or copy the structured // information into a structured log store. // // For logging errors, Logger has a method called Error. Suppose we wanted to log an // error while reconciling. With the traditional log package, we might write // log.Errorf("unable to reconcile object %s/%s: %v", object.Namespace, object.Name, err) // // With logr, we'd instead write // // assuming the above setup for log // log.Error(err, "unable to reconcile object", "object", object) // // This functions similarly to: // log.Info("unable to reconcile object", "error", err, "object", object) // // However, it ensures that a standard key for the error value ("error") is used across all // error logging. Furthermore, certain implementations may choose to attach additional // information (such as stack traces) on calls to Error, so it's preferred to use Error // to log errors. // // Parts of a log line // // Each log message from a Logger has four types of context: // logger name, log verbosity, log message, and the named values. // // The Logger name constists of a series of name "segments" added by successive calls to WithName. // These name segments will be joined in some way by the underlying implementation. It is strongly // reccomended that name segements contain simple identifiers (letters, digits, and hyphen), and do // not contain characters that could muddle the log output or confuse the joining operation (e.g. // whitespace, commas, periods, slashes, brackets, quotes, etc). // // Log verbosity represents how little a log matters. Level zero, the default, matters most. // Increasing levels matter less and less. Try to avoid lots of different verbosity levels, // and instead provide useful keys, logger names, and log messages for users to filter on. // It's illegal to pass a log level below zero. // // The log message consists of a constant message attached to the the log line. This // should generally be a simple description of what's occuring, and should never be a format string. // // Variable information can then be attached using named values (key/value pairs). Keys are arbitrary // strings, while values may be any Go value. // // Key Naming Conventions // // While users are generally free to use key names of their choice, it's generally best to avoid // using the following keys, as they're frequently used by implementations: // // - `"error"`: the underlying error value in the `Error` method. // - `"stacktrace"`: the stack trace associated with a particular log line or error // (often from the `Error` message). // - `"caller"`: the calling information (file/line) of a particular log line. // - `"msg"`: the log message. // - `"level"`: the log level. // - `"ts"`: the timestamp for a log line. // // Implementations are encouraged to make use of these keys to represent the above // concepts, when neccessary (for example, in a pure-JSON output form, it would be // necessary to represent at least message and timestamp as ordinary named values). package logr // TODO: consider adding back in format strings if they're really needed // TODO: consider other bits of zap/zapcore functionality like ObjectMarshaller (for arbitrary objects) // TODO: consider other bits of glog functionality like Flush, InfoDepth, OutputStats // InfoLogger represents the ability to log non-error messages, at a particular verbosity. type InfoLogger interface { // Info logs a non-error message with the given key/value pairs as context. // // The msg argument should be used to add some constant description to // the log line. The key/value pairs can then be used to add additional // variable information. The key/value pairs should alternate string // keys and arbitrary values. Info(msg string, keysAndValues ...interface{}) // Enabled tests whether this InfoLogger is enabled. For example, // commandline flags might be used to set the logging verbosity and disable // some info logs. Enabled() bool } // Logger represents the ability to log messages, both errors and not. type Logger interface { // All Loggers implement InfoLogger. Calling InfoLogger methods directly on // a Logger value is equivalent to calling them on a V(0) InfoLogger. For // example, logger.Info() produces the same result as logger.V(0).Info. InfoLogger // Error logs an error, with the given message and key/value pairs as context. // It functions similarly to calling Info with the "error" named value, but may // have unique behavior, and should be preferred for logging errors (see the // package documentations for more information). // // The msg field should be used to add context to any underlying error, // while the err field should be used to attach the actual error that // triggered this log line, if present. Error(err error, msg string, keysAndValues ...interface{}) // V returns an InfoLogger value for a specific verbosity level. A higher // verbosity level means a log message is less important. It's illegal to // pass a log level less than zero. V(level int) InfoLogger // WithValues adds some key-value pairs of context to a logger. // See Info for documentation on how key/value pairs work. WithValues(keysAndValues ...interface{}) Logger // WithName adds a new element to the logger's name. // Successive calls with WithName continue to append // suffixes to the logger's name. It's strongly reccomended // that name segments contain only letters, digits, and hyphens // (see the package documentation for more information). WithName(name string) Logger }