1 // Package log provides a structured logger. 2 // 3 // Structured logging produces logs easily consumed later by humans or 4 // machines. Humans might be interested in debugging errors, or tracing 5 // specific requests. Machines might be interested in counting interesting 6 // events, or aggregating information for off-line processing. In both cases, 7 // it is important that the log messages are structured and actionable. 8 // Package log is designed to encourage both of these best practices. 9 // 10 // Basic Usage 11 // 12 // The fundamental interface is Logger. Loggers create log events from 13 // key/value data. The Logger interface has a single method, Log, which 14 // accepts a sequence of alternating key/value pairs, which this package names 15 // keyvals. 16 // 17 // type Logger interface { 18 // Log(keyvals ...interface{}) error 19 // } 20 // 21 // Here is an example of a function using a Logger to create log events. 22 // 23 // func RunTask(task Task, logger log.Logger) string { 24 // logger.Log("taskID", task.ID, "event", "starting task") 25 // ... 26 // logger.Log("taskID", task.ID, "event", "task complete") 27 // } 28 // 29 // The keys in the above example are "taskID" and "event". The values are 30 // task.ID, "starting task", and "task complete". Every key is followed 31 // immediately by its value. 32 // 33 // Keys are usually plain strings. Values may be any type that has a sensible 34 // encoding in the chosen log format. With structured logging it is a good 35 // idea to log simple values without formatting them. This practice allows 36 // the chosen logger to encode values in the most appropriate way. 37 // 38 // Contextual Loggers 39 // 40 // A contextual logger stores keyvals that it includes in all log events. 41 // Building appropriate contextual loggers reduces repetition and aids 42 // consistency in the resulting log output. With, WithPrefix, and WithSuffix 43 // add context to a logger. We can use With to improve the RunTask example. 44 // 45 // func RunTask(task Task, logger log.Logger) string { 46 // logger = log.With(logger, "taskID", task.ID) 47 // logger.Log("event", "starting task") 48 // ... 49 // taskHelper(task.Cmd, logger) 50 // ... 51 // logger.Log("event", "task complete") 52 // } 53 // 54 // The improved version emits the same log events as the original for the 55 // first and last calls to Log. Passing the contextual logger to taskHelper 56 // enables each log event created by taskHelper to include the task.ID even 57 // though taskHelper does not have access to that value. Using contextual 58 // loggers this way simplifies producing log output that enables tracing the 59 // life cycle of individual tasks. (See the Contextual example for the full 60 // code of the above snippet.) 61 // 62 // Dynamic Contextual Values 63 // 64 // A Valuer function stored in a contextual logger generates a new value each 65 // time an event is logged. The Valuer example demonstrates how this feature 66 // works. 67 // 68 // Valuers provide the basis for consistently logging timestamps and source 69 // code location. The log package defines several valuers for that purpose. 70 // See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and 71 // DefaultCaller. A common logger initialization sequence that ensures all log 72 // entries contain a timestamp and source location looks like this: 73 // 74 // logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) 75 // logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) 76 // 77 // Concurrent Safety 78 // 79 // Applications with multiple goroutines want each log event written to the 80 // same logger to remain separate from other log events. Package log provides 81 // two simple solutions for concurrent safe logging. 82 // 83 // NewSyncWriter wraps an io.Writer and serializes each call to its Write 84 // method. Using a SyncWriter has the benefit that the smallest practical 85 // portion of the logging logic is performed within a mutex, but it requires 86 // the formatting Logger to make only one call to Write per log event. 87 // 88 // NewSyncLogger wraps any Logger and serializes each call to its Log method. 89 // Using a SyncLogger has the benefit that it guarantees each log event is 90 // handled atomically within the wrapped logger, but it typically serializes 91 // both the formatting and output logic. Use a SyncLogger if the formatting 92 // logger may perform multiple writes per log event. 93 // 94 // Error Handling 95 // 96 // This package relies on the practice of wrapping or decorating loggers with 97 // other loggers to provide composable pieces of functionality. It also means 98 // that Logger.Log must return an error because some 99 // implementations—especially those that output log data to an io.Writer—may 100 // encounter errors that cannot be handled locally. This in turn means that 101 // Loggers that wrap other loggers should return errors from the wrapped 102 // logger up the stack. 103 // 104 // Fortunately, the decorator pattern also provides a way to avoid the 105 // necessity to check for errors every time an application calls Logger.Log. 106 // An application required to panic whenever its Logger encounters 107 // an error could initialize its logger as follows. 108 // 109 // fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) 110 // logger := log.LoggerFunc(func(keyvals ...interface{}) error { 111 // if err := fmtlogger.Log(keyvals...); err != nil { 112 // panic(err) 113 // } 114 // return nil 115 // }) 116 package log 117