You've already forked nginx_exporter
mirror of
https://github.com/nginxinc/nginx-prometheus-exporter.git
synced 2025-07-30 10:03:04 +03:00
Don't try to connect to nginx on client creation (#504)
The client now is created without making a call to nginx. When Collect() is called we try to get the metrics from nginx and if it's not available it will set 'up' to 0 and not fail anymore.
This commit is contained in:
@ -37,14 +37,13 @@ type StubConnections struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewNginxClient creates an NginxClient.
|
// NewNginxClient creates an NginxClient.
|
||||||
func NewNginxClient(httpClient *http.Client, apiEndpoint string) (*NginxClient, error) {
|
func NewNginxClient(httpClient *http.Client, apiEndpoint string) *NginxClient {
|
||||||
client := &NginxClient{
|
client := &NginxClient{
|
||||||
apiEndpoint: apiEndpoint,
|
apiEndpoint: apiEndpoint,
|
||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.GetStubStats()
|
return client
|
||||||
return client, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStubStats fetches the stub_status metrics.
|
// GetStubStats fetches the stub_status metrics.
|
||||||
|
40
exporter.go
40
exporter.go
@ -19,7 +19,6 @@ import (
|
|||||||
"github.com/nginxinc/nginx-prometheus-exporter/collector"
|
"github.com/nginxinc/nginx-prometheus-exporter/collector"
|
||||||
|
|
||||||
"github.com/alecthomas/kingpin/v2"
|
"github.com/alecthomas/kingpin/v2"
|
||||||
"github.com/go-kit/log"
|
|
||||||
"github.com/go-kit/log/level"
|
"github.com/go-kit/log/level"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
@ -61,23 +60,6 @@ func createPositiveDurationFlag(s kingpin.Settings) (target *time.Duration) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func createClientWithRetries(getClient func() (interface{}, error), retries uint, retryInterval time.Duration, logger log.Logger) (interface{}, error) {
|
|
||||||
var err error
|
|
||||||
var nginxClient interface{}
|
|
||||||
|
|
||||||
for i := 0; i <= int(retries); i++ {
|
|
||||||
nginxClient, err = getClient()
|
|
||||||
if err == nil {
|
|
||||||
return nginxClient, nil
|
|
||||||
}
|
|
||||||
if i < int(retries) {
|
|
||||||
level.Error(logger).Log("msg", fmt.Sprintf("Could not create Nginx Client. Retrying in %v...", retryInterval))
|
|
||||||
time.Sleep(retryInterval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseUnixSocketAddress(address string) (string, string, error) {
|
func parseUnixSocketAddress(address string) (string, string, error) {
|
||||||
addressParts := strings.Split(address, ":")
|
addressParts := strings.Split(address, ":")
|
||||||
addressPartsLength := len(addressParts)
|
addressPartsLength := len(addressParts)
|
||||||
@ -106,11 +88,9 @@ var (
|
|||||||
sslCaCert = kingpin.Flag("nginx.ssl-ca-cert", "Path to the PEM encoded CA certificate file used to validate the servers SSL certificate.").Default("").Envar("SSL_CA_CERT").String()
|
sslCaCert = kingpin.Flag("nginx.ssl-ca-cert", "Path to the PEM encoded CA certificate file used to validate the servers SSL certificate.").Default("").Envar("SSL_CA_CERT").String()
|
||||||
sslClientCert = kingpin.Flag("nginx.ssl-client-cert", "Path to the PEM encoded client certificate file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_CERT").String()
|
sslClientCert = kingpin.Flag("nginx.ssl-client-cert", "Path to the PEM encoded client certificate file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_CERT").String()
|
||||||
sslClientKey = kingpin.Flag("nginx.ssl-client-key", "Path to the PEM encoded client certificate key file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_KEY").String()
|
sslClientKey = kingpin.Flag("nginx.ssl-client-key", "Path to the PEM encoded client certificate key file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_KEY").String()
|
||||||
nginxRetries = kingpin.Flag("nginx.retries", "A number of retries the exporter will make on start to connect to the NGINX stub_status page/NGINX Plus API before exiting with an error.").Default("0").Envar("NGINX_RETRIES").Uint()
|
|
||||||
|
|
||||||
// Custom command-line flags
|
// Custom command-line flags
|
||||||
timeout = createPositiveDurationFlag(kingpin.Flag("nginx.timeout", "A timeout for scraping metrics from NGINX or NGINX Plus.").Default("5s").Envar("TIMEOUT"))
|
timeout = createPositiveDurationFlag(kingpin.Flag("nginx.timeout", "A timeout for scraping metrics from NGINX or NGINX Plus.").Default("5s").Envar("TIMEOUT"))
|
||||||
nginxRetryInterval = createPositiveDurationFlag(kingpin.Flag("nginx.retry-interval", "An interval between retries to connect to the NGINX stub_status page/NGINX Plus API on start.").Default("5s").Envar("NGINX_RETRY_INTERVAL"))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const exporterName = "nginx_exporter"
|
const exporterName = "nginx_exporter"
|
||||||
@ -123,7 +103,7 @@ func main() {
|
|||||||
|
|
||||||
// convert deprecated flags to new format
|
// convert deprecated flags to new format
|
||||||
for i, arg := range os.Args {
|
for i, arg := range os.Args {
|
||||||
if strings.HasPrefix(arg, "-") && len(arg) > 1 {
|
if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") && len(arg) > 2 {
|
||||||
newArg := fmt.Sprintf("-%s", arg)
|
newArg := fmt.Sprintf("-%s", arg)
|
||||||
level.Warn(logger).Log("msg", "the flag format is deprecated and will be removed in a future release, please use the new format", "old", arg, "new", newArg)
|
level.Warn(logger).Log("msg", "the flag format is deprecated and will be removed in a future release, please use the new format", "old", arg, "new", newArg)
|
||||||
os.Args[i] = newArg
|
os.Args[i] = newArg
|
||||||
@ -195,24 +175,16 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *nginxPlus {
|
if *nginxPlus {
|
||||||
plusClient, err := createClientWithRetries(func() (interface{}, error) {
|
plusClient, err := plusclient.NewNginxClient(*scrapeURI, plusclient.WithHTTPClient(httpClient))
|
||||||
return plusclient.NewNginxClient(*scrapeURI, plusclient.WithHTTPClient(httpClient))
|
|
||||||
}, *nginxRetries, *nginxRetryInterval, logger)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
level.Error(logger).Log("msg", "Could not create Nginx Plus Client", "error", err.Error())
|
level.Error(logger).Log("msg", "Could not create Nginx Plus Client", "error", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
variableLabelNames := collector.NewVariableLabelNames(nil, nil, nil, nil, nil, nil)
|
variableLabelNames := collector.NewVariableLabelNames(nil, nil, nil, nil, nil, nil)
|
||||||
prometheus.MustRegister(collector.NewNginxPlusCollector(plusClient.(*plusclient.NginxClient), "nginxplus", variableLabelNames, constLabels, logger))
|
prometheus.MustRegister(collector.NewNginxPlusCollector(plusClient, "nginxplus", variableLabelNames, constLabels, logger))
|
||||||
} else {
|
} else {
|
||||||
ossClient, err := createClientWithRetries(func() (interface{}, error) {
|
ossClient := client.NewNginxClient(httpClient, *scrapeURI)
|
||||||
return client.NewNginxClient(httpClient, *scrapeURI)
|
prometheus.MustRegister(collector.NewNginxCollector(ossClient, "nginx", constLabels, logger))
|
||||||
}, *nginxRetries, *nginxRetryInterval, logger)
|
|
||||||
if err != nil {
|
|
||||||
level.Error(logger).Log("msg", "Could not create Nginx Client", "error", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
prometheus.MustRegister(collector.NewNginxCollector(ossClient.(*client.NginxClient), "nginx", constLabels, logger))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Handle(*metricsPath, promhttp.Handler())
|
http.Handle(*metricsPath, promhttp.Handler())
|
||||||
|
@ -1,91 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-kit/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateClientWithRetries(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
client interface{}
|
|
||||||
err error
|
|
||||||
retries uint
|
|
||||||
retryInterval time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
expectedRetries int
|
|
||||||
want interface{}
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "getClient returns a valid client",
|
|
||||||
args: args{
|
|
||||||
client: "client",
|
|
||||||
err: nil,
|
|
||||||
},
|
|
||||||
expectedRetries: 0,
|
|
||||||
want: "client",
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getClient returns an error after no retries",
|
|
||||||
args: args{
|
|
||||||
client: nil,
|
|
||||||
err: errors.New("error"),
|
|
||||||
},
|
|
||||||
expectedRetries: 0,
|
|
||||||
want: nil,
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "getClient returns an error after retries",
|
|
||||||
args: args{
|
|
||||||
client: nil,
|
|
||||||
err: errors.New("error"),
|
|
||||||
retries: 3,
|
|
||||||
retryInterval: time.Millisecond * 1,
|
|
||||||
},
|
|
||||||
expectedRetries: 3,
|
|
||||||
want: nil,
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
invocations := 0
|
|
||||||
getClient := func() (interface{}, error) {
|
|
||||||
invocations++
|
|
||||||
return tt.args.client, tt.args.err
|
|
||||||
}
|
|
||||||
|
|
||||||
got, err := createClientWithRetries(getClient, tt.args.retries, tt.args.retryInterval, log.NewNopLogger())
|
|
||||||
|
|
||||||
actualRetries := invocations - 1
|
|
||||||
|
|
||||||
if actualRetries != tt.expectedRetries {
|
|
||||||
t.Errorf("createClientWithRetries() got %v retries, expected %v", actualRetries, tt.expectedRetries)
|
|
||||||
return
|
|
||||||
} else if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("createClientWithRetries() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
} else if err != nil && tt.wantErr {
|
|
||||||
return
|
|
||||||
} else if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("createClientWithRetries() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePositiveDuration(t *testing.T) {
|
func TestParsePositiveDuration(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user