Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the complete code #49

36 changes: 21 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,24 @@ $ cat example/data.json
"count": 3,
"some_boolean": false,
"state": "ACTIVE"
},
]
}
],
"location": "mars"
}

$ cat example/config.yml
$ cat examples/config.yml
---
metrics:
- name: example_global_value
path: $.counter
help: Example of a top-level global value scrape in the json
labels:
environment: beta # static label
location: $.location # dynamic label

- name: example_value
type: object
help: Example of sub-level value scrapes from a json
path: $.values[*]?(@.state == "ACTIVE")
labels:
environment: beta # static label
Expand All @@ -55,35 +61,35 @@ $ cat example/config.yml
count: $.count # dynamic value
boolean: $.some_boolean

headers:
X-Dummy: my-test-header

$ python -m SimpleHTTPServer 8000 &
Serving HTTP on 0.0.0.0 port 8000 ...

$ ./json_exporter http://localhost:8000/example/data.json example/config.yml &
INFO[2016-02-08T22:44:38+09:00] metric registered;name:<example_global_value>
INFO[2016-02-08T22:44:38+09:00] metric registered;name:<example_value_boolean>
INFO[2016-02-08T22:44:38+09:00] metric registered;name:<example_value_active>
INFO[2016-02-08T22:44:38+09:00] metric registered;name:<example_value_count>
127.0.0.1 - - [08/Feb/2016 22:44:38] "GET /example/data.json HTTP/1.1" 200 -
$ ./json_exporter --config.file examples/config.yml &


$ curl http://localhost:7979/metrics | grep ^example
example_global_value{environment="beta"} 1234
$ curl "http://localhost:7979/probe?target=http://localhost:8000/examples/data.json" | grep ^example
example_global_value{environment="beta",location="mars"} 1234
example_value_active{environment="beta",id="id-A"} 1
example_value_active{environment="beta",id="id-C"} 1
example_value_boolean{environment="beta",id="id-A"} 1
example_value_boolean{environment="beta",id="id-C"} 0
example_value_count{environment="beta",id="id-A"} 1
example_value_count{environment="beta",id="id-C"} 3

# To test through prometheus:
$ docker run --rm -it -p 9090:9090 -v $PWD/examples/prometheus.yml:/etc/prometheus/prometheus.yml --network host prom/prometheus
```
Then head over to http://localhost:9090/graph?g0.range_input=1h&g0.expr=example_value_active&g0.tab=1 or http://localhost:9090/targets to check the scraped metrics or the targets.

# Docker

```console
docker run \
-v config.yml:/config.yml
-v $PWD/examples/config.yml:/config.yml
quay.io/prometheuscommunity/json-exporter \
http://example.com/target.json \
/config.yml
--config.file /config.yml
```

# See Also
Expand Down
112 changes: 112 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2020 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"context"
"encoding/json"
"net/http"
"os"

"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/prometheus-community/json_exporter/config"
"github.com/prometheus-community/json_exporter/exporter"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/promlog"
"github.com/prometheus/common/promlog/flag"
"github.com/prometheus/common/version"
"gopkg.in/alecthomas/kingpin.v2"
)

var (
configFile = kingpin.Flag("config.file", "JSON exporter configuration file.").Default("config.yml").ExistingFile()
listenAddress = kingpin.Flag("web.listen-address", "The address to listen on for HTTP requests.").Default(":7979").String()
configCheck = kingpin.Flag("config.check", "If true validate the config file and then exit.").Default("false").Bool()
)

func Run() {

promlogConfig := &promlog.Config{}

flag.AddFlags(kingpin.CommandLine, promlogConfig)
kingpin.Version(version.Print("json_exporter"))
kingpin.HelpFlag.Short('h')
kingpin.Parse()
logger := promlog.New(promlogConfig)

level.Info(logger).Log("msg", "Starting json_exporter", "version", version.Info()) //nolint:errcheck
level.Info(logger).Log("msg", "Build context", "build", version.BuildContext()) //nolint:errcheck

level.Info(logger).Log("msg", "Loading config file", "file", *configFile) //nolint:errcheck
config, err := config.LoadConfig(*configFile)
if err != nil {
level.Error(logger).Log("msg", "Error loading config", "err", err) //nolint:errcheck
os.Exit(1)
}
configJson, err := json.Marshal(config)
if err != nil {
level.Error(logger).Log("msg", "Failed to marshal config to JSON", "err", err) //nolint:errcheck
}
level.Info(logger).Log("msg", "Loaded config file", "config", string(configJson)) //nolint:errcheck

if *configCheck {
os.Exit(0)
}

http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/probe", func(w http.ResponseWriter, req *http.Request) {
probeHandler(w, req, logger, config)
})
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
level.Error(logger).Log("msg", "Failed to start the server", "err", err) //nolint:errcheck
}
}

func probeHandler(w http.ResponseWriter, r *http.Request, logger log.Logger, config config.Config) {

ctx, cancel := context.WithCancel(r.Context())
defer cancel()
r = r.WithContext(ctx)

registry := prometheus.NewPedanticRegistry()

metrics, err := exporter.CreateMetricsList(config)
if err != nil {
level.Error(logger).Log("msg", "Failed to create metrics list from config", "err", err) //nolint:errcheck
}

jsonMetricCollector := exporter.JsonMetricCollector{JsonMetrics: metrics}
jsonMetricCollector.Logger = logger

target := r.URL.Query().Get("target")
if target == "" {
http.Error(w, "Target parameter is missing", http.StatusBadRequest)
return
}

data, err := exporter.FetchJson(ctx, logger, target, config)
if err != nil {
http.Error(w, "Failed to fetch JSON response. TARGET: "+target+", ERROR: "+err.Error(), http.StatusServiceUnavailable)
return
}

jsonMetricCollector.Data = data

registry.MustRegister(jsonMetricCollector)
h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
h.ServeHTTP(w, r)

}
Loading