269 lines
6.6 KiB
Go
269 lines
6.6 KiB
Go
// Licensed to Elasticsearch B.V. under one or more contributor
|
|
// license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright
|
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
|
// the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package prometheus
|
|
|
|
import (
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/elastic/beats/libbeat/common"
|
|
|
|
dto "github.com/prometheus/client_model/go"
|
|
)
|
|
|
|
// MetricMap defines the mapping from Prometheus metric to a Metricbeat field
|
|
type MetricMap interface {
|
|
// GetOptions returns the list of metric options
|
|
GetOptions() []MetricOption
|
|
|
|
// GetField returns the resulting field name
|
|
GetField() string
|
|
|
|
// GetValue returns the resulting value
|
|
GetValue(m *dto.Metric) interface{}
|
|
}
|
|
|
|
// MetricOption adds settings to Metric objects behavior
|
|
type MetricOption interface {
|
|
// Process a tuple of field, value and labels from a metric, return the same tuple updated
|
|
Process(field string, value interface{}, labels common.MapStr) (string, interface{}, common.MapStr)
|
|
}
|
|
|
|
// OpFilter only processes metrics matching the given filter
|
|
func OpFilter(filter map[string]string) MetricOption {
|
|
return opFilter{
|
|
labels: filter,
|
|
}
|
|
}
|
|
|
|
// OpLowercaseValue lowercases the value if it's a string
|
|
func OpLowercaseValue() MetricOption {
|
|
return opLowercaseValue{}
|
|
}
|
|
|
|
// Metric directly maps a Prometheus metric to a Metricbeat field
|
|
func Metric(field string, options ...MetricOption) MetricMap {
|
|
return &commonMetric{
|
|
field: field,
|
|
options: options,
|
|
}
|
|
}
|
|
|
|
// KeywordMetric maps a Prometheus metric to a Metricbeat field, stores the
|
|
// given keyword when source metric value is 1
|
|
func KeywordMetric(field, keyword string, options ...MetricOption) MetricMap {
|
|
return &keywordMetric{
|
|
commonMetric{
|
|
field: field,
|
|
options: options,
|
|
},
|
|
keyword,
|
|
}
|
|
}
|
|
|
|
// BooleanMetric maps a Prometheus metric to a Metricbeat field of bool type
|
|
func BooleanMetric(field string, options ...MetricOption) MetricMap {
|
|
return &booleanMetric{
|
|
commonMetric{
|
|
field: field,
|
|
options: options,
|
|
},
|
|
}
|
|
}
|
|
|
|
// LabelMetric maps a Prometheus metric to a Metricbeat field, stores the value
|
|
// of a given label on it if the gauge value is 1
|
|
func LabelMetric(field, label string, options ...MetricOption) MetricMap {
|
|
return &labelMetric{
|
|
commonMetric{
|
|
field: field,
|
|
options: options,
|
|
},
|
|
label,
|
|
}
|
|
}
|
|
|
|
// InfoMetric obtains info labels from the given metric and puts them
|
|
// into events matching all the key labels present in the metric
|
|
func InfoMetric(options ...MetricOption) MetricMap {
|
|
return &infoMetric{
|
|
commonMetric{
|
|
options: options,
|
|
},
|
|
}
|
|
}
|
|
|
|
type commonMetric struct {
|
|
field string
|
|
options []MetricOption
|
|
}
|
|
|
|
// GetOptions returns the list of metric options
|
|
func (m *commonMetric) GetOptions() []MetricOption {
|
|
return m.options
|
|
}
|
|
|
|
// GetField returns the resulting field name
|
|
func (m *commonMetric) GetField() string {
|
|
return m.field
|
|
}
|
|
|
|
// GetValue returns the resulting value
|
|
func (m *commonMetric) GetValue(metric *dto.Metric) interface{} {
|
|
counter := metric.GetCounter()
|
|
if counter != nil {
|
|
return int64(counter.GetValue())
|
|
}
|
|
|
|
gauge := metric.GetGauge()
|
|
if gauge != nil {
|
|
return gauge.GetValue()
|
|
}
|
|
|
|
summary := metric.GetSummary()
|
|
if summary != nil {
|
|
value := common.MapStr{}
|
|
value["sum"] = summary.GetSampleSum()
|
|
value["count"] = summary.GetSampleCount()
|
|
|
|
quantiles := summary.GetQuantile()
|
|
percentileMap := common.MapStr{}
|
|
for _, quantile := range quantiles {
|
|
if !math.IsNaN(quantile.GetValue()) {
|
|
key := strconv.FormatFloat((100 * quantile.GetQuantile()), 'f', -1, 64)
|
|
percentileMap[key] = quantile.GetValue()
|
|
}
|
|
|
|
}
|
|
|
|
if len(percentileMap) != 0 {
|
|
value["percentile"] = percentileMap
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
histogram := metric.GetHistogram()
|
|
if histogram != nil {
|
|
value := common.MapStr{}
|
|
value["sum"] = histogram.GetSampleSum()
|
|
value["count"] = histogram.GetSampleCount()
|
|
|
|
buckets := histogram.GetBucket()
|
|
bucketMap := common.MapStr{}
|
|
for _, bucket := range buckets {
|
|
key := strconv.FormatFloat(bucket.GetUpperBound(), 'f', -1, 64)
|
|
bucketMap[key] = bucket.GetCumulativeCount()
|
|
}
|
|
|
|
if len(bucketMap) != 0 {
|
|
value["bucket"] = bucketMap
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
// Other types are not supported here
|
|
return nil
|
|
}
|
|
|
|
type keywordMetric struct {
|
|
commonMetric
|
|
keyword string
|
|
}
|
|
|
|
// GetValue returns the resulting value
|
|
func (m *keywordMetric) GetValue(metric *dto.Metric) interface{} {
|
|
if gauge := metric.GetGauge(); gauge != nil && gauge.GetValue() == 1 {
|
|
return m.keyword
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type booleanMetric struct {
|
|
commonMetric
|
|
}
|
|
|
|
// GetValue returns the resulting value
|
|
func (m *booleanMetric) GetValue(metric *dto.Metric) interface{} {
|
|
if gauge := metric.GetGauge(); gauge != nil {
|
|
return gauge.GetValue() == 1
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type labelMetric struct {
|
|
commonMetric
|
|
label string
|
|
}
|
|
|
|
// GetValue returns the resulting value
|
|
func (m *labelMetric) GetValue(metric *dto.Metric) interface{} {
|
|
if gauge := metric.GetGauge(); gauge != nil && gauge.GetValue() == 1 {
|
|
return getLabel(metric, m.label)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getLabel(metric *dto.Metric, name string) string {
|
|
for _, label := range metric.GetLabel() {
|
|
if label.GetName() == name {
|
|
return label.GetValue()
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
type infoMetric struct {
|
|
commonMetric
|
|
}
|
|
|
|
// GetValue returns the resulting value
|
|
func (m *infoMetric) GetValue(metric *dto.Metric) interface{} {
|
|
return ""
|
|
}
|
|
|
|
// GetField returns the resulting field name
|
|
func (m *infoMetric) GetField() string {
|
|
return ""
|
|
}
|
|
|
|
type opFilter struct {
|
|
labels map[string]string
|
|
}
|
|
|
|
// Process will return nil if labels don't match the filter
|
|
func (o opFilter) Process(field string, value interface{}, labels common.MapStr) (string, interface{}, common.MapStr) {
|
|
for k, v := range o.labels {
|
|
if labels[k] != v {
|
|
return "", nil, nil
|
|
}
|
|
}
|
|
return field, value, labels
|
|
}
|
|
|
|
type opLowercaseValue struct{}
|
|
|
|
// Process will lowercase the given value if it's a string
|
|
func (o opLowercaseValue) Process(field string, value interface{}, labels common.MapStr) (string, interface{}, common.MapStr) {
|
|
if val, ok := value.(string); ok {
|
|
value = strings.ToLower(val)
|
|
}
|
|
return field, value, labels
|
|
}
|