Add a logging hook "syslog" for logging to the system syslog. I didn't contribute this patch to the upstream because I don't wanna contribute to any Go projects. However, if you find this patch useful, feel free to send it to the upstream under your name. --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -42,6 +42,9 @@ "github.com/garyburd/redigo/redis" "github.com/gorilla/mux" "github.com/sirupsen/logrus" + + "log/syslog" + lSyslog "github.com/sirupsen/logrus/hooks/syslog" ) // randomSecretSize is the number of random bytes to generate if no secret @@ -600,6 +603,11 @@ To: configHook.MailOptions.To, } logger.Hooks.Add(hook) + case "syslog": + hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_DAEMON | syslog.LOG_INFO, "docker-registry") + if err == nil { + logger.Hooks.Add(hook) + } default: } } --- a/registry/registry.go +++ b/registry/registry.go @@ -32,4 +32,6 @@ "github.com/spf13/cobra" "github.com/yvasiyarov/gorelic" + + "bytes" ) @@ -367,6 +369,8 @@ log.SetFormatter(&logstash.LogstashFormatter{ TimestampFormat: time.RFC3339Nano, }) + case "syslog": + log.SetFormatter(&SyslogFormatter{}) default: // just let the library use default on empty string. if config.Log.Formatter != "" { @@ -390,6 +394,50 @@ } return ctx, nil +} + +type SyslogFormatter struct { +} + +func (f *SyslogFormatter) Format(entry *log.Entry) ([]byte, error) { + var b *bytes.Buffer + if entry.Buffer != nil { + b = entry.Buffer + } else { + b = &bytes.Buffer{} + } + + if entry.Message != "" { + b.WriteString(entry.Message) + } + dataWritten := false + for key, val := range entry.Data { + switch key { + // Omit these keys in syslog for brevity. + case "go.version", "instance.id", "version": + continue + } + + stringVal, ok := val.(string) + if !ok { + stringVal = fmt.Sprint(val) + } + + // The format is inspired by RFC 5424 STRUCTURED-DATA. + if dataWritten { + b.WriteByte(' ') + } else { + b.WriteString(" [meta ") + dataWritten = true + } + b.WriteString(fmt.Sprintf("%s=%q", key, stringVal)) + } + if dataWritten { + b.WriteByte(']') + } + b.WriteByte('\n') + + return b.Bytes(), nil } func logLevel(level configuration.Loglevel) log.Level { --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go @@ -0,0 +1,54 @@ +// +build !windows,!nacl,!plan9 + +package logrus_syslog + +import ( + "fmt" + "github.com/sirupsen/logrus" + "log/syslog" + "os" +) + +// SyslogHook to send logs via syslog. +type SyslogHook struct { + Writer *syslog.Writer + SyslogNetwork string + SyslogRaddr string +} + +// Creates a hook to be added to an instance of logger. This is called with +// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")` +// `if err == nil { log.Hooks.Add(hook) }` +func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) { + w, err := syslog.Dial(network, raddr, priority, tag) + return &SyslogHook{w, network, raddr}, err +} + +func (hook *SyslogHook) Fire(entry *logrus.Entry) error { + line, err := entry.String() + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) + return err + } + + switch entry.Level { + case logrus.PanicLevel: + return hook.Writer.Crit(line) + case logrus.FatalLevel: + return hook.Writer.Crit(line) + case logrus.ErrorLevel: + return hook.Writer.Err(line) + case logrus.WarnLevel: + return hook.Writer.Warning(line) + case logrus.InfoLevel: + return hook.Writer.Info(line) + case logrus.DebugLevel: + return hook.Writer.Debug(line) + default: + return nil + } +} + +func (hook *SyslogHook) Levels() []logrus.Level { + return logrus.AllLevels +}