github.com/tompao/docker@v1.9.1/daemon/logger/gelf/gelf.go (about) 1 // +build linux 2 3 // Package gelf provides the log driver for forwarding server logs to 4 // endpoints that support the Graylog Extended Log Format. 5 package gelf 6 7 import ( 8 "bytes" 9 "fmt" 10 "net" 11 "net/url" 12 "time" 13 14 "github.com/Graylog2/go-gelf/gelf" 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/docker/daemon/logger" 17 "github.com/docker/docker/daemon/logger/loggerutils" 18 "github.com/docker/docker/pkg/urlutil" 19 ) 20 21 const name = "gelf" 22 23 type gelfLogger struct { 24 writer *gelf.Writer 25 ctx logger.Context 26 hostname string 27 extra map[string]interface{} 28 } 29 30 func init() { 31 if err := logger.RegisterLogDriver(name, New); err != nil { 32 logrus.Fatal(err) 33 } 34 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 35 logrus.Fatal(err) 36 } 37 } 38 39 // New creates a gelf logger using the configuration passed in on the 40 // context. Supported context configuration variables are 41 // gelf-address, & gelf-tag. 42 func New(ctx logger.Context) (logger.Logger, error) { 43 // parse gelf address 44 address, err := parseAddress(ctx.Config["gelf-address"]) 45 if err != nil { 46 return nil, err 47 } 48 49 // collect extra data for GELF message 50 hostname, err := ctx.Hostname() 51 if err != nil { 52 return nil, fmt.Errorf("gelf: cannot access hostname to set source field") 53 } 54 55 // remove trailing slash from container name 56 containerName := bytes.TrimLeft([]byte(ctx.ContainerName), "/") 57 58 // parse log tag 59 tag, err := loggerutils.ParseLogTag(ctx, "") 60 if err != nil { 61 return nil, err 62 } 63 64 extra := map[string]interface{}{ 65 "_container_id": ctx.ContainerID, 66 "_container_name": string(containerName), 67 "_image_id": ctx.ContainerImageID, 68 "_image_name": ctx.ContainerImageName, 69 "_command": ctx.Command(), 70 "_tag": tag, 71 "_created": ctx.ContainerCreated, 72 } 73 74 extraAttrs := ctx.ExtraAttributes(func(key string) string { 75 if key[0] == '_' { 76 return key 77 } 78 return "_" + key 79 }) 80 for k, v := range extraAttrs { 81 extra[k] = v 82 } 83 84 // create new gelfWriter 85 gelfWriter, err := gelf.NewWriter(address) 86 if err != nil { 87 return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err) 88 } 89 90 return &gelfLogger{ 91 writer: gelfWriter, 92 ctx: ctx, 93 hostname: hostname, 94 extra: extra, 95 }, nil 96 } 97 98 func (s *gelfLogger) Log(msg *logger.Message) error { 99 // remove trailing and leading whitespace 100 short := bytes.TrimSpace([]byte(msg.Line)) 101 102 level := gelf.LOG_INFO 103 if msg.Source == "stderr" { 104 level = gelf.LOG_ERR 105 } 106 107 m := gelf.Message{ 108 Version: "1.1", 109 Host: s.hostname, 110 Short: string(short), 111 TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0, 112 Level: level, 113 Extra: s.extra, 114 } 115 116 if err := s.writer.WriteMessage(&m); err != nil { 117 return fmt.Errorf("gelf: cannot send GELF message: %v", err) 118 } 119 return nil 120 } 121 122 func (s *gelfLogger) Close() error { 123 return s.writer.Close() 124 } 125 126 func (s *gelfLogger) Name() string { 127 return name 128 } 129 130 // ValidateLogOpt looks for gelf specific log options gelf-address, & 131 // gelf-tag. 132 func ValidateLogOpt(cfg map[string]string) error { 133 for key := range cfg { 134 switch key { 135 case "gelf-address": 136 case "gelf-tag": 137 case "tag": 138 case "labels": 139 case "env": 140 default: 141 return fmt.Errorf("unknown log opt '%s' for gelf log driver", key) 142 } 143 } 144 145 if _, err := parseAddress(cfg["gelf-address"]); err != nil { 146 return err 147 } 148 149 return nil 150 } 151 152 func parseAddress(address string) (string, error) { 153 if address == "" { 154 return "", nil 155 } 156 if !urlutil.IsTransportURL(address) { 157 return "", fmt.Errorf("gelf-address should be in form proto://address, got %v", address) 158 } 159 url, err := url.Parse(address) 160 if err != nil { 161 return "", err 162 } 163 164 // we support only udp 165 if url.Scheme != "udp" { 166 return "", fmt.Errorf("gelf: endpoint needs to be UDP") 167 } 168 169 // get host and port 170 if _, _, err = net.SplitHostPort(url.Host); err != nil { 171 return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port") 172 } 173 174 return url.Host, nil 175 }