// +build linux package gelf import ( "bytes" "fmt" "io" "net" "net/url" "time" "github.com/Graylog2/go-gelf/gelf" "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/pkg/urlutil" ) const name = "gelf" type GelfLogger struct { writer *gelf.Writer ctx logger.Context fields GelfFields } type GelfFields struct { hostname string containerId string containerName string imageId string imageName string command string tag string created time.Time } func init() { if err := logger.RegisterLogDriver(name, New); err != nil { logrus.Fatal(err) } } func New(ctx logger.Context) (logger.Logger, error) { // parse gelf address address, err := parseAddress(ctx.Config["gelf-address"]) if err != nil { return nil, err } // collect extra data for GELF message hostname, err := ctx.Hostname() if err != nil { return nil, fmt.Errorf("gelf: cannot access hostname to set source field") } // remove trailing slash from container name containerName := bytes.TrimLeft([]byte(ctx.ContainerName), "/") fields := GelfFields{ hostname: hostname, containerId: ctx.ContainerID, containerName: string(containerName), imageId: ctx.ContainerImageID, imageName: ctx.ContainerImageName, command: ctx.Command(), tag: ctx.Config["gelf-tag"], created: ctx.ContainerCreated, } // create new gelfWriter gelfWriter, err := gelf.NewWriter(address) if err != nil { return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err) } return &GelfLogger{ writer: gelfWriter, ctx: ctx, fields: fields, }, nil } func (s *GelfLogger) Log(msg *logger.Message) error { // remove trailing and leading whitespace short := bytes.TrimSpace([]byte(msg.Line)) level := gelf.LOG_INFO if msg.Source == "stderr" { level = gelf.LOG_ERR } m := gelf.Message{ Version: "1.1", Host: s.fields.hostname, Short: string(short), TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0, Level: level, Extra: map[string]interface{}{ "_container_id": s.fields.containerId, "_container_name": s.fields.containerName, "_image_id": s.fields.imageId, "_image_name": s.fields.imageName, "_command": s.fields.command, "_tag": s.fields.tag, "_created": s.fields.created, }, } if err := s.writer.WriteMessage(&m); err != nil { return fmt.Errorf("gelf: cannot send GELF message: %v", err) } return nil } func (s *GelfLogger) GetReader() (io.Reader, error) { return nil, logger.ReadLogsNotSupported } func (s *GelfLogger) Close() error { return s.writer.Close() } func (s *GelfLogger) Name() string { return name } func parseAddress(address string) (string, error) { if urlutil.IsTransportURL(address) { url, err := url.Parse(address) if err != nil { return "", err } // we support only udp if url.Scheme != "udp" { return "", fmt.Errorf("gelf: endpoint needs to be UDP") } // get host and port if _, _, err = net.SplitHostPort(url.Host); err != nil { return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port") } return url.Host, nil } return "", nil }