Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add multiple features and fix #39

Merged
merged 14 commits into from
Apr 22, 2024
23 changes: 23 additions & 0 deletions cmd/promlinter/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"go/token"
"testing"

"github.com/stretchr/testify/assert"
"github.com/yeya24/promlinter"
)

func TestLabel(t *testing.T) {
fs := token.NewFileSet()

metrics := promlinter.RunList(fs, findFiles([]string{"../../testdata/"}, fs), true)

if len(metrics) != 10 {
t.Fatal()
}

assert.Equal(t, []string{"namespace", "name"}, metrics[7].Labels())
assert.Equal(t, []string{"namespace", "name", "const-label1=value1", "const-label2=?"}, metrics[8].Labels())
assert.Equal(t, []string{"namespace", "name"}, metrics[9].Labels())
}
204 changes: 165 additions & 39 deletions cmd/promlinter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -42,26 +43,40 @@ It is also supported to disable the lint functions using repeated flag --disable
[UnitAbbreviations]: UnitAbbreviations detects abbreviated units in the metric name.
`

var MetricType = map[int32]string{
0: "COUNTER",
1: "GAUGE",
2: "SUMMARY",
3: "UNTYPED",
4: "HISTOGRAM",
var (
MetricType = map[int32]string{
0: "COUNTER",
1: "GAUGE",
2: "SUMMARY",
3: "UNTYPED",
4: "HISTOGRAM",
}
withVendor *bool
)

func init() {
// To see the log position, added for debugging.
log.SetFlags(log.LstdFlags | log.Lshortfile)
}

func main() {

app := kingpin.New(filepath.Base(os.Args[0]), help)
app.Version("v0.0.2")
app.Version("v0.0.3")
app.HelpFlag.Short('h')

listCmd := app.Command("list", "List metrics name.")
listPaths := listCmd.Arg("files", "Files to parse metrics.").Strings()
listStrict := listCmd.Flag("strict", "Strict mode. If true, linter will output more issues including parsing failures.").
Default("false").Short('s').Bool()

listPrintAddPos := listCmd.Flag("add-position", "Add metric position column when printing the result.").Default("false").Bool()
listPrintAddModule := listCmd.Flag("add-module", "Add metric module column when printing the result.").Default("false").Bool()

listPrintAddHelp := listCmd.Flag("add-help", "Add metric help column when printing the result.").Default("false").Bool()
listPrintFormat := listCmd.Flag("output", "Print result formatted as JSON/YAML").Short('o').Enum("yaml", "json")
listPrintFormat := listCmd.Flag("output", "Print result formatted as JSON/YAML/Markdown").Short('o').Enum("yaml", "json", "md")

withVendor = listCmd.Flag("with-vendor", "Scan vendor packages.").Default("false").Bool()

lintCmd := app.Command("lint", "Lint metrics via promlint.")
lintPaths := lintCmd.Arg("files", "Files to parse metrics.").Strings()
Expand All @@ -78,7 +93,15 @@ func main() {
switch parsedCmd {
case listCmd.FullCommand():
metrics := promlinter.RunList(fileSet, findFiles(*listPaths, fileSet), *listStrict)
printMetrics(metrics, *listPrintAddPos, *listPrintAddHelp, *listPrintFormat)
p := printer{
fmt: *listPrintFormat,
addHelp: *listPrintAddHelp,
addPosition: *listPrintAddPos,
addModule: *listPrintAddModule,
metrics: metrics,
}

p.printMetrics()
case lintCmd.FullCommand():
setting := promlinter.Setting{Strict: *lintStrict, DisabledLintFuncs: *disableLintFuncs}
for _, iss := range promlinter.RunLint(fileSet, findFiles(*lintPaths, fileSet), setting) {
Expand Down Expand Up @@ -114,9 +137,11 @@ func walkDir(root string) chan string {
defer close(out)
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
sep := string(filepath.Separator)
if strings.HasPrefix(path, "vendor"+sep) || strings.Contains(path, sep+"vendor"+sep) {
if withVendor != nil && !*withVendor &&
(strings.HasPrefix(path, "vendor"+sep) || strings.Contains(path, sep+"vendor"+sep)) {
return nil
}

if !info.IsDir() && !strings.HasSuffix(info.Name(), "_test.go") &&
strings.HasSuffix(info.Name(), ".go") {
out <- path
Expand All @@ -131,48 +156,137 @@ func walkDir(root string) chan string {
return out
}

func printMetrics(metrics []promlinter.MetricFamilyWithPos, addPosition, addHelp bool, printFormat string) {
if len(printFormat) > 0 {
if printFormat == "json" {
printAsJson(metrics)
return
func (p *printer) printDefault() {
tw := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
defer tw.Flush()

fieldSep := "\t"
if p.fmt == "md" {
fieldSep = "|"
}

var (
fields []string
)

if p.addPosition || p.addModule {
fields = []string{
"POSITION", "TYPE", "NAME", "LABELS",
}
if printFormat == "yaml" {
printAsYaml(metrics)
return
} else {
fields = []string{
"TYPE", "NAME", "LABELS",
}
}
tw := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
defer tw.Flush()

var header string
if addPosition {
header = "POSITION\tTYPE\tNAME"
if p.addHelp {
fields = append(fields, "HELP")
}

if p.fmt == "md" {
fmt.Fprintf(tw, "|%s|\n", strings.Join(fields, fieldSep))
} else {
header = "TYPE\tNAME"
fmt.Fprintf(tw, "%s\n", strings.Join(fields, fieldSep))
}

if addHelp {
header += "\tHELP"
if p.fmt == "md" {
fmt.Fprintf(tw, "%s|\n", strings.Repeat("|---", len(fields)))
}

fmt.Fprintln(tw, header)
for _, m := range p.metrics {

for _, m := range metrics {
if addPosition && addHelp {
fmt.Fprintf(tw, "%v\t%v\t%v\t%v\n", m.Pos, MetricType[int32(*m.MetricFamily.Type)], *m.MetricFamily.Name, *m.MetricFamily.Help)
} else if addPosition {
fmt.Fprintf(tw, "%v\t%v\t%v\n", m.Pos, MetricType[int32(*m.MetricFamily.Type)], *m.MetricFamily.Name)
} else if addHelp {
fmt.Fprintf(tw, "%v\t%v\t%v\n", MetricType[int32(*m.MetricFamily.Type)], *m.MetricFamily.Name, *m.MetricFamily.Help)
help := "N/A"
if m.MetricFamily.Help != nil {
help = *m.MetricFamily.Help
}

labels := strings.Join(m.Labels(), ",")
if labels == "" {
labels = "N/A"
}

var lineArr []string

mname := *m.MetricFamily.Name
if p.fmt == "md" {
mname = fmt.Sprintf("`%s`", *m.MetricFamily.Name)
labels = fmt.Sprintf("`%s`", labels)
}

if (p.addPosition || p.addModule) && p.addHelp {
lineArr = []string{
p.pos(m.Pos.String()),
MetricType[int32(*m.MetricFamily.Type)],
mname,
labels,
help,
}
} else if p.addPosition || p.addModule {
lineArr = []string{
p.pos(m.Pos.String()),
MetricType[int32(*m.MetricFamily.Type)],
mname,
labels,
}

} else if p.addHelp {
lineArr = []string{
MetricType[int32(*m.MetricFamily.Type)],
mname,
labels,
help,
}
} else {
fmt.Fprintf(tw, "%v\t%v\n", MetricType[int32(*m.MetricFamily.Type)], *m.MetricFamily.Name)
lineArr = []string{
MetricType[int32(*m.MetricFamily.Type)],
mname,
labels,
}
}

if p.fmt == "md" {
fmt.Fprintf(tw, "|%s|\n", strings.Join(lineArr, fieldSep))
} else {
fmt.Fprintf(tw, "%s\n", strings.Join(lineArr, fieldSep))
}
}
}

func printAsYaml(metrics []promlinter.MetricFamilyWithPos) {
b, err := yaml.Marshal(toPrint(metrics))
type printer struct {
fmt string
addHelp, addPosition, addModule bool
metrics []promlinter.MetricFamilyWithPos
}

func (p *printer) pos(pos string) (x string) {
if p.addModule {
x = filepath.Dir(pos)
} else {
x = pos
}

if p.fmt == "md" {
return fmt.Sprintf("*%s*", x) // italic file path
}
return
}

func (p *printer) printMetrics() {
switch p.fmt {
case "json":
p.printAsJson()
return
case "yaml":
p.printAsYaml()
return
default:
p.printDefault()
return
}
}

func (p *printer) printAsYaml() {
b, err := yaml.Marshal(toPrint(p.metrics))
if err != nil {
fmt.Printf("Failed: %v", err)
os.Exit(1)
Expand All @@ -181,8 +295,8 @@ func printAsYaml(metrics []promlinter.MetricFamilyWithPos) {

}

func printAsJson(metrics []promlinter.MetricFamilyWithPos) {
b, err := json.Marshal(toPrint(metrics))
func (p *printer) printAsJson() {
b, err := json.MarshalIndent(toPrint(p.metrics), "", " ")
if err != nil {
fmt.Printf("Failed: %v", err)
os.Exit(1)
Expand All @@ -195,6 +309,7 @@ type MetricForPrinting struct {
Help string
Type string
Filename string
Labels []string
Line int
Column int
}
Expand All @@ -215,13 +330,24 @@ func toPrint(metrics []promlinter.MetricFamilyWithPos) []MetricForPrinting {
if m.MetricFamily.Help != nil {
h = *m.MetricFamily.Help
}

var labels []string
for _, m := range m.MetricFamily.Metric {
for idx, _ := range m.Label {
if m.Label[idx].Name != nil {
labels = append(labels, strings.Trim(*m.Label[idx].Name, `"`))
}
}
}

i := MetricForPrinting{
Name: n,
Help: h,
Type: MetricType[int32(*m.MetricFamily.Type)],
Filename: m.Pos.Filename,
Line: m.Pos.Line,
Column: m.Pos.Column,
Labels: labels,
}
p = append(p, i)
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.16
require (
github.com/prometheus/client_golang v1.12.1
github.com/prometheus/client_model v0.2.0
github.com/stretchr/testify v1.8.4
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.4.0
)
10 changes: 9 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,15 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -461,6 +466,9 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
Loading