github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/daemon/logger/fluentd/fluentd.go (about) 1 // Package fluentd provides the log driver for forwarding server logs 2 // to fluentd endpoints. 3 package fluentd 4 5 import ( 6 "fmt" 7 "math" 8 "net" 9 "strconv" 10 "strings" 11 12 "github.com/Sirupsen/logrus" 13 "github.com/docker/docker/daemon/logger" 14 "github.com/docker/docker/daemon/logger/loggerutils" 15 "github.com/fluent/fluent-logger-golang/fluent" 16 ) 17 18 type fluentd struct { 19 tag string 20 containerID string 21 containerName string 22 writer *fluent.Fluent 23 extra map[string]string 24 } 25 26 const ( 27 name = "fluentd" 28 defaultHostName = "localhost" 29 defaultPort = 24224 30 defaultTagPrefix = "docker" 31 defaultIgnoreConnectErrorOnStart = false // So that we do not break existing behaviour 32 defaultBufferLimit = 1 * 1024 * 1024 // 1M buffer by default 33 ) 34 35 func init() { 36 if err := logger.RegisterLogDriver(name, New); err != nil { 37 logrus.Fatal(err) 38 } 39 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 40 logrus.Fatal(err) 41 } 42 } 43 44 // New creates a fluentd logger using the configuration passed in on 45 // the context. Supported context configuration variables are 46 // fluentd-address & fluentd-tag. 47 func New(ctx logger.Context) (logger.Logger, error) { 48 host, port, err := parseAddress(ctx.Config["fluentd-address"]) 49 if err != nil { 50 return nil, err 51 } 52 53 tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}") 54 if err != nil { 55 return nil, err 56 } 57 failOnStartupError, err := loggerutils.ParseFailOnStartupErrorFlag(ctx) 58 if err != nil { 59 return nil, err 60 } 61 bufferLimit, err := parseBufferLimit(ctx.Config["buffer-limit"]) 62 if err != nil { 63 return nil, err 64 } 65 extra := ctx.ExtraAttributes(nil) 66 logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s, extra:%v.", ctx.ContainerID, host, port, tag, extra) 67 // logger tries to reconnect 2**32 - 1 times 68 // failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds] 69 log, err := fluent.New(fluent.Config{FluentPort: port, FluentHost: host, RetryWait: 1000, MaxRetry: math.MaxInt32, BufferLimit: bufferLimit}) 70 if err != nil { 71 if failOnStartupError { 72 return nil, err 73 } 74 logrus.Warnf("fluentd cannot connect to configured endpoint. Ignoring as instructed. Error: %q", err) 75 } 76 return &fluentd{ 77 tag: tag, 78 containerID: ctx.ContainerID, 79 containerName: ctx.ContainerName, 80 writer: log, 81 extra: extra, 82 }, nil 83 } 84 85 func (f *fluentd) Log(msg *logger.Message) error { 86 data := map[string]string{ 87 "container_id": f.containerID, 88 "container_name": f.containerName, 89 "source": msg.Source, 90 "log": string(msg.Line), 91 } 92 for k, v := range f.extra { 93 data[k] = v 94 } 95 // fluent-logger-golang buffers logs from failures and disconnections, 96 // and these are transferred again automatically. 97 return f.writer.PostWithTime(f.tag, msg.Timestamp, data) 98 } 99 100 func (f *fluentd) Close() error { 101 return f.writer.Close() 102 } 103 104 func (f *fluentd) Name() string { 105 return name 106 } 107 108 // ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag. 109 func ValidateLogOpt(cfg map[string]string) error { 110 for key := range cfg { 111 switch key { 112 case "fluentd-address": 113 case "fluentd-tag": 114 case "tag": 115 case "labels": 116 case "env": 117 case "fail-on-startup-error": 118 case "buffer-limit": 119 default: 120 return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key) 121 } 122 } 123 124 if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil { 125 return err 126 } 127 128 return nil 129 } 130 131 func parseAddress(address string) (string, int, error) { 132 if address == "" { 133 return defaultHostName, defaultPort, nil 134 } 135 136 host, port, err := net.SplitHostPort(address) 137 if err != nil { 138 if !strings.Contains(err.Error(), "missing port in address") { 139 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 140 } 141 return host, defaultPort, nil 142 } 143 144 portnum, err := strconv.Atoi(port) 145 if err != nil { 146 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 147 } 148 return host, portnum, nil 149 } 150 151 func parseBufferLimit(bufferLimit string) (int, error) { 152 if bufferLimit == "" { 153 return defaultBufferLimit, nil 154 } 155 limit, err := strconv.Atoi(bufferLimit) 156 if err != nil { 157 return 0, fmt.Errorf("invalid buffer limit %s: %s", bufferLimit, err) 158 } 159 return limit, nil 160 }