[promtool] Support writing output as json (#4848)

* Support writing output as json

Oftentimes I'll want to execute something based on
the output from promtool, and supporting json
makes it easy to pull out values with a supporting
tool such as jq.

Signed-off-by: stuart nelson <stuartnelson3@gmail.com>
This commit is contained in:
stuart nelson 2018-11-14 18:40:07 +01:00 committed by GitHub
parent b82f930238
commit 6a69471bc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 19 deletions

View file

@ -15,6 +15,7 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"math" "math"
"net/url" "net/url"
@ -29,6 +30,7 @@ import (
"github.com/prometheus/client_golang/api" "github.com/prometheus/client_golang/api"
"github.com/prometheus/client_golang/api/prometheus/v1" "github.com/prometheus/client_golang/api/prometheus/v1"
config_util "github.com/prometheus/common/config" config_util "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/version" "github.com/prometheus/common/version"
"github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/pkg/rulefmt" "github.com/prometheus/prometheus/pkg/rulefmt"
@ -57,6 +59,7 @@ func main() {
checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage) checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage)
queryCmd := app.Command("query", "Run query against a Prometheus server.") queryCmd := app.Command("query", "Run query against a Prometheus server.")
queryCmdFmt := queryCmd.Flag("format", "Output format of the query.").Short('o').Default("promql").Enum("promql", "json")
queryInstantCmd := queryCmd.Command("instant", "Run instant query.") queryInstantCmd := queryCmd.Command("instant", "Run instant query.")
queryServer := queryInstantCmd.Arg("server", "Prometheus server to query.").Required().String() queryServer := queryInstantCmd.Arg("server", "Prometheus server to query.").Required().String()
queryExpr := queryInstantCmd.Arg("expr", "PromQL query expression.").Required().String() queryExpr := queryInstantCmd.Arg("expr", "PromQL query expression.").Required().String()
@ -93,7 +96,17 @@ func main() {
"The unit test file.", "The unit test file.",
).Required().ExistingFiles() ).Required().ExistingFiles()
switch kingpin.MustParse(app.Parse(os.Args[1:])) { parsedCmd := kingpin.MustParse(app.Parse(os.Args[1:]))
var p printer
switch *queryCmdFmt {
case "json":
p = &jsonPrinter{}
case "promql":
p = &promqlPrinter{}
}
switch parsedCmd {
case checkConfigCmd.FullCommand(): case checkConfigCmd.FullCommand():
os.Exit(CheckConfig(*configFiles...)) os.Exit(CheckConfig(*configFiles...))
@ -104,13 +117,13 @@ func main() {
os.Exit(CheckMetrics()) os.Exit(CheckMetrics())
case queryInstantCmd.FullCommand(): case queryInstantCmd.FullCommand():
os.Exit(QueryInstant(*queryServer, *queryExpr)) os.Exit(QueryInstant(*queryServer, *queryExpr, p))
case queryRangeCmd.FullCommand(): case queryRangeCmd.FullCommand():
os.Exit(QueryRange(*queryRangeServer, *queryRangeExpr, *queryRangeBegin, *queryRangeEnd, *queryRangeStep)) os.Exit(QueryRange(*queryRangeServer, *queryRangeExpr, *queryRangeBegin, *queryRangeEnd, *queryRangeStep, p))
case querySeriesCmd.FullCommand(): case querySeriesCmd.FullCommand():
os.Exit(QuerySeries(*querySeriesServer, *querySeriesMatch, *querySeriesBegin, *querySeriesEnd)) os.Exit(QuerySeries(*querySeriesServer, *querySeriesMatch, *querySeriesBegin, *querySeriesEnd, p))
case debugPprofCmd.FullCommand(): case debugPprofCmd.FullCommand():
os.Exit(debugPprof(*debugPprofServer)) os.Exit(debugPprof(*debugPprofServer))
@ -122,7 +135,7 @@ func main() {
os.Exit(debugAll(*debugAllServer)) os.Exit(debugAll(*debugAllServer))
case queryLabelsCmd.FullCommand(): case queryLabelsCmd.FullCommand():
os.Exit(QueryLabels(*queryLabelsServer, *queryLabelsName)) os.Exit(QueryLabels(*queryLabelsServer, *queryLabelsName, p))
case testRulesCmd.FullCommand(): case testRulesCmd.FullCommand():
os.Exit(RulesUnitTest(*testRulesFiles...)) os.Exit(RulesUnitTest(*testRulesFiles...))
@ -316,7 +329,7 @@ func CheckMetrics() int {
} }
// QueryInstant performs an instant query against a Prometheus server. // QueryInstant performs an instant query against a Prometheus server.
func QueryInstant(url string, query string) int { func QueryInstant(url, query string, p printer) int {
config := api.Config{ config := api.Config{
Address: url, Address: url,
} }
@ -339,13 +352,13 @@ func QueryInstant(url string, query string) int {
return 1 return 1
} }
fmt.Println(val.String()) p.printValue(val)
return 0 return 0
} }
// QueryRange performs a range query against a Prometheus server. // QueryRange performs a range query against a Prometheus server.
func QueryRange(url, query, start, end string, step time.Duration) int { func QueryRange(url, query, start, end string, step time.Duration, p printer) int {
config := api.Config{ config := api.Config{
Address: url, Address: url,
} }
@ -400,12 +413,12 @@ func QueryRange(url, query, start, end string, step time.Duration) int {
return 1 return 1
} }
fmt.Println(val.String()) p.printValue(val)
return 0 return 0
} }
// QuerySeries queries for a series against a Prometheus server. // QuerySeries queries for a series against a Prometheus server.
func QuerySeries(url *url.URL, matchers []string, start string, end string) int { func QuerySeries(url *url.URL, matchers []string, start, end string, p printer) int {
config := api.Config{ config := api.Config{
Address: url.String(), Address: url.String(),
} }
@ -454,14 +467,12 @@ func QuerySeries(url *url.URL, matchers []string, start string, end string) int
return 1 return 1
} }
for _, v := range val { p.printSeries(val)
fmt.Println(v)
}
return 0 return 0
} }
// QueryLabels queries for label values against a Prometheus server. // QueryLabels queries for label values against a Prometheus server.
func QueryLabels(url *url.URL, name string) int { func QueryLabels(url *url.URL, name string, p printer) int {
config := api.Config{ config := api.Config{
Address: url.String(), Address: url.String(),
} }
@ -484,9 +495,7 @@ func QueryLabels(url *url.URL, name string) int {
return 1 return 1
} }
for _, v := range val { p.printLabelValues(val)
fmt.Println(v)
}
return 0 return 0
} }
@ -557,3 +566,37 @@ func debugAll(url string) int {
} }
return w.Write() return w.Write()
} }
type printer interface {
printValue(v model.Value)
printSeries(v []model.LabelSet)
printLabelValues(v model.LabelValues)
}
type promqlPrinter struct{}
func (p *promqlPrinter) printValue(v model.Value) {
fmt.Println(v)
}
func (p *promqlPrinter) printSeries(val []model.LabelSet) {
for _, v := range val {
fmt.Println(v)
}
}
func (j *promqlPrinter) printLabelValues(val model.LabelValues) {
for _, v := range val {
fmt.Println(v)
}
}
type jsonPrinter struct{}
func (j *jsonPrinter) printValue(v model.Value) {
json.NewEncoder(os.Stdout).Encode(v)
}
func (j *jsonPrinter) printSeries(v []model.LabelSet) {
json.NewEncoder(os.Stdout).Encode(v)
}
func (j *jsonPrinter) printLabelValues(v model.LabelValues) {
json.NewEncoder(os.Stdout).Encode(v)
}

