Add Prometheus provider (#3555)

Allows to load a scalar metrics from prometheus

Example config:
```
meters:
  - name: pv
    type: custom
    power:
      source: prometheus
      uri: http://prometheus:9090
      query: "scalar(p_realtime)"
```
This commit is contained in:
Luca Berneking 2022-06-05 11:18:21 +02:00 • committed by GitHub
parent d93cf01108
commit c5f6ffc51a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 111 additions and 1 deletions

5
go.mod
View file

@ -63,6 +63,7 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/philippseith/signalr v0.5.3-0.20211205201131-d57b5a34379a
github.com/prometheus/client_golang v1.12.2
github.com/prometheus/common v0.34.0
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
github.com/samber/lo v1.21.0
github.com/shurcooL/graphql v0.0.0-20220520033453-bdb1221e171e
@ -118,6 +119,7 @@ require (
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
github.com/itchyny/timefmt-go v0.1.3 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.5 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
@ -129,6 +131,8 @@ require (
github.com/miekg/dns v1.1.49 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.19.0 // indirect
github.com/pascaldekloe/name v1.0.1 // indirect
@ -137,7 +141,6 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.34.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect

2
go.sum
View file

@ -532,6 +532,7 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
github.com/jpfielding/go-http-digest v0.0.0-20211006141426-fbc93758452e h1:iLmt7czLE9ophoU4xZcckVNCXp+auTvVSQ9MGTWbgBY=
github.com/jpfielding/go-http-digest v0.0.0-20211006141426-fbc93758452e/go.mod h1:oLt6zF2euTyCg2Cxz01B3VC5SJcPpKpYnOAqvO+480s=
github.com/jpfielding/gowirelog v0.0.0-20200123170752-df8f8dccb721/go.mod h1:R8b6Hefiy6MSxbJ8dbF9Gkaa/LwFh9meFV0i9hkR5i8=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -679,6 +680,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a h1:fnzS9RRQW8B5AgNCxkN0vJ/AoX+Xfqk3sAYon3iVrzA=
github.com/muka/go-bluetooth v0.0.0-20220219050759-674a63b8741a/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxschmitt/golang-combinations v1.1.0 h1:WlIZCnDm+Xlb2pRPf+R/qPKlGOU1w8lpN69/uy5z+Zg=
github.com/mxschmitt/golang-combinations v1.1.0/go.mod h1:RbMhWvfCelHR6WROvT2bVfxJvZHoEvBj71SKe+H0MYU=

105
provider/prometheus.go Normal file
View file

@ -0,0 +1,105 @@
package provider
import (
"context"
"fmt"
"github.com/evcc-io/evcc/util/request"
"math"
"time"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/common/model"
"github.com/evcc-io/evcc/util"
"github.com/prometheus/client_golang/api"
)
// Prometheus provider
type Prometheus struct {
log *util.Logger
clientV1Api v1.API
query string
timeout time.Duration
}
func init() {
registry.Add("prometheus", NewPrometheusFromConfig)
}
func NewPrometheusFromConfig(other map[string]interface{}) (IntProvider, error) {
cc := struct {
api.Config `mapstructure:",squash"`
Query string
Timeout time.Duration
}{
Timeout: request.Timeout,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
log := util.NewLogger("prometheus")
client, err := api.NewClient(cc.Config)
if err != nil {
return nil, err
}
p := NewPrometheus(log, client, cc.Query, cc.Timeout)
return p, err
}
func NewPrometheus(log *util.Logger, client api.Client, query string, timeout time.Duration) *Prometheus {
p := &Prometheus{
log: log,
clientV1Api: v1.NewAPI(client),
query: query,
timeout: timeout,
}
return p
}
func (p *Prometheus) Query() (model.Value, error) {
ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
defer cancel()
res, warn, err := p.clientV1Api.Query(ctx, p.query, time.Now())
if err != nil {
return nil, err
}
p.log.TRACE.Printf("query %q: %+v", p.query, res)
if len(warn) > 0 {
p.log.WARN.Printf("query %q returned warnings: %v", p.query, warn)
}
return res, nil
}
var _ FloatProvider = (*Prometheus)(nil)
// FloatGetter expects scalar value from query response as float
func (p *Prometheus) FloatGetter() func() (float64, error) {
return func() (float64, error) {
res, err := p.Query()
if err != nil {
return 0, err
}
if res.Type() != model.ValScalar {
return 0, fmt.Errorf("query returned value of type %q, expected %q, consider wrapping query in scalar()", res.Type().String(), model.ValScalar.String())
}
scalarVal := res.(*model.Scalar)
return float64(scalarVal.Value), nil
}
}
var _ IntProvider = (*Prometheus)(nil)
// IntGetter expects scalar value from query response as int
func (p *Prometheus) IntGetter() func() (int64, error) {
floatGetter := p.FloatGetter()
return func() (int64, error) {
float, err := floatGetter()
return int64(math.Round(float)), err
}
}