package hclog import ( "io" "log" "os" "strings" "sync" ) var ( //DefaultOutput is used as the default log output. DefaultOutput io.Writer = os.Stderr // DefaultLevel is used as the default log level. DefaultLevel = Info ) // Level represents a log level. type Level int32 const ( // NoLevel is a special level used to indicate that no level has been // set and allow for a default to be used. NoLevel Level = 0 // Trace is the most verbose level. Intended to be used for the tracing // of actions in code, such as function enters/exits, etc. Trace Level = 1 // Debug information for programmer lowlevel analysis. Debug Level = 2 // Info information about steady state operations. Info Level = 3 // Warn information about rare but handled events. Warn Level = 4 // Error information about unrecoverable events. Error Level = 5 ) // Format is a simple convience type for when formatting is required. When // processing a value of this type, the logger automatically treats the first // argument as a Printf formatting string and passes the rest as the values // to be formatted. For example: L.Info(Fmt{"%d beans/day", beans}). type Format []interface{} // Fmt returns a Format type. This is a convience function for creating a Format // type. func Fmt(str string, args ...interface{}) Format { return append(Format{str}, args...) } // LevelFromString returns a Level type for the named log level, or "NoLevel" if // the level string is invalid. This facilitates setting the log level via // config or environment variable by name in a predictable way. func LevelFromString(levelStr string) Level { // We don't care about case. Accept both "INFO" and "info". levelStr = strings.ToLower(strings.TrimSpace(levelStr)) switch levelStr { case "trace": return Trace case "debug": return Debug case "info": return Info case "warn": return Warn case "error": return Error default: return NoLevel } } // Logger describes the interface that must be implemeted by all loggers. type Logger interface { // Args are alternating key, val pairs // keys must be strings // vals can be any type, but display is implementation specific // Emit a message and key/value pairs at the TRACE level Trace(msg string, args ...interface{}) // Emit a message and key/value pairs at the DEBUG level Debug(msg string, args ...interface{}) // Emit a message and key/value pairs at the INFO level Info(msg string, args ...interface{}) // Emit a message and key/value pairs at the WARN level Warn(msg string, args ...interface{}) // Emit a message and key/value pairs at the ERROR level Error(msg string, args ...interface{}) // Indicate if TRACE logs would be emitted. This and the other Is* guards // are used to elide expensive logging code based on the current level. IsTrace() bool // Indicate if DEBUG logs would be emitted. This and the other Is* guards IsDebug() bool // Indicate if INFO logs would be emitted. This and the other Is* guards IsInfo() bool // Indicate if WARN logs would be emitted. This and the other Is* guards IsWarn() bool // Indicate if ERROR logs would be emitted. This and the other Is* guards IsError() bool // Creates a sublogger that will always have the given key/value pairs With(args ...interface{}) Logger // Create a logger that will prepend the name string on the front of all messages. // If the logger already has a name, the new value will be appended to the current // name. That way, a major subsystem can use this to decorate all it's own logs // without losing context. Named(name string) Logger // Create a logger that will prepend the name string on the front of all messages. // This sets the name of the logger to the value directly, unlike Named which honor // the current name as well. ResetNamed(name string) Logger // Updates the level. This should affect all sub-loggers as well. If an // implementation cannot update the level on the fly, it should no-op. SetLevel(level Level) // Return a value that conforms to the stdlib log.Logger interface StandardLogger(opts *StandardLoggerOptions) *log.Logger // Return a value that conforms to io.Writer, which can be passed into log.SetOutput() StandardWriter(opts *StandardLoggerOptions) io.Writer } // StandardLoggerOptions can be used to configure a new standard logger. type StandardLoggerOptions struct { // Indicate that some minimal parsing should be done on strings to try // and detect their level and re-emit them. // This supports the strings like [ERROR], [ERR] [TRACE], [WARN], [INFO], // [DEBUG] and strip it off before reapplying it. InferLevels bool // ForceLevel is used to force all output from the standard logger to be at // the specified level. Similar to InferLevels, this will strip any level // prefix contained in the logged string before applying the forced level. // If set, this override InferLevels. ForceLevel Level } // LoggerOptions can be used to configure a new logger. type LoggerOptions struct { // Name of the subsystem to prefix logs with Name string // The threshold for the logger. Anything less severe is supressed Level Level // Where to write the logs to. Defaults to os.Stderr if nil Output io.Writer // An optional mutex pointer in case Output is shared Mutex *sync.Mutex // Control if the output should be in JSON. JSONFormat bool // Include file and line information in each log line IncludeLocation bool // The time format to use instead of the default TimeFormat string }