github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/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 fields gelfFields 27 } 28 29 type gelfFields struct { 30 hostname string 31 containerID string 32 containerName string 33 imageID string 34 imageName string 35 command string 36 tag string 37 created time.Time 38 } 39 40 func init() { 41 if err := logger.RegisterLogDriver(name, New); err != nil { 42 logrus.Fatal(err) 43 } 44 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 45 logrus.Fatal(err) 46 } 47 } 48 49 // New creates a gelf logger using the configuration passed in on the 50 // context. Supported context configuration variables are 51 // gelf-address, & gelf-tag. 52 func New(ctx logger.Context) (logger.Logger, error) { 53 // parse gelf address 54 address, err := parseAddress(ctx.Config["gelf-address"]) 55 if err != nil { 56 return nil, err 57 } 58 59 // collect extra data for GELF message 60 hostname, err := ctx.Hostname() 61 if err != nil { 62 return nil, fmt.Errorf("gelf: cannot access hostname to set source field") 63 } 64 65 // remove trailing slash from container name 66 containerName := bytes.TrimLeft([]byte(ctx.ContainerName), "/") 67 68 // parse log tag 69 tag, err := loggerutils.ParseLogTag(ctx, "") 70 if err != nil { 71 return nil, err 72 } 73 74 fields := gelfFields{ 75 hostname: hostname, 76 containerID: ctx.ContainerID, 77 containerName: string(containerName), 78 imageID: ctx.ContainerImageID, 79 imageName: ctx.ContainerImageName, 80 command: ctx.Command(), 81 tag: tag, 82 created: ctx.ContainerCreated, 83 } 84 85 // create new gelfWriter 86 gelfWriter, err := gelf.NewWriter(address) 87 if err != nil { 88 return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err) 89 } 90 91 return &gelfLogger{ 92 writer: gelfWriter, 93 ctx: ctx, 94 fields: fields, 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.fields.hostname, 110 Short: string(short), 111 TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0, 112 Level: level, 113 Extra: map[string]interface{}{ 114 "_container_id": s.fields.containerID, 115 "_container_name": s.fields.containerName, 116 "_image_id": s.fields.imageID, 117 "_image_name": s.fields.imageName, 118 "_command": s.fields.command, 119 "_tag": s.fields.tag, 120 "_created": s.fields.created, 121 }, 122 } 123 124 if err := s.writer.WriteMessage(&m); err != nil { 125 return fmt.Errorf("gelf: cannot send GELF message: %v", err) 126 } 127 return nil 128 } 129 130 func (s *gelfLogger) Close() error { 131 return s.writer.Close() 132 } 133 134 func (s *gelfLogger) Name() string { 135 return name 136 } 137 138 // ValidateLogOpt looks for gelf specific log options gelf-address, & 139 // gelf-tag. 140 func ValidateLogOpt(cfg map[string]string) error { 141 for key := range cfg { 142 switch key { 143 case "gelf-address": 144 case "gelf-tag": 145 case "tag": 146 default: 147 return fmt.Errorf("unknown log opt '%s' for gelf log driver", key) 148 } 149 } 150 151 if _, err := parseAddress(cfg["gelf-address"]); err != nil { 152 return err 153 } 154 155 return nil 156 } 157 158 func parseAddress(address string) (string, error) { 159 if address == "" { 160 return "", nil 161 } 162 if !urlutil.IsTransportURL(address) { 163 return "", fmt.Errorf("gelf-address should be in form proto://address, got %v", address) 164 } 165 url, err := url.Parse(address) 166 if err != nil { 167 return "", err 168 } 169 170 // we support only udp 171 if url.Scheme != "udp" { 172 return "", fmt.Errorf("gelf: endpoint needs to be UDP") 173 } 174 175 // get host and port 176 if _, _, err = net.SplitHostPort(url.Host); err != nil { 177 return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port") 178 } 179 180 return url.Host, nil 181 }