github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/logger/gelf/gelf.go (about) 1 // Package gelf provides the log driver for forwarding server logs to 2 // endpoints that support the Graylog Extended Log Format. 3 package gelf // import "github.com/Prakhar-Agarwal-byte/moby/daemon/logger/gelf" 4 5 import ( 6 "compress/flate" 7 "encoding/json" 8 "fmt" 9 "net" 10 "net/url" 11 "strconv" 12 "time" 13 14 "github.com/Graylog2/go-gelf/gelf" 15 "github.com/Prakhar-Agarwal-byte/moby/daemon/logger" 16 "github.com/Prakhar-Agarwal-byte/moby/daemon/logger/loggerutils" 17 ) 18 19 const name = "gelf" 20 21 type gelfLogger struct { 22 writer gelf.Writer 23 info logger.Info 24 hostname string 25 rawExtra json.RawMessage 26 } 27 28 func init() { 29 if err := logger.RegisterLogDriver(name, New); err != nil { 30 panic(err) 31 } 32 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 33 panic(err) 34 } 35 } 36 37 // New creates a gelf logger using the configuration passed in on the 38 // context. The supported context configuration variable is gelf-address. 39 func New(info logger.Info) (logger.Logger, error) { 40 // parse gelf address 41 address, err := parseAddress(info.Config["gelf-address"]) 42 if err != nil { 43 return nil, err 44 } 45 46 // collect extra data for GELF message 47 hostname, err := info.Hostname() 48 if err != nil { 49 return nil, fmt.Errorf("gelf: cannot access hostname to set source field") 50 } 51 52 // parse log tag 53 tag, err := loggerutils.ParseLogTag(info, loggerutils.DefaultTemplate) 54 if err != nil { 55 return nil, err 56 } 57 58 extra := map[string]interface{}{ 59 "_container_id": info.ContainerID, 60 "_container_name": info.Name(), 61 "_image_id": info.ContainerImageID, 62 "_image_name": info.ContainerImageName, 63 "_command": info.Command(), 64 "_tag": tag, 65 "_created": info.ContainerCreated, 66 } 67 68 extraAttrs, err := info.ExtraAttributes(func(key string) string { 69 if key[0] == '_' { 70 return key 71 } 72 return "_" + key 73 }) 74 if err != nil { 75 return nil, err 76 } 77 78 for k, v := range extraAttrs { 79 extra[k] = v 80 } 81 82 rawExtra, err := json.Marshal(extra) 83 if err != nil { 84 return nil, err 85 } 86 87 var gelfWriter gelf.Writer 88 if address.Scheme == "udp" { 89 gelfWriter, err = newGELFUDPWriter(address.Host, info) 90 if err != nil { 91 return nil, err 92 } 93 } else if address.Scheme == "tcp" { 94 gelfWriter, err = newGELFTCPWriter(address.Host, info) 95 if err != nil { 96 return nil, err 97 } 98 } 99 100 return &gelfLogger{ 101 writer: gelfWriter, 102 info: info, 103 hostname: hostname, 104 rawExtra: rawExtra, 105 }, nil 106 } 107 108 // create new TCP gelfWriter 109 func newGELFTCPWriter(address string, info logger.Info) (gelf.Writer, error) { 110 gelfWriter, err := gelf.NewTCPWriter(address) 111 if err != nil { 112 return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err) 113 } 114 115 if v, ok := info.Config["gelf-tcp-max-reconnect"]; ok { 116 i, err := strconv.Atoi(v) 117 if err != nil || i < 0 { 118 return nil, fmt.Errorf("gelf-tcp-max-reconnect must be a positive integer") 119 } 120 gelfWriter.MaxReconnect = i 121 } 122 123 if v, ok := info.Config["gelf-tcp-reconnect-delay"]; ok { 124 i, err := strconv.Atoi(v) 125 if err != nil || i < 0 { 126 return nil, fmt.Errorf("gelf-tcp-reconnect-delay must be a positive integer") 127 } 128 gelfWriter.ReconnectDelay = time.Duration(i) 129 } 130 131 return gelfWriter, nil 132 } 133 134 // create new UDP gelfWriter 135 func newGELFUDPWriter(address string, info logger.Info) (gelf.Writer, error) { 136 gelfWriter, err := gelf.NewUDPWriter(address) 137 if err != nil { 138 return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err) 139 } 140 141 if v, ok := info.Config["gelf-compression-type"]; ok { 142 switch v { 143 case "gzip": 144 gelfWriter.CompressionType = gelf.CompressGzip 145 case "zlib": 146 gelfWriter.CompressionType = gelf.CompressZlib 147 case "none": 148 gelfWriter.CompressionType = gelf.CompressNone 149 default: 150 return nil, fmt.Errorf("gelf: invalid compression type %q", v) 151 } 152 } 153 154 if v, ok := info.Config["gelf-compression-level"]; ok { 155 val, err := strconv.Atoi(v) 156 if err != nil { 157 return nil, fmt.Errorf("gelf: invalid compression level %s, err %v", v, err) 158 } 159 gelfWriter.CompressionLevel = val 160 } 161 162 return gelfWriter, nil 163 } 164 165 func (s *gelfLogger) Log(msg *logger.Message) error { 166 if len(msg.Line) == 0 { 167 return nil 168 } 169 170 level := gelf.LOG_INFO 171 if msg.Source == "stderr" { 172 level = gelf.LOG_ERR 173 } 174 175 m := gelf.Message{ 176 Version: "1.1", 177 Host: s.hostname, 178 Short: string(msg.Line), 179 TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0, 180 Level: int32(level), 181 RawExtra: s.rawExtra, 182 } 183 logger.PutMessage(msg) 184 185 if err := s.writer.WriteMessage(&m); err != nil { 186 return fmt.Errorf("gelf: cannot send GELF message: %v", err) 187 } 188 return nil 189 } 190 191 func (s *gelfLogger) Close() error { 192 return s.writer.Close() 193 } 194 195 func (s *gelfLogger) Name() string { 196 return name 197 } 198 199 // ValidateLogOpt looks for gelf specific log option gelf-address. 200 func ValidateLogOpt(cfg map[string]string) error { 201 address, err := parseAddress(cfg["gelf-address"]) 202 if err != nil { 203 return err 204 } 205 206 for key, val := range cfg { 207 switch key { 208 case "gelf-address": 209 case "tag": 210 case "labels": 211 case "labels-regex": 212 case "env": 213 case "env-regex": 214 case "gelf-compression-level": 215 if address.Scheme != "udp" { 216 return fmt.Errorf("compression is only supported on UDP") 217 } 218 i, err := strconv.Atoi(val) 219 if err != nil || i < flate.DefaultCompression || i > flate.BestCompression { 220 return fmt.Errorf("unknown value %q for log opt %q for gelf log driver", val, key) 221 } 222 case "gelf-compression-type": 223 if address.Scheme != "udp" { 224 return fmt.Errorf("compression is only supported on UDP") 225 } 226 switch val { 227 case "gzip", "zlib", "none": 228 default: 229 return fmt.Errorf("unknown value %q for log opt %q for gelf log driver", val, key) 230 } 231 case "gelf-tcp-max-reconnect", "gelf-tcp-reconnect-delay": 232 if address.Scheme != "tcp" { 233 return fmt.Errorf("%q is only valid for TCP", key) 234 } 235 i, err := strconv.Atoi(val) 236 if err != nil || i < 0 { 237 return fmt.Errorf("%q must be a positive integer", key) 238 } 239 default: 240 return fmt.Errorf("unknown log opt %q for gelf log driver", key) 241 } 242 } 243 244 return nil 245 } 246 247 func parseAddress(address string) (*url.URL, error) { 248 if address == "" { 249 return nil, fmt.Errorf("gelf-address is a required parameter") 250 } 251 addr, err := url.Parse(address) 252 if err != nil { 253 return nil, err 254 } 255 256 if addr.Scheme != "udp" && addr.Scheme != "tcp" { 257 return nil, fmt.Errorf("gelf: endpoint needs to be TCP or UDP") 258 } 259 260 if _, _, err = net.SplitHostPort(addr.Host); err != nil { 261 return nil, fmt.Errorf("gelf: please provide gelf-address as proto://host:port") 262 } 263 264 return addr, nil 265 }