From 9d1bb84020c1614dacd39f039f8ffbdab0d64b62 Mon Sep 17 00:00:00 2001 From: ricoberger Date: Tue, 14 Sep 2021 17:31:09 +0200 Subject: [PATCH] Add metric for the exit code of a script This commit adds a new metric name script_exporter_exit_code which contains the exit code for the executed script. --- README.md | 12 ++++++------ pkg/exporter/exporter.go | 2 ++ pkg/exporter/metrics.go | 8 ++++---- pkg/exporter/scripts.go | 10 +++++++--- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 356f758..6d2c6a7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The script_exporter is a [Prometheus](https://prometheus.io) exporter to execute To run the script_exporter you can use the one of the binaries from the [release](https://github.com/ricoberger/script_exporter/releases) page or the [Docker image](https://hub.docker.com/r/ricoberger/script_exporter). You can also build the script_exporter by yourself by running the following commands: -``` +```sh git clone https://github.com/ricoberger/script_exporter.git cd script_exporter make build @@ -33,18 +33,18 @@ You can also deploy the script_exporter to Kubernetes. An example Deployment fil The script_exporter is configured via a configuration file and command-line flags. -``` +```txt Usage of ./bin/script_exporter: -config.file file - Configuration file in YAML format. (default "config.yaml") + Configuration file in YAML format. (default "config.yaml") -create-token - Create bearer token for authentication. + Create bearer token for authentication. -timeout-offset seconds Offset to subtract from Prometheus-supplied timeout in seconds. (default 0.5) -version - Show version information. + Show version information. -web.listen-address string - Address to listen on for web interface and telemetry. (default ":9469") + Address to listen on for web interface and telemetry. (default ":9469") ``` The configuration file is written in YAML format, defined by the scheme described below. diff --git a/pkg/exporter/exporter.go b/pkg/exporter/exporter.go index 840e511..4111c8b 100644 --- a/pkg/exporter/exporter.go +++ b/pkg/exporter/exporter.go @@ -23,6 +23,8 @@ const ( scriptSuccessType = "# TYPE script_success gauge" scriptDurationSecondsHelp = "# HELP script_duration_seconds Script execution time, in seconds." scriptDurationSecondsType = "# TYPE script_duration_seconds gauge" + scriptExitCodeHelp = "# HELP script_exit_code The exit code of the script." + scriptExitCodeType = "# TYPE script_exit_code gauge" ) type Exporter struct { diff --git a/pkg/exporter/metrics.go b/pkg/exporter/metrics.go index 56f8e66..5a148d6 100644 --- a/pkg/exporter/metrics.go +++ b/pkg/exporter/metrics.go @@ -59,17 +59,17 @@ func (e *Exporter) MetricsHandler(w http.ResponseWriter, r *http.Request) { // configuration file. timeout := getTimeout(r, e.timeoutOffset, e.Config.GetMaxTimeout(scriptName)) - output, err := runScript(timeout, e.Config.GetTimeoutEnforced(scriptName), append(strings.Split(script, " "), paramValues...)) + output, exitCode, err := runScript(timeout, e.Config.GetTimeoutEnforced(scriptName), append(strings.Split(script, " "), paramValues...)) if err != nil { log.Printf("Script failed: %s\n", err.Error()) - fmt.Fprintf(w, "%s\n%s\n%s_success{script=\"%s\"} %d\n%s\n%s\n%s_duration_seconds{script=\"%s\"} %f\n", scriptSuccessHelp, scriptSuccessType, namespace, scriptName, 0, scriptDurationSecondsHelp, scriptDurationSecondsType, namespace, scriptName, time.Since(scriptStartTime).Seconds()) + fmt.Fprintf(w, "%s\n%s\n%s_success{script=\"%s\"} %d\n%s\n%s\n%s_duration_seconds{script=\"%s\"} %f\n%s\n%s\n%s_exit_code{script=\"%s\"} %d\n", scriptSuccessHelp, scriptSuccessType, namespace, scriptName, 0, scriptDurationSecondsHelp, scriptDurationSecondsType, namespace, scriptName, time.Since(scriptStartTime).Seconds(), scriptExitCodeHelp, scriptExitCodeType, namespace, scriptName, exitCode) return } // Get ignore output parameter and only return success and duration seconds if 'true' outputParam := params.Get("output") if outputParam == "ignore" { - fmt.Fprintf(w, "%s\n%s\n%s_success{script=\"%s\"} %d\n%s\n%s\n%s_duration_seconds{script=\"%s\"} %f\n", scriptSuccessHelp, scriptSuccessType, namespace, scriptName, 1, scriptDurationSecondsHelp, scriptDurationSecondsType, namespace, scriptName, time.Since(scriptStartTime).Seconds()) + fmt.Fprintf(w, "%s\n%s\n%s_success{script=\"%s\"} %d\n%s\n%s\n%s_duration_seconds{script=\"%s\"} %f\n%s\n%s\n%s_exit_code{script=\"%s\"} %d\n", scriptSuccessHelp, scriptSuccessType, namespace, scriptName, 1, scriptDurationSecondsHelp, scriptDurationSecondsType, namespace, scriptName, time.Since(scriptStartTime).Seconds(), scriptExitCodeHelp, scriptExitCodeType, namespace, scriptName, exitCode) return } @@ -103,7 +103,7 @@ func (e *Exporter) MetricsHandler(w http.ResponseWriter, r *http.Request) { } } - fmt.Fprintf(w, "%s\n%s\n%s_success{script=\"%s\"} %d\n%s\n%s\n%s_duration_seconds{script=\"%s\"} %f\n%s\n", scriptSuccessHelp, scriptSuccessType, namespace, scriptName, 1, scriptDurationSecondsHelp, scriptDurationSecondsType, namespace, scriptName, time.Since(scriptStartTime).Seconds(), formatedOutput) + fmt.Fprintf(w, "%s\n%s\n%s_success{script=\"%s\"} %d\n%s\n%s\n%s_duration_seconds{script=\"%s\"} %f\n%s\n%s\n%s_exit_code{script=\"%s\"} %d\n%s\n", scriptSuccessHelp, scriptSuccessType, namespace, scriptName, 1, scriptDurationSecondsHelp, scriptDurationSecondsType, namespace, scriptName, time.Since(scriptStartTime).Seconds(), scriptExitCodeHelp, scriptExitCodeType, namespace, scriptName, exitCode, formatedOutput) } // SetupMetrics creates and registers our internal Prometheus metrics, diff --git a/pkg/exporter/scripts.go b/pkg/exporter/scripts.go index aba923b..883ae6d 100644 --- a/pkg/exporter/scripts.go +++ b/pkg/exporter/scripts.go @@ -32,7 +32,7 @@ import ( // be subject to abrupt termination regardless of any 'enforced:' // settings. Right now, abrupt termination requires opting in in // the configuration file. -func runScript(timeout float64, enforced bool, args []string) (string, error) { +func runScript(timeout float64, enforced bool, args []string) (string, int, error) { var output []byte var err error @@ -73,10 +73,14 @@ func runScript(timeout float64, enforced bool, args []string) (string, error) { output, err = cmd.Output() if err != nil { - return "", err + if exitError, ok := err.(*exec.ExitError); ok { + return "", exitError.ExitCode(), err + } + + return "", -1, err } - return string(output), nil + return string(output), 0, nil } // getTimeout gets the Prometheus scrape timeout (in seconds) from the