feat(parsers.openmetrics): Add parser for OpenMetrics format (#15298)
This commit is contained in:
parent
dcb6177263
commit
48963846f2
|
|
@ -18,6 +18,7 @@ Protocol, JSON format, or Apache Avro format.
|
||||||
- [JSON v2](/plugins/parsers/json_v2)
|
- [JSON v2](/plugins/parsers/json_v2)
|
||||||
- [Logfmt](/plugins/parsers/logfmt)
|
- [Logfmt](/plugins/parsers/logfmt)
|
||||||
- [Nagios](/plugins/parsers/nagios)
|
- [Nagios](/plugins/parsers/nagios)
|
||||||
|
- [OpenMetrics](/plugins/parsers/openmetrics)
|
||||||
- [OpenTSDB](/plugins/parsers/opentsdb)
|
- [OpenTSDB](/plugins/parsers/opentsdb)
|
||||||
- [Parquet](/plugins/parsers/parquet)
|
- [Parquet](/plugins/parsers/parquet)
|
||||||
- [Prometheus](/plugins/parsers/prometheus)
|
- [Prometheus](/plugins/parsers/prometheus)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,14 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||||
## If set to true, the gather time will be used.
|
## If set to true, the gather time will be used.
|
||||||
# ignore_timestamp = false
|
# ignore_timestamp = false
|
||||||
|
|
||||||
|
## Override content-type of the returned message
|
||||||
|
## Available options are for prometheus:
|
||||||
|
## text, protobuf-delimiter, protobuf-compact, protobuf-text,
|
||||||
|
## and for openmetrics:
|
||||||
|
## openmetrics-text, openmetrics-protobuf
|
||||||
|
## By default the content-type of the response is used.
|
||||||
|
# content_type_override = ""
|
||||||
|
|
||||||
## An array of Kubernetes services to scrape metrics from.
|
## An array of Kubernetes services to scrape metrics from.
|
||||||
# kubernetes_services = ["http://my-service-dns.my-namespace:9100/metrics"]
|
# kubernetes_services = ["http://my-service-dns.my-namespace:9100/metrics"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/expfmt"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
@ -26,6 +27,7 @@ import (
|
||||||
"github.com/influxdata/telegraf/models"
|
"github.com/influxdata/telegraf/models"
|
||||||
httpconfig "github.com/influxdata/telegraf/plugins/common/http"
|
httpconfig "github.com/influxdata/telegraf/plugins/common/http"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
"github.com/influxdata/telegraf/plugins/parsers/openmetrics"
|
||||||
parser "github.com/influxdata/telegraf/plugins/parsers/prometheus"
|
parser "github.com/influxdata/telegraf/plugins/parsers/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -46,62 +48,57 @@ const (
|
||||||
type PodID string
|
type PodID string
|
||||||
|
|
||||||
type Prometheus struct {
|
type Prometheus struct {
|
||||||
// An array of urls to scrape metrics from.
|
|
||||||
URLs []string `toml:"urls"`
|
URLs []string `toml:"urls"`
|
||||||
|
|
||||||
// An array of Kubernetes services to scrape metrics from.
|
|
||||||
KubernetesServices []string
|
|
||||||
|
|
||||||
// Location of kubernetes config file
|
|
||||||
KubeConfig string
|
|
||||||
|
|
||||||
// Label Selector/s for Kubernetes
|
|
||||||
KubernetesLabelSelector string `toml:"kubernetes_label_selector"`
|
|
||||||
|
|
||||||
// Field Selector/s for Kubernetes
|
|
||||||
KubernetesFieldSelector string `toml:"kubernetes_field_selector"`
|
|
||||||
|
|
||||||
// Consul SD configuration
|
|
||||||
ConsulConfig ConsulConfig `toml:"consul"`
|
|
||||||
|
|
||||||
// Bearer Token authorization file path
|
|
||||||
BearerToken string `toml:"bearer_token"`
|
BearerToken string `toml:"bearer_token"`
|
||||||
BearerTokenString string `toml:"bearer_token_string"`
|
BearerTokenString string `toml:"bearer_token_string"`
|
||||||
|
|
||||||
// Basic authentication credentials
|
|
||||||
Username string `toml:"username"`
|
Username string `toml:"username"`
|
||||||
Password string `toml:"password"`
|
Password string `toml:"password"`
|
||||||
|
|
||||||
HTTPHeaders map[string]string `toml:"http_headers"`
|
HTTPHeaders map[string]string `toml:"http_headers"`
|
||||||
|
|
||||||
ContentLengthLimit config.Size `toml:"content_length_limit"`
|
ContentLengthLimit config.Size `toml:"content_length_limit"`
|
||||||
|
ContentTypeOverride string `toml:"content_type_override"`
|
||||||
EnableRequestMetrics bool `toml:"enable_request_metrics"`
|
EnableRequestMetrics bool `toml:"enable_request_metrics"`
|
||||||
|
|
||||||
MetricVersion int `toml:"metric_version"`
|
MetricVersion int `toml:"metric_version"`
|
||||||
|
|
||||||
URLTag string `toml:"url_tag"`
|
URLTag string `toml:"url_tag"`
|
||||||
|
|
||||||
IgnoreTimestamp bool `toml:"ignore_timestamp"`
|
IgnoreTimestamp bool `toml:"ignore_timestamp"`
|
||||||
|
|
||||||
Log telegraf.Logger
|
// Kubernetes service discovery
|
||||||
|
|
||||||
httpconfig.HTTPClientConfig
|
|
||||||
|
|
||||||
client *http.Client
|
|
||||||
headers map[string]string
|
|
||||||
|
|
||||||
nsStore cache.Store
|
|
||||||
|
|
||||||
nsAnnotationPass []models.TagFilter
|
|
||||||
nsAnnotationDrop []models.TagFilter
|
|
||||||
|
|
||||||
// Should we scrape Kubernetes services for prometheus annotations
|
|
||||||
MonitorPods bool `toml:"monitor_kubernetes_pods"`
|
MonitorPods bool `toml:"monitor_kubernetes_pods"`
|
||||||
PodScrapeScope string `toml:"pod_scrape_scope"`
|
PodScrapeScope string `toml:"pod_scrape_scope"`
|
||||||
NodeIP string `toml:"node_ip"`
|
NodeIP string `toml:"node_ip"`
|
||||||
PodScrapeInterval int `toml:"pod_scrape_interval"`
|
PodScrapeInterval int `toml:"pod_scrape_interval"`
|
||||||
PodNamespace string `toml:"monitor_kubernetes_pods_namespace"`
|
PodNamespace string `toml:"monitor_kubernetes_pods_namespace"`
|
||||||
PodNamespaceLabelName string `toml:"pod_namespace_label_name"`
|
PodNamespaceLabelName string `toml:"pod_namespace_label_name"`
|
||||||
|
KubernetesServices []string `toml:"kubernetes_services"`
|
||||||
|
KubeConfig string `toml:"kube_config"`
|
||||||
|
KubernetesLabelSelector string `toml:"kubernetes_label_selector"`
|
||||||
|
KubernetesFieldSelector string `toml:"kubernetes_field_selector"`
|
||||||
|
MonitorKubernetesPodsMethod MonitorMethod `toml:"monitor_kubernetes_pods_method"`
|
||||||
|
MonitorKubernetesPodsScheme string `toml:"monitor_kubernetes_pods_scheme"`
|
||||||
|
MonitorKubernetesPodsPath string `toml:"monitor_kubernetes_pods_path"`
|
||||||
|
MonitorKubernetesPodsPort int `toml:"monitor_kubernetes_pods_port"`
|
||||||
|
NamespaceAnnotationPass map[string][]string `toml:"namespace_annotation_pass"`
|
||||||
|
NamespaceAnnotationDrop map[string][]string `toml:"namespace_annotation_drop"`
|
||||||
|
PodAnnotationInclude []string `toml:"pod_annotation_include"`
|
||||||
|
PodAnnotationExclude []string `toml:"pod_annotation_exclude"`
|
||||||
|
PodLabelInclude []string `toml:"pod_label_include"`
|
||||||
|
PodLabelExclude []string `toml:"pod_label_exclude"`
|
||||||
|
CacheRefreshInterval int `toml:"cache_refresh_interval"`
|
||||||
|
|
||||||
|
// Consul discovery
|
||||||
|
ConsulConfig ConsulConfig `toml:"consul"`
|
||||||
|
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
httpconfig.HTTPClientConfig
|
||||||
|
|
||||||
|
client *http.Client
|
||||||
|
headers map[string]string
|
||||||
|
contentType string
|
||||||
|
|
||||||
|
nsStore cache.Store
|
||||||
|
nsAnnotationPass []models.TagFilter
|
||||||
|
nsAnnotationDrop []models.TagFilter
|
||||||
|
|
||||||
|
// Should we scrape Kubernetes services for prometheus annotations
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
kubernetesPods map[PodID]URLAndAddress
|
kubernetesPods map[PodID]URLAndAddress
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
@ -111,29 +108,11 @@ type Prometheus struct {
|
||||||
podLabelSelector labels.Selector
|
podLabelSelector labels.Selector
|
||||||
podFieldSelector fields.Selector
|
podFieldSelector fields.Selector
|
||||||
isNodeScrapeScope bool
|
isNodeScrapeScope bool
|
||||||
|
|
||||||
MonitorKubernetesPodsMethod MonitorMethod `toml:"monitor_kubernetes_pods_method"`
|
|
||||||
MonitorKubernetesPodsScheme string `toml:"monitor_kubernetes_pods_scheme"`
|
|
||||||
MonitorKubernetesPodsPath string `toml:"monitor_kubernetes_pods_path"`
|
|
||||||
MonitorKubernetesPodsPort int `toml:"monitor_kubernetes_pods_port"`
|
|
||||||
|
|
||||||
NamespaceAnnotationPass map[string][]string `toml:"namespace_annotation_pass"`
|
|
||||||
NamespaceAnnotationDrop map[string][]string `toml:"namespace_annotation_drop"`
|
|
||||||
|
|
||||||
PodAnnotationInclude []string `toml:"pod_annotation_include"`
|
|
||||||
PodAnnotationExclude []string `toml:"pod_annotation_exclude"`
|
|
||||||
|
|
||||||
PodLabelInclude []string `toml:"pod_label_include"`
|
|
||||||
PodLabelExclude []string `toml:"pod_label_exclude"`
|
|
||||||
|
|
||||||
podAnnotationIncludeFilter filter.Filter
|
podAnnotationIncludeFilter filter.Filter
|
||||||
podAnnotationExcludeFilter filter.Filter
|
podAnnotationExcludeFilter filter.Filter
|
||||||
podLabelIncludeFilter filter.Filter
|
podLabelIncludeFilter filter.Filter
|
||||||
podLabelExcludeFilter filter.Filter
|
podLabelExcludeFilter filter.Filter
|
||||||
|
|
||||||
// Only for monitor_kubernetes_pods=true
|
|
||||||
CacheRefreshInterval int `toml:"cache_refresh_interval"`
|
|
||||||
|
|
||||||
// List of consul services to scrape
|
// List of consul services to scrape
|
||||||
consulServices map[string]URLAndAddress
|
consulServices map[string]URLAndAddress
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +122,29 @@ func (*Prometheus) SampleConfig() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prometheus) Init() error {
|
func (p *Prometheus) Init() error {
|
||||||
|
// Setup content-type override if requested
|
||||||
|
switch p.ContentTypeOverride {
|
||||||
|
case "": // No override
|
||||||
|
case "text":
|
||||||
|
p.contentType = string(expfmt.NewFormat(expfmt.TypeTextPlain))
|
||||||
|
case "protobuf-delimiter":
|
||||||
|
p.contentType = string(expfmt.NewFormat(expfmt.TypeProtoDelim))
|
||||||
|
case "protobuf-compact":
|
||||||
|
p.contentType = string(expfmt.NewFormat(expfmt.TypeProtoCompact))
|
||||||
|
case "protobuf-text":
|
||||||
|
p.contentType = string(expfmt.NewFormat(expfmt.TypeProtoText))
|
||||||
|
case "openmetrics-text":
|
||||||
|
f, err := expfmt.NewOpenMetricsFormat(expfmt.OpenMetricsVersion_1_0_0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.contentType = string(f)
|
||||||
|
case "openmetrics-protobuf":
|
||||||
|
p.contentType = "application/openmetrics-protobuf;version=1.0.0"
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid 'content_type_override' setting %q", p.ContentTypeOverride)
|
||||||
|
}
|
||||||
|
|
||||||
// Config processing for node scrape scope for monitor_kubernetes_pods
|
// Config processing for node scrape scope for monitor_kubernetes_pods
|
||||||
p.isNodeScrapeScope = strings.EqualFold(p.PodScrapeScope, "node")
|
p.isNodeScrapeScope = strings.EqualFold(p.PodScrapeScope, "node")
|
||||||
if p.isNodeScrapeScope {
|
if p.isNodeScrapeScope {
|
||||||
|
|
@ -491,15 +493,30 @@ func (p *Prometheus) gatherURL(u URLAndAddress, acc telegraf.Accumulator) (map[s
|
||||||
return requestFields, tags, fmt.Errorf("error reading body: %w", err)
|
return requestFields, tags, fmt.Errorf("error reading body: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestFields["content_length"] = len(body)
|
requestFields["content_length"] = len(body)
|
||||||
|
|
||||||
|
// Override the response format if the user requested it
|
||||||
|
if p.contentType != "" {
|
||||||
|
resp.Header.Set("Content-Type", p.contentType)
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the metrics
|
// Parse the metrics
|
||||||
metricParser := parser.Parser{
|
var metricParser telegraf.Parser
|
||||||
|
if openmetrics.AcceptsContent(resp.Header) {
|
||||||
|
metricParser = &openmetrics.Parser{
|
||||||
Header: resp.Header,
|
Header: resp.Header,
|
||||||
MetricVersion: p.MetricVersion,
|
MetricVersion: p.MetricVersion,
|
||||||
IgnoreTimestamp: p.IgnoreTimestamp,
|
IgnoreTimestamp: p.IgnoreTimestamp,
|
||||||
Log: p.Log,
|
Log: p.Log,
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
metricParser = &parser.Parser{
|
||||||
|
Header: resp.Header,
|
||||||
|
MetricVersion: p.MetricVersion,
|
||||||
|
IgnoreTimestamp: p.IgnoreTimestamp,
|
||||||
|
Log: p.Log,
|
||||||
|
}
|
||||||
|
}
|
||||||
metrics, err := metricParser.Parse(body)
|
metrics, err := metricParser.Parse(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return requestFields, tags, fmt.Errorf("error reading metrics for %q: %w", u.URL, err)
|
return requestFields, tags, fmt.Errorf("error reading metrics for %q: %w", u.URL, err)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -706,3 +708,167 @@ func TestPrometheusInternalNoWeb(t *testing.T) {
|
||||||
require.Error(t, acc.GatherError(p.Gather))
|
require.Error(t, acc.GatherError(p.Gather))
|
||||||
testutil.RequireMetricsSubset(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreFields("content_length", "response_time"), testutil.IgnoreTime())
|
testutil.RequireMetricsSubset(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreFields("content_length", "response_time"), testutil.IgnoreTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenmetricsText(t *testing.T) {
|
||||||
|
const data = `
|
||||||
|
# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started.
|
||||||
|
# TYPE go_memstats_gc_cpu_fraction gauge
|
||||||
|
go_memstats_gc_cpu_fraction -0.00014404354379774563
|
||||||
|
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
|
||||||
|
# TYPE go_memstats_gc_sys_bytes gauge
|
||||||
|
go_memstats_gc_sys_bytes 6.0936192e+07
|
||||||
|
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
|
||||||
|
# TYPE go_memstats_heap_alloc_bytes gauge
|
||||||
|
go_memstats_heap_alloc_bytes 1.581062048e+09
|
||||||
|
# EOF
|
||||||
|
`
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/openmetrics-text;version=1.0.0")
|
||||||
|
_, _ = w.Write([]byte(data))
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
p := &Prometheus{
|
||||||
|
Log: &testutil.Logger{},
|
||||||
|
URLs: []string{ts.URL},
|
||||||
|
URLTag: "",
|
||||||
|
MetricVersion: 2,
|
||||||
|
}
|
||||||
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_gc_cpu_fraction": float64(-0.00014404354379774563)},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_gc_sys_bytes": 6.0936192e+07},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_heap_alloc_bytes": 1.581062048e+09},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenmetricsProtobuf(t *testing.T) {
|
||||||
|
data, err := os.ReadFile(filepath.Join("testdata", "openmetric-proto.bin"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/openmetrics-protobuf;version=1.0.0")
|
||||||
|
_, _ = w.Write(data)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
p := &Prometheus{
|
||||||
|
Log: &testutil.Logger{},
|
||||||
|
URLs: []string{ts.URL},
|
||||||
|
URLTag: "",
|
||||||
|
MetricVersion: 2,
|
||||||
|
}
|
||||||
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_gc_cpu_fraction": float64(-0.00014404354379774563)},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_gc_sys_bytes": 6.0936192e+07},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_heap_alloc_bytes": 1.581062048e+09},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContentTypeOverride(t *testing.T) {
|
||||||
|
const data = `
|
||||||
|
# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started.
|
||||||
|
# TYPE go_memstats_gc_cpu_fraction gauge
|
||||||
|
go_memstats_gc_cpu_fraction -0.00014404354379774563
|
||||||
|
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
|
||||||
|
# TYPE go_memstats_gc_sys_bytes gauge
|
||||||
|
go_memstats_gc_sys_bytes 6.0936192e+07
|
||||||
|
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
|
||||||
|
# TYPE go_memstats_heap_alloc_bytes gauge
|
||||||
|
go_memstats_heap_alloc_bytes 1.581062048e+09
|
||||||
|
# EOF
|
||||||
|
`
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
// Provide a wrong version
|
||||||
|
w.Header().Add("Content-Type", "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited")
|
||||||
|
_, _ = w.Write([]byte(data))
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
p := &Prometheus{
|
||||||
|
Log: &testutil.Logger{},
|
||||||
|
URLs: []string{ts.URL},
|
||||||
|
URLTag: "",
|
||||||
|
MetricVersion: 2,
|
||||||
|
ContentTypeOverride: "openmetrics-text",
|
||||||
|
}
|
||||||
|
require.NoError(t, p.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, p.Gather(&acc))
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_gc_cpu_fraction": float64(-0.00014404354379774563)},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_gc_sys_bytes": 6.0936192e+07},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"openmetric",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{"go_memstats_heap_alloc_bytes": 1.581062048e+09},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
telegraf.Gauge,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), testutil.SortMetrics())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,14 @@
|
||||||
## If set to true, the gather time will be used.
|
## If set to true, the gather time will be used.
|
||||||
# ignore_timestamp = false
|
# ignore_timestamp = false
|
||||||
|
|
||||||
|
## Override content-type of the returned message
|
||||||
|
## Available options are for prometheus:
|
||||||
|
## text, protobuf-delimiter, protobuf-compact, protobuf-text,
|
||||||
|
## and for openmetrics:
|
||||||
|
## openmetrics-text, openmetrics-protobuf
|
||||||
|
## By default the content-type of the response is used.
|
||||||
|
# content_type_override = ""
|
||||||
|
|
||||||
## An array of Kubernetes services to scrape metrics from.
|
## An array of Kubernetes services to scrape metrics from.
|
||||||
# kubernetes_services = ["http://my-service-dns.my-namespace:9100/metrics"]
|
# kubernetes_services = ["http://my-service-dns.my-namespace:9100/metrics"]
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
||||||
|
//go:build !custom || parsers || parsers.openmetrics
|
||||||
|
|
||||||
|
package all
|
||||||
|
|
||||||
|
import _ "github.com/influxdata/telegraf/plugins/parsers/openmetrics" // register plugin
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
# OpenMetrics Format Parser Plugin
|
||||||
|
|
||||||
|
This plugin allows to parse the [OpenMetrics Text Format][] into Telegraf
|
||||||
|
metrics. It is used internally in [prometheus input](/plugins/inputs/prometheus)
|
||||||
|
but can also be used by e.g.
|
||||||
|
[http_listener_v2](/plugins/inputs/http_listener_v2) to simulate a Pushgateway.
|
||||||
|
|
||||||
|
The plugin allows to output different metric formats as described in the
|
||||||
|
[Metric Formats section](#metric-formats).
|
||||||
|
|
||||||
|
[OpenMetrics Text Format]: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[inputs.file]]
|
||||||
|
files = ["example"]
|
||||||
|
|
||||||
|
## Data format to consume.
|
||||||
|
## Each data format has its own unique set of configuration options, read
|
||||||
|
## more about them here:
|
||||||
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metric Formats
|
||||||
|
|
||||||
|
The metric_version setting controls how telegraf translates OpenMetrics'
|
||||||
|
metrics to Telegraf metrics. There are two options.
|
||||||
|
|
||||||
|
### `v1` format
|
||||||
|
|
||||||
|
In this version, the OpenMetrics metric-family name becomes the Telegraf metric
|
||||||
|
name, labels become tags and values become fields. The fields are named based
|
||||||
|
on the type of the OpenMetric metric. This option produces metrics that are
|
||||||
|
dense (contrary to the sparse metrics of`v2`).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
# TYPE acme_http_router_request_seconds summary
|
||||||
|
# UNIT acme_http_router_request_seconds seconds
|
||||||
|
# HELP acme_http_router_request_seconds Latency though all of ACME's HTTP request router.
|
||||||
|
acme_http_router_request_seconds_sum{path="/api/v1",method="GET"} 9036.32
|
||||||
|
acme_http_router_request_seconds_count{path="/api/v1",method="GET"} 807283.0
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.5"} 1.29854
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.9"} 54.85479
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.99"} 6884.32324
|
||||||
|
acme_http_router_request_seconds_created{path="/api/v1",method="GET"} 1605281325.0
|
||||||
|
acme_http_router_request_seconds_sum{path="/api/v2",method="POST"} 479.3
|
||||||
|
acme_http_router_request_seconds_count{path="/api/v2",method="POST"} 34.0
|
||||||
|
acme_http_router_request_seconds_created{path="/api/v2",method="POST"} 1605281325.0
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.5"} 0.85412
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.9"} 1.15429
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.99"} 3698.48132
|
||||||
|
# TYPE go_goroutines gauge
|
||||||
|
# HELP go_goroutines Number of goroutines that currently exist.
|
||||||
|
go_goroutines 69
|
||||||
|
# TYPE process_cpu_seconds counter
|
||||||
|
# UNIT process_cpu_seconds seconds
|
||||||
|
# HELP process_cpu_seconds Total user and system CPU time spent in seconds.
|
||||||
|
process_cpu_seconds_total 4.20072246e+06
|
||||||
|
# EOF```
|
||||||
|
|
||||||
|
becomes
|
||||||
|
|
||||||
|
```text
|
||||||
|
acme_http_router_request_seconds,unit=seconds,path=/api/v1,method=GET sum=9036.32,count=807283.0,created=1605281325.0,0.5=1.29854,0.9=54.85479,0.99=6884.32324
|
||||||
|
acme_http_router_request_seconds,unit=seconds,path=/api/v2,method=POST sum=479.3,count=34.0,created=1605281325.0,0.5=0.85412,0.9=1.15429,0.99=3698.48132
|
||||||
|
go_goroutines gauge=69
|
||||||
|
process_cpu_seconds,unit=seconds counter=4200722.46
|
||||||
|
```
|
||||||
|
|
||||||
|
This is especially useful and efficient for outputs with row-oriented data
|
||||||
|
models.
|
||||||
|
|
||||||
|
### `v2` format
|
||||||
|
|
||||||
|
In this version, each OpenMetrics MetricPoint becomes a Telegraf metric with
|
||||||
|
a few exceptions for complex types like histograms. All Telegraf metrics are
|
||||||
|
named `prometheus` with OpenMetrics' labels become tags and the field-name is
|
||||||
|
based on the OpenMetrics metric-name.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
# TYPE acme_http_router_request_seconds summary
|
||||||
|
# UNIT acme_http_router_request_seconds seconds
|
||||||
|
# HELP acme_http_router_request_seconds Latency though all of ACME's HTTP request router.
|
||||||
|
acme_http_router_request_seconds_sum{path="/api/v1",method="GET"} 9036.32
|
||||||
|
acme_http_router_request_seconds_count{path="/api/v1",method="GET"} 807283.0
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.5"} 1.29854
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.9"} 54.85479
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.99"} 6884.32324
|
||||||
|
acme_http_router_request_seconds_created{path="/api/v1",method="GET"} 1605281325.0
|
||||||
|
acme_http_router_request_seconds_sum{path="/api/v2",method="POST"} 479.3
|
||||||
|
acme_http_router_request_seconds_count{path="/api/v2",method="POST"} 34.0
|
||||||
|
acme_http_router_request_seconds_created{path="/api/v2",method="POST"} 1605281325.0
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.5"} 0.85412
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.9"} 1.15429
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.99"} 3698.48132
|
||||||
|
# TYPE go_goroutines gauge
|
||||||
|
# HELP go_goroutines Number of goroutines that currently exist.
|
||||||
|
go_goroutines 69
|
||||||
|
# TYPE process_cpu_seconds counter
|
||||||
|
# UNIT process_cpu_seconds seconds
|
||||||
|
# HELP process_cpu_seconds Total user and system CPU time spent in seconds.
|
||||||
|
process_cpu_seconds_total 4.20072246e+06
|
||||||
|
# EOF```
|
||||||
|
|
||||||
|
becomes
|
||||||
|
|
||||||
|
```text
|
||||||
|
prometheus,method=GET,path=/api/v1,unit=seconds acme_http_router_request_seconds_count=807283,acme_http_router_request_seconds_created=1605281325,acme_http_router_request_seconds_sum=9036.32
|
||||||
|
prometheus,method=GET,path=/api/v1,quantile=0.5,unit=seconds acme_http_router_request_seconds=1.29854
|
||||||
|
prometheus,method=GET,path=/api/v1,quantile=0.9,unit=seconds acme_http_router_request_seconds=54.85479
|
||||||
|
prometheus,method=GET,path=/api/v1,quantile=0.99,unit=seconds acme_http_router_request_seconds=6884.32324
|
||||||
|
prometheus,method=POST,path=/api/v2,unit=seconds acme_http_router_request_seconds_count=34,acme_http_router_request_seconds_created=1605281325,acme_http_router_request_seconds_sum=479.3
|
||||||
|
prometheus,method=POST,path=/api/v2,quantile=0.5,unit=seconds acme_http_router_request_seconds=0.85412
|
||||||
|
prometheus,method=POST,path=/api/v2,quantile=0.9,unit=seconds acme_http_router_request_seconds=1.15429
|
||||||
|
prometheus,method=POST,path=/api/v2,quantile=0.99,unit=seconds acme_http_router_request_seconds=3698.48132
|
||||||
|
prometheus go_goroutines=69
|
||||||
|
prometheus,unit=seconds process_cpu_seconds=4200722.46
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting metrics are sparse, but for some outputs they may be easier to
|
||||||
|
process or query, including those that are more efficient with column-oriented
|
||||||
|
data. The telegraf metric name is the same for all metrics in the input
|
||||||
|
instance. It can be set with the `name_override` setting and defaults to
|
||||||
|
"prometheus". To have multiple metric names, you can use multiple instances of
|
||||||
|
the plugin, each with its own `name_override`.
|
||||||
|
|
||||||
|
`metric_version = 2` uses the same histogram format as the histogram aggregator
|
||||||
|
|
||||||
|
## Regenerating OpenMetrics code
|
||||||
|
|
||||||
|
Download the latest version of the protocol-buffer definition
|
||||||
|
|
||||||
|
```text
|
||||||
|
wget https://raw.githubusercontent.com/OpenObservability/OpenMetrics/main/proto/openmetrics_data_model.proto
|
||||||
|
```
|
||||||
|
|
||||||
|
and generate the go-code for the definition using
|
||||||
|
|
||||||
|
```text
|
||||||
|
protoc --proto_path=. \
|
||||||
|
--go_out=. \
|
||||||
|
--go_opt=paths=source_relative \
|
||||||
|
--go_opt=Mopenmetrics_data_model.proto=github.com/influxdata/telegraf/plugins/parsers/openmetrics \
|
||||||
|
openmetrics_data_model.proto
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
package openmetrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Parser) extractMetricsV1(ometrics *MetricFamily) []telegraf.Metric {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
// Convert each prometheus metrics to the corresponding telegraf metrics.
|
||||||
|
// You will get one telegraf metric with one field per prometheus metric
|
||||||
|
// for "simple" types like Gauge and Counter but a telegraf metric with
|
||||||
|
// multiple fields for "complex" types like Summary or Histogram.
|
||||||
|
var metrics []telegraf.Metric
|
||||||
|
metricName := ometrics.GetName()
|
||||||
|
metricType := ometrics.GetType()
|
||||||
|
for _, om := range ometrics.GetMetrics() {
|
||||||
|
// Extract the timestamp of the metric if it exists and should
|
||||||
|
// not be ignored.
|
||||||
|
t := now
|
||||||
|
|
||||||
|
// Convert the labels to tags
|
||||||
|
tags := getTagsFromLabels(om, p.DefaultTags)
|
||||||
|
if ometrics.Unit != "" {
|
||||||
|
tags["unit"] = ometrics.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the metric points and construct a metric for each
|
||||||
|
for _, omp := range om.GetMetricPoints() {
|
||||||
|
if omp.Timestamp != nil {
|
||||||
|
t = omp.GetTimestamp().AsTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the metrics
|
||||||
|
switch metricType {
|
||||||
|
case MetricType_UNKNOWN:
|
||||||
|
x := omp.GetUnknownValue().GetValue()
|
||||||
|
if x == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var value float64
|
||||||
|
switch v := x.(type) {
|
||||||
|
case *UnknownValue_DoubleValue:
|
||||||
|
value = v.DoubleValue
|
||||||
|
case *UnknownValue_IntValue:
|
||||||
|
value = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{"value": value}
|
||||||
|
metrics = append(metrics, metric.New(metricName, tags, fields, t, telegraf.Untyped))
|
||||||
|
case MetricType_GAUGE:
|
||||||
|
x := omp.GetGaugeValue().GetValue()
|
||||||
|
if x == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var value float64
|
||||||
|
switch v := x.(type) {
|
||||||
|
case *GaugeValue_DoubleValue:
|
||||||
|
value = v.DoubleValue
|
||||||
|
case *GaugeValue_IntValue:
|
||||||
|
value = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{"gauge": value}
|
||||||
|
metrics = append(metrics, metric.New(metricName, tags, fields, t, telegraf.Gauge))
|
||||||
|
case MetricType_COUNTER:
|
||||||
|
x := omp.GetCounterValue().GetTotal()
|
||||||
|
if x == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var value float64
|
||||||
|
switch v := x.(type) {
|
||||||
|
case *CounterValue_DoubleValue:
|
||||||
|
value = v.DoubleValue
|
||||||
|
case *CounterValue_IntValue:
|
||||||
|
value = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{"counter": value}
|
||||||
|
metrics = append(metrics, metric.New(metricName, tags, fields, t, telegraf.Counter))
|
||||||
|
case MetricType_STATE_SET:
|
||||||
|
stateset := omp.GetStateSetValue()
|
||||||
|
// Collect the fields
|
||||||
|
fields := make(map[string]interface{}, len(stateset.States))
|
||||||
|
for _, state := range stateset.GetStates() {
|
||||||
|
fname := strings.ReplaceAll(state.GetName(), " ", "_")
|
||||||
|
fields[fname] = state.GetEnabled()
|
||||||
|
}
|
||||||
|
metrics = append(metrics, metric.New(metricName, tags, fields, t, telegraf.Untyped))
|
||||||
|
case MetricType_INFO:
|
||||||
|
info := omp.GetInfoValue().GetInfo()
|
||||||
|
fields := map[string]interface{}{"info": uint64(1)}
|
||||||
|
mptags := make(map[string]string, len(tags)+len(info))
|
||||||
|
for k, v := range tags {
|
||||||
|
mptags[k] = v
|
||||||
|
}
|
||||||
|
for _, itag := range info {
|
||||||
|
mptags[itag.Name] = itag.Value
|
||||||
|
}
|
||||||
|
metrics = append(metrics, metric.New(metricName, mptags, fields, t, telegraf.Untyped))
|
||||||
|
case MetricType_HISTOGRAM, MetricType_GAUGE_HISTOGRAM:
|
||||||
|
histogram := omp.GetHistogramValue()
|
||||||
|
|
||||||
|
// Collect the fields
|
||||||
|
fields := make(map[string]interface{}, len(histogram.Buckets)+3)
|
||||||
|
fields["count"] = float64(histogram.GetCount())
|
||||||
|
if s := histogram.GetSum(); s != nil {
|
||||||
|
switch v := s.(type) {
|
||||||
|
case *HistogramValue_DoubleValue:
|
||||||
|
fields["sum"] = v.DoubleValue
|
||||||
|
case *HistogramValue_IntValue:
|
||||||
|
fields["sum"] = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ts := histogram.GetCreated(); ts != nil {
|
||||||
|
fields["created"] = float64(ts.Seconds) + float64(ts.Nanos)/float64(time.Nanosecond)
|
||||||
|
}
|
||||||
|
for _, b := range histogram.Buckets {
|
||||||
|
fname := strconv.FormatFloat(b.GetUpperBound(), 'g', -1, 64)
|
||||||
|
fields[fname] = float64(b.GetCount())
|
||||||
|
}
|
||||||
|
metrics = append(metrics, metric.New(metricName, tags, fields, t, telegraf.Histogram))
|
||||||
|
case MetricType_SUMMARY:
|
||||||
|
summary := omp.GetSummaryValue()
|
||||||
|
|
||||||
|
// Collect the fields
|
||||||
|
fields := make(map[string]interface{}, len(summary.Quantile)+2)
|
||||||
|
fields["count"] = float64(summary.GetCount())
|
||||||
|
if s := summary.GetSum(); s != nil {
|
||||||
|
switch v := s.(type) {
|
||||||
|
case *SummaryValue_DoubleValue:
|
||||||
|
fields["sum"] = v.DoubleValue
|
||||||
|
case *SummaryValue_IntValue:
|
||||||
|
fields["sum"] = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ts := summary.GetCreated(); ts != nil {
|
||||||
|
fields["created"] = float64(ts.Seconds) + float64(ts.Nanos)/float64(time.Second)
|
||||||
|
}
|
||||||
|
for _, q := range summary.GetQuantile() {
|
||||||
|
if v := q.GetValue(); !math.IsNaN(v) {
|
||||||
|
fname := strconv.FormatFloat(q.GetQuantile(), 'g', -1, 64)
|
||||||
|
fields[fname] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metrics = append(metrics, metric.New(metricName, tags, fields, t, telegraf.Summary))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metrics
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
package openmetrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Parser) extractMetricsV2(ometrics *MetricFamily) []telegraf.Metric {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
// Convert each openmetric metric to a corresponding telegraf metric
|
||||||
|
// with one field each. The process will filter NaNs in values and skip
|
||||||
|
// the corresponding metrics.
|
||||||
|
var metrics []telegraf.Metric
|
||||||
|
metricName := ometrics.GetName()
|
||||||
|
metricType := ometrics.GetType()
|
||||||
|
for _, om := range ometrics.GetMetrics() {
|
||||||
|
// Extract the timestamp of the metric if it exists and should
|
||||||
|
// not be ignored.
|
||||||
|
t := now
|
||||||
|
|
||||||
|
// Convert the labels to tags
|
||||||
|
tags := getTagsFromLabels(om, p.DefaultTags)
|
||||||
|
if ometrics.Unit != "" {
|
||||||
|
tags["unit"] = ometrics.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the metrics
|
||||||
|
for _, omp := range om.GetMetricPoints() {
|
||||||
|
if omp.Timestamp != nil {
|
||||||
|
t = omp.GetTimestamp().AsTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch metricType {
|
||||||
|
case MetricType_UNKNOWN:
|
||||||
|
x := omp.GetUnknownValue().GetValue()
|
||||||
|
if x == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var value float64
|
||||||
|
switch v := x.(type) {
|
||||||
|
case *UnknownValue_DoubleValue:
|
||||||
|
value = v.DoubleValue
|
||||||
|
case *UnknownValue_IntValue:
|
||||||
|
value = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{metricName: value}
|
||||||
|
metrics = append(metrics, metric.New("openmetric", tags, fields, t, telegraf.Untyped))
|
||||||
|
case MetricType_GAUGE:
|
||||||
|
x := omp.GetGaugeValue().GetValue()
|
||||||
|
if x == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var value float64
|
||||||
|
switch v := x.(type) {
|
||||||
|
case *GaugeValue_DoubleValue:
|
||||||
|
value = v.DoubleValue
|
||||||
|
case *GaugeValue_IntValue:
|
||||||
|
value = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{metricName: value}
|
||||||
|
metrics = append(metrics, metric.New("openmetric", tags, fields, t, telegraf.Gauge))
|
||||||
|
case MetricType_COUNTER:
|
||||||
|
x := omp.GetCounterValue().GetTotal()
|
||||||
|
if x == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var value float64
|
||||||
|
switch v := x.(type) {
|
||||||
|
case *CounterValue_DoubleValue:
|
||||||
|
value = v.DoubleValue
|
||||||
|
case *CounterValue_IntValue:
|
||||||
|
value = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
if math.IsNaN(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{metricName: value}
|
||||||
|
metrics = append(metrics, metric.New("openmetric", tags, fields, t, telegraf.Counter))
|
||||||
|
case MetricType_STATE_SET:
|
||||||
|
stateset := omp.GetStateSetValue()
|
||||||
|
|
||||||
|
// Add one metric per state
|
||||||
|
for _, state := range stateset.GetStates() {
|
||||||
|
sn := strings.ReplaceAll(state.GetName(), " ", "_")
|
||||||
|
fields := map[string]interface{}{metricName + "_" + sn: state.GetEnabled()}
|
||||||
|
metrics = append(metrics, metric.New("openmetric", tags, fields, t, telegraf.Untyped))
|
||||||
|
}
|
||||||
|
case MetricType_INFO:
|
||||||
|
info := omp.GetInfoValue().GetInfo()
|
||||||
|
mptags := make(map[string]string, len(tags)+len(info))
|
||||||
|
for k, v := range tags {
|
||||||
|
mptags[k] = v
|
||||||
|
}
|
||||||
|
for _, itag := range info {
|
||||||
|
mptags[itag.Name] = itag.Value
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{metricName + "_info": uint64(1)}
|
||||||
|
metrics = append(metrics, metric.New("openmetric", mptags, fields, t, telegraf.Untyped))
|
||||||
|
case MetricType_HISTOGRAM, MetricType_GAUGE_HISTOGRAM:
|
||||||
|
histogram := omp.GetHistogramValue()
|
||||||
|
|
||||||
|
// Add an overall metric containing the number of samples and and its sum
|
||||||
|
histFields := make(map[string]interface{})
|
||||||
|
histFields[metricName+"_count"] = float64(histogram.GetCount())
|
||||||
|
if s := histogram.GetSum(); s != nil {
|
||||||
|
switch v := s.(type) {
|
||||||
|
case *HistogramValue_DoubleValue:
|
||||||
|
histFields[metricName+"_sum"] = v.DoubleValue
|
||||||
|
case *HistogramValue_IntValue:
|
||||||
|
histFields[metricName+"_sum"] = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ts := histogram.GetCreated(); ts != nil {
|
||||||
|
histFields[metricName+"_created"] = float64(ts.Seconds) + float64(ts.Nanos)/float64(time.Nanosecond)
|
||||||
|
}
|
||||||
|
metrics = append(metrics, metric.New("openmetric", tags, histFields, t, telegraf.Histogram))
|
||||||
|
|
||||||
|
// Add one metric per histogram bucket
|
||||||
|
var infSeen bool
|
||||||
|
for _, b := range histogram.GetBuckets() {
|
||||||
|
bucketTags := tags
|
||||||
|
bucketTags["le"] = strconv.FormatFloat(b.GetUpperBound(), 'g', -1, 64)
|
||||||
|
bucketFields := map[string]interface{}{
|
||||||
|
metricName + "_bucket": float64(b.GetCount()),
|
||||||
|
}
|
||||||
|
m := metric.New("openmetric", bucketTags, bucketFields, t, telegraf.Histogram)
|
||||||
|
metrics = append(metrics, m)
|
||||||
|
|
||||||
|
// Record if any of the buckets marks an infinite upper bound
|
||||||
|
infSeen = infSeen || math.IsInf(b.GetUpperBound(), +1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infinity bucket is required for proper function of histogram in openmetric
|
||||||
|
if !infSeen {
|
||||||
|
infTags := tags
|
||||||
|
infTags["le"] = "+Inf"
|
||||||
|
infFields := map[string]interface{}{
|
||||||
|
metricName + "_bucket": float64(histogram.GetCount()),
|
||||||
|
}
|
||||||
|
m := metric.New("openmetric", infTags, infFields, t, telegraf.Histogram)
|
||||||
|
metrics = append(metrics, m)
|
||||||
|
}
|
||||||
|
case MetricType_SUMMARY:
|
||||||
|
summary := omp.GetSummaryValue()
|
||||||
|
|
||||||
|
// Add an overall metric containing the number of samples and and its sum
|
||||||
|
summaryFields := make(map[string]interface{})
|
||||||
|
summaryFields[metricName+"_count"] = float64(summary.GetCount())
|
||||||
|
|
||||||
|
if s := summary.GetSum(); s != nil {
|
||||||
|
switch v := s.(type) {
|
||||||
|
case *SummaryValue_DoubleValue:
|
||||||
|
summaryFields[metricName+"_sum"] = v.DoubleValue
|
||||||
|
case *SummaryValue_IntValue:
|
||||||
|
summaryFields[metricName+"_sum"] = float64(v.IntValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ts := summary.GetCreated(); ts != nil {
|
||||||
|
summaryFields[metricName+"_created"] = float64(ts.Seconds) + float64(ts.Nanos)/float64(time.Nanosecond)
|
||||||
|
}
|
||||||
|
metrics = append(metrics, metric.New("openmetric", tags, summaryFields, t, telegraf.Summary))
|
||||||
|
|
||||||
|
// Add one metric per quantile
|
||||||
|
for _, q := range summary.Quantile {
|
||||||
|
quantileTags := tags
|
||||||
|
quantileTags["quantile"] = strconv.FormatFloat(q.GetQuantile(), 'g', -1, 64)
|
||||||
|
quantileFields := map[string]interface{}{
|
||||||
|
metricName: q.GetValue(),
|
||||||
|
}
|
||||||
|
m := metric.New("openmetric", quantileTags, quantileFields, t, telegraf.Summary)
|
||||||
|
metrics = append(metrics, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metrics
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,214 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
// The OpenMetrics protobuf schema which defines the protobuf wire format.
|
||||||
|
// Ensure to interpret "required" as semantically required for a valid message.
|
||||||
|
// All string fields MUST be UTF-8 encoded strings.
|
||||||
|
package openmetrics;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
// The top-level container type that is encoded and sent over the wire.
|
||||||
|
message MetricSet {
|
||||||
|
// Each MetricFamily has one or more MetricPoints for a single Metric.
|
||||||
|
repeated MetricFamily metric_families = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One or more Metrics for a single MetricFamily, where each Metric
|
||||||
|
// has one or more MetricPoints.
|
||||||
|
message MetricFamily {
|
||||||
|
// Required.
|
||||||
|
string name = 1;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
MetricType type = 2;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
string unit = 3;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
string help = 4;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
repeated Metric metrics = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type of a Metric.
|
||||||
|
enum MetricType {
|
||||||
|
// Unknown must use unknown MetricPoint values.
|
||||||
|
UNKNOWN = 0;
|
||||||
|
// Gauge must use gauge MetricPoint values.
|
||||||
|
GAUGE = 1;
|
||||||
|
// Counter must use counter MetricPoint values.
|
||||||
|
COUNTER = 2;
|
||||||
|
// State set must use state set MetricPoint values.
|
||||||
|
STATE_SET = 3;
|
||||||
|
// Info must use info MetricPoint values.
|
||||||
|
INFO = 4;
|
||||||
|
// Histogram must use histogram value MetricPoint values.
|
||||||
|
HISTOGRAM = 5;
|
||||||
|
// Gauge histogram must use histogram value MetricPoint values.
|
||||||
|
GAUGE_HISTOGRAM = 6;
|
||||||
|
// Summary quantiles must use summary value MetricPoint values.
|
||||||
|
SUMMARY = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single metric with a unique set of labels within a metric family.
|
||||||
|
message Metric {
|
||||||
|
// Optional.
|
||||||
|
repeated Label labels = 1;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
repeated MetricPoint metric_points = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A name-value pair. These are used in multiple places: identifying
|
||||||
|
// timeseries, value of INFO metrics, and exemplars in Histograms.
|
||||||
|
message Label {
|
||||||
|
// Required.
|
||||||
|
string name = 1;
|
||||||
|
|
||||||
|
// Required.
|
||||||
|
string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A MetricPoint in a Metric.
|
||||||
|
message MetricPoint {
|
||||||
|
// Required.
|
||||||
|
oneof value {
|
||||||
|
UnknownValue unknown_value = 1;
|
||||||
|
GaugeValue gauge_value = 2;
|
||||||
|
CounterValue counter_value = 3;
|
||||||
|
HistogramValue histogram_value = 4;
|
||||||
|
StateSetValue state_set_value = 5;
|
||||||
|
InfoValue info_value = 6;
|
||||||
|
SummaryValue summary_value = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
google.protobuf.Timestamp timestamp = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value for UNKNOWN MetricPoint.
|
||||||
|
message UnknownValue {
|
||||||
|
// Required.
|
||||||
|
oneof value {
|
||||||
|
double double_value = 1;
|
||||||
|
int64 int_value = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value for GAUGE MetricPoint.
|
||||||
|
message GaugeValue {
|
||||||
|
// Required.
|
||||||
|
oneof value {
|
||||||
|
double double_value = 1;
|
||||||
|
int64 int_value = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value for COUNTER MetricPoint.
|
||||||
|
message CounterValue {
|
||||||
|
// Required.
|
||||||
|
oneof total {
|
||||||
|
double double_value = 1;
|
||||||
|
uint64 int_value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The time values began being collected for this counter.
|
||||||
|
// Optional.
|
||||||
|
google.protobuf.Timestamp created = 3;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
Exemplar exemplar = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value for HISTOGRAM or GAUGE_HISTOGRAM MetricPoint.
|
||||||
|
message HistogramValue {
|
||||||
|
// Optional.
|
||||||
|
oneof sum {
|
||||||
|
double double_value = 1;
|
||||||
|
int64 int_value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
uint64 count = 3;
|
||||||
|
|
||||||
|
// The time values began being collected for this histogram.
|
||||||
|
// Optional.
|
||||||
|
google.protobuf.Timestamp created = 4;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
repeated Bucket buckets = 5;
|
||||||
|
|
||||||
|
// Bucket is the number of values for a bucket in the histogram
|
||||||
|
// with an optional exemplar.
|
||||||
|
message Bucket {
|
||||||
|
// Required.
|
||||||
|
uint64 count = 1;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
double upper_bound = 2;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
Exemplar exemplar = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Exemplar {
|
||||||
|
// Required.
|
||||||
|
double value = 1;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
google.protobuf.Timestamp timestamp = 2;
|
||||||
|
|
||||||
|
// Labels are additional information about the exemplar value (e.g. trace id).
|
||||||
|
// Optional.
|
||||||
|
repeated Label label = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value for STATE_SET MetricPoint.
|
||||||
|
message StateSetValue {
|
||||||
|
// Optional.
|
||||||
|
repeated State states = 1;
|
||||||
|
|
||||||
|
message State {
|
||||||
|
// Required.
|
||||||
|
bool enabled = 1;
|
||||||
|
|
||||||
|
// Required.
|
||||||
|
string name = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value for INFO MetricPoint.
|
||||||
|
message InfoValue {
|
||||||
|
// Optional.
|
||||||
|
repeated Label info = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value for SUMMARY MetricPoint.
|
||||||
|
message SummaryValue {
|
||||||
|
// Optional.
|
||||||
|
oneof sum {
|
||||||
|
double double_value = 1;
|
||||||
|
int64 int_value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
uint64 count = 3;
|
||||||
|
|
||||||
|
// The time sum and count values began being collected for this summary.
|
||||||
|
// Optional.
|
||||||
|
google.protobuf.Timestamp created = 4;
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
repeated Quantile quantile = 5;
|
||||||
|
|
||||||
|
message Quantile {
|
||||||
|
// Required.
|
||||||
|
double quantile = 1;
|
||||||
|
|
||||||
|
// Required.
|
||||||
|
double value = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
package openmetrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/expfmt"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/plugins/parsers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AcceptsContent(header http.Header) bool {
|
||||||
|
contentType := header.Get("Content-Type")
|
||||||
|
if contentType == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mediaType, params, err := mime.ParseMediaType(contentType)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch mediaType {
|
||||||
|
case expfmt.OpenMetricsType:
|
||||||
|
return true
|
||||||
|
case "application/openmetrics-protobuf":
|
||||||
|
return params["version"] == "1.0.0"
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
IgnoreTimestamp bool `toml:"openmetrics_ignore_timestamp"`
|
||||||
|
MetricVersion int `toml:"openmetrics_metric_version"`
|
||||||
|
Header http.Header `toml:"-"` // set by the input plugin
|
||||||
|
DefaultTags map[string]string `toml:"-"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) SetDefaultTags(tags map[string]string) {
|
||||||
|
p.DefaultTags = tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Parse(data []byte) ([]telegraf.Metric, error) {
|
||||||
|
// Determine the metric transport-type derived from the response header
|
||||||
|
contentType := p.Header.Get("Content-Type")
|
||||||
|
var mediaType string
|
||||||
|
var params map[string]string
|
||||||
|
if contentType == "" {
|
||||||
|
// Fallback to text type if no content-type is given
|
||||||
|
mediaType = expfmt.OpenMetricsType
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
mediaType, params, err = mime.ParseMediaType(contentType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unknown media-type in %q", contentType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the raw data into OpenMetrics metrics
|
||||||
|
var metricFamilies []*MetricFamily
|
||||||
|
switch mediaType {
|
||||||
|
case expfmt.OpenMetricsType:
|
||||||
|
// Make sure we have a finishing newline but no trailing one
|
||||||
|
data = bytes.TrimPrefix(data, []byte("\n"))
|
||||||
|
if !bytes.HasSuffix(data, []byte("\n")) {
|
||||||
|
data = append(data, []byte("\n")...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
metricFamilies, err = TextToMetricFamilies(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing text format failed: %w", err)
|
||||||
|
}
|
||||||
|
case "application/openmetrics-protobuf":
|
||||||
|
if version := params["version"]; version != "1.0.0" {
|
||||||
|
return nil, fmt.Errorf("unsupported binary version %q", version)
|
||||||
|
}
|
||||||
|
var metricSet MetricSet
|
||||||
|
if err := proto.Unmarshal(data, &metricSet); err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing binary format failed: %w", err)
|
||||||
|
}
|
||||||
|
metricFamilies = metricSet.GetMetricFamilies()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the OpenMetrics metrics into Telegraf metrics
|
||||||
|
var metrics []telegraf.Metric
|
||||||
|
for _, mf := range metricFamilies {
|
||||||
|
switch p.MetricVersion {
|
||||||
|
case 0, 2:
|
||||||
|
metrics = append(metrics, p.extractMetricsV2(mf)...)
|
||||||
|
case 1:
|
||||||
|
metrics = append(metrics, p.extractMetricsV1(mf)...)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown metric version %d", p.MetricVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metrics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) ParseLine(line string) (telegraf.Metric, error) {
|
||||||
|
metrics, err := p.Parse([]byte(line))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(metrics) < 1 {
|
||||||
|
return nil, errors.New("no metrics in line")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(metrics) > 1 {
|
||||||
|
return nil, errors.New("more than one metric in line")
|
||||||
|
}
|
||||||
|
|
||||||
|
return metrics[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTagsFromLabels(m *Metric, defaultTags map[string]string) map[string]string {
|
||||||
|
result := make(map[string]string, len(defaultTags)+len(m.Labels))
|
||||||
|
|
||||||
|
for key, value := range defaultTags {
|
||||||
|
result[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, label := range m.Labels {
|
||||||
|
if v := label.GetValue(); v != "" {
|
||||||
|
result[label.Name] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
parsers.Add("openmetrics",
|
||||||
|
func(string) telegraf.Parser {
|
||||||
|
return &Parser{}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
package openmetrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf/config"
|
||||||
|
"github.com/influxdata/telegraf/models"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
test "github.com/influxdata/telegraf/testutil/plugin_input"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCases(t *testing.T) {
|
||||||
|
// Get all directories in testcases
|
||||||
|
folders, err := os.ReadDir("testcases")
|
||||||
|
require.NoError(t, err)
|
||||||
|
// Make sure testdata contains data
|
||||||
|
require.NotEmpty(t, folders)
|
||||||
|
|
||||||
|
for _, f := range folders {
|
||||||
|
fname := f.Name()
|
||||||
|
testdataPath := filepath.Join("testcases", fname)
|
||||||
|
configFilename := filepath.Join(testdataPath, "telegraf.conf")
|
||||||
|
|
||||||
|
// Run tests as metric version 1
|
||||||
|
t.Run(fname+"_v1", func(t *testing.T) {
|
||||||
|
// Load the configuration
|
||||||
|
cfg := config.NewConfig()
|
||||||
|
require.NoError(t, cfg.LoadConfig(configFilename))
|
||||||
|
require.Len(t, cfg.Inputs, 1)
|
||||||
|
|
||||||
|
// Tune plugin
|
||||||
|
plugin := cfg.Inputs[0].Input.(*test.Plugin)
|
||||||
|
plugin.Path = testdataPath
|
||||||
|
plugin.UseTypeTag = "_type"
|
||||||
|
plugin.ExpectedFilename = "expected_v1.out"
|
||||||
|
|
||||||
|
parser := plugin.Parser.(*models.RunningParser).Parser.(*Parser)
|
||||||
|
parser.MetricVersion = 1
|
||||||
|
if raw, found := plugin.AdditionalParams["headers"]; found {
|
||||||
|
headers, ok := raw.(map[string]interface{})
|
||||||
|
require.Truef(t, ok, "unknown header type %T", raw)
|
||||||
|
parser.Header = make(http.Header)
|
||||||
|
for k, rv := range headers {
|
||||||
|
v, ok := rv.(string)
|
||||||
|
require.Truef(t, ok, "unknown header value type %T for %q", raw, k)
|
||||||
|
parser.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Gather data and check errors
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err := plugin.Gather(&acc)
|
||||||
|
switch len(plugin.ExpectedErrors) {
|
||||||
|
case 0:
|
||||||
|
require.NoError(t, err)
|
||||||
|
case 1:
|
||||||
|
require.ErrorContains(t, err, plugin.ExpectedErrors[0])
|
||||||
|
default:
|
||||||
|
require.Contains(t, plugin.ExpectedErrors, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine checking options
|
||||||
|
options := []cmp.Option{
|
||||||
|
testutil.SortMetrics(),
|
||||||
|
}
|
||||||
|
if plugin.ShouldIgnoreTimestamp || parser.IgnoreTimestamp {
|
||||||
|
options = append(options, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the resulting metrics
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsEqual(t, plugin.Expected, actual, options...)
|
||||||
|
|
||||||
|
// Special checks
|
||||||
|
if parser.IgnoreTimestamp {
|
||||||
|
t.Log("testing ignore-timestamp case")
|
||||||
|
for i, m := range actual {
|
||||||
|
expected := plugin.Expected[i]
|
||||||
|
require.Greaterf(t, m.Time(), expected.Time(), "metric time not after prometheus value in %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Run tests as metric version 2
|
||||||
|
t.Run(fname+"_v2", func(t *testing.T) {
|
||||||
|
// Load the configuration
|
||||||
|
cfg := config.NewConfig()
|
||||||
|
require.NoError(t, cfg.LoadConfig(configFilename))
|
||||||
|
require.Len(t, cfg.Inputs, 1)
|
||||||
|
|
||||||
|
// Tune plugin
|
||||||
|
plugin := cfg.Inputs[0].Input.(*test.Plugin)
|
||||||
|
plugin.Path = testdataPath
|
||||||
|
plugin.UseTypeTag = "_type"
|
||||||
|
plugin.ExpectedFilename = "expected_v2.out"
|
||||||
|
|
||||||
|
parser := plugin.Parser.(*models.RunningParser).Parser.(*Parser)
|
||||||
|
parser.MetricVersion = 2
|
||||||
|
if raw, found := plugin.AdditionalParams["headers"]; found {
|
||||||
|
headers, ok := raw.(map[string]interface{})
|
||||||
|
require.Truef(t, ok, "unknown header type %T", raw)
|
||||||
|
parser.Header = make(http.Header)
|
||||||
|
for k, rv := range headers {
|
||||||
|
v, ok := rv.(string)
|
||||||
|
require.Truef(t, ok, "unknown header value type %T for %q", raw, k)
|
||||||
|
parser.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Gather data and check errors
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err := plugin.Gather(&acc)
|
||||||
|
switch len(plugin.ExpectedErrors) {
|
||||||
|
case 0:
|
||||||
|
require.NoError(t, err)
|
||||||
|
case 1:
|
||||||
|
require.ErrorContains(t, err, plugin.ExpectedErrors[0])
|
||||||
|
default:
|
||||||
|
require.Contains(t, plugin.ExpectedErrors, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine checking options
|
||||||
|
options := []cmp.Option{
|
||||||
|
testutil.SortMetrics(),
|
||||||
|
}
|
||||||
|
if plugin.ShouldIgnoreTimestamp || parser.IgnoreTimestamp {
|
||||||
|
options = append(options, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the resulting metrics
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsEqual(t, plugin.Expected, actual, options...)
|
||||||
|
|
||||||
|
// Special checks
|
||||||
|
if parser.IgnoreTimestamp {
|
||||||
|
t.Log("testing ignore-timestamp case")
|
||||||
|
for i, m := range actual {
|
||||||
|
expected := plugin.Expected[i]
|
||||||
|
require.Greaterf(t, m.Time(), expected.Time(), "metric time not after prometheus value in %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkParsingMetricVersion1(b *testing.B) {
|
||||||
|
plugin := &Parser{MetricVersion: 1}
|
||||||
|
|
||||||
|
benchmarkData, err := os.ReadFile(filepath.FromSlash("testcases/benchmark/input.txt"))
|
||||||
|
require.NoError(b, err)
|
||||||
|
require.NotEmpty(b, benchmarkData)
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
_, _ = plugin.Parse(benchmarkData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkParsingMetricVersion2(b *testing.B) {
|
||||||
|
plugin := &Parser{MetricVersion: 2}
|
||||||
|
|
||||||
|
benchmarkData, err := os.ReadFile(filepath.FromSlash("testcases/benchmark/input.txt"))
|
||||||
|
require.NoError(b, err)
|
||||||
|
require.NotEmpty(b, benchmarkData)
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
_, _ = plugin.Parse(benchmarkData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
process_start_time_seconds,_type=gauge gauge=1606393397
|
||||||
|
dovecot_build,revision=38ecc424a,version=2.4.devel info=1u
|
||||||
|
dovecot_auth_success,_type=counter counter=892
|
||||||
|
dovecot_auth_success_duration_seconds,_type=counter counter=0.085479
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LIST counter=423
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LIST,tagged_reply_state=OK counter=423
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STATUS counter=468
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STATUS,tagged_reply_state=OK counter=468
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=SELECT counter=890
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=SELECT,tagged_reply_state=OK counter=890
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=APPEND counter=449
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=APPEND,tagged_reply_state=OK counter=449
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LOGOUT counter=892
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK counter=892
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=UID\ FETCH counter=888
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK counter=888
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=FETCH counter=2148
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=FETCH,tagged_reply_state=OK counter=2148
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STORE counter=794
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STORE,tagged_reply_state=OK counter=794
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=EXPUNGE counter=888
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK counter=888
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LIST counter=0.099115
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LIST,tagged_reply_state=OK counter=0.099115
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STATUS counter=0.161195
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STATUS,tagged_reply_state=OK counter=0.161195
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=SELECT counter=0.184907
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=SELECT,tagged_reply_state=OK counter=0.184907
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=APPEND counter=0.273893
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=APPEND,tagged_reply_state=OK counter=0.273893
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LOGOUT counter=0.033494
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK counter=0.033494
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=UID\ FETCH counter=0.181319
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK counter=0.181319
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=FETCH counter=1.169456
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=FETCH,tagged_reply_state=OK counter=1.169456
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STORE counter=0.368621
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STORE,tagged_reply_state=OK counter=0.368621
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=EXPUNGE counter=0.247657
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK counter=0.247657
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=LHLO,status_code=250 +Inf=1,10=0,100=1,1000=1,10000=1,100000=1,count=1,sum=0.00002
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=MAIL,status_code=250 +Inf=1,10=0,100=1,1000=1,10000=1,100000=1,count=1,sum=0.000021
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=RCPT,status_code=250 +Inf=1,10=0,100=0,1000=1,10000=1,100000=1,count=1,sum=0.000195
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=DATA,status_code=250 +Inf=1,10=0,100=0,1000=0,10000=1,100000=1,count=1,sum=0.001249
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=QUIT,status_code=221 +Inf=1,10=1,100=1,1000=1,10000=1,100000=1,count=1,sum=0.00001
|
||||||
|
dovecot_mail_delivery,_type=histogram +Inf=1,10=0,100=0,1000=1,10000=1,100000=1,count=1,sum=0.000656
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
openmetric,_type=gauge process_start_time_seconds=1606393397
|
||||||
|
openmetric,_type=untyped,version=2.4.devel,revision=38ecc424a dovecot_build_info=1u
|
||||||
|
openmetric,_type=counter dovecot_auth_success=892
|
||||||
|
openmetric,_type=counter dovecot_auth_success_duration_seconds=0.085479
|
||||||
|
openmetric,_type=counter,cmd_name=LIST dovecot_imap_command=423
|
||||||
|
openmetric,_type=counter,cmd_name=LIST,tagged_reply_state=OK dovecot_imap_command=423
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS dovecot_imap_command=468
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS,tagged_reply_state=OK dovecot_imap_command=468
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT dovecot_imap_command=890
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT,tagged_reply_state=OK dovecot_imap_command=890
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND dovecot_imap_command=449
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND,tagged_reply_state=OK dovecot_imap_command=449
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT dovecot_imap_command=892
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK dovecot_imap_command=892
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH dovecot_imap_command=2148
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH,tagged_reply_state=OK dovecot_imap_command=2148
|
||||||
|
openmetric,_type=counter,cmd_name=STORE dovecot_imap_command=794
|
||||||
|
openmetric,_type=counter,cmd_name=STORE,tagged_reply_state=OK dovecot_imap_command=794
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=LIST dovecot_imap_command_duration_seconds=0.099115
|
||||||
|
openmetric,_type=counter,cmd_name=LIST,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.099115
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS dovecot_imap_command_duration_seconds=0.161195
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.161195
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT dovecot_imap_command_duration_seconds=0.184907
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.184907
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND dovecot_imap_command_duration_seconds=0.273893
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.273893
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT dovecot_imap_command_duration_seconds=0.033494
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.033494
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH dovecot_imap_command_duration_seconds=0.181319
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.181319
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH dovecot_imap_command_duration_seconds=1.169456
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH,tagged_reply_state=OK dovecot_imap_command_duration_seconds=1.169456
|
||||||
|
openmetric,_type=counter,cmd_name=STORE dovecot_imap_command_duration_seconds=0.368621
|
||||||
|
openmetric,_type=counter,cmd_name=STORE,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.368621
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE dovecot_imap_command_duration_seconds=0.247657
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.247657
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.00002
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=100,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=1000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.000021
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=100,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=1000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.000195
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=100,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=1000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.001249
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=100,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=1000,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,status_code=221 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.00001
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=10,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=100,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=1000,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=10000,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=100000,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=+Inf,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram dovecot_mail_delivery_count=1,dovecot_mail_delivery_sum=0.000656
|
||||||
|
openmetric,_type=histogram,le=10 dovecot_mail_delivery_bucket=0
|
||||||
|
openmetric,_type=histogram,le=100 dovecot_mail_delivery_bucket=0
|
||||||
|
openmetric,_type=histogram,le=1000 dovecot_mail_delivery_bucket=1
|
||||||
|
openmetric,_type=histogram,le=10000 dovecot_mail_delivery_bucket=1
|
||||||
|
openmetric,_type=histogram,le=100000 dovecot_mail_delivery_bucket=1
|
||||||
|
openmetric,_type=histogram,le=+Inf dovecot_mail_delivery_bucket=1
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
# HELP process_start_time_seconds Timestamp of service start
|
||||||
|
# TYPE process_start_time_seconds gauge
|
||||||
|
process_start_time_seconds 1606393397
|
||||||
|
# HELP dovecot_build Dovecot build information
|
||||||
|
# TYPE dovecot_build info
|
||||||
|
dovecot_build_info{version="2.4.devel",revision="38ecc424a"} 1
|
||||||
|
# HELP dovecot_auth_success Total number of all events of this kind
|
||||||
|
# TYPE dovecot_auth_success counter
|
||||||
|
dovecot_auth_success_total 892
|
||||||
|
# HELP dovecot_auth_success_duration_seconds Total duration of all events of this kind
|
||||||
|
# TYPE dovecot_auth_success_duration_seconds counter
|
||||||
|
dovecot_auth_success_duration_seconds_total 0.085479
|
||||||
|
# HELP dovecot_imap_command Total number of all events of this kind
|
||||||
|
# TYPE dovecot_imap_command counter
|
||||||
|
dovecot_imap_command_total{cmd_name="LIST"} 423
|
||||||
|
dovecot_imap_command_total{cmd_name="LIST",tagged_reply_state="OK"} 423
|
||||||
|
dovecot_imap_command_total{cmd_name="STATUS"} 468
|
||||||
|
dovecot_imap_command_total{cmd_name="STATUS",tagged_reply_state="OK"} 468
|
||||||
|
dovecot_imap_command_total{cmd_name="SELECT"} 890
|
||||||
|
dovecot_imap_command_total{cmd_name="SELECT",tagged_reply_state="OK"} 890
|
||||||
|
dovecot_imap_command_total{cmd_name="APPEND"} 449
|
||||||
|
dovecot_imap_command_total{cmd_name="APPEND",tagged_reply_state="OK"} 449
|
||||||
|
dovecot_imap_command_total{cmd_name="LOGOUT"} 892
|
||||||
|
dovecot_imap_command_total{cmd_name="LOGOUT",tagged_reply_state="OK"} 892
|
||||||
|
dovecot_imap_command_total{cmd_name="UID FETCH"} 888
|
||||||
|
dovecot_imap_command_total{cmd_name="UID FETCH",tagged_reply_state="OK"} 888
|
||||||
|
dovecot_imap_command_total{cmd_name="FETCH"} 2148
|
||||||
|
dovecot_imap_command_total{cmd_name="FETCH",tagged_reply_state="OK"} 2148
|
||||||
|
dovecot_imap_command_total{cmd_name="STORE"} 794
|
||||||
|
dovecot_imap_command_total{cmd_name="STORE",tagged_reply_state="OK"} 794
|
||||||
|
dovecot_imap_command_total{cmd_name="EXPUNGE"} 888
|
||||||
|
dovecot_imap_command_total{cmd_name="EXPUNGE",tagged_reply_state="OK"} 888
|
||||||
|
dovecot_imap_command_count 7840
|
||||||
|
# HELP dovecot_imap_command_duration_seconds Total duration of all events of this kind
|
||||||
|
# TYPE dovecot_imap_command_duration_seconds counter
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="LIST"} 0.099115
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="LIST",tagged_reply_state="OK"} 0.099115
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="STATUS"} 0.161195
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="STATUS",tagged_reply_state="OK"} 0.161195
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="SELECT"} 0.184907
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="SELECT",tagged_reply_state="OK"} 0.184907
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="APPEND"} 0.273893
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="APPEND",tagged_reply_state="OK"} 0.273893
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="LOGOUT"} 0.033494
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="LOGOUT",tagged_reply_state="OK"} 0.033494
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="UID FETCH"} 0.181319
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="UID FETCH",tagged_reply_state="OK"} 0.181319
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="FETCH"} 1.169456
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="FETCH",tagged_reply_state="OK"} 1.169456
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="STORE"} 0.368621
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="STORE",tagged_reply_state="OK"} 0.368621
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="EXPUNGE"} 0.247657
|
||||||
|
dovecot_imap_command_duration_seconds_total{cmd_name="EXPUNGE",tagged_reply_state="OK"} 0.247657
|
||||||
|
dovecot_imap_command_duration_seconds_sum 2.719657
|
||||||
|
# HELP dovecot_smtp_command Histogram
|
||||||
|
# TYPE dovecot_smtp_command histogram
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="10"} 0
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="100"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="1000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="10000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="100000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="+Inf"} 1
|
||||||
|
dovecot_smtp_command_sum{cmd_name="LHLO",status_code="250"} 0.000020
|
||||||
|
dovecot_smtp_command_count{cmd_name="LHLO",status_code="250"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="10"} 0
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="100"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="1000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="10000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="100000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="+Inf"} 1
|
||||||
|
dovecot_smtp_command_sum{cmd_name="MAIL",status_code="250"} 0.000021
|
||||||
|
dovecot_smtp_command_count{cmd_name="MAIL",status_code="250"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="10"} 0
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="100"} 0
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="1000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="10000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="100000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="+Inf"} 1
|
||||||
|
dovecot_smtp_command_sum{cmd_name="RCPT",status_code="250"} 0.000195
|
||||||
|
dovecot_smtp_command_count{cmd_name="RCPT",status_code="250"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="10"} 0
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="100"} 0
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="1000"} 0
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="10000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="100000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="+Inf"} 1
|
||||||
|
dovecot_smtp_command_sum{cmd_name="DATA",status_code="250"} 0.001249
|
||||||
|
dovecot_smtp_command_count{cmd_name="DATA",status_code="250"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="10"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="100"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="1000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="10000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="100000"} 1
|
||||||
|
dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="+Inf"} 1
|
||||||
|
dovecot_smtp_command_sum{cmd_name="QUIT",status_code="221"} 0.000010
|
||||||
|
dovecot_smtp_command_count{cmd_name="QUIT",status_code="221"} 1
|
||||||
|
# HELP dovecot_mail_delivery Histogram
|
||||||
|
# TYPE dovecot_mail_delivery histogram
|
||||||
|
dovecot_mail_delivery_bucket{le="10"} 0
|
||||||
|
dovecot_mail_delivery_bucket{le="100"} 0
|
||||||
|
dovecot_mail_delivery_bucket{le="1000"} 1
|
||||||
|
dovecot_mail_delivery_bucket{le="10000"} 1
|
||||||
|
dovecot_mail_delivery_bucket{le="100000"} 1
|
||||||
|
dovecot_mail_delivery_bucket{le="+Inf"} 1
|
||||||
|
dovecot_mail_delivery_sum 0.000656
|
||||||
|
dovecot_mail_delivery_count 1
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
acme_http_router_request_seconds,_type=summary,unit=seconds,path=/api/v1,method=GET sum=9036.32,count=807283.0,created=1605281325.0,0.5=1.29854,0.9=54.85479,0.99=6884.32324
|
||||||
|
acme_http_router_request_seconds,_type=summary,unit=seconds,path=/api/v2,method=POST sum=479.3,count=34.0,created=1605281325.0,0.5=0.85412,0.9=1.15429,0.99=3698.48132
|
||||||
|
go_goroutines,_type=gauge gauge=69
|
||||||
|
process_cpu_seconds,_type=counter,unit=seconds counter=4200722.46
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
openmetric,_type=summary,method=GET,path=/api/v1,unit=seconds acme_http_router_request_seconds_count=807283,acme_http_router_request_seconds_created=1605281325,acme_http_router_request_seconds_sum=9036.32
|
||||||
|
openmetric,_type=summary,method=GET,path=/api/v1,quantile=0.5,unit=seconds acme_http_router_request_seconds=1.29854
|
||||||
|
openmetric,_type=summary,method=GET,path=/api/v1,quantile=0.9,unit=seconds acme_http_router_request_seconds=54.85479
|
||||||
|
openmetric,_type=summary,method=GET,path=/api/v1,quantile=0.99,unit=seconds acme_http_router_request_seconds=6884.32324
|
||||||
|
openmetric,_type=summary,method=POST,path=/api/v2,unit=seconds acme_http_router_request_seconds_count=34,acme_http_router_request_seconds_created=1605281325,acme_http_router_request_seconds_sum=479.3
|
||||||
|
openmetric,_type=summary,method=POST,path=/api/v2,quantile=0.5,unit=seconds acme_http_router_request_seconds=0.85412
|
||||||
|
openmetric,_type=summary,method=POST,path=/api/v2,quantile=0.9,unit=seconds acme_http_router_request_seconds=1.15429
|
||||||
|
openmetric,_type=summary,method=POST,path=/api/v2,quantile=0.99,unit=seconds acme_http_router_request_seconds=3698.48132
|
||||||
|
openmetric,_type=gauge go_goroutines=69
|
||||||
|
openmetric,_type=counter,unit=seconds process_cpu_seconds=4200722.46
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# TYPE acme_http_router_request_seconds summary
|
||||||
|
# UNIT acme_http_router_request_seconds seconds
|
||||||
|
# HELP acme_http_router_request_seconds Latency though all of ACME's HTTP request router.
|
||||||
|
acme_http_router_request_seconds_sum{path="/api/v1",method="GET"} 9036.32
|
||||||
|
acme_http_router_request_seconds_count{path="/api/v1",method="GET"} 807283.0
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.5"} 1.29854
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.9"} 54.85479
|
||||||
|
acme_http_router_request_seconds{path="/api/v1",method="GET",quantile="0.99"} 6884.32324
|
||||||
|
acme_http_router_request_seconds_created{path="/api/v1",method="GET"} 1605281325.0
|
||||||
|
acme_http_router_request_seconds_sum{path="/api/v2",method="POST"} 479.3
|
||||||
|
acme_http_router_request_seconds_count{path="/api/v2",method="POST"} 34.0
|
||||||
|
acme_http_router_request_seconds_created{path="/api/v2",method="POST"} 1605281325.0
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.5"} 0.85412
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.9"} 1.15429
|
||||||
|
acme_http_router_request_seconds{path="/api/v2",method="POST",quantile="0.99"} 3698.48132
|
||||||
|
# TYPE go_goroutines gauge
|
||||||
|
# HELP go_goroutines Number of goroutines that currently exist.
|
||||||
|
go_goroutines 69
|
||||||
|
# TYPE process_cpu_seconds counter
|
||||||
|
# UNIT process_cpu_seconds seconds
|
||||||
|
# HELP process_cpu_seconds Total user and system CPU time spent in seconds.
|
||||||
|
process_cpu_seconds_total 4.20072246e+06
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
process_start_time_seconds,_type=gauge gauge=1606393397
|
||||||
|
dovecot_build,revision=38ecc424a,version=2.4.devel info=1u
|
||||||
|
dovecot_auth_success,_type=counter counter=892
|
||||||
|
dovecot_auth_success_duration_seconds,_type=counter counter=0.085479
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LIST counter=423
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LIST,tagged_reply_state=OK counter=423
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STATUS counter=468
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STATUS,tagged_reply_state=OK counter=468
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=SELECT counter=890
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=SELECT,tagged_reply_state=OK counter=890
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=APPEND counter=449
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=APPEND,tagged_reply_state=OK counter=449
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LOGOUT counter=892
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK counter=892
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=UID\ FETCH counter=888
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK counter=888
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=FETCH counter=2148
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=FETCH,tagged_reply_state=OK counter=2148
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STORE counter=794
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=STORE,tagged_reply_state=OK counter=794
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=EXPUNGE counter=888
|
||||||
|
dovecot_imap_command,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK counter=888
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LIST counter=0.099115
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LIST,tagged_reply_state=OK counter=0.099115
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STATUS counter=0.161195
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STATUS,tagged_reply_state=OK counter=0.161195
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=SELECT counter=0.184907
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=SELECT,tagged_reply_state=OK counter=0.184907
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=APPEND counter=0.273893
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=APPEND,tagged_reply_state=OK counter=0.273893
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LOGOUT counter=0.033494
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK counter=0.033494
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=UID\ FETCH counter=0.181319
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK counter=0.181319
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=FETCH counter=1.169456
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=FETCH,tagged_reply_state=OK counter=1.169456
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STORE counter=0.368621
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=STORE,tagged_reply_state=OK counter=0.368621
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=EXPUNGE counter=0.247657
|
||||||
|
dovecot_imap_command_duration_seconds,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK counter=0.247657
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=LHLO,status_code=250 +Inf=1,10=0,100=1,1000=1,10000=1,100000=1,count=1,sum=0.00002
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=MAIL,status_code=250 +Inf=1,10=0,100=1,1000=1,10000=1,100000=1,count=1,sum=0.000021
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=RCPT,status_code=250 +Inf=1,10=0,100=0,1000=1,10000=1,100000=1,count=1,sum=0.000195
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=DATA,status_code=250 +Inf=1,10=0,100=0,1000=0,10000=1,100000=1,count=1,sum=0.001249
|
||||||
|
dovecot_smtp_command,_type=histogram,cmd_name=QUIT,status_code=221 +Inf=1,10=1,100=1,1000=1,10000=1,100000=1,count=1,sum=0.00001
|
||||||
|
dovecot_mail_delivery,_type=histogram +Inf=1,10=0,100=0,1000=1,10000=1,100000=1,count=1,sum=0.000656
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
openmetric,_type=gauge process_start_time_seconds=1606393397
|
||||||
|
openmetric,_type=untyped,version=2.4.devel,revision=38ecc424a dovecot_build_info=1u
|
||||||
|
openmetric,_type=counter dovecot_auth_success=892
|
||||||
|
openmetric,_type=counter dovecot_auth_success_duration_seconds=0.085479
|
||||||
|
openmetric,_type=counter,cmd_name=LIST dovecot_imap_command=423
|
||||||
|
openmetric,_type=counter,cmd_name=LIST,tagged_reply_state=OK dovecot_imap_command=423
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS dovecot_imap_command=468
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS,tagged_reply_state=OK dovecot_imap_command=468
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT dovecot_imap_command=890
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT,tagged_reply_state=OK dovecot_imap_command=890
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND dovecot_imap_command=449
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND,tagged_reply_state=OK dovecot_imap_command=449
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT dovecot_imap_command=892
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK dovecot_imap_command=892
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH dovecot_imap_command=2148
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH,tagged_reply_state=OK dovecot_imap_command=2148
|
||||||
|
openmetric,_type=counter,cmd_name=STORE dovecot_imap_command=794
|
||||||
|
openmetric,_type=counter,cmd_name=STORE,tagged_reply_state=OK dovecot_imap_command=794
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK dovecot_imap_command=888
|
||||||
|
openmetric,_type=counter,cmd_name=LIST dovecot_imap_command_duration_seconds=0.099115
|
||||||
|
openmetric,_type=counter,cmd_name=LIST,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.099115
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS dovecot_imap_command_duration_seconds=0.161195
|
||||||
|
openmetric,_type=counter,cmd_name=STATUS,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.161195
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT dovecot_imap_command_duration_seconds=0.184907
|
||||||
|
openmetric,_type=counter,cmd_name=SELECT,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.184907
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND dovecot_imap_command_duration_seconds=0.273893
|
||||||
|
openmetric,_type=counter,cmd_name=APPEND,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.273893
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT dovecot_imap_command_duration_seconds=0.033494
|
||||||
|
openmetric,_type=counter,cmd_name=LOGOUT,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.033494
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH dovecot_imap_command_duration_seconds=0.181319
|
||||||
|
openmetric,_type=counter,cmd_name=UID\ FETCH,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.181319
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH dovecot_imap_command_duration_seconds=1.169456
|
||||||
|
openmetric,_type=counter,cmd_name=FETCH,tagged_reply_state=OK dovecot_imap_command_duration_seconds=1.169456
|
||||||
|
openmetric,_type=counter,cmd_name=STORE dovecot_imap_command_duration_seconds=0.368621
|
||||||
|
openmetric,_type=counter,cmd_name=STORE,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.368621
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE dovecot_imap_command_duration_seconds=0.247657
|
||||||
|
openmetric,_type=counter,cmd_name=EXPUNGE,tagged_reply_state=OK dovecot_imap_command_duration_seconds=0.247657
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.00002
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=100,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=1000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=LHLO,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.000021
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=100,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=1000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=MAIL,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.000195
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=100,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=1000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=RCPT,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,status_code=250 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.001249
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=10,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=100,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=1000,status_code=250 dovecot_smtp_command_bucket=0
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=10000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=100000,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=DATA,le=+Inf,status_code=250 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,status_code=221 dovecot_smtp_command_count=1,dovecot_smtp_command_sum=0.00001
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=10,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=100,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=1000,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=10000,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=100000,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram,cmd_name=QUIT,le=+Inf,status_code=221 dovecot_smtp_command_bucket=1
|
||||||
|
openmetric,_type=histogram dovecot_mail_delivery_count=1,dovecot_mail_delivery_sum=0.000656
|
||||||
|
openmetric,_type=histogram,le=10 dovecot_mail_delivery_bucket=0
|
||||||
|
openmetric,_type=histogram,le=100 dovecot_mail_delivery_bucket=0
|
||||||
|
openmetric,_type=histogram,le=1000 dovecot_mail_delivery_bucket=1
|
||||||
|
openmetric,_type=histogram,le=10000 dovecot_mail_delivery_bucket=1
|
||||||
|
openmetric,_type=histogram,le=100000 dovecot_mail_delivery_bucket=1
|
||||||
|
openmetric,_type=histogram,le=+Inf dovecot_mail_delivery_bucket=1
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,6 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.bin"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
[inputs.test.additional_params]
|
||||||
|
headers = {Content-Type = "application/openmetrics-protobuf;version=1.0.0"}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
device_versioning,_type=untyped,location=office,device=machine,version=1.29.2,environment=production,build=42 info=1u
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
openmetric,_type=untyped,location=office,device=machine,version=1.29.2,environment=production,build=42 device_versioning_info=1u
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
‘
|
||||||
|
device_versioning"A test info metric*f
|
||||||
|
|
||||||
|
locationoffice
|
||||||
|
|
||||||
|
devicemachine=2;
|
||||||
|
|
||||||
|
version1.29.2
|
||||||
|
|
||||||
|
environment
|
||||||
|
production
|
||||||
|
|
||||||
|
build42
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.bin"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
[inputs.test.additional_params]
|
||||||
|
headers = {Content-Type = "application/openmetrics-protobuf;version=1.0.0"}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
get_token_fail_count,_type=counter counter=8
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
openmetric,_type=counter get_token_fail_count=8
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# TYPE get_token_fail_count counter
|
||||||
|
# HELP get_token_fail_count help
|
||||||
|
get_token_fail_count_total 8 123 # {a="b"} 0.5
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
go_memstats_gc_cpu_fraction,_type=gauge gauge=-0.00014404354379774563
|
||||||
|
go_memstats_gc_sys_bytes,_type=gauge gauge=60936192
|
||||||
|
go_memstats_heap_alloc_bytes,_type=gauge gauge=1581062048
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
openmetric,_type=gauge go_memstats_gc_cpu_fraction=-0.00014404354379774563
|
||||||
|
openmetric,_type=gauge go_memstats_gc_sys_bytes=60936192
|
||||||
|
openmetric,_type=gauge go_memstats_heap_alloc_bytes=1581062048
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started.
|
||||||
|
# TYPE go_memstats_gc_cpu_fraction gauge
|
||||||
|
go_memstats_gc_cpu_fraction -0.00014404354379774563
|
||||||
|
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
|
||||||
|
# TYPE go_memstats_gc_sys_bytes gauge
|
||||||
|
go_memstats_gc_sys_bytes 6.0936192e+07
|
||||||
|
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
|
||||||
|
# TYPE go_memstats_heap_alloc_bytes gauge
|
||||||
|
go_memstats_heap_alloc_bytes 1.581062048e+09
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
apiserver_request_latencies_microseconds,_type=histogram,unit=microseconds,resource=bindings,verb=POST +Inf=2025,125000=1994,1e+06=2005,250000=1997,2e+06=2012,4e+06=2017,500000=2000,8e+06=2024,count=2025,sum=102726334
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
openmetric,_type=histogram,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_count=2025,apiserver_request_latencies_microseconds_sum=102726334
|
||||||
|
openmetric,_type=histogram,le=125000,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=1994
|
||||||
|
openmetric,_type=histogram,le=250000,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=1997
|
||||||
|
openmetric,_type=histogram,le=500000,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2000
|
||||||
|
openmetric,_type=histogram,le=1e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2005
|
||||||
|
openmetric,_type=histogram,le=2e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2012
|
||||||
|
openmetric,_type=histogram,le=4e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2017
|
||||||
|
openmetric,_type=histogram,le=8e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2024
|
||||||
|
openmetric,_type=histogram,le=+Inf,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2025
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
# HELP apiserver_request_latencies_microseconds Response latency distribution in microseconds for each verb, resource and client.
|
||||||
|
# TYPE apiserver_request_latencies_microseconds histogram
|
||||||
|
# UNIT apiserver_request_latencies_microseconds microseconds
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="125000"} 1994
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="250000"} 1997
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="500000"} 2000
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="1e+06"} 2005
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="2e+06"} 2012
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="4e+06"} 2017
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="8e+06"} 2024
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="+Inf"} 2025
|
||||||
|
apiserver_request_latencies_microseconds_sum{resource="bindings",verb="POST"} 1.02726334e+08
|
||||||
|
apiserver_request_latencies_microseconds_count{resource="bindings",verb="POST"} 2025
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
apiserver_request_latencies_microseconds,_type=histogram,unit=microseconds,resource=bindings,verb=POST +Inf=2025,125000=1994,1e+06=2005,250000=1997,2e+06=2012,4e+06=2017,500000=2000,8e+06=2024,count=2025,sum=102726334
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
openmetric,_type=histogram,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_count=2025,apiserver_request_latencies_microseconds_sum=102726334
|
||||||
|
openmetric,_type=histogram,le=125000,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=1994
|
||||||
|
openmetric,_type=histogram,le=250000,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=1997
|
||||||
|
openmetric,_type=histogram,le=500000,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2000
|
||||||
|
openmetric,_type=histogram,le=1e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2005
|
||||||
|
openmetric,_type=histogram,le=2e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2012
|
||||||
|
openmetric,_type=histogram,le=4e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2017
|
||||||
|
openmetric,_type=histogram,le=8e+06,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2024
|
||||||
|
openmetric,_type=histogram,le=+Inf,unit=microseconds,resource=bindings,verb=POST apiserver_request_latencies_microseconds_bucket=2025
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
# HELP apiserver_request_latencies_microseconds Response latency distribution in microseconds for each verb, resource and client.
|
||||||
|
# TYPE apiserver_request_latencies_microseconds histogram
|
||||||
|
# UNIT apiserver_request_latencies_microseconds microseconds
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="125000"} 1994
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="250000"} 1997
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="500000"} 2000
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="1e+06"} 2005
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="2e+06"} 2012
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="4e+06"} 2017
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="8e+06"} 2024
|
||||||
|
apiserver_request_latencies_microseconds_bucket{resource="bindings",verb="POST",le="+Inf"} 2025
|
||||||
|
apiserver_request_latencies_microseconds_sum{resource="bindings",verb="POST"} 1.02726334e+08
|
||||||
|
apiserver_request_latencies_microseconds_count{resource="bindings",verb="POST"} 2025
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
cadvisor_version,_type=untyped,dockerVersion=1.8.2,kernelVersion=3.10.0-229.20.1.el7.x86_64,osVersion=CentOS\ Linux\ 7\ (Core) info=1u
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
openmetric,_type=untyped,dockerVersion=1.8.2,kernelVersion=3.10.0-229.20.1.el7.x86_64,osVersion=CentOS\ Linux\ 7\ (Core) cadvisor_version_info=1u
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# HELP cadvisor_version A metric with a constant '1' value labeled by kernel version, OS version, docker version, cadvisor version & cadvisor revision.
|
||||||
|
# TYPE cadvisor_version info
|
||||||
|
cadvisor_version{cadvisorRevision="",cadvisorVersion="",dockerVersion="1.8.2",kernelVersion="3.10.0-229.20.1.el7.x86_64",osVersion="CentOS Linux 7 (Core)"} 1
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
alarm_system_state,_type=untyped,entity=ground\ floor door_ok=true,window_1_ok=true,window_2_ok=false
|
||||||
|
alarm_system_state,_type=untyped,entity=basement window_1_ok=true,window_2_ok=true,window_3_ok=true
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
openmetric,_type=untyped,entity=ground\ floor alarm_system_state_door_ok=true
|
||||||
|
openmetric,_type=untyped,entity=ground\ floor alarm_system_state_window_1_ok=true
|
||||||
|
openmetric,_type=untyped,entity=ground\ floor alarm_system_state_window_2_ok=false
|
||||||
|
openmetric,_type=untyped,entity=basement alarm_system_state_window_1_ok=true
|
||||||
|
openmetric,_type=untyped,entity=basement alarm_system_state_window_2_ok=true
|
||||||
|
openmetric,_type=untyped,entity=basement alarm_system_state_window_3_ok=true
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# TYPE alarm_system_state stateset
|
||||||
|
alarm_system_state{entity="ground floor",alarm_system_state="door ok"} 1.0
|
||||||
|
alarm_system_state{entity="ground floor",alarm_system_state="window 1 ok"} 1.0
|
||||||
|
alarm_system_state{entity="ground floor",alarm_system_state="window 2 ok"} 0.0
|
||||||
|
alarm_system_state{entity="basement",alarm_system_state="window 1 ok"} 1.0
|
||||||
|
alarm_system_state{entity="basement",alarm_system_state="window 2 ok"} 1.0
|
||||||
|
alarm_system_state{entity="basement",alarm_system_state="window 3 ok"} 1.0
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
http_request_duration_microseconds,_type=summary,unit=microseconds,handler=prometheus 0.5=552048.506,0.9=5876804.288,0.99=5876804.288,count=9,sum=18909097.205,created=1705509488.3
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
openmetric,_type=summary,unit=microseconds,handler=prometheus http_request_duration_microseconds_count=9,http_request_duration_microseconds_sum=18909097.205,http_request_duration_microseconds_created=2005509488
|
||||||
|
openmetric,_type=summary,unit=microseconds,handler=prometheus,quantile=0.5 http_request_duration_microseconds=552048.506
|
||||||
|
openmetric,_type=summary,unit=microseconds,handler=prometheus,quantile=0.9 http_request_duration_microseconds=5876804.288
|
||||||
|
openmetric,_type=summary,unit=microseconds,handler=prometheus,quantile=0.99 http_request_duration_microseconds=5876804.288
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# TYPE http_request_duration_microseconds summary
|
||||||
|
# UNIT http_request_duration_microseconds microseconds
|
||||||
|
# HELP http_request_duration_microseconds The HTTP request latencies in microseconds.
|
||||||
|
http_request_duration_microseconds{handler="prometheus",quantile="0.5"} 552048.506
|
||||||
|
http_request_duration_microseconds{handler="prometheus",quantile="0.9"} 5.876804288e+06
|
||||||
|
http_request_duration_microseconds{handler="prometheus",quantile="0.99"} 5.876804288e+06
|
||||||
|
http_request_duration_microseconds_created{handler="prometheus"} 1705509488.3
|
||||||
|
http_request_duration_microseconds_sum{handler="prometheus"} 1.8909097205e+07
|
||||||
|
http_request_duration_microseconds_count{handler="prometheus"} 9
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
redis_connected_clients,_type=untyped,instance=rough-snowflake-web,port=6380 value=10.0
|
||||||
|
redis_connected_clients,_type=untyped,instance=rough-snowflake-web,port=6381 value=12.0
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
openmetric,_type=untyped,instance=rough-snowflake-web,port=6380 redis_connected_clients=10.0
|
||||||
|
openmetric,_type=untyped,instance=rough-snowflake-web,port=6381 redis_connected_clients=12.0
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# HELP redis_connected_clients Redis connected clients
|
||||||
|
# TYPE redis_connected_clients unknown
|
||||||
|
redis_connected_clients{instance="rough-snowflake-web",port="6380"} 10.0
|
||||||
|
redis_connected_clients{instance="rough-snowflake-web",port="6381"} 12.0
|
||||||
|
# EOF
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
[[inputs.test]]
|
||||||
|
files = ["input.txt"]
|
||||||
|
data_format = "openmetrics"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,393 @@
|
||||||
|
package openmetrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"hash/maphash"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/model/labels"
|
||||||
|
"github.com/prometheus/prometheus/model/textparse"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TextToMetricFamilies(data []byte) ([]*MetricFamily, error) {
|
||||||
|
var metrics []*MetricFamily
|
||||||
|
|
||||||
|
parser := textparse.NewOpenMetricsParser(data)
|
||||||
|
|
||||||
|
seed := maphash.MakeSeed()
|
||||||
|
mf := &MetricFamily{}
|
||||||
|
mfMetric := &Metric{}
|
||||||
|
mfMetricKey := uint64(0)
|
||||||
|
mfMetricPoint := &MetricPoint{}
|
||||||
|
for {
|
||||||
|
entry, err := parser.Next()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
if mf.Name != "" {
|
||||||
|
if mfMetricPoint.Value != nil {
|
||||||
|
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||||
|
}
|
||||||
|
if len(mfMetric.MetricPoints) > 0 {
|
||||||
|
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||||
|
}
|
||||||
|
metrics = append(metrics, mf)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("parsing failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch entry {
|
||||||
|
case textparse.EntryInvalid:
|
||||||
|
continue
|
||||||
|
case textparse.EntryType:
|
||||||
|
name, mtype := parser.Type()
|
||||||
|
if len(name) == 0 {
|
||||||
|
return nil, errors.New("empty metric-family name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mf.Name == "" {
|
||||||
|
mf.Name = string(name)
|
||||||
|
} else if mf.Name != string(name) {
|
||||||
|
if mfMetricPoint.Value != nil {
|
||||||
|
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||||
|
}
|
||||||
|
if len(mfMetric.MetricPoints) > 0 {
|
||||||
|
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||||
|
}
|
||||||
|
metrics = append(metrics, mf)
|
||||||
|
mf = &MetricFamily{Name: string(name)}
|
||||||
|
mfMetric = &Metric{}
|
||||||
|
mfMetricKey = 0
|
||||||
|
mfMetricPoint = &MetricPoint{}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mtype {
|
||||||
|
case textparse.MetricTypeCounter:
|
||||||
|
mf.Type = MetricType_COUNTER
|
||||||
|
case textparse.MetricTypeGauge:
|
||||||
|
mf.Type = MetricType_GAUGE
|
||||||
|
case textparse.MetricTypeHistogram:
|
||||||
|
mf.Type = MetricType_HISTOGRAM
|
||||||
|
case textparse.MetricTypeGaugeHistogram:
|
||||||
|
mf.Type = MetricType_GAUGE_HISTOGRAM
|
||||||
|
case textparse.MetricTypeSummary:
|
||||||
|
mf.Type = MetricType_SUMMARY
|
||||||
|
case textparse.MetricTypeInfo:
|
||||||
|
mf.Type = MetricType_INFO
|
||||||
|
case textparse.MetricTypeStateset:
|
||||||
|
mf.Type = MetricType_STATE_SET
|
||||||
|
case textparse.MetricTypeUnknown:
|
||||||
|
mf.Type = MetricType_UNKNOWN
|
||||||
|
}
|
||||||
|
case textparse.EntryHelp:
|
||||||
|
name, mhelp := parser.Help()
|
||||||
|
if len(name) == 0 {
|
||||||
|
return nil, errors.New("empty metric-family name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mf.Name == "" {
|
||||||
|
mf.Name = string(name)
|
||||||
|
} else if mf.Name != string(name) {
|
||||||
|
if mfMetricPoint.Value != nil {
|
||||||
|
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||||
|
}
|
||||||
|
if len(mfMetric.MetricPoints) > 0 {
|
||||||
|
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||||
|
}
|
||||||
|
metrics = append(metrics, mf)
|
||||||
|
mf = &MetricFamily{Name: string(name)}
|
||||||
|
mfMetric = &Metric{}
|
||||||
|
mfMetricKey = 0
|
||||||
|
mfMetricPoint = &MetricPoint{}
|
||||||
|
}
|
||||||
|
mf.Help = string(mhelp)
|
||||||
|
case textparse.EntrySeries:
|
||||||
|
series, ts, value := parser.Series()
|
||||||
|
|
||||||
|
// Extract the metric name and labels
|
||||||
|
dn, _, _ := bytes.Cut(series, []byte("{"))
|
||||||
|
if len(dn) == 0 {
|
||||||
|
return nil, errors.New("empty metric name")
|
||||||
|
}
|
||||||
|
sampleName := string(dn)
|
||||||
|
|
||||||
|
var metricLabels labels.Labels
|
||||||
|
parser.Metric(&metricLabels)
|
||||||
|
|
||||||
|
// There might be metrics without meta-information, however in this
|
||||||
|
// case the metric is of type UNKNOWN according to the spec and do
|
||||||
|
// only contain a single metric. Therefore, we can use the metric
|
||||||
|
// name as metric-family name
|
||||||
|
if mf.Name == "" {
|
||||||
|
mf.Name = sampleName
|
||||||
|
}
|
||||||
|
|
||||||
|
// The name contained in the sample is constructed using the metric
|
||||||
|
// name and an optional sample-type suffix used for more complex
|
||||||
|
// types (e.g. histograms).
|
||||||
|
sampleType, seriesLabels := extractSampleType(sampleName, mf.Name, mf.Type, &metricLabels)
|
||||||
|
|
||||||
|
// Check if we are still in the same metric, if not, add the
|
||||||
|
// previous one to the metric family and create a new one...
|
||||||
|
key := getSeriesKey(seriesLabels, seed)
|
||||||
|
if mfMetricKey != key {
|
||||||
|
if mfMetricPoint.Value != nil {
|
||||||
|
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||||
|
}
|
||||||
|
if len(mfMetric.MetricPoints) > 0 {
|
||||||
|
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||||
|
}
|
||||||
|
mfMetric = &Metric{}
|
||||||
|
mfMetricKey = key
|
||||||
|
mfMetricPoint = &MetricPoint{}
|
||||||
|
mfMetric.Labels = make([]*Label, 0, len(*seriesLabels))
|
||||||
|
for _, l := range *seriesLabels {
|
||||||
|
mfMetric.Labels = append(mfMetric.Labels, &Label{
|
||||||
|
Name: l.Name,
|
||||||
|
Value: l.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we are still in the same metric-point
|
||||||
|
var mpTimestamp int64
|
||||||
|
if mfMetricPoint.Timestamp != nil {
|
||||||
|
mpTimestamp = mfMetricPoint.Timestamp.Seconds * int64(time.Second)
|
||||||
|
mpTimestamp += int64(mfMetricPoint.Timestamp.Nanos)
|
||||||
|
}
|
||||||
|
var timestamp int64
|
||||||
|
if ts != nil {
|
||||||
|
timestamp = *ts * int64(time.Millisecond)
|
||||||
|
}
|
||||||
|
if mpTimestamp != timestamp {
|
||||||
|
if mfMetricPoint.Value != nil {
|
||||||
|
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||||
|
}
|
||||||
|
mfMetricPoint = &MetricPoint{}
|
||||||
|
if ts != nil {
|
||||||
|
mfMetricPoint.Timestamp = timestamppb.New(time.Unix(0, timestamp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the metric-point
|
||||||
|
mfMetricPoint.set(mf.Name, mf.Type, sampleType, value, &metricLabels)
|
||||||
|
case textparse.EntryComment:
|
||||||
|
// ignore comments
|
||||||
|
case textparse.EntryUnit:
|
||||||
|
name, munit := parser.Unit()
|
||||||
|
if len(name) == 0 {
|
||||||
|
return nil, errors.New("empty metric-family name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mf.Name == "" {
|
||||||
|
mf.Name = string(name)
|
||||||
|
} else if mf.Name != string(name) {
|
||||||
|
if mfMetricPoint.Value != nil {
|
||||||
|
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||||
|
}
|
||||||
|
if len(mfMetric.MetricPoints) > 0 {
|
||||||
|
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||||
|
}
|
||||||
|
metrics = append(metrics, mf)
|
||||||
|
mf = &MetricFamily{Name: string(name)}
|
||||||
|
mfMetric = &Metric{}
|
||||||
|
mfMetricKey = 0
|
||||||
|
mfMetricPoint = &MetricPoint{}
|
||||||
|
}
|
||||||
|
mf.Unit = string(munit)
|
||||||
|
case textparse.EntryHistogram:
|
||||||
|
// not supported yet
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown entry type %v", entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metrics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSeriesKey(seriesLabels *labels.Labels, seed maphash.Seed) uint64 {
|
||||||
|
sorted := make([]string, 0, len(*seriesLabels))
|
||||||
|
for _, l := range *seriesLabels {
|
||||||
|
sorted = append(sorted, l.Name+"="+l.Value)
|
||||||
|
}
|
||||||
|
slices.Sort(sorted)
|
||||||
|
|
||||||
|
var h maphash.Hash
|
||||||
|
h.SetSeed(seed)
|
||||||
|
for _, p := range sorted {
|
||||||
|
h.WriteString(p)
|
||||||
|
h.WriteByte(0)
|
||||||
|
}
|
||||||
|
return h.Sum64()
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractSampleType(raw, name string, mtype MetricType, metricLabels *labels.Labels) (string, *labels.Labels) {
|
||||||
|
suffix := strings.TrimLeft(strings.TrimPrefix(raw, name), "_")
|
||||||
|
var seriesLabels labels.Labels
|
||||||
|
for _, l := range *metricLabels {
|
||||||
|
// filter out special labels
|
||||||
|
switch {
|
||||||
|
case l.Name == "__name__":
|
||||||
|
case mtype == MetricType_STATE_SET && l.Name == name:
|
||||||
|
case mtype == MetricType_HISTOGRAM && l.Name == "le":
|
||||||
|
case mtype == MetricType_GAUGE_HISTOGRAM && l.Name == "le":
|
||||||
|
case mtype == MetricType_SUMMARY && l.Name == "quantile":
|
||||||
|
default:
|
||||||
|
seriesLabels = append(seriesLabels, labels.Label{Name: l.Name, Value: l.Value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return suffix, &seriesLabels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mp *MetricPoint) set(mname string, mtype MetricType, stype string, value float64, mlabels *labels.Labels) {
|
||||||
|
switch mtype {
|
||||||
|
case MetricType_UNKNOWN:
|
||||||
|
mp.Value = &MetricPoint_UnknownValue{
|
||||||
|
UnknownValue: &UnknownValue{
|
||||||
|
Value: &UnknownValue_DoubleValue{DoubleValue: value},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case MetricType_GAUGE:
|
||||||
|
mp.Value = &MetricPoint_GaugeValue{
|
||||||
|
GaugeValue: &GaugeValue{
|
||||||
|
Value: &GaugeValue_DoubleValue{DoubleValue: value},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case MetricType_COUNTER:
|
||||||
|
var v *MetricPoint_CounterValue
|
||||||
|
if mp.Value != nil {
|
||||||
|
v = mp.Value.(*MetricPoint_CounterValue)
|
||||||
|
} else {
|
||||||
|
v = &MetricPoint_CounterValue{
|
||||||
|
CounterValue: &CounterValue{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch stype {
|
||||||
|
case "total":
|
||||||
|
v.CounterValue.Total = &CounterValue_DoubleValue{DoubleValue: value}
|
||||||
|
case "created":
|
||||||
|
t := time.Unix(0, int64(value*float64(time.Second)))
|
||||||
|
v.CounterValue.Created = timestamppb.New(t)
|
||||||
|
}
|
||||||
|
mp.Value = v
|
||||||
|
case MetricType_STATE_SET:
|
||||||
|
var v *MetricPoint_StateSetValue
|
||||||
|
if mp.Value != nil {
|
||||||
|
v = mp.Value.(*MetricPoint_StateSetValue)
|
||||||
|
} else {
|
||||||
|
v = &MetricPoint_StateSetValue{
|
||||||
|
StateSetValue: &StateSetValue{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var name string
|
||||||
|
for _, l := range *mlabels {
|
||||||
|
if l.Name == mname {
|
||||||
|
name = l.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.StateSetValue.States = append(v.StateSetValue.States, &StateSetValue_State{
|
||||||
|
Enabled: value > 0,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
mp.Value = v
|
||||||
|
case MetricType_INFO:
|
||||||
|
mp.Value = &MetricPoint_InfoValue{
|
||||||
|
InfoValue: &InfoValue{},
|
||||||
|
}
|
||||||
|
case MetricType_HISTOGRAM, MetricType_GAUGE_HISTOGRAM:
|
||||||
|
var v *MetricPoint_HistogramValue
|
||||||
|
if mp.Value != nil {
|
||||||
|
v = mp.Value.(*MetricPoint_HistogramValue)
|
||||||
|
} else {
|
||||||
|
v = &MetricPoint_HistogramValue{
|
||||||
|
HistogramValue: &HistogramValue{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch stype {
|
||||||
|
case "sum", "gsum":
|
||||||
|
v.HistogramValue.Sum = &HistogramValue_DoubleValue{DoubleValue: value}
|
||||||
|
case "count", "gcount":
|
||||||
|
v.HistogramValue.Count = uint64(value)
|
||||||
|
case "created":
|
||||||
|
t := time.Unix(0, int64(value*float64(time.Second)))
|
||||||
|
v.HistogramValue.Created = timestamppb.New(t)
|
||||||
|
case "bucket":
|
||||||
|
var boundLabel string
|
||||||
|
for _, l := range *mlabels {
|
||||||
|
if l.Name == "le" {
|
||||||
|
boundLabel = l.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var bound float64
|
||||||
|
if boundLabel == "+Inf" {
|
||||||
|
bound = math.Inf(1)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
if bound, err = strconv.ParseFloat(boundLabel, 64); err != nil {
|
||||||
|
bound = math.NaN()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v.HistogramValue.Buckets = append(v.HistogramValue.Buckets, &HistogramValue_Bucket{
|
||||||
|
Count: uint64(value),
|
||||||
|
UpperBound: bound,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
mp.Value = v
|
||||||
|
case MetricType_SUMMARY:
|
||||||
|
var v *MetricPoint_SummaryValue
|
||||||
|
if mp.Value != nil {
|
||||||
|
v = mp.Value.(*MetricPoint_SummaryValue)
|
||||||
|
} else {
|
||||||
|
v = &MetricPoint_SummaryValue{
|
||||||
|
SummaryValue: &SummaryValue{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch stype {
|
||||||
|
case "sum":
|
||||||
|
v.SummaryValue.Sum = &SummaryValue_DoubleValue{DoubleValue: value}
|
||||||
|
case "count":
|
||||||
|
v.SummaryValue.Count = uint64(value)
|
||||||
|
case "created":
|
||||||
|
t := time.Unix(0, int64(value*float64(time.Second)))
|
||||||
|
v.SummaryValue.Created = timestamppb.New(t)
|
||||||
|
default:
|
||||||
|
var quantileLabel string
|
||||||
|
for _, l := range *mlabels {
|
||||||
|
if l.Name == "quantile" {
|
||||||
|
quantileLabel = l.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var quantile float64
|
||||||
|
if quantileLabel == "+Inf" {
|
||||||
|
quantile = math.MaxFloat64
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
if quantile, err = strconv.ParseFloat(quantileLabel, 64); err != nil {
|
||||||
|
quantile = math.NaN()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SummaryValue.Quantile = append(v.SummaryValue.Quantile, &SummaryValue_Quantile{
|
||||||
|
Quantile: quantile,
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
mp.Value = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,10 @@ import (
|
||||||
"github.com/influxdata/telegraf/plugins/parsers"
|
"github.com/influxdata/telegraf/plugins/parsers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func AcceptsContent(header http.Header) bool {
|
||||||
|
return expfmt.ResponseFormat(header).FormatType() != expfmt.TypeUnknown
|
||||||
|
}
|
||||||
|
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
IgnoreTimestamp bool `toml:"prometheus_ignore_timestamp"`
|
IgnoreTimestamp bool `toml:"prometheus_ignore_timestamp"`
|
||||||
MetricVersion int `toml:"prometheus_metric_version"`
|
MetricVersion int `toml:"prometheus_metric_version"`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue