@@ -29,7 +29,13 @@ import (
29
29
// A Linter is a Prometheus metrics linter. It identifies issues with metric
30
30
// names, types, and metadata, and reports them to the caller.
31
31
type Linter struct {
32
- r io.Reader
32
+ // The linter will read metrics in the Prometheus text format from r and
33
+ // then lint it, _and_ it will lint the metrics provided directly as
34
+ // MetricFamily proto messages in mfs. Note, however, that the current
35
+ // constructor functions New and NewWithMetricFamilies only ever set one
36
+ // of them.
37
+ r io.Reader
38
+ mfs []* dto.MetricFamily
33
39
}
34
40
35
41
// A Problem is an issue detected by a Linter.
@@ -42,40 +48,52 @@ type Problem struct {
42
48
}
43
49
44
50
// newProblem is helper function to create a Problem.
45
- func newProblem (mf dto.MetricFamily , text string ) Problem {
51
+ func newProblem (mf * dto.MetricFamily , text string ) Problem {
46
52
return Problem {
47
53
Metric : mf .GetName (),
48
54
Text : text ,
49
55
}
50
56
}
51
57
52
- // New creates a new Linter that reads an input stream of Prometheus metrics.
53
- // Only the Prometheus text exposition format is supported .
58
+ // New creates a new Linter that reads an input stream of Prometheus metrics in
59
+ // the Prometheus text exposition format.
54
60
func New (r io.Reader ) * Linter {
55
61
return & Linter {
56
62
r : r ,
57
63
}
58
64
}
59
65
66
+ // NewWithMetricFamilies creates a new Linter that reads from a slice of
67
+ // MetricFamily protobuf messages.
68
+ func NewWithMetricFamilies (mfs []* dto.MetricFamily ) * Linter {
69
+ return & Linter {
70
+ mfs : mfs ,
71
+ }
72
+ }
73
+
60
74
// Lint performs a linting pass, returning a slice of Problems indicating any
61
- // issues found in the metrics stream. The slice is sorted by metric name
75
+ // issues found in the metrics stream. The slice is sorted by metric name
62
76
// and issue description.
63
77
func (l * Linter ) Lint () ([]Problem , error ) {
64
- // TODO(mdlayher): support for protobuf exposition format?
65
- d := expfmt .NewDecoder (l .r , expfmt .FmtText )
66
-
67
78
var problems []Problem
68
79
69
- var mf dto.MetricFamily
70
- for {
71
- if err := d .Decode (& mf ); err != nil {
72
- if err == io .EOF {
73
- break
80
+ if l .r != nil {
81
+ d := expfmt .NewDecoder (l .r , expfmt .FmtText )
82
+
83
+ mf := & dto.MetricFamily {}
84
+ for {
85
+ if err := d .Decode (mf ); err != nil {
86
+ if err == io .EOF {
87
+ break
88
+ }
89
+
90
+ return nil , err
74
91
}
75
92
76
- return nil , err
93
+ problems = append ( problems , lint ( mf ) ... )
77
94
}
78
-
95
+ }
96
+ for _ , mf := range l .mfs {
79
97
problems = append (problems , lint (mf )... )
80
98
}
81
99
@@ -91,8 +109,8 @@ func (l *Linter) Lint() ([]Problem, error) {
91
109
}
92
110
93
111
// lint is the entry point for linting a single metric.
94
- func lint (mf dto.MetricFamily ) []Problem {
95
- fns := []func (mf dto.MetricFamily ) []Problem {
112
+ func lint (mf * dto.MetricFamily ) []Problem {
113
+ fns := []func (mf * dto.MetricFamily ) []Problem {
96
114
lintHelp ,
97
115
lintMetricUnits ,
98
116
lintCounter ,
@@ -113,7 +131,7 @@ func lint(mf dto.MetricFamily) []Problem {
113
131
}
114
132
115
133
// lintHelp detects issues related to the help text for a metric.
116
- func lintHelp (mf dto.MetricFamily ) []Problem {
134
+ func lintHelp (mf * dto.MetricFamily ) []Problem {
117
135
var problems []Problem
118
136
119
137
// Expect all metrics to have help text available.
@@ -125,7 +143,7 @@ func lintHelp(mf dto.MetricFamily) []Problem {
125
143
}
126
144
127
145
// lintMetricUnits detects issues with metric unit names.
128
- func lintMetricUnits (mf dto.MetricFamily ) []Problem {
146
+ func lintMetricUnits (mf * dto.MetricFamily ) []Problem {
129
147
var problems []Problem
130
148
131
149
unit , base , ok := metricUnits (* mf .Name )
@@ -146,7 +164,7 @@ func lintMetricUnits(mf dto.MetricFamily) []Problem {
146
164
147
165
// lintCounter detects issues specific to counters, as well as patterns that should
148
166
// only be used with counters.
149
- func lintCounter (mf dto.MetricFamily ) []Problem {
167
+ func lintCounter (mf * dto.MetricFamily ) []Problem {
150
168
var problems []Problem
151
169
152
170
isCounter := mf .GetType () == dto .MetricType_COUNTER
@@ -165,7 +183,7 @@ func lintCounter(mf dto.MetricFamily) []Problem {
165
183
166
184
// lintHistogramSummaryReserved detects when other types of metrics use names or labels
167
185
// reserved for use by histograms and/or summaries.
168
- func lintHistogramSummaryReserved (mf dto.MetricFamily ) []Problem {
186
+ func lintHistogramSummaryReserved (mf * dto.MetricFamily ) []Problem {
169
187
// These rules do not apply to untyped metrics.
170
188
t := mf .GetType ()
171
189
if t == dto .MetricType_UNTYPED {
@@ -206,7 +224,7 @@ func lintHistogramSummaryReserved(mf dto.MetricFamily) []Problem {
206
224
}
207
225
208
226
// lintMetricTypeInName detects when metric types are included in the metric name.
209
- func lintMetricTypeInName (mf dto.MetricFamily ) []Problem {
227
+ func lintMetricTypeInName (mf * dto.MetricFamily ) []Problem {
210
228
var problems []Problem
211
229
n := strings .ToLower (mf .GetName ())
212
230
@@ -224,7 +242,7 @@ func lintMetricTypeInName(mf dto.MetricFamily) []Problem {
224
242
}
225
243
226
244
// lintReservedChars detects colons in metric names.
227
- func lintReservedChars (mf dto.MetricFamily ) []Problem {
245
+ func lintReservedChars (mf * dto.MetricFamily ) []Problem {
228
246
var problems []Problem
229
247
if strings .Contains (mf .GetName (), ":" ) {
230
248
problems = append (problems , newProblem (mf , "metric names should not contain ':'" ))
@@ -235,7 +253,7 @@ func lintReservedChars(mf dto.MetricFamily) []Problem {
235
253
var camelCase = regexp .MustCompile (`[a-z][A-Z]` )
236
254
237
255
// lintCamelCase detects metric names and label names written in camelCase.
238
- func lintCamelCase (mf dto.MetricFamily ) []Problem {
256
+ func lintCamelCase (mf * dto.MetricFamily ) []Problem {
239
257
var problems []Problem
240
258
if camelCase .FindString (mf .GetName ()) != "" {
241
259
problems = append (problems , newProblem (mf , "metric names should be written in 'snake_case' not 'camelCase'" ))
@@ -252,7 +270,7 @@ func lintCamelCase(mf dto.MetricFamily) []Problem {
252
270
}
253
271
254
272
// lintUnitAbbreviations detects abbreviated units in the metric name.
255
- func lintUnitAbbreviations (mf dto.MetricFamily ) []Problem {
273
+ func lintUnitAbbreviations (mf * dto.MetricFamily ) []Problem {
256
274
var problems []Problem
257
275
n := strings .ToLower (mf .GetName ())
258
276
for _ , s := range unitAbbreviations {
0 commit comments