github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/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 } 24 25 const ( 26 name = "fluentd" 27 defaultHostName = "localhost" 28 defaultPort = 24224 29 defaultTagPrefix = "docker" 30 ) 31 32 func init() { 33 if err := logger.RegisterLogDriver(name, New); err != nil { 34 logrus.Fatal(err) 35 } 36 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 37 logrus.Fatal(err) 38 } 39 } 40 41 // New creates a fluentd logger using the configuration passed in on 42 // the context. Supported context configuration variables are 43 // fluentd-address & fluentd-tag. 44 func New(ctx logger.Context) (logger.Logger, error) { 45 host, port, err := parseAddress(ctx.Config["fluentd-address"]) 46 if err != nil { 47 return nil, err 48 } 49 50 tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}") 51 if err != nil { 52 return nil, err 53 } 54 55 logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s.", ctx.ContainerID, host, port, tag) 56 57 // logger tries to recoonect 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 }, nil 69 } 70 71 func (f *fluentd) Log(msg *logger.Message) error { 72 data := map[string]string{ 73 "container_id": f.containerID, 74 "container_name": f.containerName, 75 "source": msg.Source, 76 "log": string(msg.Line), 77 } 78 // fluent-logger-golang buffers logs from failures and disconnections, 79 // and these are transferred again automatically. 80 return f.writer.PostWithTime(f.tag, msg.Timestamp, data) 81 } 82 83 func (f *fluentd) Close() error { 84 return f.writer.Close() 85 } 86 87 func (f *fluentd) Name() string { 88 return name 89 } 90 91 // ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag. 92 func ValidateLogOpt(cfg map[string]string) error { 93 for key := range cfg { 94 switch key { 95 case "fluentd-address": 96 case "fluentd-tag": 97 case "tag": 98 default: 99 return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key) 100 } 101 } 102 103 if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil { 104 return err 105 } 106 107 return nil 108 } 109 110 func parseAddress(address string) (string, int, error) { 111 if address == "" { 112 return defaultHostName, defaultPort, nil 113 } 114 115 host, port, err := net.SplitHostPort(address) 116 if err != nil { 117 if !strings.Contains(err.Error(), "missing port in address") { 118 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 119 } 120 return host, defaultPort, nil 121 } 122 123 portnum, err := strconv.Atoi(port) 124 if err != nil { 125 return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err) 126 } 127 return host, portnum, nil 128 }