package log import ( "fmt" "io" "log/syslog" "os" "path/filepath" "strings" "github.com/Sirupsen/logrus" lsyslog "github.com/Sirupsen/logrus/hooks/syslog" "github.com/rancher/os/config/cmdline" ) var logFile *os.File var userHook *ShowuserlogHook var defaultLogLevel logrus.Level var debugThisLogger = false type Fields logrus.Fields type Level logrus.Level type Logger logrus.Logger const ( // PanicLevel level, highest level of severity. Logs and then calls panic with the // message passed to Debug, Info, ... PanicLevel Level = iota // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the // logging level is set to Panic. FatalLevel // ErrorLevel level. Logs. Used for errors that should definitely be noted. // Commonly used for hooks to send errors to an error tracking service. ErrorLevel // WarnLevel level. Non-critical entries that deserve eyes. WarnLevel // InfoLevel level. General operational entries about what's going on inside the // application. InfoLevel // DebugLevel level. Usually only enabled when debugging. Very verbose logging. DebugLevel ) func SetOutput(out io.Writer) { logrus.SetOutput(out) } func SetDefaultLevel(level Level) { defaultLogLevel = logrus.Level(level) } func SetLevel(level Level) { if userHook != nil { userHook.Level = logrus.Level(level) } else { logrus.SetLevel(logrus.Level(level)) } } func GetLevel() Level { if userHook != nil { return Level(userHook.Level) } return Level(logrus.GetLevel()) } func Debugf(format string, args ...interface{}) { logrus.Debugf(format, args...) } func Infof(format string, args ...interface{}) { logrus.Infof(format, args...) } func Printf(format string, args ...interface{}) { logrus.Printf(format, args...) } func Warnf(format string, args ...interface{}) { logrus.Warnf(format, args...) } func Warningf(format string, args ...interface{}) { logrus.Warningf(format, args...) } func Errorf(format string, args ...interface{}) { logrus.Errorf(format, args...) } func Fatalf(format string, args ...interface{}) { logrus.Fatalf(format, args...) } func Panicf(format string, args ...interface{}) { logrus.Panicf(format, args...) } func Debug(args ...interface{}) { logrus.Debug(args...) } func Info(args ...interface{}) { logrus.Info(args...) } func Print(args ...interface{}) { logrus.Print(args...) } func Warn(args ...interface{}) { logrus.Warn(args...) } func Warning(args ...interface{}) { logrus.Warning(args...) } func Error(args ...interface{}) { logrus.Error(args...) } func Fatal(args ...interface{}) { logrus.Fatal(args...) } func Panic(args ...interface{}) { logrus.Panic(args...) } func WithField(key string, value interface{}) *logrus.Entry { return logrus.WithField(key, value) } func WithFields(fields Fields) *logrus.Entry { return logrus.WithFields(logrus.Fields(fields)) } // InitLogger sets up Logging to log to /dev/kmsg and to Syslog func InitLogger() { if logTheseApps() { innerInit(false) FsReady() AddRSyslogHook() pwd, err := os.Getwd() if err != nil { logrus.Error(err) } logrus.Debugf("START: %v in %s", os.Args, pwd) } } func logTheseApps() bool { // TODO: mmm, not very functional. if filepath.Base(os.Args[0]) == "ros" || // filepath.Base(os.Args[0]) == "system-docker" || filepath.Base(os.Args[0]) == "host_ros" { return false } return true } // InitDeferedLogger stores the log messages until FsReady() is called // TODO: actually store them :) // TODO: need to work out how to pass entries from a binary run before we switchfs back to init and have it store and write it later func InitDeferedLogger() { if logTheseApps() { innerInit(true) //logrus.SetOutput(ioutil.Discard) // write to dmesg until we can write to file. (maybe we can do this if rancher.debug=true?) f, err := os.OpenFile("/dev/kmsg", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err == nil { logFile = f logrus.SetOutput(logFile) } pwd, err := os.Getwd() if err != nil { logrus.Error(err) } logrus.Debugf("START: %v in %s", os.Args, pwd) } } func innerInit(deferedHook bool) { if userHook != nil { return // we've already initialised it } // All logs go through the Hooks, and they choose what to do with them. logrus.StandardLogger().Level = logrus.DebugLevel if logTheseApps() { AddUserHook(deferedHook) } } // AddRSyslogHook only needs to be called separately when using the InitDeferedLogger // init.Main can't read /proc/cmdline at start. // and then fails due to the network not being up // TODO: create a "defered SyslogHook that always gets initialised, but if it fails to connect, stores the logs // and retries connecting every time its triggered.... func AddRSyslogHook() { val := cmdline.GetCmdline("netconsole") netconsole := val.(string) if netconsole != "" { // "loglevel=8 netconsole=9999@10.0.2.14/,514@192.168.33.148/" // 192.168.33.148:514 n := strings.Split(netconsole, ",") if len(n) == 2 { d := strings.Split(n[1], "@") if len(d) == 2 { netconsoleDestination := fmt.Sprintf("%s:%s", strings.TrimRight(d[1], "/"), d[0]) hook, err := lsyslog.NewSyslogHook("udp", netconsoleDestination, syslog.LOG_DEBUG, "") if err == nil { logrus.StandardLogger().Hooks.Add(hook) Infof("Sending RancherOS Logs to: %s", netconsoleDestination) } else { Errorf("Error creating SyslogHook: %s", err) } } } } } func FsReady() { filename := "/var/log/boot/" + filepath.Base(os.Args[0]) + ".log" if err := os.MkdirAll(filepath.Dir(filename), os.ModeDir|0755); debugThisLogger && err != nil { logrus.Errorf("FsReady mkdir(%s): %s", filename, err) } f, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { if debugThisLogger { logrus.Errorf("FsReady opening %s: %s", filename, err) } } else { if debugThisLogger { logrus.Infof("Setting log output for %s to: %s", os.Args[0], filename) } logFile = f logrus.SetOutput(logFile) } } // AddUserHook is used to filter what log messages are written to the screen func AddUserHook(deferedHook bool) error { if userHook != nil { return nil } printLogLevel := logrus.InfoLevel uh, err := NewShowuserlogHook(printLogLevel, filepath.Base(os.Args[0])) if err != nil { logrus.Errorf("error creating userHook(%s): %s", os.Args[0], err) return err } userHook = uh logrus.StandardLogger().Hooks.Add(uh) if debugThisLogger { if deferedHook { logrus.Debugf("------------info Starting defered User Hook (%s)", os.Args[0]) } else { logrus.Debugf("------------info Starting User Hook (%s)", os.Args[0]) } } return nil }