github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/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 "bytes" 7 "fmt" 8 "math" 9 "net" 10 "strconv" 11 "strings" 12 "text/template" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/docker/daemon/logger" 16 "github.com/fluent/fluent-logger-golang/fluent" 17 ) 18 19 type fluentd struct { 20 tag string 21 containerID string 22 containerName string 23 writer *fluent.Fluent 24 } 25 26 type receiver struct { 27 ID string 28 FullID string 29 Name string 30 } 31 32 const ( 33 name = "fluentd" 34 defaultHostName = "localhost" 35 defaultPort = 24224 36 defaultTagPrefix = "docker" 37 ) 38 39 func init() { 40 if err := logger.RegisterLogDriver(name, New); err != nil { 41 logrus.Fatal(err) 42 } 43 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 44 logrus.Fatal(err) 45 } 46 } 47 48 func parseConfig(ctx logger.Context) (string, int, string, error) { 49 host := defaultHostName 50 port := defaultPort 51 tag := "docker." + ctx.ContainerID[:12] 52 53 config := ctx.Config 54 55 if address := config["fluentd-address"]; address != "" { 56 if h, p, err := net.SplitHostPort(address); err != nil { 57 if !strings.Contains(err.Error(), "missing port in address") { 58 return "", 0, "", err 59 } 60 host = h 61 } else { 62 portnum, err := strconv.Atoi(p) 63 if err != nil { 64 return "", 0, "", err 65 } 66 host = h 67 port = portnum 68 } 69 } 70 71 if config["fluentd-tag"] != "" { 72 receiver := &receiver{ 73 ID: ctx.ContainerID[:12], 74 FullID: ctx.ContainerID, 75 Name: ctx.ContainerName, 76 } 77 tmpl, err := template.New("tag").Parse(config["fluentd-tag"]) 78 if err != nil { 79 return "", 0, "", err 80 } 81 buf := new(bytes.Buffer) 82 if err := tmpl.Execute(buf, receiver); err != nil { 83 return "", 0, "", err 84 } 85 tag = buf.String() 86 } 87 88 return host, port, tag, nil 89 } 90 91 // New creates a fluentd logger using the configuration passed in on 92 // the context. Supported context configuration variables are 93 // fluentd-address & fluentd-tag. 94 func New(ctx logger.Context) (logger.Logger, error) { 95 host, port, tag, err := parseConfig(ctx) 96 if err != nil { 97 return nil, err 98 } 99 logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s.", ctx.ContainerID, host, port, tag) 100 101 // logger tries to recoonect 2**32 - 1 times 102 // failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds] 103 log, err := fluent.New(fluent.Config{FluentPort: port, FluentHost: host, RetryWait: 1000, MaxRetry: math.MaxInt32}) 104 if err != nil { 105 return nil, err 106 } 107 return &fluentd{ 108 tag: tag, 109 containerID: ctx.ContainerID, 110 containerName: ctx.ContainerName, 111 writer: log, 112 }, nil 113 } 114 115 func (f *fluentd) Log(msg *logger.Message) error { 116 data := map[string]string{ 117 "container_id": f.containerID, 118 "container_name": f.containerName, 119 "source": msg.Source, 120 "log": string(msg.Line), 121 } 122 // fluent-logger-golang buffers logs from failures and disconnections, 123 // and these are transferred again automatically. 124 return f.writer.PostWithTime(f.tag, msg.Timestamp, data) 125 } 126 127 // ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag. 128 func ValidateLogOpt(cfg map[string]string) error { 129 for key := range cfg { 130 switch key { 131 case "fluentd-address": 132 case "fluentd-tag": 133 default: 134 return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key) 135 } 136 } 137 return nil 138 } 139 140 func (f *fluentd) Close() error { 141 return f.writer.Close() 142 } 143 144 func (f *fluentd) Name() string { 145 return name 146 }