Skip to content

Commit 6844f6a

Browse files
authored
Add promlinter to lint metrics name (#1265)
1 parent 5baff12 commit 6844f6a

File tree

7 files changed

+132
-1
lines changed

7 files changed

+132
-1
lines changed

.golangci.example.yml

+14
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,20 @@ linters-settings:
347347
simple: true
348348
range-loops: true # Report preallocation suggestions on range loops, true by default
349349
for-loops: false # Report preallocation suggestions on for loops, false by default
350+
promlinter:
351+
# Promlinter cannot infer all metrics name in static analysis.
352+
# Enable strict mode will also include the errors caused by failing to parse the args.
353+
strict: false
354+
# Please refer to https://github.com/yeya24/promlinter#usage for detailed usage.
355+
disabled-linters:
356+
# - "Help"
357+
# - "MetricUnits"
358+
# - "Counter"
359+
# - "HistogramSummaryReserved"
360+
# - "MetricTypeInName"
361+
# - "ReservedChars"
362+
# - "CamelCase"
363+
# - "lintUnitAbbreviations"
350364
predeclared:
351365
# comma-separated list of predeclared identifiers to not report on
352366
ignore: ""

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ require (
8080
github.com/ultraware/whitespace v0.0.4
8181
github.com/uudashr/gocognit v1.0.1
8282
github.com/valyala/quicktemplate v1.6.3
83+
github.com/yeya24/promlinter v0.1.0
8384
golang.org/x/tools v0.1.0
8485
gopkg.in/yaml.v2 v2.4.0
8586
honnef.co/go/tools v0.1.3

go.sum

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/config/config.go

+6
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ type LintersSettings struct {
277277
Cyclop Cyclop
278278
ImportAs ImportAsSettings
279279
GoModDirectives GoModDirectivesSettings
280+
Promlinter PromlinterSettings
280281

281282
Custom map[string]CustomLinterSettings
282283
}
@@ -457,6 +458,11 @@ type PredeclaredSettings struct {
457458
Qualified bool `mapstructure:"q"`
458459
}
459460

461+
type PromlinterSettings struct {
462+
Strict bool `mapstructure:"strict"`
463+
DisabledLinters []string `mapstructure:"disabled-linters"`
464+
}
465+
460466
type Cyclop struct {
461467
MaxComplexity int `mapstructure:"max-complexity"`
462468
PackageAverage float64 `mapstructure:"package-average"`

pkg/golinters/promlinter.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package golinters
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
7+
"github.com/yeya24/promlinter"
8+
"golang.org/x/tools/go/analysis"
9+
10+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
11+
"github.com/golangci/golangci-lint/pkg/lint/linter"
12+
"github.com/golangci/golangci-lint/pkg/result"
13+
)
14+
15+
func NewPromlinter() *goanalysis.Linter {
16+
var mu sync.Mutex
17+
var resIssues []goanalysis.Issue
18+
19+
const linterName = "promlinter"
20+
analyzer := &analysis.Analyzer{
21+
Name: linterName,
22+
Doc: goanalysis.TheOnlyanalyzerDoc,
23+
}
24+
return goanalysis.NewLinter(
25+
linterName,
26+
"Check Prometheus metrics naming via promlint",
27+
[]*analysis.Analyzer{analyzer},
28+
nil,
29+
).WithContextSetter(func(lintCtx *linter.Context) {
30+
strict := lintCtx.Cfg.LintersSettings.Promlinter.Strict
31+
disabledLinters := lintCtx.Cfg.LintersSettings.Promlinter.DisabledLinters
32+
33+
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
34+
issues := promlinter.RunLint(pass.Fset, pass.Files, promlinter.Setting{
35+
Strict: strict,
36+
DisabledLintFuncs: disabledLinters,
37+
})
38+
39+
if len(issues) == 0 {
40+
return nil, nil
41+
}
42+
43+
res := make([]goanalysis.Issue, len(issues))
44+
for k, i := range issues {
45+
issue := result.Issue{
46+
Pos: i.Pos,
47+
Text: fmt.Sprintf("Metric: %s Error: %s", i.Metric, i.Text),
48+
FromLinter: linterName,
49+
}
50+
51+
res[k] = goanalysis.NewIssue(&issue, pass)
52+
}
53+
54+
mu.Lock()
55+
resIssues = append(resIssues, res...)
56+
mu.Unlock()
57+
58+
return nil, nil
59+
}
60+
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
61+
return resIssues
62+
}).WithLoadMode(goanalysis.LoadModeSyntax)
63+
}

pkg/lint/lintersdb/manager.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
479479
WithSince("v1.39.0").
480480
WithPresets(linter.PresetStyle, linter.PresetModule).
481481
WithURL("https://github.com/ldez/gomoddirectives"),
482-
482+
linter.NewConfig(golinters.NewPromlinter()).
483+
WithSince("v1.40.0").
484+
WithPresets(linter.PresetStyle).
485+
WithURL("https://github.com/yeya24/promlinter"),
483486
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
484487
linter.NewConfig(golinters.NewNoLintLint()).
485488
WithSince("v1.26.0").

test/testdata/promlinter.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//args: -Epromlinter
2+
package testdata
3+
4+
import (
5+
"github.com/prometheus/client_golang/prometheus"
6+
"github.com/prometheus/client_golang/prometheus/promauto"
7+
)
8+
9+
var (
10+
_ = promauto.NewCounterVec(
11+
prometheus.CounterOpts{ // ERROR `Metric: test_metric_name Error: counter metrics should have "_total" suffix`
12+
Name: "test_metric_name",
13+
Help: "test help text",
14+
}, []string{},
15+
)
16+
17+
_ = promauto.NewCounterVec(
18+
prometheus.CounterOpts{ // ERROR "Metric: test_metric_total Error: no help text"
19+
Name: "test_metric_total",
20+
}, []string{},
21+
)
22+
23+
_ = promauto.NewCounterVec(
24+
prometheus.CounterOpts{ // ERROR `Metric: metric_type_in_name_counter_total Error: metric name should not include type 'counter'`
25+
Name: "metric_type_in_name_counter_total",
26+
Help: "foo",
27+
}, []string{},
28+
)
29+
30+
_ = prometheus.NewHistogram(prometheus.HistogramOpts{ // ERROR `Metric: test_duration_milliseconds Error: use base unit "seconds" instead of "milliseconds"`
31+
Name: "test_duration_milliseconds",
32+
Help: "",
33+
})
34+
)

0 commit comments

Comments
 (0)