// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package log implements logging via logrus. // Implementation forked from github.com/prometheus/common package log import ( "fmt" "io" "io/ioutil" "log" "net/url" "os" "runtime" "strconv" "strings" "github.com/alecthomas/kingpin/v2" "github.com/sirupsen/logrus" ) // setSyslogFormatter is nil if the target architecture does not support syslog. var setSyslogFormatter func(logger, string, string) error // setEventlogFormatter is nil if the target OS does not support Eventlog (i.e., is not Windows). var setEventlogFormatter func(logger, string, bool) error func setJSONFormatter() { origLogger.Formatter = &logrus.JSONFormatter{} } type loggerSettings struct { level string format string } func (s *loggerSettings) apply(ctx *kingpin.ParseContext) error { err := baseLogger.SetLevel(s.level) if err != nil { return err } err = baseLogger.SetFormat(s.format) return err } // AddFlags adds the flags used by this package to the Kingpin application. // To use the default Kingpin application, call AddFlags(kingpin.CommandLine) func AddFlags(a *kingpin.Application) { s := loggerSettings{} a.Flag("log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]"). Default(origLogger.Level.String()). StringVar(&s.level) defaultFormat := url.URL{Scheme: "logger", Opaque: "stderr"} a.Flag("log.format", `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`). Default(defaultFormat.String()). StringVar(&s.format) a.Action(s.apply) } // Logger is the interface for loggers used in the Prometheus components. type Logger interface { Debug(...interface{}) Debugln(...interface{}) Debugf(string, ...interface{}) Info(...interface{}) Infoln(...interface{}) Infof(string, ...interface{}) Warn(...interface{}) Warnln(...interface{}) Warnf(string, ...interface{}) Error(...interface{}) Errorln(...interface{}) Errorf(string, ...interface{}) Fatal(...interface{}) Fatalln(...interface{}) Fatalf(string, ...interface{}) With(key string, value interface{}) Logger SetFormat(string) error SetLevel(string) error } type logger struct { entry *logrus.Entry } func (l logger) With(key string, value interface{}) Logger { return logger{l.entry.WithField(key, value)} } // Debug logs a message at level Debug on the standard logger. func (l logger) Debug(args ...interface{}) { l.sourced().Debug(args...) } // Debug logs a message at level Debug on the standard logger. func (l logger) Debugln(args ...interface{}) { l.sourced().Debugln(args...) } // Debugf logs a message at level Debug on the standard logger. func (l logger) Debugf(format string, args ...interface{}) { l.sourced().Debugf(format, args...) } // Info logs a message at level Info on the standard logger. func (l logger) Info(args ...interface{}) { l.sourced().Info(args...) } // Info logs a message at level Info on the standard logger. func (l logger) Infoln(args ...interface{}) { l.sourced().Infoln(args...) } // Infof logs a message at level Info on the standard logger. func (l logger) Infof(format string, args ...interface{}) { l.sourced().Infof(format, args...) } // Warn logs a message at level Warn on the standard logger. func (l logger) Warn(args ...interface{}) { l.sourced().Warn(args...) } // Warn logs a message at level Warn on the standard logger. func (l logger) Warnln(args ...interface{}) { l.sourced().Warnln(args...) } // Warnf logs a message at level Warn on the standard logger. func (l logger) Warnf(format string, args ...interface{}) { l.sourced().Warnf(format, args...) } // Error logs a message at level Error on the standard logger. func (l logger) Error(args ...interface{}) { l.sourced().Error(args...) } // Error logs a message at level Error on the standard logger. func (l logger) Errorln(args ...interface{}) { l.sourced().Errorln(args...) } // Errorf logs a message at level Error on the standard logger. func (l logger) Errorf(format string, args ...interface{}) { l.sourced().Errorf(format, args...) } // Fatal logs a message at level Fatal on the standard logger. func (l logger) Fatal(args ...interface{}) { l.sourced().Fatal(args...) } // Fatal logs a message at level Fatal on the standard logger. func (l logger) Fatalln(args ...interface{}) { l.sourced().Fatalln(args...) } // Fatalf logs a message at level Fatal on the standard logger. func (l logger) Fatalf(format string, args ...interface{}) { l.sourced().Fatalf(format, args...) } func (l logger) SetLevel(level string) error { lvl, err := logrus.ParseLevel(level) if err != nil { return err } l.entry.Logger.Level = lvl return nil } func (l logger) SetFormat(format string) error { u, err := url.Parse(format) if err != nil { return err } if u.Scheme != "logger" { return fmt.Errorf("invalid scheme %s", u.Scheme) } jsonq := u.Query().Get("json") if jsonq == "true" { setJSONFormatter() } switch u.Opaque { case "syslog": if setSyslogFormatter == nil { return fmt.Errorf("system does not support syslog") } appname := u.Query().Get("appname") facility := u.Query().Get("local") return setSyslogFormatter(l, appname, facility) case "eventlog": if setEventlogFormatter == nil { return fmt.Errorf("system does not support eventlog") } name := u.Query().Get("name") debugAsInfo := false debugAsInfoRaw := u.Query().Get("debugAsInfo") if parsedDebugAsInfo, err := strconv.ParseBool(debugAsInfoRaw); err == nil { debugAsInfo = parsedDebugAsInfo } return setEventlogFormatter(l, name, debugAsInfo) case "stdout": l.entry.Logger.Out = os.Stdout case "stderr": l.entry.Logger.Out = os.Stderr default: return fmt.Errorf("unsupported logger %q", u.Opaque) } return nil } // sourced adds a source field to the logger that contains // the file name and line where the logging happened. func (l logger) sourced() *logrus.Entry { _, file, line, ok := runtime.Caller(2) if !ok { file = "" line = 1 } else { slash := strings.LastIndex(file, "/") file = file[slash+1:] } return l.entry.WithField("source", fmt.Sprintf("%s:%d", file, line)) } var origLogger = logrus.New() var baseLogger = logger{entry: logrus.NewEntry(origLogger)} // Base returns the default Logger logging to func Base() Logger { return baseLogger } // NewLogger returns a new Logger logging to out. func NewLogger(w io.Writer) Logger { l := logrus.New() l.Out = w return logger{entry: logrus.NewEntry(l)} } // NewNopLogger returns a logger that discards all log messages. func NewNopLogger() Logger { l := logrus.New() l.Out = ioutil.Discard return logger{entry: logrus.NewEntry(l)} } // With adds a field to the logger. func With(key string, value interface{}) Logger { return baseLogger.With(key, value) } // Debug logs a message at level Debug on the standard logger. func Debug(args ...interface{}) { baseLogger.sourced().Debug(args...) } // Debugln logs a message at level Debug on the standard logger. func Debugln(args ...interface{}) { baseLogger.sourced().Debugln(args...) } // Debugf logs a message at level Debug on the standard logger. func Debugf(format string, args ...interface{}) { baseLogger.sourced().Debugf(format, args...) } // Info logs a message at level Info on the standard logger. func Info(args ...interface{}) { baseLogger.sourced().Info(args...) } // Infoln logs a message at level Info on the standard logger. func Infoln(args ...interface{}) { baseLogger.sourced().Infoln(args...) } // Infof logs a message at level Info on the standard logger. func Infof(format string, args ...interface{}) { baseLogger.sourced().Infof(format, args...) } // Warn logs a message at level Warn on the standard logger. func Warn(args ...interface{}) { baseLogger.sourced().Warn(args...) } // Warnln logs a message at level Warn on the standard logger. func Warnln(args ...interface{}) { baseLogger.sourced().Warnln(args...) } // Warnf logs a message at level Warn on the standard logger. func Warnf(format string, args ...interface{}) { baseLogger.sourced().Warnf(format, args...) } // Error logs a message at level Error on the standard logger. func Error(args ...interface{}) { baseLogger.sourced().Error(args...) } // Errorln logs a message at level Error on the standard logger. func Errorln(args ...interface{}) { baseLogger.sourced().Errorln(args...) } // Errorf logs a message at level Error on the standard logger. func Errorf(format string, args ...interface{}) { baseLogger.sourced().Errorf(format, args...) } // Fatal logs a message at level Fatal on the standard logger. func Fatal(args ...interface{}) { baseLogger.sourced().Fatal(args...) } // Fatalln logs a message at level Fatal on the standard logger. func Fatalln(args ...interface{}) { baseLogger.sourced().Fatalln(args...) } // Fatalf logs a message at level Fatal on the standard logger. func Fatalf(format string, args ...interface{}) { baseLogger.sourced().Fatalf(format, args...) } // AddHook adds hook to Prometheus' original logger. func AddHook(hook logrus.Hook) { origLogger.Hooks.Add(hook) } type errorLogWriter struct{} func (errorLogWriter) Write(b []byte) (int, error) { baseLogger.sourced().Error(string(b)) return len(b), nil } // NewErrorLogger returns a log.Logger that is meant to be used // in the ErrorLog field of an http.Server to log HTTP server errors. func NewErrorLogger() *log.Logger { return log.New(&errorLogWriter{}, "", 0) }