diff --git a/log/log.go b/log/log.go index d72ea532..d202b8f5 100644 --- a/log/log.go +++ b/log/log.go @@ -16,6 +16,8 @@ package log import ( "flag" "fmt" + "net/url" + "os" "runtime" "strings" @@ -39,10 +41,68 @@ func (f levelFlag) Set(level string) error { return nil } +func setSyslogFormatter(appname, facility string) error { + if appname == "" || facility == "" { + return fmt.Errorf("your missing a appname(%s) or local(%s)", appname, facility) + } + + fmter, err := newSyslogger(appname, facility, origLogger.Formatter) + if err != nil { + fmt.Fprintf(os.Stderr, "error creating syslog formatter: %v\n", err) + origLogger.Errorf("can't connect logger to syslog: %v", err) + return err + } + origLogger.Formatter = fmter + return nil +} + +func setJSONFormatter() { + origLogger.Formatter = &logrus.JSONFormatter{} +} + +type logFormatFlag struct{ uri string } + +// String implements flag.Value. +func (f logFormatFlag) String() string { + return f.uri +} + +// Set implements flag.Value. +func (f logFormatFlag) Set(format string) error { + f.uri = format + 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": + appname := u.Query().Get("appname") + facility := u.Query().Get("local") + return setSyslogFormatter(appname, facility) + case "stdout": + origLogger.Out = os.Stdout + case "stderr": + origLogger.Out = os.Stderr + + default: + return fmt.Errorf("unsupported logger %s", u.Opaque) + } + return nil +} + func init() { - // In order for this flag to take effect, the user of the package must call + // In order for these flags to take effect, the user of the package must call // flag.Parse() before logging anything. flag.Var(levelFlag{}, "log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal].") + flag.Var(logFormatFlag{}, "log.format", "If set use a syslog logger or JSON logging. Example: logger:syslog?appname=bob&local=7 or logger:stdout?json=true. Defaults to stderr.") } type Logger interface { diff --git a/log/syslog_formatter.go b/log/syslog_formatter.go new file mode 100644 index 00000000..d3c90d19 --- /dev/null +++ b/log/syslog_formatter.go @@ -0,0 +1,84 @@ +package log + +import ( + "fmt" + "log/syslog" + "os" + + "github.com/Sirupsen/logrus" +) + +var ceeTag = []byte("@cee:") + +type syslogger struct { + wrap logrus.Formatter + out *syslog.Writer +} + +func newSyslogger(appname string, facility string, fmter logrus.Formatter) (*syslogger, error) { + priority, err := getFacility(facility) + if err != nil { + return nil, err + } + out, err := syslog.New(priority, appname) + return &syslogger{ + out: out, + wrap: fmter, + }, err +} + +func getFacility(facility string) (syslog.Priority, error) { + switch facility { + case "0": + return syslog.LOG_LOCAL0, nil + case "1": + return syslog.LOG_LOCAL1, nil + case "2": + return syslog.LOG_LOCAL2, nil + case "3": + return syslog.LOG_LOCAL3, nil + case "4": + return syslog.LOG_LOCAL4, nil + case "5": + return syslog.LOG_LOCAL5, nil + case "6": + return syslog.LOG_LOCAL6, nil + case "7": + return syslog.LOG_LOCAL7, nil + } + return syslog.LOG_LOCAL0, fmt.Errorf("invalid local(%s) for syslog", facility) +} + +func (s *syslogger) Format(e *logrus.Entry) ([]byte, error) { + data, err := s.wrap.Format(e) + if err != nil { + fmt.Fprintf(os.Stderr, "syslogger: can't format entry: %v\n", err) + return data, err + } + // only append tag to data sent to syslog (line), not to what + // is returned + line := string(append(ceeTag, data...)) + + switch e.Level { + case logrus.PanicLevel: + err = s.out.Crit(line) + case logrus.FatalLevel: + err = s.out.Crit(line) + case logrus.ErrorLevel: + err = s.out.Err(line) + case logrus.WarnLevel: + err = s.out.Warning(line) + case logrus.InfoLevel: + err = s.out.Info(line) + case logrus.DebugLevel: + err = s.out.Debug(line) + default: + err = s.out.Notice(line) + } + + if err != nil { + fmt.Fprintf(os.Stderr, "syslogger: can't send log to syslog: %v\n", err) + } + + return data, err +}