github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/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 ) 32 33 func init() { 34 if err := logger.RegisterLogDriver(name, New); err != nil { 35 logrus.Fatal(err) 36 } 37 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 38 logrus.Fatal(err) 39 } 40 } 41 42 // New creates a fluentd logger using the configuration passed in on 43 // the context. Supported context configuration variables are 44 // fluentd-address & fluentd-tag. 45 func New(ctx logger.Context) (logger.Logger, error) { 46 host, port, err := parseAddress(ctx.Config["fluentd-address"]) 47 if err != nil { 48 return nil, err 49 } 50 51 tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}") 52 if err != nil { 53 return nil, err 54 } 55 extra := ctx.ExtraAttributes(nil) 56 logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s, extra:%v.", ctx.ContainerID, host, port, tag, extra) 57 // logger tries to reconnect 2**32 - 1 times 58 // failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds] 59 log, err := fluent.New(fluent.Config{FluentPort: port, FluentHost: host, RetryWait: 1000, MaxRetry: math.MaxInt32}) 60 if err != nil { 61 return nil, err 62 } 63 return &fluentd{ 64 tag: tag, 65 containerID: ctx.ContainerID, 66 containerName: ctx.ContainerName, 67 writer: log, 68 extra: extra, 69 }, nil 70 } 71 72 func (f *fluentd) Log(msg *logger.Message) error { 73 data := map[string]string{ 74 "container_id": f.containerID, 75 "container_name": f.containerName, 76 "source": msg.Source, 77 "log": string(msg.Line), 78 } 79 for k, v := range f.extra { 80 data[k] = v 81 } 82 // fluent-logger-golang buffers logs from failures and disconnections, 83 // and these are transferred again automatically. 84 return f.writer.PostWithTime(f.tag, msg.Timestamp, data) 85 } 86 87 func (f *fluentd) Close() error { 88 return f.writer.Close() 89 } 90 91 func (f *fluentd) Name() string { 92 return name 93 } 94 95 // ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag. 96 func ValidateLogOpt(cfg map[string]string) error { 97 for key := range cfg { 98 switch key { 99 case "fluentd-address": 100 case "fluentd-tag": 101 case "tag": 102 case "labels": 103 case "env": 104 default: 105 return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key) 106 } 107 } 108 109 if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil { 110 return err 111 } 112 113 return nil 114 } 115 116 func parseAddress(address string) (string, int, error) { 117 if address == "" { 118 return defaultHostName, defaultPort, nil 119 } 120 121 host, port, err := net.SplitHostPort(address) 122 if err != nil { 123 if !strings.Contains(err.Error(), "missing port in address") { 124 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 125 } 126 return host, defaultPort, nil 127 } 128 129 portnum, err := strconv.Atoi(port) 130 if err != nil { 131 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 132 } 133 return host, portnum, nil 134 }