View file

@ -26,7 +26,8 @@ func TestQueryRange(t *testing.T) {
s, getURL := mockServer(200, `{"status": "success", "data": {"resultType": "matrix", "result": []}}`) s, getURL := mockServer(200, `{"status": "success", "data": {"resultType": "matrix", "result": []}}`)
defer s.Close() defer s.Close()
exitCode := QueryRange(s.URL, "up", "0", "300", 0) p := &promqlPrinter{}
exitCode := QueryRange(s.URL, "up", "0", "300", 0, p)
expectedPath := "/api/v1/query_range" expectedPath := "/api/v1/query_range"
if getURL().Path != expectedPath { if getURL().Path != expectedPath {
t.Errorf("unexpected URL path %s (wanted %s)", getURL().Path, expectedPath) t.Errorf("unexpected URL path %s (wanted %s)", getURL().Path, expectedPath)
@ -43,7 +44,7 @@ func TestQueryRange(t *testing.T) {
t.Error() t.Error()
} }
exitCode = QueryRange(s.URL, "up", "0", "300", 10*time.Millisecond) exitCode = QueryRange(s.URL, "up", "0", "300", 10*time.Millisecond, p)
if getURL().Path != expectedPath { if getURL().Path != expectedPath {
t.Errorf("unexpected URL path %s (wanted %s)", getURL().Path, expectedPath) t.Errorf("unexpected URL path %s (wanted %s)", getURL().Path, expectedPath)
} }