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
+}
