-
Notifications
You must be signed in to change notification settings - Fork 0
/
fluentd.go
134 lines (118 loc) · 3.35 KB
/
fluentd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Package fluentd provides the log driver for forwarding server logs
// to fluentd endpoints.
package fluentd
import (
"fmt"
"math"
"net"
"strconv"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/logger"
"github.com/docker/docker/daemon/logger/loggerutils"
"github.com/fluent/fluent-logger-golang/fluent"
)
type fluentd struct {
tag string
containerID string
containerName string
writer *fluent.Fluent
extra map[string]string
}
const (
name = "fluentd"
defaultHostName = "localhost"
defaultPort = 24224
defaultTagPrefix = "docker"
)
func init() {
if err := logger.RegisterLogDriver(name, New); err != nil {
logrus.Fatal(err)
}
if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
logrus.Fatal(err)
}
}
// New creates a fluentd logger using the configuration passed in on
// the context. Supported context configuration variables are
// fluentd-address & fluentd-tag.
func New(ctx logger.Context) (logger.Logger, error) {
host, port, err := parseAddress(ctx.Config["fluentd-address"])
if err != nil {
return nil, err
}
tag, err := loggerutils.ParseLogTag(ctx, "docker.{{.ID}}")
if err != nil {
return nil, err
}
extra := ctx.ExtraAttributes(nil)
logrus.Debugf("logging driver fluentd configured for container:%s, host:%s, port:%d, tag:%s, extra:%v.", ctx.ContainerID, host, port, tag, extra)
// logger tries to reconnect 2**32 - 1 times
// failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds]
log, err := fluent.New(fluent.Config{FluentPort: port, FluentHost: host, RetryWait: 1000, MaxRetry: math.MaxInt32})
if err != nil {
return nil, err
}
return &fluentd{
tag: tag,
containerID: ctx.ContainerID,
containerName: ctx.ContainerName,
writer: log,
extra: extra,
}, nil
}
func (f *fluentd) Log(msg *logger.Message) error {
data := map[string]string{
"container_id": f.containerID,
"container_name": f.containerName,
"source": msg.Source,
"log": string(msg.Line),
}
for k, v := range f.extra {
data[k] = v
}
// fluent-logger-golang buffers logs from failures and disconnections,
// and these are transferred again automatically.
return f.writer.PostWithTime(f.tag, msg.Timestamp, data)
}
func (f *fluentd) Close() error {
return f.writer.Close()
}
func (f *fluentd) Name() string {
return name
}
// ValidateLogOpt looks for fluentd specific log options fluentd-address & fluentd-tag.
func ValidateLogOpt(cfg map[string]string) error {
for key := range cfg {
switch key {
case "fluentd-address":
case "fluentd-tag":
case "tag":
case "labels":
case "env":
default:
return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key)
}
}
if _, _, err := parseAddress(cfg["fluentd-address"]); err != nil {
return err
}
return nil
}
func parseAddress(address string) (string, int, error) {
if address == "" {
return defaultHostName, defaultPort, nil
}
host, port, err := net.SplitHostPort(address)
if err != nil {
if !strings.Contains(err.Error(), "missing port in address") {
return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
}
return host, defaultPort, nil
}
portnum, err := strconv.Atoi(port)
if err != nil {
return "", 0, fmt.Errorf("invalid fluentd-address %s: %s", address, err)
}
return host, portnum, nil
}