github.com/ojongerius/docker@v1.11.2/daemon/logger/syslog/syslog.go (about) 1 // +build linux 2 3 // Package syslog provides the logdriver for forwarding server logs to syslog endpoints. 4 package syslog 5 6 import ( 7 "crypto/tls" 8 "errors" 9 "fmt" 10 "net" 11 "net/url" 12 "os" 13 "path" 14 "strconv" 15 "strings" 16 "time" 17 18 syslog "github.com/RackSec/srslog" 19 20 "github.com/Sirupsen/logrus" 21 "github.com/docker/docker/daemon/logger" 22 "github.com/docker/docker/daemon/logger/loggerutils" 23 "github.com/docker/docker/pkg/urlutil" 24 "github.com/docker/go-connections/tlsconfig" 25 ) 26 27 const ( 28 name = "syslog" 29 secureProto = "tcp+tls" 30 ) 31 32 var facilities = map[string]syslog.Priority{ 33 "kern": syslog.LOG_KERN, 34 "user": syslog.LOG_USER, 35 "mail": syslog.LOG_MAIL, 36 "daemon": syslog.LOG_DAEMON, 37 "auth": syslog.LOG_AUTH, 38 "syslog": syslog.LOG_SYSLOG, 39 "lpr": syslog.LOG_LPR, 40 "news": syslog.LOG_NEWS, 41 "uucp": syslog.LOG_UUCP, 42 "cron": syslog.LOG_CRON, 43 "authpriv": syslog.LOG_AUTHPRIV, 44 "ftp": syslog.LOG_FTP, 45 "local0": syslog.LOG_LOCAL0, 46 "local1": syslog.LOG_LOCAL1, 47 "local2": syslog.LOG_LOCAL2, 48 "local3": syslog.LOG_LOCAL3, 49 "local4": syslog.LOG_LOCAL4, 50 "local5": syslog.LOG_LOCAL5, 51 "local6": syslog.LOG_LOCAL6, 52 "local7": syslog.LOG_LOCAL7, 53 } 54 55 type syslogger struct { 56 writer *syslog.Writer 57 } 58 59 func init() { 60 if err := logger.RegisterLogDriver(name, New); err != nil { 61 logrus.Fatal(err) 62 } 63 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 64 logrus.Fatal(err) 65 } 66 } 67 68 // rsyslog uses appname part of syslog message to fill in an %syslogtag% template 69 // attribute in rsyslog.conf. In order to be backward compatible to rfc3164 70 // tag will be also used as an appname 71 func rfc5424formatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content string) string { 72 timestamp := time.Now().Format(time.RFC3339) 73 pid := os.Getpid() 74 msg := fmt.Sprintf("<%d>%d %s %s %s %d %s %s", 75 p, 1, timestamp, hostname, tag, pid, tag, content) 76 return msg 77 } 78 79 // New creates a syslog logger using the configuration passed in on 80 // the context. Supported context configuration variables are 81 // syslog-address, syslog-facility, & syslog-tag. 82 func New(ctx logger.Context) (logger.Logger, error) { 83 tag, err := loggerutils.ParseLogTag(ctx, "{{.ID}}") 84 if err != nil { 85 return nil, err 86 } 87 88 proto, address, err := parseAddress(ctx.Config["syslog-address"]) 89 if err != nil { 90 return nil, err 91 } 92 93 facility, err := parseFacility(ctx.Config["syslog-facility"]) 94 if err != nil { 95 return nil, err 96 } 97 98 syslogFormatter, syslogFramer, err := parseLogFormat(ctx.Config["syslog-format"]) 99 if err != nil { 100 return nil, err 101 } 102 103 logTag := path.Base(os.Args[0]) + "/" + tag 104 105 var log *syslog.Writer 106 if proto == secureProto { 107 tlsConfig, tlsErr := parseTLSConfig(ctx.Config) 108 if tlsErr != nil { 109 return nil, tlsErr 110 } 111 log, err = syslog.DialWithTLSConfig(proto, address, facility, logTag, tlsConfig) 112 } else { 113 log, err = syslog.Dial(proto, address, facility, logTag) 114 } 115 116 if err != nil { 117 return nil, err 118 } 119 120 log.SetFormatter(syslogFormatter) 121 log.SetFramer(syslogFramer) 122 123 return &syslogger{ 124 writer: log, 125 }, nil 126 } 127 128 func (s *syslogger) Log(msg *logger.Message) error { 129 if msg.Source == "stderr" { 130 return s.writer.Err(string(msg.Line)) 131 } 132 return s.writer.Info(string(msg.Line)) 133 } 134 135 func (s *syslogger) Close() error { 136 return s.writer.Close() 137 } 138 139 func (s *syslogger) Name() string { 140 return name 141 } 142 143 func parseAddress(address string) (string, string, error) { 144 if address == "" { 145 return "", "", nil 146 } 147 if !urlutil.IsTransportURL(address) { 148 return "", "", fmt.Errorf("syslog-address should be in form proto://address, got %v", address) 149 } 150 url, err := url.Parse(address) 151 if err != nil { 152 return "", "", err 153 } 154 155 // unix socket validation 156 if url.Scheme == "unix" { 157 if _, err := os.Stat(url.Path); err != nil { 158 return "", "", err 159 } 160 return url.Scheme, url.Path, nil 161 } 162 163 // here we process tcp|udp 164 host := url.Host 165 if _, _, err := net.SplitHostPort(host); err != nil { 166 if !strings.Contains(err.Error(), "missing port in address") { 167 return "", "", err 168 } 169 host = host + ":514" 170 } 171 172 return url.Scheme, host, nil 173 } 174 175 // ValidateLogOpt looks for syslog specific log options 176 // syslog-address, syslog-facility, & syslog-tag. 177 func ValidateLogOpt(cfg map[string]string) error { 178 for key := range cfg { 179 switch key { 180 case "syslog-address": 181 case "syslog-facility": 182 case "syslog-tag": 183 case "syslog-tls-ca-cert": 184 case "syslog-tls-cert": 185 case "syslog-tls-key": 186 case "syslog-tls-skip-verify": 187 case "tag": 188 case "syslog-format": 189 default: 190 return fmt.Errorf("unknown log opt '%s' for syslog log driver", key) 191 } 192 } 193 if _, _, err := parseAddress(cfg["syslog-address"]); err != nil { 194 return err 195 } 196 if _, err := parseFacility(cfg["syslog-facility"]); err != nil { 197 return err 198 } 199 if _, _, err := parseLogFormat(cfg["syslog-format"]); err != nil { 200 return err 201 } 202 return nil 203 } 204 205 func parseFacility(facility string) (syslog.Priority, error) { 206 if facility == "" { 207 return syslog.LOG_DAEMON, nil 208 } 209 210 if syslogFacility, valid := facilities[facility]; valid { 211 return syslogFacility, nil 212 } 213 214 fInt, err := strconv.Atoi(facility) 215 if err == nil && 0 <= fInt && fInt <= 23 { 216 return syslog.Priority(fInt << 3), nil 217 } 218 219 return syslog.Priority(0), errors.New("invalid syslog facility") 220 } 221 222 func parseTLSConfig(cfg map[string]string) (*tls.Config, error) { 223 _, skipVerify := cfg["syslog-tls-skip-verify"] 224 225 opts := tlsconfig.Options{ 226 CAFile: cfg["syslog-tls-ca-cert"], 227 CertFile: cfg["syslog-tls-cert"], 228 KeyFile: cfg["syslog-tls-key"], 229 InsecureSkipVerify: skipVerify, 230 } 231 232 return tlsconfig.Client(opts) 233 } 234 235 func parseLogFormat(logFormat string) (syslog.Formatter, syslog.Framer, error) { 236 switch logFormat { 237 case "": 238 return syslog.UnixFormatter, syslog.DefaultFramer, nil 239 case "rfc3164": 240 return syslog.RFC3164Formatter, syslog.DefaultFramer, nil 241 case "rfc5424": 242 return rfc5424formatterWithAppNameAsTag, syslog.RFC5425MessageLengthFramer, nil 243 default: 244 return nil, nil, errors.New("Invalid syslog format") 245 } 246 247 }