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