-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Expand file tree
/
Copy pathparser.go
More file actions
143 lines (124 loc) · 3.54 KB
/
parser.go
File metadata and controls
143 lines (124 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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{}
},
)
}