-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support push config for InitPush (#53)
PushConfig represents a struct package uses for pushing metrics to remote destination. Having a structure helps to extend functionality in the future, without touching the signature of existing functions. For example, `PushConfig` supports custom HTTP headers via `Headers` param. Updates #52 Updates #36 Co-authored-by: hagen1778 <[email protected]>
- Loading branch information
1 parent
2ec1497
commit 42c28a8
Showing
5 changed files
with
205 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package metrics | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
// PushConfig is config for pushing registered metrics to the given URL with the given Interval. | ||
// | ||
// URL and Interval are required fields | ||
type PushConfig struct { | ||
// URL defines URL where metrics would be pushed. | ||
URL string | ||
// Interval determines the frequency of pushing metrics. | ||
Interval time.Duration | ||
|
||
// Headers contain optional http request Headers | ||
Headers http.Header | ||
// ExtraLabels may contain comma-separated list of `label="value"` labels, which will be added | ||
// to all the metrics before pushing them to URL. | ||
ExtraLabels string | ||
// WriteMetricsFn is a callback to write metrics to w in Prometheus text exposition format without timestamps and trailing comments. | ||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format | ||
WriteMetricsFn func(w io.Writer) | ||
|
||
pushURL *url.URL | ||
} | ||
|
||
// Validate validates correctness of PushConfig fields | ||
func (pc *PushConfig) Validate() error { | ||
if pc.Interval <= 0 { | ||
return fmt.Errorf("invalid Interval=%s: must be positive", pc.Interval) | ||
} | ||
if err := validateTags(pc.ExtraLabels); err != nil { | ||
return fmt.Errorf("invalid ExtraLabels=%q: %w", pc.ExtraLabels, err) | ||
} | ||
pu, err := url.Parse(pc.URL) | ||
if err != nil { | ||
return fmt.Errorf("cannot parse URL=%q: %w", pc.URL, err) | ||
} | ||
if pu.Scheme != "http" && pu.Scheme != "https" { | ||
return fmt.Errorf("unsupported scheme in URL=%q; expecting 'http' or 'https'", pc.URL) | ||
} | ||
if pu.Host == "" { | ||
return fmt.Errorf("missing host in URL=%q", pc.URL) | ||
} | ||
pc.pushURL = pu | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package metrics | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestPushConfigValidateError(t *testing.T) { | ||
f := func(config *PushConfig) { | ||
t.Helper() | ||
if err := config.Validate(); err == nil { | ||
t.Fatalf("expecting non-nil error when validating %v", config) | ||
} | ||
} | ||
|
||
f(&PushConfig{}) | ||
f(&PushConfig{URL: "", Interval: time.Second}) | ||
f(&PushConfig{URL: "https://localhost:8080", Interval: -1 * time.Second}) | ||
f(&PushConfig{URL: "htt://localhost:8080", Interval: time.Second}) | ||
f(&PushConfig{URL: "http://localhost:8080", Interval: time.Second, ExtraLabels: "a{} "}) | ||
} | ||
|
||
func TestPushConfigValidateSuccess(t *testing.T) { | ||
f := func(config *PushConfig) { | ||
t.Helper() | ||
if err := config.Validate(); err != nil { | ||
t.Fatalf("expecting nil error when validating %v; err: %s", config, err) | ||
} | ||
} | ||
|
||
f(&PushConfig{URL: "http://localhost:8080", Interval: time.Second}) | ||
f(&PushConfig{URL: "http://localhost:8080", Interval: time.Second, ExtraLabels: `foo="bar"`}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package metrics_test | ||
|
||
import ( | ||
"compress/gzip" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/http/httptest" | ||
"time" | ||
|
||
"github.com/VictoriaMetrics/metrics" | ||
) | ||
|
||
func ExampleInitPushWithConfig() { | ||
syncCh := make(chan string) | ||
srv := newServer(syncCh) | ||
defer srv.Close() | ||
|
||
cfg := metrics.PushConfig{ | ||
URL: srv.URL, | ||
Interval: time.Millisecond * 100, | ||
WriteMetricsFn: func(w io.Writer) { | ||
fmt.Fprint(w, "foo{label=\"bar\"} 1\n") | ||
fmt.Fprint(w, "foo{label=\"baz\"} 2\n") | ||
}, | ||
} | ||
if err := metrics.InitPushWithConfig(cfg); err != nil { | ||
panic(fmt.Sprintf("BUG: unexpected error: %s", err)) | ||
} | ||
fmt.Println(<-syncCh) | ||
|
||
// Output: | ||
// foo{label="bar"} 1 | ||
// foo{label="baz"} 2 | ||
} | ||
|
||
func ExampleInitPushExt() { | ||
syncCh := make(chan string) | ||
srv := newServer(syncCh) | ||
defer srv.Close() | ||
|
||
writeFn := func(w io.Writer) { | ||
fmt.Fprint(w, "foo{label=\"bar\"} 11\n") | ||
fmt.Fprint(w, "foo{label=\"baz\"} 22\n") | ||
} | ||
|
||
err := metrics.InitPushExt(srv.URL, time.Millisecond*100, "", writeFn) | ||
if err != nil { | ||
panic(fmt.Sprintf("BUG: unexpected error: %s", err)) | ||
} | ||
fmt.Println(<-syncCh) | ||
|
||
// Output: | ||
// foo{label="bar"} 11 | ||
// foo{label="baz"} 22 | ||
} | ||
|
||
func newServer(ch chan string) *httptest.Server { | ||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
gr, err := gzip.NewReader(r.Body) | ||
if err != nil { | ||
panic(fmt.Sprintf("BUG: unexpected error: %s", err)) | ||
} | ||
defer gr.Close() | ||
|
||
b, err := io.ReadAll(gr) | ||
if err != nil { | ||
panic(fmt.Sprintf("BUG: unexpected error: %s", err)) | ||
} | ||
ch <- string(b) | ||
})) | ||
return srv | ||
} |