github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/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 "compress/flate" 9 "encoding/json" 10 "fmt" 11 "net" 12 "net/url" 13 "strconv" 14 "time" 15 16 "github.com/Graylog2/go-gelf/gelf" 17 "github.com/Sirupsen/logrus" 18 "github.com/docker/docker/daemon/logger" 19 "github.com/docker/docker/daemon/logger/loggerutils" 20 "github.com/docker/docker/pkg/urlutil" 21 ) 22 23 const name = "gelf" 24 25 type gelfLogger struct { 26 writer *gelf.Writer 27 ctx logger.Context 28 hostname string 29 rawExtra json.RawMessage 30 } 31 32 func init() { 33 if err := logger.RegisterLogDriver(name, New); err != nil { 34 logrus.Fatal(err) 35 } 36 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 37 logrus.Fatal(err) 38 } 39 } 40 41 // New creates a gelf logger using the configuration passed in on the 42 // context. The supported context configuration variable is gelf-address. 43 func New(ctx logger.Context) (logger.Logger, error) { 44 // parse gelf address 45 address, err := parseAddress(ctx.Config["gelf-address"]) 46 if err != nil { 47 return nil, err 48 } 49 50 // collect extra data for GELF message 51 hostname, err := ctx.Hostname() 52 if err != nil { 53 return nil, fmt.Errorf("gelf: cannot access hostname to set source field") 54 } 55 56 // parse log tag 57 tag, err := loggerutils.ParseLogTag(ctx, loggerutils.DefaultTemplate) 58 if err != nil { 59 return nil, err 60 } 61 62 extra := map[string]interface{}{ 63 "_container_id": ctx.ContainerID, 64 "_container_name": ctx.Name(), 65 "_image_id": ctx.ContainerImageID, 66 "_image_name": ctx.ContainerImageName, 67 "_command": ctx.Command(), 68 "_tag": tag, 69 "_created": ctx.ContainerCreated, 70 } 71 72 extraAttrs := ctx.ExtraAttributes(func(key string) string { 73 if key[0] == '_' { 74 return key 75 } 76 return "_" + key 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 // create new gelfWriter 88 gelfWriter, err := gelf.NewWriter(address) 89 if err != nil { 90 return nil, fmt.Errorf("gelf: cannot connect to GELF endpoint: %s %v", address, err) 91 } 92 93 if v, ok := ctx.Config["gelf-compression-type"]; ok { 94 switch v { 95 case "gzip": 96 gelfWriter.CompressionType = gelf.CompressGzip 97 case "zlib": 98 gelfWriter.CompressionType = gelf.CompressZlib 99 case "none": 100 gelfWriter.CompressionType = gelf.CompressNone 101 default: 102 return nil, fmt.Errorf("gelf: invalid compression type %q", v) 103 } 104 } 105 106 if v, ok := ctx.Config["gelf-compression-level"]; ok { 107 val, err := strconv.Atoi(v) 108 if err != nil { 109 return nil, fmt.Errorf("gelf: invalid compression level %s, err %v", v, err) 110 } 111 gelfWriter.CompressionLevel = val 112 } 113 114 return &gelfLogger{ 115 writer: gelfWriter, 116 ctx: ctx, 117 hostname: hostname, 118 rawExtra: rawExtra, 119 }, nil 120 } 121 122 func (s *gelfLogger) Log(msg *logger.Message) error { 123 level := gelf.LOG_INFO 124 if msg.Source == "stderr" { 125 level = gelf.LOG_ERR 126 } 127 128 m := gelf.Message{ 129 Version: "1.1", 130 Host: s.hostname, 131 Short: string(msg.Line), 132 TimeUnix: float64(msg.Timestamp.UnixNano()/int64(time.Millisecond)) / 1000.0, 133 Level: level, 134 RawExtra: s.rawExtra, 135 } 136 137 if err := s.writer.WriteMessage(&m); err != nil { 138 return fmt.Errorf("gelf: cannot send GELF message: %v", err) 139 } 140 return nil 141 } 142 143 func (s *gelfLogger) Close() error { 144 return s.writer.Close() 145 } 146 147 func (s *gelfLogger) Name() string { 148 return name 149 } 150 151 // ValidateLogOpt looks for gelf specific log option gelf-address. 152 func ValidateLogOpt(cfg map[string]string) error { 153 for key, val := range cfg { 154 switch key { 155 case "gelf-address": 156 case "tag": 157 case "labels": 158 case "env": 159 case "gelf-compression-level": 160 i, err := strconv.Atoi(val) 161 if err != nil || i < flate.DefaultCompression || i > flate.BestCompression { 162 return fmt.Errorf("unknown value %q for log opt %q for gelf log driver", val, key) 163 } 164 case "gelf-compression-type": 165 switch val { 166 case "gzip", "zlib", "none": 167 default: 168 return fmt.Errorf("unknown value %q for log opt %q for gelf log driver", val, key) 169 } 170 default: 171 return fmt.Errorf("unknown log opt %q for gelf log driver", key) 172 } 173 } 174 175 if _, err := parseAddress(cfg["gelf-address"]); err != nil { 176 return err 177 } 178 179 return nil 180 } 181 182 func parseAddress(address string) (string, error) { 183 if address == "" { 184 return "", nil 185 } 186 if !urlutil.IsTransportURL(address) { 187 return "", fmt.Errorf("gelf-address should be in form proto://address, got %v", address) 188 } 189 url, err := url.Parse(address) 190 if err != nil { 191 return "", err 192 } 193 194 // we support only udp 195 if url.Scheme != "udp" { 196 return "", fmt.Errorf("gelf: endpoint needs to be UDP") 197 } 198 199 // get host and port 200 if _, _, err = net.SplitHostPort(url.Host); err != nil { 201 return "", fmt.Errorf("gelf: please provide gelf-address as udp://host:port") 202 } 203 204 return url.Host, nil 205 }