1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,157 @@
# Nginx Plus Input Plugin
This plugin gathers metrics from the commercial
[Nginx Plus web server][nginx_plus] via the [status module][status_module].
> [!NOTE]
> Using this plugin requires a license.
For more information about differences between Nginx (F/OSS) and Nginx Plus, see
the Nginx [documentation][diff_doc].
⭐ Telegraf v1.5.0
🏷️ server, web
💻 all
[nginx_plus]: https://www.f5.com/products/nginx/nginx-plus
[status_module]: http://nginx.org/en/docs/http/ngx_http_status_module.html
[diff_doc]: https://www.nginx.com/blog/whats-difference-nginx-foss-nginx-plus/
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Read Nginx Plus' advanced status information
[[inputs.nginx_plus]]
## An array of Nginx status URIs to gather stats.
urls = ["http://localhost/status"]
# HTTP response timeout (default: 5s)
response_timeout = "5s"
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
```
## Metrics
- nginx_plus_processes
- respawned
- nginx_plus_connections
- accepted
- dropped
- active
- idle
- nginx_plus_ssl
- handshakes
- handshakes_failed
- session_reuses
- nginx_plus_requests
- total
- current
- nginx_plus_upstream, nginx_plus_stream_upstream
- keepalive
- zombies
- nginx_plus_upstream_peer, nginx_plus_stream_upstream_peer
- requests
- unavail
- healthchecks_checks
- header_time
- response_time
- state
- active
- downstart
- healthchecks_last_passed
- weight
- responses_1xx
- responses_2xx
- responses_3xx
- responses_4xx
- responses_5xx
- received
- selected
- healthchecks_fails
- healthchecks_unhealthy
- backup
- responses_total
- sent
- fails
- downtime
### Tags
- nginx_plus_processes, nginx_plus_connections, nginx_plus_ssl, nginx_plus_requests
- server
- port
- nginx_plus_upstream, nginx_plus_stream_upstream
- upstream
- server
- port
- nginx_plus_upstream_peer, nginx_plus_stream_upstream_peer
- id
- upstream
- server
- port
- upstream_address
## Example Output
Using this configuration:
```toml
[[inputs.nginx_plus]]
## An array of Nginx Plus status URIs to gather stats.
urls = ["http://localhost/status"]
```
When run with:
```sh
./telegraf -config telegraf.conf -input-filter nginx_plus -test
```
It produces:
```text
* Plugin: inputs.nginx_plus, Collection 1
> nginx_plus_processes,server=localhost,port=12021,host=word.local respawned=0i 1505782513000000000
> nginx_plus_connections,server=localhost,port=12021,host=word.local accepted=5535735212i,dropped=10140186i,active=9541i,idle=67540i 1505782513000000000
> nginx_plus_ssl,server=localhost,port=12021,host=word.local handshakes=0i,handshakes_failed=0i,session_reuses=0i 1505782513000000000
> nginx_plus_requests,server=localhost,port=12021,host=word.local total=186780541173i,current=9037i 1505782513000000000
> nginx_plus_upstream,port=12021,host=word.local,upstream=dataserver80,server=localhost keepalive=0i,zombies=0i 1505782513000000000
> nginx_plus_upstream_peer,upstream=dataserver80,upstream_address=10.10.102.181:80,id=0,server=localhost,port=12021,host=word.local sent=53806910399i,received=7516943964i,fails=207i,downtime=2325979i,selected=1505782512000i,backup=false,active=6i,responses_4xx=6935i,header_time=80i,response_time=80i,healthchecks_last_passed=true,responses_1xx=0i,responses_2xx=36299890i,responses_5xx=360450i,responses_total=36667275i,unavail=154i,downstart=0i,state="up",requests=36673741i,responses_3xx=0i,healthchecks_unhealthy=5i,weight=1i,healthchecks_checks=177209i,healthchecks_fails=29i 1505782513000000000
> nginx_plus_stream_upstream,server=localhost,port=12021,host=word.local,upstream=dataserver443 zombies=0i 1505782513000000000
> nginx_plus_stream_upstream_peer,server=localhost,upstream_address=10.10.102.181:443,id=0,port=12021,host=word.local,upstream=dataserver443 active=1i,healthchecks_unhealthy=1i,weight=1i,unavail=0i,connect_time=24i,first_byte_time=78i,healthchecks_last_passed=true,state="up",sent=4457713140i,received=698065272i,fails=0i,healthchecks_checks=178421i,downstart=0i,selected=1505782512000i,response_time=5156i,backup=false,connections=56251i,healthchecks_fails=20i,downtime=391017i 1505782513000000000
```
### Reference material
Subsequent versions of status response structure available here:
- [version 1](http://web.archive.org/web/20130805111222/http://nginx.org/en/docs/http/ngx_http_status_module.html)
- [version 2](http://web.archive.org/web/20131218101504/http://nginx.org/en/docs/http/ngx_http_status_module.html)
- version 3 - not available
- [version 4](http://web.archive.org/web/20141218170938/http://nginx.org/en/docs/http/ngx_http_status_module.html)
- [version 5](http://web.archive.org/web/20150414043916/http://nginx.org/en/docs/http/ngx_http_status_module.html)
- [version 6](http://web.archive.org/web/20150918163811/http://nginx.org/en/docs/http/ngx_http_status_module.html)
- [version 7](http://web.archive.org/web/20161107221028/http://nginx.org/en/docs/http/ngx_http_status_module.html)

View file

@ -0,0 +1,569 @@
//go:generate ../../../tools/readme_config_includer/generator
package nginx_plus
import (
"bufio"
_ "embed"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/common/tls"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
type NginxPlus struct {
Urls []string `toml:"urls"`
ResponseTimeout config.Duration `toml:"response_timeout"`
tls.ClientConfig
client *http.Client
}
func (*NginxPlus) SampleConfig() string {
return sampleConfig
}
func (n *NginxPlus) 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 %q: %w", 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 *NginxPlus) createHTTPClient() (*http.Client, error) {
if n.ResponseTimeout < config.Duration(time.Second) {
n.ResponseTimeout = config.Duration(time.Second * 5)
}
tlsConfig, err := n.ClientConfig.TLSConfig()
if err != nil {
return nil, err
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
Timeout: time.Duration(n.ResponseTimeout),
}
return client, nil
}
func (n *NginxPlus) 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 %q: %w", 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)
}
}
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{"server": host, "port": port}
}
type responseStats struct {
Responses1xx int64 `json:"1xx"`
Responses2xx int64 `json:"2xx"`
Responses3xx int64 `json:"3xx"`
Responses4xx int64 `json:"4xx"`
Responses5xx int64 `json:"5xx"`
Total int64 `json:"total"`
}
type basicHitStats struct {
Responses int64 `json:"responses"`
Bytes int64 `json:"bytes"`
}
type extendedHitStats struct {
basicHitStats
ResponsesWritten int64 `json:"responses_written"`
BytesWritten int64 `json:"bytes_written"`
}
type healthCheckStats struct {
Checks int64 `json:"checks"`
Fails int64 `json:"fails"`
Unhealthy int64 `json:"unhealthy"`
LastPassed *bool `json:"last_passed"`
}
type status struct {
Version int `json:"version"`
NginxVersion string `json:"nginx_version"`
Address string `json:"address"`
Generation *int `json:"generation"` // added in version 5
LoadTimestamp *int64 `json:"load_timestamp"` // added in version 2
Timestamp int64 `json:"timestamp"`
Pid *int `json:"pid"` // added in version 6
Processes *struct { // added in version 5
Respawned *int `json:"respawned"`
} `json:"processes"`
Connections struct {
Accepted int64 `json:"accepted"`
Dropped int64 `json:"dropped"`
Active int64 `json:"active"`
Idle int64 `json:"idle"`
} `json:"connections"`
Ssl *struct { // added in version 6
Handshakes int64 `json:"handshakes"`
HandshakesFailed int64 `json:"handshakes_failed"`
SessionReuses int64 `json:"session_reuses"`
} `json:"ssl"`
Requests struct {
Total int64 `json:"total"`
Current int `json:"current"`
} `json:"requests"`
ServerZones map[string]struct { // added in version 2
Processing int `json:"processing"`
Requests int64 `json:"requests"`
Responses responseStats `json:"responses"`
Discarded *int64 `json:"discarded"` // added in version 6
Received int64 `json:"received"`
Sent int64 `json:"sent"`
} `json:"server_zones"`
Upstreams map[string]struct {
Peers []struct {
ID *int `json:"id"` // added in version 3
Server string `json:"server"`
Backup bool `json:"backup"`
Weight int `json:"weight"`
State string `json:"state"`
Active int `json:"active"`
Keepalive *int `json:"keepalive"` // removed in version 5
MaxConns *int `json:"max_conns"` // added in version 3
Requests int64 `json:"requests"`
Responses responseStats `json:"responses"`
Sent int64 `json:"sent"`
Received int64 `json:"received"`
Fails int64 `json:"fails"`
Unavail int64 `json:"unavail"`
HealthChecks healthCheckStats `json:"health_checks"`
Downtime int64 `json:"downtime"`
Downstart int64 `json:"downstart"`
Selected *int64 `json:"selected"` // added in version 4
HeaderTime *int64 `json:"header_time"` // added in version 5
ResponseTime *int64 `json:"response_time"` // added in version 5
} `json:"peers"`
Keepalive int `json:"keepalive"`
Zombies int `json:"zombies"` // added in version 6
Queue *struct { // added in version 6
Size int `json:"size"`
MaxSize int `json:"max_size"`
Overflows int64 `json:"overflows"`
} `json:"queue"`
} `json:"upstreams"`
Caches map[string]struct { // added in version 2
Size int64 `json:"size"`
MaxSize int64 `json:"max_size"`
Cold bool `json:"cold"`
Hit basicHitStats `json:"hit"`
Stale basicHitStats `json:"stale"`
Updating basicHitStats `json:"updating"`
Revalidated *basicHitStats `json:"revalidated"` // added in version 3
Miss extendedHitStats `json:"miss"`
Expired extendedHitStats `json:"expired"`
Bypass extendedHitStats `json:"bypass"`
} `json:"caches"`
Stream struct {
ServerZones map[string]struct {
Processing int `json:"processing"`
Connections int `json:"connections"`
Sessions *responseStats `json:"sessions"`
Discarded *int64 `json:"discarded"` // added in version 7
Received int64 `json:"received"`
Sent int64 `json:"sent"`
} `json:"server_zones"`
Upstreams map[string]struct {
Peers []struct {
ID int `json:"id"`
Server string `json:"server"`
Backup bool `json:"backup"`
Weight int `json:"weight"`
State string `json:"state"`
Active int `json:"active"`
Connections int64 `json:"connections"`
ConnectTime *int `json:"connect_time"`
FirstByteTime *int `json:"first_byte_time"`
ResponseTime *int `json:"response_time"`
Sent int64 `json:"sent"`
Received int64 `json:"received"`
Fails int64 `json:"fails"`
Unavail int64 `json:"unavail"`
HealthChecks healthCheckStats `json:"health_checks"`
Downtime int64 `json:"downtime"`
Downstart int64 `json:"downstart"`
Selected int64 `json:"selected"`
} `json:"peers"`
Zombies int `json:"zombies"`
} `json:"upstreams"`
} `json:"stream"`
}
func gatherStatusURL(r *bufio.Reader, tags map[string]string, acc telegraf.Accumulator) error {
dec := json.NewDecoder(r)
status := &status{}
if err := dec.Decode(status); err != nil {
return errors.New("error while decoding JSON response")
}
status.gather(tags, acc)
return nil
}
func (s *status) gather(tags map[string]string, acc telegraf.Accumulator) {
s.gatherProcessesMetrics(tags, acc)
s.gatherConnectionsMetrics(tags, acc)
s.gatherSslMetrics(tags, acc)
s.gatherRequestMetrics(tags, acc)
s.gatherZoneMetrics(tags, acc)
s.gatherUpstreamMetrics(tags, acc)
s.gatherCacheMetrics(tags, acc)
s.gatherStreamMetrics(tags, acc)
}
func (s *status) gatherProcessesMetrics(tags map[string]string, acc telegraf.Accumulator) {
var respawned int
if s.Processes.Respawned != nil {
respawned = *s.Processes.Respawned
}
acc.AddFields(
"nginx_plus_processes",
map[string]interface{}{
"respawned": respawned,
},
tags,
)
}
func (s *status) gatherConnectionsMetrics(tags map[string]string, acc telegraf.Accumulator) {
acc.AddFields(
"nginx_plus_connections",
map[string]interface{}{
"accepted": s.Connections.Accepted,
"dropped": s.Connections.Dropped,
"active": s.Connections.Active,
"idle": s.Connections.Idle,
},
tags,
)
}
func (s *status) gatherSslMetrics(tags map[string]string, acc telegraf.Accumulator) {
acc.AddFields(
"nginx_plus_ssl",
map[string]interface{}{
"handshakes": s.Ssl.Handshakes,
"handshakes_failed": s.Ssl.HandshakesFailed,
"session_reuses": s.Ssl.SessionReuses,
},
tags,
)
}
func (s *status) gatherRequestMetrics(tags map[string]string, acc telegraf.Accumulator) {
acc.AddFields(
"nginx_plus_requests",
map[string]interface{}{
"total": s.Requests.Total,
"current": s.Requests.Current,
},
tags,
)
}
func (s *status) gatherZoneMetrics(tags map[string]string, acc telegraf.Accumulator) {
for zoneName, zone := range s.ServerZones {
zoneTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
zoneTags[k] = v
}
zoneTags["zone"] = zoneName
acc.AddFields(
"nginx_plus_zone",
func() map[string]interface{} {
result := map[string]interface{}{
"processing": zone.Processing,
"requests": zone.Requests,
"responses_1xx": zone.Responses.Responses1xx,
"responses_2xx": zone.Responses.Responses2xx,
"responses_3xx": zone.Responses.Responses3xx,
"responses_4xx": zone.Responses.Responses4xx,
"responses_5xx": zone.Responses.Responses5xx,
"responses_total": zone.Responses.Total,
"received": zone.Received,
"sent": zone.Sent,
}
if zone.Discarded != nil {
result["discarded"] = *zone.Discarded
}
return result
}(),
zoneTags,
)
}
}
func (s *status) gatherUpstreamMetrics(tags map[string]string, acc telegraf.Accumulator) {
for upstreamName, upstream := range s.Upstreams {
upstreamTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
upstreamTags[k] = v
}
upstreamTags["upstream"] = upstreamName
upstreamFields := map[string]interface{}{
"keepalive": upstream.Keepalive,
"zombies": upstream.Zombies,
}
if upstream.Queue != nil {
upstreamFields["queue_size"] = upstream.Queue.Size
upstreamFields["queue_max_size"] = upstream.Queue.MaxSize
upstreamFields["queue_overflows"] = upstream.Queue.Overflows
}
acc.AddFields(
"nginx_plus_upstream",
upstreamFields,
upstreamTags,
)
for _, peer := range upstream.Peers {
var selected int64
if peer.Selected != nil {
selected = *peer.Selected
}
peerFields := map[string]interface{}{
"backup": peer.Backup,
"weight": peer.Weight,
"state": peer.State,
"active": peer.Active,
"requests": peer.Requests,
"responses_1xx": peer.Responses.Responses1xx,
"responses_2xx": peer.Responses.Responses2xx,
"responses_3xx": peer.Responses.Responses3xx,
"responses_4xx": peer.Responses.Responses4xx,
"responses_5xx": peer.Responses.Responses5xx,
"responses_total": peer.Responses.Total,
"sent": peer.Sent,
"received": peer.Received,
"fails": peer.Fails,
"unavail": peer.Unavail,
"healthchecks_checks": peer.HealthChecks.Checks,
"healthchecks_fails": peer.HealthChecks.Fails,
"healthchecks_unhealthy": peer.HealthChecks.Unhealthy,
"downtime": peer.Downtime,
"downstart": peer.Downstart,
"selected": selected,
}
if peer.HealthChecks.LastPassed != nil {
peerFields["healthchecks_last_passed"] = *peer.HealthChecks.LastPassed
}
if peer.HeaderTime != nil {
peerFields["header_time"] = *peer.HeaderTime
}
if peer.ResponseTime != nil {
peerFields["response_time"] = *peer.ResponseTime
}
if peer.MaxConns != nil {
peerFields["max_conns"] = *peer.MaxConns
}
peerTags := make(map[string]string, len(upstreamTags)+2)
for k, v := range upstreamTags {
peerTags[k] = v
}
peerTags["upstream_address"] = peer.Server
if peer.ID != nil {
peerTags["id"] = strconv.Itoa(*peer.ID)
}
acc.AddFields("nginx_plus_upstream_peer", peerFields, peerTags)
}
}
}
func (s *status) gatherCacheMetrics(tags map[string]string, acc telegraf.Accumulator) {
for cacheName, cache := range s.Caches {
cacheTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
cacheTags[k] = v
}
cacheTags["cache"] = cacheName
acc.AddFields(
"nginx_plus_cache",
map[string]interface{}{
"size": cache.Size,
"max_size": cache.MaxSize,
"cold": cache.Cold,
"hit_responses": cache.Hit.Responses,
"hit_bytes": cache.Hit.Bytes,
"stale_responses": cache.Stale.Responses,
"stale_bytes": cache.Stale.Bytes,
"updating_responses": cache.Updating.Responses,
"updating_bytes": cache.Updating.Bytes,
"revalidated_responses": cache.Revalidated.Responses,
"revalidated_bytes": cache.Revalidated.Bytes,
"miss_responses": cache.Miss.Responses,
"miss_bytes": cache.Miss.Bytes,
"miss_responses_written": cache.Miss.ResponsesWritten,
"miss_bytes_written": cache.Miss.BytesWritten,
"expired_responses": cache.Expired.Responses,
"expired_bytes": cache.Expired.Bytes,
"expired_responses_written": cache.Expired.ResponsesWritten,
"expired_bytes_written": cache.Expired.BytesWritten,
"bypass_responses": cache.Bypass.Responses,
"bypass_bytes": cache.Bypass.Bytes,
"bypass_responses_written": cache.Bypass.ResponsesWritten,
"bypass_bytes_written": cache.Bypass.BytesWritten,
},
cacheTags,
)
}
}
func (s *status) gatherStreamMetrics(tags map[string]string, acc telegraf.Accumulator) {
for zoneName, zone := range s.Stream.ServerZones {
zoneTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
zoneTags[k] = v
}
zoneTags["zone"] = zoneName
acc.AddFields(
"nginx.stream.zone",
map[string]interface{}{
"processing": zone.Processing,
"connections": zone.Connections,
"received": zone.Received,
"sent": zone.Sent,
},
zoneTags,
)
}
for upstreamName, upstream := range s.Stream.Upstreams {
upstreamTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
upstreamTags[k] = v
}
upstreamTags["upstream"] = upstreamName
acc.AddFields(
"nginx_plus_stream_upstream",
map[string]interface{}{
"zombies": upstream.Zombies,
},
upstreamTags,
)
for _, peer := range upstream.Peers {
peerFields := map[string]interface{}{
"backup": peer.Backup,
"weight": peer.Weight,
"state": peer.State,
"active": peer.Active,
"connections": peer.Connections,
"sent": peer.Sent,
"received": peer.Received,
"fails": peer.Fails,
"unavail": peer.Unavail,
"healthchecks_checks": peer.HealthChecks.Checks,
"healthchecks_fails": peer.HealthChecks.Fails,
"healthchecks_unhealthy": peer.HealthChecks.Unhealthy,
"downtime": peer.Downtime,
"downstart": peer.Downstart,
"selected": peer.Selected,
}
if peer.HealthChecks.LastPassed != nil {
peerFields["healthchecks_last_passed"] = *peer.HealthChecks.LastPassed
}
if peer.ConnectTime != nil {
peerFields["connect_time"] = *peer.ConnectTime
}
if peer.FirstByteTime != nil {
peerFields["first_byte_time"] = *peer.FirstByteTime
}
if peer.ResponseTime != nil {
peerFields["response_time"] = *peer.ResponseTime
}
peerTags := make(map[string]string, len(upstreamTags)+2)
for k, v := range upstreamTags {
peerTags[k] = v
}
peerTags["upstream_address"] = peer.Server
peerTags["id"] = strconv.Itoa(peer.ID)
acc.AddFields("nginx_plus_stream_upstream_peer", peerFields, peerTags)
}
}
}
func init() {
inputs.Add("nginx_plus", func() telegraf.Input {
return &NginxPlus{}
})
}

View file

@ -0,0 +1,410 @@
package nginx_plus
import (
"fmt"
"net"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/testutil"
)
const sampleStatusResponse = `
{
"version": 6,
"nginx_version": "1.22.333",
"address": "1.2.3.4",
"generation": 88,
"load_timestamp": 1451606400000,
"timestamp": 1451606400000,
"pid": 9999,
"processes": {
"respawned": 9999
},
"connections": {
"accepted": 1234567890000,
"dropped": 2345678900000,
"active": 345,
"idle": 567
},
"ssl": {
"handshakes": 1234567800000,
"handshakes_failed": 5432100000000,
"session_reuses": 6543210000000
},
"requests": {
"total": 9876543210000,
"current": 98
},
"server_zones": {
"zone.a_80": {
"processing": 12,
"requests": 34,
"responses": {
"1xx": 111,
"2xx": 222,
"3xx": 333,
"4xx": 444,
"5xx": 555,
"total": 999
},
"discarded": 11,
"received": 22,
"sent": 33
},
"zone.a_443": {
"processing": 45,
"requests": 67,
"responses": {
"1xx": 1111,
"2xx": 2222,
"3xx": 3333,
"4xx": 4444,
"5xx": 5555,
"total": 999
},
"discarded": 44,
"received": 55,
"sent": 66
}
},
"upstreams": {
"first_upstream": {
"peers": [
{
"id": 0,
"server": "1.2.3.123:80",
"backup": false,
"weight": 1,
"state": "up",
"active": 0,
"requests": 9876,
"responses": {
"1xx": 1111,
"2xx": 2222,
"3xx": 3333,
"4xx": 4444,
"5xx": 5555,
"total": 987654
},
"sent": 987654321,
"received": 87654321,
"fails": 98,
"unavail": 65,
"health_checks": {
"checks": 54,
"fails": 32,
"unhealthy": 21
},
"downtime": 5432,
"downstart": 4321,
"selected": 1451606400000
},
{
"id": 1,
"server": "1.2.3.123:80",
"backup": true,
"weight": 1,
"state": "up",
"active": 0,
"requests": 8765,
"responses": {
"1xx": 1112,
"2xx": 2223,
"3xx": 3334,
"4xx": 4445,
"5xx": 5556,
"total": 987655
},
"sent": 987654322,
"received": 87654322,
"fails": 99,
"unavail": 88,
"health_checks": {
"checks": 77,
"fails": 66,
"unhealthy": 55
},
"downtime": 5433,
"downstart": 4322,
"selected": 1451606400000
}
],
"keepalive": 1,
"zombies": 2
}
},
"caches": {
"cache_01": {
"size": 12,
"max_size": 23,
"cold": false,
"hit": {
"responses": 34,
"bytes": 45
},
"stale": {
"responses": 56,
"bytes": 67
},
"updating": {
"responses": 78,
"bytes": 89
},
"revalidated": {
"responses": 90,
"bytes": 98
},
"miss": {
"responses": 87,
"bytes": 76,
"responses_written": 65,
"bytes_written": 54
},
"expired": {
"responses": 43,
"bytes": 32,
"responses_written": 21,
"bytes_written": 10
},
"bypass": {
"responses": 13,
"bytes": 35,
"responses_written": 57,
"bytes_written": 79
}
}
},
"stream": {
"server_zones": {
"stream.zone.01": {
"processing": 24,
"connections": 46,
"received": 68,
"sent": 80
},
"stream.zone.02": {
"processing": 96,
"connections": 63,
"received": 31,
"sent": 25
}
},
"upstreams": {
"upstream.01": {
"peers": [
{
"id": 0,
"server": "4.3.2.1:2345",
"backup": false,
"weight": 1,
"state": "up",
"active": 0,
"connections": 0,
"sent": 0,
"received": 0,
"fails": 0,
"unavail": 0,
"health_checks": {
"checks": 40848,
"fails": 0,
"unhealthy": 0,
"last_passed": true
},
"downtime": 0,
"downstart": 0,
"selected": 0
},
{
"id": 1,
"server": "5.4.3.2:2345",
"backup": false,
"weight": 1,
"state": "up",
"active": 0,
"connections": 0,
"sent": 0,
"received": 0,
"fails": 0,
"unavail": 0,
"health_checks": {
"checks": 40851,
"fails": 0,
"unhealthy": 0,
"last_passed": true
},
"downtime": 0,
"downstart": 0,
"selected": 0
}
],
"zombies": 0
}
}
}
}`
func TestNginxPlusGeneratesMetrics(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/status" {
w.WriteHeader(http.StatusInternalServerError)
t.Errorf("Cannot handle request, expected: %q, actual: %q", "/status", r.URL.Path)
return
}
w.Header()["Content-Type"] = []string{"application/json"}
if _, err := fmt.Fprintln(w, sampleStatusResponse); err != nil {
w.WriteHeader(http.StatusInternalServerError)
t.Error(err)
return
}
}))
defer ts.Close()
n := &NginxPlus{
Urls: []string{ts.URL + "/status"},
}
var acc testutil.Accumulator
errNginx := n.Gather(&acc)
require.NoError(t, errNginx)
addr, err := url.Parse(ts.URL)
require.NoError(t, 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_plus_processes",
map[string]interface{}{
"respawned": int(9999),
},
map[string]string{
"server": host,
"port": port,
})
acc.AssertContainsTaggedFields(
t,
"nginx_plus_connections",
map[string]interface{}{
"accepted": int64(1234567890000),
"dropped": int64(2345678900000),
"active": int64(345),
"idle": int64(567),
},
map[string]string{
"server": host,
"port": port,
})
acc.AssertContainsTaggedFields(
t,
"nginx_plus_ssl",
map[string]interface{}{
"handshakes": int64(1234567800000),
"handshakes_failed": int64(5432100000000),
"session_reuses": int64(6543210000000),
},
map[string]string{
"server": host,
"port": port,
})
acc.AssertContainsTaggedFields(
t,
"nginx_plus_requests",
map[string]interface{}{
"total": int64(9876543210000),
"current": int(98),
},
map[string]string{
"server": host,
"port": port,
})
acc.AssertContainsTaggedFields(
t,
"nginx_plus_zone",
map[string]interface{}{
"processing": int(12),
"requests": int64(34),
"responses_1xx": int64(111),
"responses_2xx": int64(222),
"responses_3xx": int64(333),
"responses_4xx": int64(444),
"responses_5xx": int64(555),
"responses_total": int64(999),
"discarded": int64(11),
"received": int64(22),
"sent": int64(33),
},
map[string]string{
"server": host,
"port": port,
"zone": "zone.a_80",
})
acc.AssertContainsTaggedFields(
t,
"nginx_plus_upstream",
map[string]interface{}{
"keepalive": int(1),
"zombies": int(2),
},
map[string]string{
"server": host,
"port": port,
"upstream": "first_upstream",
})
acc.AssertContainsTaggedFields(
t,
"nginx_plus_upstream_peer",
map[string]interface{}{
"backup": false,
"weight": int(1),
"state": "up",
"active": int(0),
"requests": int64(9876),
"responses_1xx": int64(1111),
"responses_2xx": int64(2222),
"responses_3xx": int64(3333),
"responses_4xx": int64(4444),
"responses_5xx": int64(5555),
"responses_total": int64(987654),
"sent": int64(987654321),
"received": int64(87654321),
"fails": int64(98),
"unavail": int64(65),
"healthchecks_checks": int64(54),
"healthchecks_fails": int64(32),
"healthchecks_unhealthy": int64(21),
"downtime": int64(5432),
"downstart": int64(4321),
"selected": int64(1451606400000),
},
map[string]string{
"server": host,
"port": port,
"upstream": "first_upstream",
"upstream_address": "1.2.3.123:80",
"id": "0",
})
}

View file

@ -0,0 +1,14 @@
# Read Nginx Plus' advanced status information
[[inputs.nginx_plus]]
## An array of Nginx status URIs to gather stats.
urls = ["http://localhost/status"]
# HTTP response timeout (default: 5s)
response_timeout = "5s"
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false