diff --git a/go.mod b/go.mod index a2f6619283..29bbafa454 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,7 @@ require ( github.com/mattn/go-isatty v0.0.8 github.com/mattn/go-zglob v0.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 + github.com/mileusna/useragent v0.0.0-20190129205925-3e331f0949a5 github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mna/pigeon v1.0.1-0.20180808201053-bb0192cfc2ae github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae // indirect diff --git a/go.sum b/go.sum index 166c20d7c4..5822ded0d2 100644 --- a/go.sum +++ b/go.sum @@ -302,6 +302,8 @@ github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mileusna/useragent v0.0.0-20190129205925-3e331f0949a5 h1:pXqZHmHOz6LN+zbbUgqyGgAWRnnZEI40IzG3tMsXcSI= +github.com/mileusna/useragent v0.0.0-20190129205925-3e331f0949a5/go.mod h1:JWhYAp2EXqUtsxTKdeGlY8Wp44M7VxThC9FEoNGi2IE= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= diff --git a/http/handler.go b/http/handler.go index 45e3907c22..302d5010ab 100644 --- a/http/handler.go +++ b/http/handler.go @@ -85,12 +85,8 @@ func NewHandlerFromRegistry(name string, reg *prom.Registry) *Handler { func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { var span opentracing.Span span, r = tracing.ExtractFromHTTPRequest(r, h.name) - userAgent := r.Header.Get("User-Agent") - if userAgent == "" { - userAgent = "unknown" - } - - span.LogKV("user_agent", userAgent, "referer", r.Referer(), "remote_addr", r.RemoteAddr) + ua := userAgent(r) + span.LogKV("user_agent", ua, "referer", r.Referer(), "remote_addr", r.RemoteAddr) for k, v := range r.Header { if len(v) == 0 { continue @@ -114,14 +110,14 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { "method": r.Method, "path": r.URL.Path, "status": statusClass, - "user_agent": userAgent, + "user_agent": ua, }).Inc() h.requestDur.With(prometheus.Labels{ "handler": h.name, "method": r.Method, "path": r.URL.Path, "status": statusClass, - "user_agent": userAgent, + "user_agent": ua, }).Observe(duration.Seconds()) }(time.Now()) diff --git a/http/ua.go b/http/ua.go new file mode 100644 index 0000000000..52179b1b9a --- /dev/null +++ b/http/ua.go @@ -0,0 +1,17 @@ +package http + +import ( + "net/http" + + useragent "github.com/mileusna/useragent" +) + +func userAgent(r *http.Request) string { + header := r.Header.Get("User-Agent") + if header == "" { + return "unknown" + } + + ua := useragent.Parse(header) + return ua.Name +} diff --git a/http/ua_test.go b/http/ua_test.go new file mode 100644 index 0000000000..29e65723c6 --- /dev/null +++ b/http/ua_test.go @@ -0,0 +1,59 @@ +package http + +import ( + nethttp "net/http" + "testing" +) + +func Test_userAgent(t *testing.T) { + tests := []struct { + name string + ua string + want string + }{ + { + name: "linux chrome", + ua: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", + want: "Chrome", + }, + { + name: "telegraf", + ua: "Telegraf/1.12.6", + want: "Telegraf", + }, + { + name: "curl", + ua: "curl/7.67.0", + want: "curl", + }, + { + name: "go", + ua: "Go-http-client/1.1", + want: "Go-http-client", + }, + { + name: "influx client", + ua: "InfluxDBClient/0.0.1 (golang; windows; amd64)", + want: "InfluxDBClient", + }, + { + name: "iphone", + ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Mobile/15E148 Safari/604.1", + want: "Safari", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &nethttp.Request{ + Header: nethttp.Header{ + "User-Agent": []string{tt.ua}, + }, + } + + got := userAgent(r) + if got != tt.want { + t.Fatalf("userAgent %v want %v", got, tt.want) + } + }) + } +}