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,329 @@
# Nginx Plus API Input Plugin
This plugin gathers metrics from the commercial
[Nginx Plus web server][nginx_plus] via the [REST API][api].
> [!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.9.0
🏷️ server, web
💻 all
[nginx_plus]: https://www.f5.com/products/nginx/nginx-plus
[api]: https://demo.nginx.com/swagger-ui/
[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 API advanced status information
[[inputs.nginx_plus_api]]
## An array of Nginx API URIs to gather stats.
urls = ["http://localhost/api"]
# Nginx API version, default: 3
# api_version = 3
# 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
```
## Migration from Nginx Plus (Status) input plugin
| Nginx Plus | Nginx Plus API |
|---------------------------------|--------------------------------------|
| nginx_plus_processes | nginx_plus_api_processes |
| nginx_plus_connections | nginx_plus_api_connections |
| nginx_plus_ssl | nginx_plus_api_ssl |
| nginx_plus_requests | nginx_plus_api_http_requests |
| nginx_plus_zone | nginx_plus_api_http_server_zones |
| nginx_plus_upstream | nginx_plus_api_http_upstreams |
| nginx_plus_upstream_peer | nginx_plus_api_http_upstream_peers |
| nginx_plus_cache | nginx_plus_api_http_caches |
| nginx_plus_stream_upstream | nginx_plus_api_stream_upstreams |
| nginx_plus_stream_upstream_peer | nginx_plus_api_stream_upstream_peers |
| nginx.stream.zone | nginx_plus_api_stream_server_zones |
## Measurements by API version
| Measurement | API version (api_version) |
|--------------------------------------|---------------------------|
| nginx_plus_api_processes | >= 3 |
| nginx_plus_api_connections | >= 3 |
| nginx_plus_api_ssl | >= 3 |
| nginx_plus_api_slabs_pages | >= 3 |
| nginx_plus_api_slabs_slots | >= 3 |
| nginx_plus_api_http_requests | >= 3 |
| nginx_plus_api_http_server_zones | >= 3 |
| nginx_plus_api_http_upstreams | >= 3 |
| nginx_plus_api_http_upstream_peers | >= 3 |
| nginx_plus_api_http_caches | >= 3 |
| nginx_plus_api_stream_upstreams | >= 3 |
| nginx_plus_api_stream_upstream_peers | >= 3 |
| nginx_plus_api_stream_server_zones | >= 3 |
| nginx_plus_api_http_location_zones | >= 5 |
| nginx_plus_api_resolver_zones | >= 5 |
| nginx_plus_api_http_limit_reqs | >= 6 |
## Metrics
- nginx_plus_api_processes
- respawned
- nginx_plus_api_connections
- accepted
- dropped
- active
- idle
- nginx_plus_api_slabs_pages
- used
- free
- nginx_plus_api_slabs_slots
- used
- free
- reqs
- fails
- nginx_plus_api_ssl
- handshakes
- handshakes_failed
- session_reuses
- nginx_plus_api_http_requests
- total
- current
- nginx_plus_api_http_server_zones
- processing
- requests
- responses_1xx
- responses_2xx
- responses_3xx
- responses_4xx
- responses_5xx
- responses_total
- received
- sent
- discarded
- nginx_plus_api_http_upstreams
- keepalive
- zombies
- nginx_plus_api_http_upstream_peers
- requests
- unavail
- healthchecks_checks
- header_time
- state
- response_time
- active
- healthchecks_last_passed
- weight
- responses_1xx
- responses_2xx
- responses_3xx
- responses_4xx
- responses_5xx
- received
- healthchecks_fails
- healthchecks_unhealthy
- backup
- responses_total
- sent
- fails
- downtime
- nginx_plus_api_http_caches
- size
- max_size
- cold
- hit_responses
- hit_bytes
- stale_responses
- stale_bytes
- updating_responses
- updating_bytes
- revalidated_responses
- revalidated_bytes
- miss_responses
- miss_bytes
- miss_responses_written
- miss_bytes_written
- expired_responses
- expired_bytes
- expired_responses_written
- expired_bytes_written
- bypass_responses
- bypass_bytes
- bypass_responses_written
- bypass_bytes_written
- nginx_plus_api_stream_upstreams
- zombies
- nginx_plus_api_stream_upstream_peers
- unavail
- healthchecks_checks
- healthchecks_fails
- healthchecks_unhealthy
- healthchecks_last_passed
- response_time
- state
- active
- weight
- received
- backup
- sent
- fails
- downtime
- nginx_plus_api_stream_server_zones
- processing
- connections
- received
- sent
- nginx_plus_api_location_zones
- requests
- responses_1xx
- responses_2xx
- responses_3xx
- responses_4xx
- responses_5xx
- responses_total
- received
- sent
- discarded
- nginx_plus_api_resolver_zones
- name
- srv
- addr
- noerror
- formerr
- servfail
- nxdomain
- notimp
- refused
- timedout
- unknown
- nginx_plus_api_http_limit_reqs
- passed
- delayed
- rejected
- delayed_dry_run
- rejected_dry_run
### Tags
- nginx_plus_api_processes, nginx_plus_api_connections, nginx_plus_api_ssl, nginx_plus_api_http_requests
- source
- port
- nginx_plus_api_http_upstreams, nginx_plus_api_stream_upstreams
- upstream
- source
- port
- nginx_plus_api_http_server_zones, nginx_plus_api_upstream_server_zones, nginx_plus_api_http_location_zones, nginx_plus_api_resolver_zones, nginx_plus_api_slabs_pages
- source
- port
- zone
- nginx_plus_api_slabs_slots
- source
- port
- zone
- slot
- nginx_plus_api_upstream_peers, nginx_plus_api_stream_upstream_peers
- id
- upstream
- source
- port
- upstream_address
- nginx_plus_api_http_caches
- source
- port
- nginx_plus_api_http_limit_reqs
- source
- port
- limit
## Example Output
Using this configuration:
```toml
[[inputs.nginx_plus_api]]
## An array of Nginx Plus API URIs to gather stats.
urls = ["http://localhost/api"]
```
When run with:
```sh
./telegraf -config telegraf.conf -input-filter nginx_plus_api -test
```
It produces:
```text
nginx_plus_api_processes,port=80,source=demo.nginx.com respawned=0i 1570696321000000000
nginx_plus_api_connections,port=80,source=demo.nginx.com accepted=68998606i,active=7i,dropped=0i,idle=57i 1570696322000000000
nginx_plus_api_slabs_pages,port=80,source=demo.nginx.com,zone=hg.nginx.org used=1i,free=503i 1570696322000000000
nginx_plus_api_slabs_pages,port=80,source=demo.nginx.com,zone=trac.nginx.org used=3i,free=500i 1570696322000000000
nginx_plus_api_slabs_slots,port=80,source=demo.nginx.com,zone=hg.nginx.org,slot=8 used=1i,free=503i,reqs=10i,fails=0i 1570696322000000000
nginx_plus_api_slabs_slots,port=80,source=demo.nginx.com,zone=hg.nginx.org,slot=16 used=3i,free=500i,reqs=1024i,fails=0i 1570696322000000000
nginx_plus_api_slabs_slots,port=80,source=demo.nginx.com,zone=trac.nginx.org,slot=8 used=1i,free=503i,reqs=10i,fails=0i 1570696322000000000
nginx_plus_api_slabs_slots,port=80,source=demo.nginx.com,zone=trac.nginx.org,slot=16 used=0i,free=1520i,reqs=0i,fails=1i 1570696322000000000
nginx_plus_api_ssl,port=80,source=demo.nginx.com handshakes=9398978i,handshakes_failed=289353i,session_reuses=1004389i 1570696322000000000
nginx_plus_api_http_requests,port=80,source=demo.nginx.com current=51i,total=264649353i 1570696322000000000
nginx_plus_api_http_server_zones,port=80,source=demo.nginx.com,zone=hg.nginx.org discarded=5i,processing=0i,received=24123604i,requests=60138i,responses_1xx=0i,responses_2xx=59353i,responses_3xx=531i,responses_4xx=249i,responses_5xx=0i,responses_total=60133i,sent=830165221i 1570696322000000000
nginx_plus_api_http_server_zones,port=80,source=demo.nginx.com,zone=trac.nginx.org discarded=250i,processing=0i,received=2184618i,requests=12404i,responses_1xx=0i,responses_2xx=8579i,responses_3xx=2513i,responses_4xx=583i,responses_5xx=479i,responses_total=12154i,sent=139384159i 1570696322000000000
nginx_plus_api_http_server_zones,port=80,source=demo.nginx.com,zone=lxr.nginx.org discarded=1i,processing=0i,received=1011701i,requests=4523i,responses_1xx=0i,responses_2xx=4332i,responses_3xx=28i,responses_4xx=39i,responses_5xx=123i,responses_total=4522i,sent=72631354i 1570696322000000000
nginx_plus_api_http_upstreams,port=80,source=demo.nginx.com,upstream=trac-backend keepalive=0i,zombies=0i 1570696322000000000
nginx_plus_api_http_upstream_peers,id=0,port=80,source=demo.nginx.com,upstream=trac-backend,upstream_address=10.0.0.1:8080 active=0i,backup=false,downtime=0i,fails=0i,header_time=235i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=88581178i,requests=3180i,response_time=235i,responses_1xx=0i,responses_2xx=3168i,responses_3xx=5i,responses_4xx=6i,responses_5xx=0i,responses_total=3179i,sent=1321720i,state="up",unavail=0i,weight=1i 1570696322000000000
nginx_plus_api_http_upstream_peers,id=1,port=80,source=demo.nginx.com,upstream=trac-backend,upstream_address=10.0.0.1:8081 active=0i,backup=true,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,requests=0i,responses_1xx=0i,responses_2xx=0i,responses_3xx=0i,responses_4xx=0i,responses_5xx=0i,responses_total=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696322000000000
nginx_plus_api_http_upstreams,port=80,source=demo.nginx.com,upstream=hg-backend keepalive=0i,zombies=0i 1570696322000000000
nginx_plus_api_http_upstream_peers,id=0,port=80,source=demo.nginx.com,upstream=hg-backend,upstream_address=10.0.0.1:8088 active=0i,backup=false,downtime=0i,fails=0i,header_time=22i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=909402572i,requests=18514i,response_time=88i,responses_1xx=0i,responses_2xx=17799i,responses_3xx=531i,responses_4xx=179i,responses_5xx=0i,responses_total=18509i,sent=10608107i,state="up",unavail=0i,weight=5i 1570696322000000000
nginx_plus_api_http_upstream_peers,id=1,port=80,source=demo.nginx.com,upstream=hg-backend,upstream_address=10.0.0.1:8089 active=0i,backup=true,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,requests=0i,responses_1xx=0i,responses_2xx=0i,responses_3xx=0i,responses_4xx=0i,responses_5xx=0i,responses_total=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696322000000000
nginx_plus_api_http_upstreams,port=80,source=demo.nginx.com,upstream=lxr-backend keepalive=0i,zombies=0i 1570696322000000000
nginx_plus_api_http_upstream_peers,id=0,port=80,source=demo.nginx.com,upstream=lxr-backend,upstream_address=unix:/tmp/cgi.sock active=0i,backup=false,downtime=0i,fails=123i,header_time=91i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=71782888i,requests=4354i,response_time=91i,responses_1xx=0i,responses_2xx=4230i,responses_3xx=0i,responses_4xx=0i,responses_5xx=0i,responses_total=4230i,sent=3088656i,state="up",unavail=0i,weight=1i 1570696322000000000
nginx_plus_api_http_upstream_peers,id=1,port=80,source=demo.nginx.com,upstream=lxr-backend,upstream_address=unix:/tmp/cgib.sock active=0i,backup=true,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,max_conns=42i,received=0i,requests=0i,responses_1xx=0i,responses_2xx=0i,responses_3xx=0i,responses_4xx=0i,responses_5xx=0i,responses_total=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696322000000000
nginx_plus_api_http_upstreams,port=80,source=demo.nginx.com,upstream=demo-backend keepalive=0i,zombies=0i 1570696322000000000
nginx_plus_api_http_upstream_peers,id=0,port=80,source=demo.nginx.com,upstream=demo-backend,upstream_address=10.0.0.2:15431 active=0i,backup=false,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,requests=0i,responses_1xx=0i,responses_2xx=0i,responses_3xx=0i,responses_4xx=0i,responses_5xx=0i,responses_total=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696322000000000
nginx_plus_api_http_caches,cache=http_cache,port=80,source=demo.nginx.com bypass_bytes=0i,bypass_bytes_written=0i,bypass_responses=0i,bypass_responses_written=0i,cold=false,expired_bytes=381518640i,expired_bytes_written=363449785i,expired_responses=42114i,expired_responses_written=39954i,hit_bytes=6321885979i,hit_responses=596730i,max_size=536870912i,miss_bytes=48512185i,miss_bytes_written=155600i,miss_responses=6052i,miss_responses_written=136i,revalidated_bytes=0i,revalidated_responses=0i,size=765952i,stale_bytes=0i,stale_responses=0i,updating_bytes=0i,updating_responses=0i 1570696323000000000
nginx_plus_api_stream_server_zones,port=80,source=demo.nginx.com,zone=postgresql_loadbalancer connections=0i,processing=0i,received=0i,sent=0i 1570696323000000000
nginx_plus_api_stream_server_zones,port=80,source=demo.nginx.com,zone=dns_loadbalancer connections=0i,processing=0i,received=0i,sent=0i 1570696323000000000
nginx_plus_api_stream_upstreams,port=80,source=demo.nginx.com,upstream=postgresql_backends zombies=0i 1570696323000000000
nginx_plus_api_stream_upstream_peers,id=0,port=80,source=demo.nginx.com,upstream=postgresql_backends,upstream_address=10.0.0.2:15432 active=0i,backup=false,connections=0i,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696323000000000
nginx_plus_api_stream_upstream_peers,id=1,port=80,source=demo.nginx.com,upstream=postgresql_backends,upstream_address=10.0.0.2:15433 active=0i,backup=false,connections=0i,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696323000000000
nginx_plus_api_stream_upstream_peers,id=2,port=80,source=demo.nginx.com,upstream=postgresql_backends,upstream_address=10.0.0.2:15434 active=0i,backup=false,connections=0i,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696323000000000
nginx_plus_api_stream_upstream_peers,id=3,port=80,source=demo.nginx.com,upstream=postgresql_backends,upstream_address=10.0.0.2:15435 active=0i,backup=false,connections=0i,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,sent=0i,state="down",unavail=0i,weight=1i 1570696323000000000
nginx_plus_api_stream_upstreams,port=80,source=demo.nginx.com,upstream=dns_udp_backends zombies=0i 1570696323000000000
nginx_plus_api_stream_upstream_peers,id=0,port=80,source=demo.nginx.com,upstream=dns_udp_backends,upstream_address=10.0.0.5:53 active=0i,backup=false,connections=0i,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,sent=0i,state="up",unavail=0i,weight=2i 1570696323000000000
nginx_plus_api_stream_upstream_peers,id=1,port=80,source=demo.nginx.com,upstream=dns_udp_backends,upstream_address=10.0.0.2:53 active=0i,backup=false,connections=0i,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,sent=0i,state="up",unavail=0i,weight=1i 1570696323000000000
nginx_plus_api_stream_upstream_peers,id=2,port=80,source=demo.nginx.com,upstream=dns_udp_backends,upstream_address=10.0.0.7:53 active=0i,backup=false,connections=0i,downtime=0i,fails=0i,healthchecks_checks=0i,healthchecks_fails=0i,healthchecks_unhealthy=0i,received=0i,sent=0i,state="down",unavail=0i,weight=1i 1570696323000000000
nginx_plus_api_stream_upstreams,port=80,source=demo.nginx.com,upstream=unused_tcp_backends zombies=0i 1570696323000000000
nginx_plus_api_http_location_zones,port=80,source=demo.nginx.com,zone=swagger discarded=0i,received=1622i,requests=8i,responses_1xx=0i,responses_2xx=7i,responses_3xx=0i,responses_4xx=1i,responses_5xx=0i,responses_total=8i,sent=638333i 1570696323000000000
nginx_plus_api_http_location_zones,port=80,source=demo.nginx.com,zone=api-calls discarded=64i,received=337530181i,requests=1726513i,responses_1xx=0i,responses_2xx=1726428i,responses_3xx=0i,responses_4xx=21i,responses_5xx=0i,responses_total=1726449i,sent=1902577668i 1570696323000000000
nginx_plus_api_resolver_zones,port=80,source=demo.nginx.com,zone=resolver1 addr=0i,formerr=0i,name=0i,noerror=0i,notimp=0i,nxdomain=0i,refused=0i,servfail=0i,srv=0i,timedout=0i,unknown=0i 1570696324000000000
nginx_plus_api_http_limit_reqs,port=80,source=demo.nginx.com,limit=limit_1 delayed=0i,delayed_dry_run=0i,passed=6i,rejected=9i,rejected_dry_run=0i 1570696322000000000
nginx_plus_api_http_limit_reqs,port=80,source=demo.nginx.com,limit=limit_2 delayed=13i,delayed_dry_run=3i,passed=6i,rejected=1i,rejected_dry_run=31i 1570696322000000000
```
### Reference material
- [api documentation](http://demo.nginx.com/swagger-ui/#/)
- [nginx_api_module documentation](http://nginx.org/en/docs/http/ngx_http_api_module.html)

View file

@ -0,0 +1,116 @@
//go:generate ../../../tools/readme_config_includer/generator
package nginx_plus_api
import (
_ "embed"
"fmt"
"net/http"
"net/url"
"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
const (
// Default settings
defaultAPIVersion = 3
// Paths
processesPath = "processes"
connectionsPath = "connections"
slabsPath = "slabs"
sslPath = "ssl"
httpRequestsPath = "http/requests"
httpServerZonesPath = "http/server_zones"
httpLocationZonesPath = "http/location_zones"
httpUpstreamsPath = "http/upstreams"
httpCachesPath = "http/caches"
httpLimitReqsPath = "http/limit_reqs"
resolverZonesPath = "resolvers"
streamServerZonesPath = "stream/server_zones"
streamUpstreamsPath = "stream/upstreams"
)
type NginxPlusAPI struct {
Urls []string `toml:"urls"`
APIVersion int64 `toml:"api_version"`
ResponseTimeout config.Duration `toml:"response_timeout"`
tls.ClientConfig
client *http.Client
}
func (*NginxPlusAPI) SampleConfig() string {
return sampleConfig
}
func (n *NginxPlusAPI) Gather(acc telegraf.Accumulator) error {
var wg sync.WaitGroup
// Create an HTTP client that is re-used for each
// collection interval
if n.APIVersion == 0 {
n.APIVersion = defaultAPIVersion
}
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()
n.gatherMetrics(addr, acc)
}(addr)
}
wg.Wait()
return nil
}
func (n *NginxPlusAPI) 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 init() {
inputs.Add("nginx_plus_api", func() telegraf.Input {
return &NginxPlusAPI{}
})
}

View file

@ -0,0 +1,669 @@
package nginx_plus_api
import (
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/influxdata/telegraf"
)
var (
// errNotFound signals that the NGINX API routes does not exist.
errNotFound = errors.New("not found")
)
func (n *NginxPlusAPI) gatherMetrics(addr *url.URL, acc telegraf.Accumulator) {
addError(acc, n.gatherProcessesMetrics(addr, acc))
addError(acc, n.gatherConnectionsMetrics(addr, acc))
addError(acc, n.gatherSlabsMetrics(addr, acc))
addError(acc, n.gatherSslMetrics(addr, acc))
addError(acc, n.gatherHTTPRequestsMetrics(addr, acc))
addError(acc, n.gatherHTTPServerZonesMetrics(addr, acc))
addError(acc, n.gatherHTTPUpstreamsMetrics(addr, acc))
addError(acc, n.gatherHTTPCachesMetrics(addr, acc))
addError(acc, n.gatherStreamServerZonesMetrics(addr, acc))
addError(acc, n.gatherStreamUpstreamsMetrics(addr, acc))
if n.APIVersion >= 5 {
addError(acc, n.gatherHTTPLocationZonesMetrics(addr, acc))
addError(acc, n.gatherResolverZonesMetrics(addr, acc))
}
if n.APIVersion >= 6 {
addError(acc, n.gatherHTTPLimitReqsMetrics(addr, acc))
}
}
func addError(acc telegraf.Accumulator, err error) {
// This plugin has hardcoded API resource paths it checks that may not
// be in the nginx.conf. Currently, this is to prevent logging of
// paths that are not configured.
//
// The correct solution is to do a GET to /api to get the available paths
// on the server rather than simply ignore.
if !errors.Is(err, errNotFound) {
acc.AddError(err)
}
}
func (n *NginxPlusAPI) gatherURL(addr *url.URL, path string) ([]byte, error) {
address := fmt.Sprintf("%s/%d/%s", addr.String(), n.APIVersion, path)
resp, err := n.client.Get(address)
if err != nil {
return nil, fmt.Errorf("error making HTTP request to %q: %w", address, err)
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
case http.StatusNotFound:
// format as special error to catch and ignore as some nginx API
// features are either optional, or only available in some versions
return nil, errNotFound
default:
return nil, fmt.Errorf("%s returned HTTP status %s", address, resp.Status)
}
contentType := strings.Split(resp.Header.Get("Content-Type"), ";")[0]
switch contentType {
case "application/json":
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
default:
return nil, fmt.Errorf("%s returned unexpected content type %s", address, contentType)
}
}
func (n *NginxPlusAPI) gatherProcessesMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, processesPath)
if err != nil {
return err
}
var processes = &processes{}
if err := json.Unmarshal(body, processes); err != nil {
return err
}
acc.AddFields(
"nginx_plus_api_processes",
map[string]interface{}{
"respawned": processes.Respawned,
},
getTags(addr),
)
return nil
}
func (n *NginxPlusAPI) gatherConnectionsMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, connectionsPath)
if err != nil {
return err
}
var connections = &connections{}
if err := json.Unmarshal(body, connections); err != nil {
return err
}
acc.AddFields(
"nginx_plus_api_connections",
map[string]interface{}{
"accepted": connections.Accepted,
"dropped": connections.Dropped,
"active": connections.Active,
"idle": connections.Idle,
},
getTags(addr),
)
return nil
}
func (n *NginxPlusAPI) gatherSlabsMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, slabsPath)
if err != nil {
return err
}
var slabs slabs
if err := json.Unmarshal(body, &slabs); err != nil {
return err
}
tags := getTags(addr)
for zoneName, slab := range slabs {
slabTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
slabTags[k] = v
}
slabTags["zone"] = zoneName
acc.AddFields(
"nginx_plus_api_slabs_pages",
map[string]interface{}{
"used": slab.Pages.Used,
"free": slab.Pages.Free,
},
slabTags,
)
for slotID, slot := range slab.Slots {
slotTags := make(map[string]string, len(slabTags)+1)
for k, v := range slabTags {
slotTags[k] = v
}
slotTags["slot"] = slotID
acc.AddFields(
"nginx_plus_api_slabs_slots",
map[string]interface{}{
"used": slot.Used,
"free": slot.Free,
"reqs": slot.Reqs,
"fails": slot.Fails,
},
slotTags,
)
}
}
return nil
}
func (n *NginxPlusAPI) gatherSslMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, sslPath)
if err != nil {
return err
}
var ssl = &ssl{}
if err := json.Unmarshal(body, ssl); err != nil {
return err
}
acc.AddFields(
"nginx_plus_api_ssl",
map[string]interface{}{
"handshakes": ssl.Handshakes,
"handshakes_failed": ssl.HandshakesFailed,
"session_reuses": ssl.SessionReuses,
},
getTags(addr),
)
return nil
}
func (n *NginxPlusAPI) gatherHTTPRequestsMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, httpRequestsPath)
if err != nil {
return err
}
var httpRequests = &httpRequests{}
if err := json.Unmarshal(body, httpRequests); err != nil {
return err
}
acc.AddFields(
"nginx_plus_api_http_requests",
map[string]interface{}{
"total": httpRequests.Total,
"current": httpRequests.Current,
},
getTags(addr),
)
return nil
}
func (n *NginxPlusAPI) gatherHTTPServerZonesMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, httpServerZonesPath)
if err != nil {
return err
}
var httpServerZones httpServerZones
if err := json.Unmarshal(body, &httpServerZones); err != nil {
return err
}
tags := getTags(addr)
for zoneName, zone := range httpServerZones {
zoneTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
zoneTags[k] = v
}
zoneTags["zone"] = zoneName
acc.AddFields(
"nginx_plus_api_http_server_zones",
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,
)
}
return nil
}
// Added in 5 API version
func (n *NginxPlusAPI) gatherHTTPLocationZonesMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, httpLocationZonesPath)
if err != nil {
return err
}
var httpLocationZones httpLocationZones
if err := json.Unmarshal(body, &httpLocationZones); err != nil {
return err
}
tags := getTags(addr)
for zoneName, zone := range httpLocationZones {
zoneTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
zoneTags[k] = v
}
zoneTags["zone"] = zoneName
acc.AddFields(
"nginx_plus_api_http_location_zones",
func() map[string]interface{} {
result := map[string]interface{}{
"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,
)
}
return nil
}
func (n *NginxPlusAPI) gatherHTTPUpstreamsMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, httpUpstreamsPath)
if err != nil {
return err
}
var httpUpstreams httpUpstreams
if err := json.Unmarshal(body, &httpUpstreams); err != nil {
return err
}
tags := getTags(addr)
for upstreamName, upstream := range httpUpstreams {
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_api_http_upstreams",
upstreamFields,
upstreamTags,
)
for _, peer := range upstream.Peers {
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,
// "selected": peer.Selected.toInt64,
// "downstart": peer.Downstart.toInt64,
}
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_api_http_upstream_peers", peerFields, peerTags)
}
}
return nil
}
func (n *NginxPlusAPI) gatherHTTPCachesMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, httpCachesPath)
if err != nil {
return err
}
var httpCaches httpCaches
if err := json.Unmarshal(body, &httpCaches); err != nil {
return err
}
tags := getTags(addr)
for cacheName, cache := range httpCaches {
cacheTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
cacheTags[k] = v
}
cacheTags["cache"] = cacheName
acc.AddFields(
"nginx_plus_api_http_caches",
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,
)
}
return nil
}
func (n *NginxPlusAPI) gatherStreamServerZonesMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, streamServerZonesPath)
if err != nil {
return err
}
var streamServerZones streamServerZones
if err := json.Unmarshal(body, &streamServerZones); err != nil {
return err
}
tags := getTags(addr)
for zoneName, zone := range streamServerZones {
zoneTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
zoneTags[k] = v
}
zoneTags["zone"] = zoneName
acc.AddFields(
"nginx_plus_api_stream_server_zones",
map[string]interface{}{
"processing": zone.Processing,
"connections": zone.Connections,
"received": zone.Received,
"sent": zone.Sent,
},
zoneTags,
)
}
return nil
}
// Added in 5 API version
func (n *NginxPlusAPI) gatherResolverZonesMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, resolverZonesPath)
if err != nil {
return err
}
var resolverZones resolverZones
if err := json.Unmarshal(body, &resolverZones); err != nil {
return err
}
tags := getTags(addr)
for zoneName, resolver := range resolverZones {
zoneTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
zoneTags[k] = v
}
zoneTags["zone"] = zoneName
acc.AddFields(
"nginx_plus_api_resolver_zones",
map[string]interface{}{
"name": resolver.Requests.Name,
"srv": resolver.Requests.Srv,
"addr": resolver.Requests.Addr,
"noerror": resolver.Responses.Noerror,
"formerr": resolver.Responses.Formerr,
"servfail": resolver.Responses.Servfail,
"nxdomain": resolver.Responses.Nxdomain,
"notimp": resolver.Responses.Notimp,
"refused": resolver.Responses.Refused,
"timedout": resolver.Responses.Timedout,
"unknown": resolver.Responses.Unknown,
},
zoneTags,
)
}
return nil
}
func (n *NginxPlusAPI) gatherStreamUpstreamsMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, streamUpstreamsPath)
if err != nil {
return err
}
var streamUpstreams streamUpstreams
if err := json.Unmarshal(body, &streamUpstreams); err != nil {
return err
}
tags := getTags(addr)
for upstreamName, upstream := range streamUpstreams {
upstreamTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
upstreamTags[k] = v
}
upstreamTags["upstream"] = upstreamName
acc.AddFields(
"nginx_plus_api_stream_upstreams",
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,
}
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_api_stream_upstream_peers", peerFields, peerTags)
}
}
return nil
}
// Added in 6 API version
func (n *NginxPlusAPI) gatherHTTPLimitReqsMetrics(addr *url.URL, acc telegraf.Accumulator) error {
body, err := n.gatherURL(addr, httpLimitReqsPath)
if err != nil {
return err
}
var httpLimitReqs httpLimitReqs
if err := json.Unmarshal(body, &httpLimitReqs); err != nil {
return err
}
tags := getTags(addr)
for limitReqName, limit := range httpLimitReqs {
limitReqsTags := make(map[string]string, len(tags)+1)
for k, v := range tags {
limitReqsTags[k] = v
}
limitReqsTags["limit"] = limitReqName
acc.AddFields(
"nginx_plus_api_http_limit_reqs",
map[string]interface{}{
"passed": limit.Passed,
"delayed": limit.Delayed,
"rejected": limit.Rejected,
"delayed_dry_run": limit.DelayedDryRun,
"rejected_dry_run": limit.RejectedDryRun,
},
limitReqsTags,
)
}
return nil
}
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}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,180 @@
package nginx_plus_api
type processes struct {
Respawned int `json:"respawned"`
}
type connections struct {
Accepted int64 `json:"accepted"`
Dropped int64 `json:"dropped"`
Active int64 `json:"active"`
Idle int64 `json:"idle"`
}
type slabs map[string]struct {
Pages struct {
Used int64 `json:"used"`
Free int64 `json:"free"`
} `json:"pages"`
Slots map[string]struct {
Used int64 `json:"used"`
Free int64 `json:"free"`
Reqs int64 `json:"reqs"`
Fails int64 `json:"fails"`
} `json:"slots"`
}
type ssl struct { // added in version 6
Handshakes int64 `json:"handshakes"`
HandshakesFailed int64 `json:"handshakes_failed"`
SessionReuses int64 `json:"session_reuses"`
}
type resolverZones map[string]struct {
Requests struct {
Name int64 `json:"name"`
Srv int64 `json:"srv"`
Addr int64 `json:"addr"`
} `json:"requests"`
Responses struct {
Noerror int64 `json:"noerror"`
Formerr int64 `json:"formerr"`
Servfail int64 `json:"servfail"`
Nxdomain int64 `json:"nxdomain"`
Notimp int64 `json:"notimp"`
Refused int64 `json:"refused"`
Timedout int64 `json:"timedout"`
Unknown int64 `json:"unknown"`
} `json:"responses"`
}
type httpRequests struct {
Total int64 `json:"total"`
Current int64 `json:"current"`
}
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 httpServerZones map[string]struct {
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"`
}
type httpLocationZones map[string]struct {
Requests int64 `json:"requests"`
Responses responseStats `json:"responses"`
Discarded *int64 `json:"discarded"` // added in version 6
Received int64 `json:"received"`
Sent int64 `json:"sent"`
}
type healthCheckStats struct {
Checks int64 `json:"checks"`
Fails int64 `json:"fails"`
Unhealthy int64 `json:"unhealthy"`
LastPassed *bool `json:"last_passed"`
}
type httpUpstreams 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"`
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"`
}
type streamServerZones 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"`
}
type streamUpstreams 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"`
} `json:"peers"`
Zombies int `json:"zombies"`
}
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 httpCaches 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"`
}
type httpLimitReqs map[string]struct {
Passed int64 `json:"passed"`
Delayed int64 `json:"delayed"`
Rejected int64 `json:"rejected"`
DelayedDryRun int64 `json:"delayed_dry_run"`
RejectedDryRun int64 `json:"rejected_dry_run"`
}

View file

@ -0,0 +1,16 @@
# Read Nginx Plus API advanced status information
[[inputs.nginx_plus_api]]
## An array of Nginx API URIs to gather stats.
urls = ["http://localhost/api"]
# Nginx API version, default: 3
# api_version = 3
# 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