github.com/dpiddy/docker@v1.12.2-rc1/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 "time" 12 13 "github.com/Sirupsen/logrus" 14 "github.com/docker/docker/daemon/logger" 15 "github.com/docker/docker/daemon/logger/loggerutils" 16 "github.com/docker/go-units" 17 "github.com/fluent/fluent-logger-golang/fluent" 18 ) 19 20 type fluentd struct { 21 tag string 22 containerID string 23 containerName string 24 writer *fluent.Fluent 25 extra map[string]string 26 } 27 28 const ( 29 name = "fluentd" 30 31 defaultHost = "127.0.0.1" 32 defaultPort = 24224 33 defaultBufferLimit = 1024 * 1024 34 defaultTagPrefix = "docker" 35 36 // logger tries to reconnect 2**32 - 1 times 37 // failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds] 38 defaultRetryWait = 1000 39 defaultTimeout = 3 * time.Second 40 defaultMaxRetries = math.MaxInt32 41 defaultReconnectWaitIncreRate = 1.5 42 43 addressKey = "fluentd-address" 44 bufferLimitKey = "fluentd-buffer-limit" 45 retryWaitKey = "fluentd-retry-wait" 46 maxRetriesKey = "fluentd-max-retries" 47 asyncConnectKey = "fluentd-async-connect" 48 ) 49 50 func init() { 51 if err := logger.RegisterLogDriver(name, New); err != nil { 52 logrus.Fatal(err) 53 } 54 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 55 logrus.Fatal(err) 56 } 57 } 58 59 // New creates a fluentd logger using the configuration passed in on 60 // the context. The supported context configuration variable is 61 // fluentd-address. 62 func New(ctx logger.Context) (logger.Logger, error) { 63 host, port, err := parseAddress(ctx.Config[addressKey]) 64 if err != nil { 65 return nil, err 66 } 67 68 tag, err := loggerutils.ParseLogTag(ctx, "{{.DaemonName}}.{{.ID}}") 69 if err != nil { 70 return nil, err 71 } 72 73 extra := ctx.ExtraAttributes(nil) 74 75 bufferLimit := defaultBufferLimit 76 if ctx.Config[bufferLimitKey] != "" { 77 bl64, err := units.RAMInBytes(ctx.Config[bufferLimitKey]) 78 if err != nil { 79 return nil, err 80 } 81 bufferLimit = int(bl64) 82 } 83 84 retryWait := defaultRetryWait 85 if ctx.Config[retryWaitKey] != "" { 86 rwd, err := time.ParseDuration(ctx.Config[retryWaitKey]) 87 if err != nil { 88 return nil, err 89 } 90 retryWait = int(rwd.Seconds() * 1000) 91 } 92 93 maxRetries := defaultMaxRetries 94 if ctx.Config[maxRetriesKey] != "" { 95 mr64, err := strconv.ParseUint(ctx.Config[maxRetriesKey], 10, strconv.IntSize) 96 if err != nil { 97 return nil, err 98 } 99 maxRetries = int(mr64) 100 } 101 102 asyncConnect := false 103 if ctx.Config[asyncConnectKey] != "" { 104 if asyncConnect, err = strconv.ParseBool(ctx.Config[asyncConnectKey]); err != nil { 105 return nil, err 106 } 107 } 108 109 fluentConfig := fluent.Config{ 110 FluentPort: port, 111 FluentHost: host, 112 BufferLimit: bufferLimit, 113 RetryWait: retryWait, 114 MaxRetry: maxRetries, 115 AsyncConnect: asyncConnect, 116 } 117 118 logrus.WithField("container", ctx.ContainerID).WithField("config", fluentConfig). 119 Debug("logging driver fluentd configured") 120 121 log, err := fluent.New(fluentConfig) 122 if err != nil { 123 return nil, err 124 } 125 return &fluentd{ 126 tag: tag, 127 containerID: ctx.ContainerID, 128 containerName: ctx.ContainerName, 129 writer: log, 130 extra: extra, 131 }, nil 132 } 133 134 func (f *fluentd) Log(msg *logger.Message) error { 135 data := map[string]string{ 136 "container_id": f.containerID, 137 "container_name": f.containerName, 138 "source": msg.Source, 139 "log": string(msg.Line), 140 } 141 for k, v := range f.extra { 142 data[k] = v 143 } 144 // fluent-logger-golang buffers logs from failures and disconnections, 145 // and these are transferred again automatically. 146 return f.writer.PostWithTime(f.tag, msg.Timestamp, data) 147 } 148 149 func (f *fluentd) Close() error { 150 return f.writer.Close() 151 } 152 153 func (f *fluentd) Name() string { 154 return name 155 } 156 157 // ValidateLogOpt looks for fluentd specific log option fluentd-address. 158 func ValidateLogOpt(cfg map[string]string) error { 159 for key := range cfg { 160 switch key { 161 case "env": 162 case "labels": 163 case "tag": 164 case addressKey: 165 case bufferLimitKey: 166 case retryWaitKey: 167 case maxRetriesKey: 168 case asyncConnectKey: 169 // Accepted 170 default: 171 return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key) 172 } 173 } 174 175 if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil { 176 return err 177 } 178 179 return nil 180 } 181 182 func parseAddress(address string) (string, int, error) { 183 if address == "" { 184 return defaultHost, defaultPort, nil 185 } 186 187 host, port, err := net.SplitHostPort(address) 188 if err != nil { 189 if !strings.Contains(err.Error(), "missing port in address") { 190 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 191 } 192 return host, defaultPort, nil 193 } 194 195 portnum, err := strconv.Atoi(port) 196 if err != nil { 197 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 198 } 199 return host, portnum, nil 200 }