162 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
package prometheus
 | 
						|
 | 
						|
// Parser inspired from
 | 
						|
// https://github.com/prometheus/prom2json/blob/master/main.go
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"math"
 | 
						|
	"mime"
 | 
						|
	"net/http"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/influxdata/telegraf"
 | 
						|
	"github.com/influxdata/telegraf/metric"
 | 
						|
 | 
						|
	"github.com/matttproud/golang_protobuf_extensions/pbutil"
 | 
						|
	dto "github.com/prometheus/client_model/go"
 | 
						|
	"github.com/prometheus/common/expfmt"
 | 
						|
)
 | 
						|
 | 
						|
// Parse returns a slice of Metrics from a text representation of a
 | 
						|
// metrics
 | 
						|
func Parse(buf []byte, header http.Header) ([]telegraf.Metric, error) {
 | 
						|
	var metrics []telegraf.Metric
 | 
						|
	var parser expfmt.TextParser
 | 
						|
	// parse even if the buffer begins with a newline
 | 
						|
	buf = bytes.TrimPrefix(buf, []byte("\n"))
 | 
						|
	// Read raw data
 | 
						|
	buffer := bytes.NewBuffer(buf)
 | 
						|
	reader := bufio.NewReader(buffer)
 | 
						|
 | 
						|
	mediatype, params, err := mime.ParseMediaType(header.Get("Content-Type"))
 | 
						|
	// Prepare output
 | 
						|
	metricFamilies := make(map[string]*dto.MetricFamily)
 | 
						|
 | 
						|
	if err == nil && mediatype == "application/vnd.google.protobuf" &&
 | 
						|
		params["encoding"] == "delimited" &&
 | 
						|
		params["proto"] == "io.prometheus.client.MetricFamily" {
 | 
						|
		for {
 | 
						|
			mf := &dto.MetricFamily{}
 | 
						|
			if _, ierr := pbutil.ReadDelimited(reader, mf); ierr != nil {
 | 
						|
				if ierr == io.EOF {
 | 
						|
					break
 | 
						|
				}
 | 
						|
				return nil, fmt.Errorf("reading metric family protocol buffer failed: %s", ierr)
 | 
						|
			}
 | 
						|
			metricFamilies[mf.GetName()] = mf
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		metricFamilies, err = parser.TextToMetricFamilies(reader)
 | 
						|
		if err != nil {
 | 
						|
			return nil, fmt.Errorf("reading text format failed: %s", err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// read metrics
 | 
						|
	for metricName, mf := range metricFamilies {
 | 
						|
		for _, m := range mf.Metric {
 | 
						|
			// reading tags
 | 
						|
			tags := makeLabels(m)
 | 
						|
			// reading fields
 | 
						|
			fields := make(map[string]interface{})
 | 
						|
			if mf.GetType() == dto.MetricType_SUMMARY {
 | 
						|
				// summary metric
 | 
						|
				fields = makeQuantiles(m)
 | 
						|
				fields["count"] = float64(m.GetSummary().GetSampleCount())
 | 
						|
				fields["sum"] = float64(m.GetSummary().GetSampleSum())
 | 
						|
			} else if mf.GetType() == dto.MetricType_HISTOGRAM {
 | 
						|
				// histogram metric
 | 
						|
				fields = makeBuckets(m)
 | 
						|
				fields["count"] = float64(m.GetHistogram().GetSampleCount())
 | 
						|
				fields["sum"] = float64(m.GetHistogram().GetSampleSum())
 | 
						|
 | 
						|
			} else {
 | 
						|
				// standard metric
 | 
						|
				fields = getNameAndValue(m)
 | 
						|
			}
 | 
						|
			// converting to telegraf metric
 | 
						|
			if len(fields) > 0 {
 | 
						|
				var t time.Time
 | 
						|
				if m.TimestampMs != nil && *m.TimestampMs > 0 {
 | 
						|
					t = time.Unix(0, *m.TimestampMs*1000000)
 | 
						|
				} else {
 | 
						|
					t = time.Now()
 | 
						|
				}
 | 
						|
				metric, err := metric.New(metricName, tags, fields, t, valueType(mf.GetType()))
 | 
						|
				if err == nil {
 | 
						|
					metrics = append(metrics, metric)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return metrics, err
 | 
						|
}
 | 
						|
 | 
						|
func valueType(mt dto.MetricType) telegraf.ValueType {
 | 
						|
	switch mt {
 | 
						|
	case dto.MetricType_COUNTER:
 | 
						|
		return telegraf.Counter
 | 
						|
	case dto.MetricType_GAUGE:
 | 
						|
		return telegraf.Gauge
 | 
						|
	case dto.MetricType_SUMMARY:
 | 
						|
		return telegraf.Summary
 | 
						|
	case dto.MetricType_HISTOGRAM:
 | 
						|
		return telegraf.Histogram
 | 
						|
	default:
 | 
						|
		return telegraf.Untyped
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Get Quantiles from summary metric
 | 
						|
func makeQuantiles(m *dto.Metric) map[string]interface{} {
 | 
						|
	fields := make(map[string]interface{})
 | 
						|
	for _, q := range m.GetSummary().Quantile {
 | 
						|
		if !math.IsNaN(q.GetValue()) {
 | 
						|
			fields[fmt.Sprint(q.GetQuantile())] = float64(q.GetValue())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fields
 | 
						|
}
 | 
						|
 | 
						|
// Get Buckets  from histogram metric
 | 
						|
func makeBuckets(m *dto.Metric) map[string]interface{} {
 | 
						|
	fields := make(map[string]interface{})
 | 
						|
	for _, b := range m.GetHistogram().Bucket {
 | 
						|
		fields[fmt.Sprint(b.GetUpperBound())] = float64(b.GetCumulativeCount())
 | 
						|
	}
 | 
						|
	return fields
 | 
						|
}
 | 
						|
 | 
						|
// Get labels from metric
 | 
						|
func makeLabels(m *dto.Metric) map[string]string {
 | 
						|
	result := map[string]string{}
 | 
						|
	for _, lp := range m.Label {
 | 
						|
		result[lp.GetName()] = lp.GetValue()
 | 
						|
	}
 | 
						|
	return result
 | 
						|
}
 | 
						|
 | 
						|
// Get name and value from metric
 | 
						|
func getNameAndValue(m *dto.Metric) map[string]interface{} {
 | 
						|
	fields := make(map[string]interface{})
 | 
						|
	if m.Gauge != nil {
 | 
						|
		if !math.IsNaN(m.GetGauge().GetValue()) {
 | 
						|
			fields["gauge"] = float64(m.GetGauge().GetValue())
 | 
						|
		}
 | 
						|
	} else if m.Counter != nil {
 | 
						|
		if !math.IsNaN(m.GetCounter().GetValue()) {
 | 
						|
			fields["counter"] = float64(m.GetCounter().GetValue())
 | 
						|
		}
 | 
						|
	} else if m.Untyped != nil {
 | 
						|
		if !math.IsNaN(m.GetUntyped().GetValue()) {
 | 
						|
			fields["value"] = float64(m.GetUntyped().GetValue())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return fields
 | 
						|
}
 |