From 9a864d11d2bfb3639a9ea5592ec7b66387ff9a7a Mon Sep 17 00:00:00 2001 From: Aleksejs Sinicins Date: Sat, 3 Nov 2018 03:18:40 +0200 Subject: [PATCH] Add nginx-module-vts input plugin. (#3782) --- README.md | 1 + plugins/inputs/all/all.go | 1 + plugins/inputs/nginx_vts/README.md | 121 +++++ plugins/inputs/nginx_vts/nginx_vts.go | 341 +++++++++++++++ plugins/inputs/nginx_vts/nginx_vts_test.go | 486 +++++++++++++++++++++ 5 files changed, 950 insertions(+) create mode 100644 plugins/inputs/nginx_vts/README.md create mode 100644 plugins/inputs/nginx_vts/nginx_vts.go create mode 100644 plugins/inputs/nginx_vts/nginx_vts_test.go diff --git a/README.md b/README.md index 6381efb92..193bfa8d8 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,7 @@ For documentation on the latest development code see the [documentation index][d * [nginx_plus](./plugins/inputs/nginx_plus) * [nginx_plus_api](./plugins/inputs/nginx_plus_api) * [nsq_consumer](./plugins/inputs/nsq_consumer) +* [nginx_vts](./plugins/inputs/nginx_vts) * [nsq](./plugins/inputs/nsq) * [nstat](./plugins/inputs/nstat) * [ntpq](./plugins/inputs/ntpq) diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index d45013bf3..c64fec0a7 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -82,6 +82,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/nginx" _ "github.com/influxdata/telegraf/plugins/inputs/nginx_plus" _ "github.com/influxdata/telegraf/plugins/inputs/nginx_plus_api" + _ "github.com/influxdata/telegraf/plugins/inputs/nginx_vts" _ "github.com/influxdata/telegraf/plugins/inputs/nsq" _ "github.com/influxdata/telegraf/plugins/inputs/nsq_consumer" _ "github.com/influxdata/telegraf/plugins/inputs/nstat" diff --git a/plugins/inputs/nginx_vts/README.md b/plugins/inputs/nginx_vts/README.md new file mode 100644 index 000000000..d4be55139 --- /dev/null +++ b/plugins/inputs/nginx_vts/README.md @@ -0,0 +1,121 @@ +# Telegraf Plugin: nginx_vts + +This plugin gathers Nginx status using external virtual host traffic status module - https://github.com/vozlt/nginx-module-vts. This is an Nginx module that provides access to virtual host status information. It contains the current status such as servers, upstreams, caches. This is similar to the live activity monitoring of Nginx plus. +For module configuration details please see its [documentation](https://github.com/vozlt/nginx-module-vts#synopsis). + +### Configuration: + +``` +# Read nginx status information using nginx-module-vts module +[[inputs.nginx_vts]] + ## An array of Nginx status URIs to gather stats. + urls = ["http://localhost/status"] +``` + +### Measurements & Fields: + +- nginx_vts_connections + - active + - reading + - writing + - waiting + - accepted + - handled + - requests +- nginx_vts_server, nginx_vts_filter + - requests + - request_time + - in_bytes + - out_bytes + - response_1xx_count + - response_2xx_count + - response_3xx_count + - response_4xx_count + - response_5xx_count + - cache_miss + - cache_bypass + - cache_expired + - cache_stale + - cache_updating + - cache_revalidated + - cache_hit + - cache_scarce +- nginx_vts_upstream + - requests + - request_time + - response_time + - in_bytes + - out_bytes + - response_1xx_count + - response_2xx_count + - response_3xx_count + - response_4xx_count + - response_5xx_count + - weight + - max_fails + - fail_timeout + - backup + - down +- nginx_vts_cache + - max_bytes + - used_bytes + - in_bytes + - out_bytes + - miss + - bypass + - expired + - stale + - updating + - revalidated + - hit + - scarce + + +### Tags: + +- nginx_vts_connections + - server + - port +- nginx_vts_server + - server + - port + - zone +- nginx_vts_filter + - server + - port + - filter_name + - filter_key +- nginx_vts_upstream + - server + - port + - upstream + - upstream_address +- nginx_vts_cache + - server + - port + - zone + + +### Example Output: + +Using this configuration: +``` +[[inputs.nginx_vts]] + ## An array of Nginx status URIs to gather stats. + urls = ["http://localhost/status"] +``` + +When run with: +``` +./telegraf -config telegraf.conf -input-filter nginx_vts -test +``` + +It produces: +``` +nginx_vts_connections,server=localhost,port=80,host=localhost waiting=30i,accepted=295333i,handled=295333i,requests=6833487i,active=33i,reading=0i,writing=3i 1518341521000000000 +nginx_vts_server,zone=example.com,port=80,host=localhost,server=localhost cache_hit=158915i,in_bytes=1935528964i,out_bytes=6531366419i,response_2xx_count=809994i,response_4xx_count=16664i,cache_bypass=0i,cache_stale=0i,cache_revalidated=0i,requests=2187977i,response_1xx_count=0i,response_3xx_count=1360390i,cache_miss=2249i,cache_updating=0i,cache_scarce=0i,request_time=13i,response_5xx_count=929i,cache_expired=0i 1518341521000000000 +nginx_vts_server,host=localhost,server=localhost,port=80,zone=* requests=6775284i,in_bytes=5003242389i,out_bytes=36858233827i,cache_expired=318881i,cache_updating=0i,request_time=51i,response_1xx_count=0i,response_2xx_count=4385916i,response_4xx_count=83680i,response_5xx_count=1186i,cache_bypass=0i,cache_revalidated=0i,cache_hit=1972222i,cache_scarce=0i,response_3xx_count=2304502i,cache_miss=408251i,cache_stale=0i 1518341521000000000 +nginx_vts_filter,filter_key=FI,filter_name=country,port=80,host=localhost,server=localhost request_time=0i,in_bytes=139701i,response_3xx_count=0i,out_bytes=2644495i,response_1xx_count=0i,cache_expired=0i,cache_scarce=0i,requests=179i,cache_miss=0i,cache_bypass=0i,cache_stale=0i,cache_updating=0i,cache_revalidated=0i,cache_hit=0i,response_2xx_count=177i,response_4xx_count=2i,response_5xx_count=0i 1518341521000000000 +nginx_vts_upstream,port=80,host=localhost,upstream=backend_cluster,upstream_address=127.0.0.1:6000,server=localhost fail_timeout=10i,backup=false,request_time=31i,response_5xx_count=1081i,response_2xx_count=1877498i,max_fails=1i,in_bytes=2763336289i,out_bytes=19470265071i,weight=1i,down=false,response_time=31i,response_1xx_count=0i,response_4xx_count=76125i,requests=3379232i,response_3xx_count=1424528i 1518341521000000000 +nginx_vts_cache,server=localhost,port=80,host=localhost,zone=example stale=0i,used_bytes=64334336i,miss=394573i,bypass=0i,expired=318788i,updating=0i,revalidated=0i,hit=689883i,scarce=0i,max_bytes=9223372036854775296i,in_bytes=1111161581i,out_bytes=19175548290i 1518341521000000000 +``` diff --git a/plugins/inputs/nginx_vts/nginx_vts.go b/plugins/inputs/nginx_vts/nginx_vts.go new file mode 100644 index 000000000..66a16e6c1 --- /dev/null +++ b/plugins/inputs/nginx_vts/nginx_vts.go @@ -0,0 +1,341 @@ +package nginx_vts + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "strings" + "sync" + "time" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/plugins/inputs" +) + +type NginxVTS struct { + Urls []string + + client *http.Client + + ResponseTimeout internal.Duration +} + +var sampleConfig = ` + ## An array of ngx_http_status_module or status URI to gather stats. + urls = ["http://localhost/status"] + + ## HTTP response timeout (default: 5s) + response_timeout = "5s" +` + +func (n *NginxVTS) SampleConfig() string { + return sampleConfig +} + +func (n *NginxVTS) Description() string { + return "Read Nginx virtual host traffic status module information (nginx-module-vts)" +} + +func (n *NginxVTS) Gather(acc telegraf.Accumulator) error { + var wg sync.WaitGroup + + // Create an HTTP client that is re-used for each + // collection interval + + if n.client == nil { + client, err := n.createHTTPClient() + if err != nil { + return err + } + n.client = client + } + + for _, u := range n.Urls { + addr, err := url.Parse(u) + if err != nil { + acc.AddError(fmt.Errorf("Unable to parse address '%s': %s", u, err)) + continue + } + + wg.Add(1) + go func(addr *url.URL) { + defer wg.Done() + acc.AddError(n.gatherURL(addr, acc)) + }(addr) + } + + wg.Wait() + return nil +} + +func (n *NginxVTS) createHTTPClient() (*http.Client, error) { + if n.ResponseTimeout.Duration < time.Second { + n.ResponseTimeout.Duration = time.Second * 5 + } + + client := &http.Client{ + Transport: &http.Transport{}, + Timeout: n.ResponseTimeout.Duration, + } + + return client, nil +} + +func (n *NginxVTS) gatherURL(addr *url.URL, acc telegraf.Accumulator) error { + resp, err := n.client.Get(addr.String()) + if err != nil { + return fmt.Errorf("error making HTTP request to %s: %s", addr.String(), err) + } + + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("%s returned HTTP status %s", addr.String(), resp.Status) + } + contentType := strings.Split(resp.Header.Get("Content-Type"), ";")[0] + switch contentType { + case "application/json": + return gatherStatusURL(bufio.NewReader(resp.Body), getTags(addr), acc) + default: + return fmt.Errorf("%s returned unexpected content type %s", addr.String(), contentType) + } +} + +type NginxVTSResponse struct { + Connections struct { + Active uint64 `json:"active"` + Reading uint64 `json:"reading"` + Writing uint64 `json:"writing"` + Waiting uint64 `json:"waiting"` + Accepted uint64 `json:"accepted"` + Handled uint64 `json:"handled"` + Requests uint64 `json:"requests"` + } `json:"connections"` + ServerZones map[string]Server `json:"serverZones"` + FilterZones map[string]map[string]Server `json:"filterZones"` + UpstreamZones map[string][]Upstream `json:"upstreamZones"` + CacheZones map[string]Cache `json:"cacheZones"` +} + +type Server struct { + RequestCounter uint64 `json:"requestCounter"` + InBytes uint64 `json:"inBytes"` + OutBytes uint64 `json:"outBytes"` + RequestMsec uint64 `json:"requestMsec"` + Responses struct { + OneXx uint64 `json:"1xx"` + TwoXx uint64 `json:"2xx"` + ThreeXx uint64 `json:"3xx"` + FourXx uint64 `json:"4xx"` + FiveXx uint64 `json:"5xx"` + Miss uint64 `json:"miss"` + Bypass uint64 `json:"bypass"` + Expired uint64 `json:"expired"` + Stale uint64 `json:"stale"` + Updating uint64 `json:"updating"` + Revalidated uint64 `json:"revalidated"` + Hit uint64 `json:"hit"` + Scarce uint64 `json:"scarce"` + } `json:"responses"` +} + +type Upstream struct { + Server string `json:"server"` + RequestCounter uint64 `json:"requestCounter"` + InBytes uint64 `json:"inBytes"` + OutBytes uint64 `json:"outBytes"` + Responses struct { + OneXx uint64 `json:"1xx"` + TwoXx uint64 `json:"2xx"` + ThreeXx uint64 `json:"3xx"` + FourXx uint64 `json:"4xx"` + FiveXx uint64 `json:"5xx"` + } `json:"responses"` + ResponseMsec uint64 `json:"responseMsec"` + RequestMsec uint64 `json:"requestMsec"` + Weight uint64 `json:"weight"` + MaxFails uint64 `json:"maxFails"` + FailTimeout uint64 `json:"failTimeout"` + Backup bool `json:"backup"` + Down bool `json:"down"` +} + +type Cache struct { + MaxSize uint64 `json:"maxSize"` + UsedSize uint64 `json:"usedSize"` + InBytes uint64 `json:"inBytes"` + OutBytes uint64 `json:"outBytes"` + Responses struct { + Miss uint64 `json:"miss"` + Bypass uint64 `json:"bypass"` + Expired uint64 `json:"expired"` + Stale uint64 `json:"stale"` + Updating uint64 `json:"updating"` + Revalidated uint64 `json:"revalidated"` + Hit uint64 `json:"hit"` + Scarce uint64 `json:"scarce"` + } `json:"responses"` +} + +func gatherStatusURL(r *bufio.Reader, tags map[string]string, acc telegraf.Accumulator) error { + dec := json.NewDecoder(r) + status := &NginxVTSResponse{} + if err := dec.Decode(status); err != nil { + return fmt.Errorf("Error while decoding JSON response") + } + + acc.AddFields("nginx_vts_connections", map[string]interface{}{ + "active": status.Connections.Active, + "reading": status.Connections.Reading, + "writing": status.Connections.Writing, + "waiting": status.Connections.Waiting, + "accepted": status.Connections.Accepted, + "handled": status.Connections.Handled, + "requests": status.Connections.Requests, + }, tags) + + for zoneName, zone := range status.ServerZones { + zoneTags := map[string]string{} + for k, v := range tags { + zoneTags[k] = v + } + zoneTags["zone"] = zoneName + + acc.AddFields("nginx_vts_server", map[string]interface{}{ + "requests": zone.RequestCounter, + "request_time": zone.RequestMsec, + "in_bytes": zone.InBytes, + "out_bytes": zone.OutBytes, + + "response_1xx_count": zone.Responses.OneXx, + "response_2xx_count": zone.Responses.TwoXx, + "response_3xx_count": zone.Responses.ThreeXx, + "response_4xx_count": zone.Responses.FourXx, + "response_5xx_count": zone.Responses.FiveXx, + + "cache_miss": zone.Responses.Miss, + "cache_bypass": zone.Responses.Bypass, + "cache_expired": zone.Responses.Expired, + "cache_stale": zone.Responses.Stale, + "cache_updating": zone.Responses.Updating, + "cache_revalidated": zone.Responses.Revalidated, + "cache_hit": zone.Responses.Hit, + "cache_scarce": zone.Responses.Scarce, + }, zoneTags) + } + + for filterName, filters := range status.FilterZones { + for filterKey, upstream := range filters { + filterTags := map[string]string{} + for k, v := range tags { + filterTags[k] = v + } + filterTags["filter_key"] = filterKey + filterTags["filter_name"] = filterName + + acc.AddFields("nginx_vts_filter", map[string]interface{}{ + "requests": upstream.RequestCounter, + "request_time": upstream.RequestMsec, + "in_bytes": upstream.InBytes, + "out_bytes": upstream.OutBytes, + + "response_1xx_count": upstream.Responses.OneXx, + "response_2xx_count": upstream.Responses.TwoXx, + "response_3xx_count": upstream.Responses.ThreeXx, + "response_4xx_count": upstream.Responses.FourXx, + "response_5xx_count": upstream.Responses.FiveXx, + + "cache_miss": upstream.Responses.Miss, + "cache_bypass": upstream.Responses.Bypass, + "cache_expired": upstream.Responses.Expired, + "cache_stale": upstream.Responses.Stale, + "cache_updating": upstream.Responses.Updating, + "cache_revalidated": upstream.Responses.Revalidated, + "cache_hit": upstream.Responses.Hit, + "cache_scarce": upstream.Responses.Scarce, + }, filterTags) + } + } + + for upstreamName, upstreams := range status.UpstreamZones { + for _, upstream := range upstreams { + upstreamServerTags := map[string]string{} + for k, v := range tags { + upstreamServerTags[k] = v + } + upstreamServerTags["upstream"] = upstreamName + upstreamServerTags["upstream_address"] = upstream.Server + acc.AddFields("nginx_vts_upstream", map[string]interface{}{ + "requests": upstream.RequestCounter, + "request_time": upstream.RequestMsec, + "response_time": upstream.ResponseMsec, + "in_bytes": upstream.InBytes, + "out_bytes": upstream.OutBytes, + + "response_1xx_count": upstream.Responses.OneXx, + "response_2xx_count": upstream.Responses.TwoXx, + "response_3xx_count": upstream.Responses.ThreeXx, + "response_4xx_count": upstream.Responses.FourXx, + "response_5xx_count": upstream.Responses.FiveXx, + + "weight": upstream.Weight, + "max_fails": upstream.MaxFails, + "fail_timeout": upstream.FailTimeout, + "backup": upstream.Backup, + "down": upstream.Down, + }, upstreamServerTags) + } + } + + for zoneName, zone := range status.CacheZones { + zoneTags := map[string]string{} + for k, v := range tags { + zoneTags[k] = v + } + zoneTags["zone"] = zoneName + + acc.AddFields("nginx_vts_cache", map[string]interface{}{ + "max_bytes": zone.MaxSize, + "used_bytes": zone.UsedSize, + "in_bytes": zone.InBytes, + "out_bytes": zone.OutBytes, + + "miss": zone.Responses.Miss, + "bypass": zone.Responses.Bypass, + "expired": zone.Responses.Expired, + "stale": zone.Responses.Stale, + "updating": zone.Responses.Updating, + "revalidated": zone.Responses.Revalidated, + "hit": zone.Responses.Hit, + "scarce": zone.Responses.Scarce, + }, zoneTags) + } + + return nil +} + +// Get tag(s) for the nginx plugin +func getTags(addr *url.URL) map[string]string { + h := addr.Host + host, port, err := net.SplitHostPort(h) + if err != nil { + host = addr.Host + if addr.Scheme == "http" { + port = "80" + } else if addr.Scheme == "https" { + port = "443" + } else { + port = "" + } + } + return map[string]string{"source": host, "port": port} +} + +func init() { + inputs.Add("nginx_vts", func() telegraf.Input { + return &NginxVTS{} + }) +} diff --git a/plugins/inputs/nginx_vts/nginx_vts_test.go b/plugins/inputs/nginx_vts/nginx_vts_test.go new file mode 100644 index 000000000..085fc3843 --- /dev/null +++ b/plugins/inputs/nginx_vts/nginx_vts_test.go @@ -0,0 +1,486 @@ +package nginx_vts + +import ( + "fmt" + "net" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/influxdata/telegraf/testutil" + "github.com/stretchr/testify/require" +) + +const sampleStatusResponse = ` +{ + "hostName": "test.example.com", + "nginxVersion": "1.12.2", + "loadMsec": 1518180328331, + "nowMsec": 1518256058416, + "connections": { + "active": 111, + "reading": 222, + "writing": 333, + "waiting": 444, + "accepted": 555, + "handled": 666, + "requests": 777 + }, + "serverZones": { + "example.com": { + "requestCounter": 1415887, + "inBytes": 1296356607, + "outBytes": 4404939605, + "responses": { + "1xx": 100, + "2xx": 200, + "3xx": 300, + "4xx": 400, + "5xx": 500, + "miss": 14, + "bypass": 15, + "expired": 16, + "stale": 17, + "updating": 18, + "revalidated": 19, + "hit": 20, + "scarce": 21 + }, + "requestMsec": 13 + }, + "other.example.com": { + "requestCounter": 505, + "inBytes": 171388, + "outBytes": 1273382, + "responses": { + "1xx": 101, + "2xx": 201, + "3xx": 301, + "4xx": 401, + "5xx": 501, + "miss": 22, + "bypass": 23, + "expired": 24, + "stale": 25, + "updating": 26, + "revalidated": 27, + "hit": 28, + "scarce": 29 + }, + "requestMsec": 12 + } + }, + "filterZones": { + "country": { + "FI": { + "requestCounter": 60, + "inBytes": 2570, + "outBytes": 53597, + "responses": { + "1xx": 106, + "2xx": 206, + "3xx": 306, + "4xx": 406, + "5xx": 506, + "miss": 61, + "bypass": 62, + "expired": 63, + "stale": 64, + "updating": 65, + "revalidated": 66, + "hit": 67, + "scarce": 68 + }, + "requestMsec": 69 + } + } + }, + "upstreamZones": { + "backend_cluster": [ + { + "server": "127.0.0.1:6000", + "requestCounter": 2103849, + "inBytes": 1774680141, + "outBytes": 11727669190, + "responses": { + "1xx": 103, + "2xx": 203, + "3xx": 303, + "4xx": 403, + "5xx": 503 + }, + "requestMsec": 30, + "responseMsec": 31, + "weight": 32, + "maxFails": 33, + "failTimeout": 34, + "backup": false, + "down": false + } + ], + "::nogroups": [ + { + "server": "127.0.0.1:4433", + "requestCounter": 8, + "inBytes": 5013, + "outBytes": 487585, + "responses": { + "1xx": 104, + "2xx": 204, + "3xx": 304, + "4xx": 404, + "5xx": 504 + }, + "requestMsec": 34, + "responseMsec": 35, + "weight": 36, + "maxFails": 37, + "failTimeout": 38, + "backup": true, + "down": false + }, + { + "server": "127.0.0.1:8080", + "requestCounter": 7, + "inBytes": 2926, + "outBytes": 3846638, + "responses": { + "1xx": 105, + "2xx": 205, + "3xx": 305, + "4xx": 405, + "5xx": 505 + }, + "requestMsec": 39, + "responseMsec": 40, + "weight": 41, + "maxFails": 42, + "failTimeout": 43, + "backup": true, + "down": true + } + ] + }, + "cacheZones": { + "example": { + "maxSize": 9223372036854776000, + "usedSize": 68639232, + "inBytes": 697138673, + "outBytes": 11305044106, + "responses": { + "miss": 44, + "bypass": 45, + "expired": 46, + "stale": 47, + "updating": 48, + "revalidated": 49, + "hit": 50, + "scarce": 51 + } + }, + "static": { + "maxSize": 9223372036854776000, + "usedSize": 569856, + "inBytes": 551652333, + "outBytes": 1114889271, + "responses": { + "miss": 52, + "bypass": 53, + "expired": 54, + "stale": 55, + "updating": 56, + "revalidated": 57, + "hit": 58, + "scarce": 59 + } + } + } +} +` + +func TestNginxPlusGeneratesMetrics(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var rsp string + + if r.URL.Path == "/status" { + rsp = sampleStatusResponse + w.Header()["Content-Type"] = []string{"application/json"} + } else { + panic("Cannot handle request") + } + + fmt.Fprintln(w, rsp) + })) + defer ts.Close() + + n := &NginxVTS{ + Urls: []string{fmt.Sprintf("%s/status", ts.URL)}, + } + + var acc testutil.Accumulator + + err := n.Gather(&acc) + + require.NoError(t, err) + + addr, err := url.Parse(ts.URL) + if err != nil { + panic(err) + } + + host, port, err := net.SplitHostPort(addr.Host) + if err != nil { + host = addr.Host + if addr.Scheme == "http" { + port = "80" + } else if addr.Scheme == "https" { + port = "443" + } else { + port = "" + } + } + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_connections", + map[string]interface{}{ + "accepted": uint64(555), + "active": uint64(111), + "handled": uint64(666), + "reading": uint64(222), + "requests": uint64(777), + "waiting": uint64(444), + "writing": uint64(333), + }, + map[string]string{ + "source": host, + "port": port, + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_server", + map[string]interface{}{ + "requests": uint64(1415887), + "request_time": uint64(13), + "in_bytes": uint64(1296356607), + "out_bytes": uint64(4404939605), + + "response_1xx_count": uint64(100), + "response_2xx_count": uint64(200), + "response_3xx_count": uint64(300), + "response_4xx_count": uint64(400), + "response_5xx_count": uint64(500), + + "cache_miss": uint64(14), + "cache_bypass": uint64(15), + "cache_expired": uint64(16), + "cache_stale": uint64(17), + "cache_updating": uint64(18), + "cache_revalidated": uint64(19), + "cache_hit": uint64(20), + "cache_scarce": uint64(21), + }, + map[string]string{ + "source": host, + "port": port, + "zone": "example.com", + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_filter", + map[string]interface{}{ + "requests": uint64(60), + "request_time": uint64(69), + "in_bytes": uint64(2570), + "out_bytes": uint64(53597), + + "response_1xx_count": uint64(106), + "response_2xx_count": uint64(206), + "response_3xx_count": uint64(306), + "response_4xx_count": uint64(406), + "response_5xx_count": uint64(506), + + "cache_miss": uint64(61), + "cache_bypass": uint64(62), + "cache_expired": uint64(63), + "cache_stale": uint64(64), + "cache_updating": uint64(65), + "cache_revalidated": uint64(66), + "cache_hit": uint64(67), + "cache_scarce": uint64(68), + }, + map[string]string{ + "source": host, + "port": port, + "filter_key": "FI", + "filter_name": "country", + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_server", + map[string]interface{}{ + "requests": uint64(505), + "request_time": uint64(12), + "in_bytes": uint64(171388), + "out_bytes": uint64(1273382), + + "response_1xx_count": uint64(101), + "response_2xx_count": uint64(201), + "response_3xx_count": uint64(301), + "response_4xx_count": uint64(401), + "response_5xx_count": uint64(501), + + "cache_miss": uint64(22), + "cache_bypass": uint64(23), + "cache_expired": uint64(24), + "cache_stale": uint64(25), + "cache_updating": uint64(26), + "cache_revalidated": uint64(27), + "cache_hit": uint64(28), + "cache_scarce": uint64(29), + }, + map[string]string{ + "source": host, + "port": port, + "zone": "other.example.com", + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_upstream", + map[string]interface{}{ + "requests": uint64(2103849), + "request_time": uint64(30), + "response_time": uint64(31), + "in_bytes": uint64(1774680141), + "out_bytes": uint64(11727669190), + + "response_1xx_count": uint64(103), + "response_2xx_count": uint64(203), + "response_3xx_count": uint64(303), + "response_4xx_count": uint64(403), + "response_5xx_count": uint64(503), + + "weight": uint64(32), + "max_fails": uint64(33), + "fail_timeout": uint64(34), + "backup": bool(false), + "down": bool(false), + }, + map[string]string{ + "source": host, + "port": port, + "upstream": "backend_cluster", + "upstream_address": "127.0.0.1:6000", + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_upstream", + map[string]interface{}{ + "requests": uint64(8), + "request_time": uint64(34), + "response_time": uint64(35), + "in_bytes": uint64(5013), + "out_bytes": uint64(487585), + + "response_1xx_count": uint64(104), + "response_2xx_count": uint64(204), + "response_3xx_count": uint64(304), + "response_4xx_count": uint64(404), + "response_5xx_count": uint64(504), + + "weight": uint64(36), + "max_fails": uint64(37), + "fail_timeout": uint64(38), + "backup": bool(true), + "down": bool(false), + }, + map[string]string{ + "source": host, + "port": port, + "upstream": "::nogroups", + "upstream_address": "127.0.0.1:4433", + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_upstream", + map[string]interface{}{ + "requests": uint64(7), + "request_time": uint64(39), + "response_time": uint64(40), + "in_bytes": uint64(2926), + "out_bytes": uint64(3846638), + + "response_1xx_count": uint64(105), + "response_2xx_count": uint64(205), + "response_3xx_count": uint64(305), + "response_4xx_count": uint64(405), + "response_5xx_count": uint64(505), + + "weight": uint64(41), + "max_fails": uint64(42), + "fail_timeout": uint64(43), + "backup": bool(true), + "down": bool(true), + }, + map[string]string{ + "source": host, + "port": port, + "upstream": "::nogroups", + "upstream_address": "127.0.0.1:8080", + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_cache", + map[string]interface{}{ + "max_bytes": uint64(9223372036854776000), + "used_bytes": uint64(68639232), + "in_bytes": uint64(697138673), + "out_bytes": uint64(11305044106), + + "miss": uint64(44), + "bypass": uint64(45), + "expired": uint64(46), + "stale": uint64(47), + "updating": uint64(48), + "revalidated": uint64(49), + "hit": uint64(50), + "scarce": uint64(51), + }, + map[string]string{ + "source": host, + "port": port, + "zone": "example", + }) + + acc.AssertContainsTaggedFields( + t, + "nginx_vts_cache", + map[string]interface{}{ + "max_bytes": uint64(9223372036854776000), + "used_bytes": uint64(569856), + "in_bytes": uint64(551652333), + "out_bytes": uint64(1114889271), + + "miss": uint64(52), + "bypass": uint64(53), + "expired": uint64(54), + "stale": uint64(55), + "updating": uint64(56), + "revalidated": uint64(57), + "hit": uint64(58), + "scarce": uint64(59), + }, + map[string]string{ + "source": host, + "port": port, + "zone": "static", + }) +}