package flume import ( "encoding/json" "fmt" "go.uber.org/zap/zapcore" "os" "strings" "time" ) // DefaultConfigEnvVars is a list of the environment variables // that ConfigFromEnv will search by default. var DefaultConfigEnvVars = []string{"FLUME"} // ConfigFromEnv configures flume from environment variables. // It should be called from main(): // // func main() { // flume.ConfigFromEnv() // ... // } // // It searches envvars for the first environment // variable that is set, and attempts to parse the value. // // If no environment variable is set, it silently does nothing. // // If an environment variable with a value is found, but parsing // fails, an error is printed to stdout, and the error is returned. // // If envvars is empty, it defaults to DefaultConfigEnvVars. // func ConfigFromEnv(envvars ...string) error { if len(envvars) == 0 { envvars = DefaultConfigEnvVars } var configString string for _, v := range envvars { configString = os.Getenv(v) if configString != "" { err := ConfigString(configString) if err != nil { fmt.Println("error parsing log config from env var " + v + ": " + err.Error()) } return err } } return nil } // Config offers a declarative way to configure a Factory. // // The same things can be done by calling Factory methods, but // Configs can be unmarshaled from JSON, making it a convenient // way to configure most logging options from env vars or files, i.e.: // // err := flume.ConfigString(os.Getenv("flume")) // // Configs can be created and applied programmatically: // // err := flume.Configure(flume.Config{}) // // Defaults are appropriate for a JSON encoded production logger: // // - LTSV encoder // - full timestamps // - default log level set to INFO // - call sites are not logged // // An alternate set of defaults, more appropriate for development environments, // can be configured with `Config{Development:true}`: // // err := flume.Configure(flume.Config{Development:true}) // // - colorized terminal encoder // - short timestamps // - call sites are logged // // err := flume.Configure(flume.Config{Development:true}) // // Any of the other configuration options can be specified to override // the defaults. // // Note: If configuring the EncoderConfig setting, if any of the *Key properties // are omitted, that entire field will be omitted. type Config struct { // DefaultLevel is the default log level for all loggers not // otherwise configured by Levels. Defaults to Info. DefaultLevel Level `json:"level" yaml:"level"` // Levels configures log levels for particular named loggers. See // LevelsString for format. Levels string `json:"levels" yaml:"levels"` // AddCaller annotates logs with the calling function's file // name and line number. Defaults to true when the Development // flag is set, false otherwise. AddCaller *bool `json:"addCaller" yaml:"addCaller"` // Encoding sets the logger's encoding. Valid values are "json", // "console", "ltsv", "term", and "term-color". // Defaults to "term-color" if development is true, else // "ltsv" Encoding string `json:"encoding" yaml:"encoding"` // Development toggles the defaults used for the other // settings. Defaults to false. Development bool `json:"development" yaml:"development"` // EncoderConfig sets options for the chosen encoder. See // EncoderConfig for details. Defaults to NewEncoderConfig() if // Development is false, otherwise defaults to NewDevelopmentEncoderConfig(). EncoderConfig *EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"` } // SetAddCaller sets the Config's AddCaller flag. func (c *Config) SetAddCaller(b bool) { c.AddCaller = &b } // UnsetAddCaller unsets the Config's AddCaller flag (reverting to defaults). func (c *Config) UnsetAddCaller() { c.AddCaller = nil } // EncoderConfig captures the options for encoders. // Type alias to avoid exporting zap. type EncoderConfig zapcore.EncoderConfig type privEncCfg struct { EncodeLevel string `json:"levelEncoder" yaml:"levelEncoder"` EncodeTime string `json:"timeEncoder" yaml:"timeEncoder"` } // UnmarshalJSON implements json.Marshaler func (enc *EncoderConfig) UnmarshalJSON(b []byte) error { var zapCfg zapcore.EncoderConfig err := json.Unmarshal(b, &zapCfg) if err != nil { return err } var pc privEncCfg err = json.Unmarshal(b, &pc) if err == nil { switch pc.EncodeLevel { case "", "abbr": zapCfg.EncodeLevel = AbbrLevelEncoder } switch pc.EncodeTime { case "": zapCfg.EncodeTime = zapcore.ISO8601TimeEncoder case "justtime": zapCfg.EncodeTime = JustTimeEncoder } } *enc = EncoderConfig(zapCfg) return nil } // NewEncoderConfig returns an EncoderConfig with default settings. func NewEncoderConfig() *EncoderConfig { return &EncoderConfig{ MessageKey: "msg", TimeKey: "time", LevelKey: "level", NameKey: "name", CallerKey: "caller", StacktraceKey: "stacktrace", EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeLevel: AbbrLevelEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } } // NewDevelopmentEncoderConfig returns an EncoderConfig which is intended // for local development. func NewDevelopmentEncoderConfig() *EncoderConfig { cfg := NewEncoderConfig() cfg.EncodeTime = JustTimeEncoder cfg.EncodeDuration = zapcore.StringDurationEncoder return cfg } // JustTimeEncoder is a timestamp encoder function which encodes time // as a simple time of day, without a date. Intended for development and testing. // Not good in a production system, where you probably need to know the date. // // encConfig := flume.EncoderConfig{} // encConfig.EncodeTime = flume.JustTimeEncoder // func JustTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("15:04:05.000")) } // AbbrLevelEncoder encodes logging levels to the strings in the log entries. // Encodes levels as 3-char abbreviations in upper case. // // encConfig := flume.EncoderConfig{} // encConfig.EncodeTime = flume.AbbrLevelEncoder // func AbbrLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { switch l { case zapcore.DebugLevel: enc.AppendString("DBG") case zapcore.InfoLevel: enc.AppendString("INF") case zapcore.WarnLevel: enc.AppendString("WRN") case zapcore.ErrorLevel: enc.AppendString("ERR") case zapcore.PanicLevel, zapcore.FatalLevel, zapcore.DPanicLevel: enc.AppendString("FTL") default: s := l.String() if len(s) > 3 { s = s[:3] } enc.AppendString(strings.ToUpper(s)) } }