Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f7f0213

Browse files
committed
chore: address comments
1 parent 42ac780 commit f7f0213

1 file changed

Lines changed: 52 additions & 38 deletions

File tree

scripts/metricsdocgen/scanner/scanner.go

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
// Package main provides a tool to scan Go source files and extract Prometheus
2-
// metric definitions. It outputs metrics in Prometheus text format for use
3-
// by the documentation generator.
2+
// metric definitions. It outputs metrics in Prometheus text exposition format
3+
// to stdout for use by the documentation generator.
44
//
55
// Usage:
66
//
7-
// go run ./scripts/metricsdocgen/scanner [-output FILE]
7+
// go run ./scripts/metricsdocgen/scanner > scripts/metricsdocgen/generated_metrics
88
package main
99

1010
import (
11-
"flag"
1211
"fmt"
1312
"go/ast"
1413
"go/parser"
1514
"go/token"
15+
"io"
1616
"io/fs"
1717
"log"
1818
"os"
@@ -24,14 +24,15 @@ import (
2424
)
2525

2626
// Directories to scan for metric definitions, relative to the repository root.
27+
// Add or remove directories here to control the scanner's scope.
2728
var scanDirs = []string{
2829
"agent",
2930
"coderd",
3031
"enterprise",
3132
"provisionerd",
3233
}
3334

34-
// MetricType represents the type of a Prometheus metric.
35+
// MetricType represents the type of Prometheus metric.
3536
type MetricType string
3637

3738
const (
@@ -49,27 +50,30 @@ type Metric struct {
4950
Labels []string // Label names for this metric
5051
}
5152

52-
var outputFile string
53-
5453
func main() {
55-
flag.StringVar(&outputFile, "output", "scripts/metricsdocgen/generated_metrics", "Output file path")
56-
flag.Parse()
57-
5854
metrics, err := scanAllDirs()
5955
if err != nil {
6056
log.Fatalf("Failed to scan directories: %v", err)
6157
}
6258

59+
// Duplicates are not expected since Prometheus enforces unique metric names at registration.
60+
uniqueMetrics := make(map[string]Metric)
61+
for _, m := range metrics {
62+
uniqueMetrics[m.Name] = m
63+
}
64+
metrics = make([]Metric, 0, len(uniqueMetrics))
65+
for _, m := range uniqueMetrics {
66+
metrics = append(metrics, m)
67+
}
68+
6369
// Sort metrics by name for consistent output across runs.
6470
sort.Slice(metrics, func(i, j int) bool {
6571
return metrics[i].Name < metrics[j].Name
6672
})
6773

68-
if err := writeMetrics(metrics, outputFile); err != nil {
69-
log.Fatalf("Failed to write metrics: %v", err)
70-
}
74+
writeMetrics(metrics, os.Stdout)
7175

72-
_, _ = fmt.Fprintf(os.Stderr, "Wrote %d metrics to %s\n", len(metrics), outputFile)
76+
log.Printf("Successfully parsed %d metrics", len(metrics))
7377
}
7478

7579
// scanAllDirs scans all configured directories for metric definitions.
@@ -81,6 +85,8 @@ func scanAllDirs() ([]Metric, error) {
8185
if err != nil {
8286
return nil, xerrors.Errorf("scanning %s: %w", dir, err)
8387
}
88+
89+
log.Printf("scanning %s: found %d metrics", dir, len(metrics))
8490
allMetrics = append(allMetrics, metrics...)
8591
}
8692

@@ -110,6 +116,10 @@ func scanDirectory(root string) ([]Metric, error) {
110116
if err != nil {
111117
return xerrors.Errorf("scanning %s: %w", path, err)
112118
}
119+
120+
if len(fileMetrics) > 0 {
121+
log.Printf("scanning %s: found %d metrics", path, len(fileMetrics))
122+
}
113123
metrics = append(metrics, fileMetrics...)
114124

115125
return nil
@@ -121,7 +131,7 @@ func scanDirectory(root string) ([]Metric, error) {
121131
// scanFile parses a single Go file and extracts all Prometheus metric definitions.
122132
func scanFile(path string) ([]Metric, error) {
123133
fset := token.NewFileSet()
124-
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
134+
file, err := parser.ParseFile(fset, path, nil, parser.SkipObjectResolution)
125135
if err != nil {
126136
return nil, xerrors.Errorf("parsing file: %w", err)
127137
}
@@ -130,12 +140,12 @@ func scanFile(path string) ([]Metric, error) {
130140

131141
// Walk the AST looking for metric registration calls.
132142
ast.Inspect(file, func(n ast.Node) bool {
133-
_, ok := n.(*ast.CallExpr)
143+
call, ok := n.(*ast.CallExpr)
134144
if !ok {
135145
return true
136146
}
137147

138-
metric, ok := extractMetricFromCall()
148+
metric, ok := extractMetricFromCall(call)
139149
if ok {
140150
metrics = append(metrics, metric)
141151
}
@@ -154,7 +164,7 @@ func scanFile(path string) ([]Metric, error) {
154164
// - prometheus.NewDesc() calls
155165
// - prometheus.New*() and prometheus.New*Vec() with *Opts{}
156166
// - promauto.With(reg).New*() and factory.New*() patterns
157-
func extractMetricFromCall() (Metric, bool) {
167+
func extractMetricFromCall(_ *ast.CallExpr) (Metric, bool) {
158168
// TODO(ssncferreira): Implement upstack.
159169
// Handle prometheus.NewDesc()
160170
// Handle prometheus.New*Vec() and prometheus.New*() with *Opts{}
@@ -163,31 +173,35 @@ func extractMetricFromCall() (Metric, bool) {
163173
return Metric{}, false
164174
}
165175

166-
// writeMetrics writes the metrics in Prometheus text exposition format.
176+
// String returns the metric in Prometheus text exposition format.
167177
// Label values are empty strings and metric values are 0 since only
168178
// metadata (name, type, help, label names) is used for documentation generation.
169-
func writeMetrics(metrics []Metric, path string) error {
179+
func (m Metric) String() string {
170180
var buf strings.Builder
171181

172-
for _, m := range metrics {
173-
// Write HELP line.
174-
_, _ = buf.WriteString(fmt.Sprintf("# HELP %s %s\n", m.Name, m.Help))
175-
176-
// Write TYPE line.
177-
_, _ = buf.WriteString(fmt.Sprintf("# TYPE %s %s\n", m.Name, m.Type))
178-
179-
// Write a sample metric line with empty label values and zero metric value.
180-
if len(m.Labels) > 0 {
181-
labelPairs := make([]string, len(m.Labels))
182-
for i, l := range m.Labels {
183-
labelPairs[i] = fmt.Sprintf("%s=\"\"", l)
184-
}
185-
_, _ = buf.WriteString(fmt.Sprintf("%s{%s} 0\n", m.Name, strings.Join(labelPairs, ",")))
186-
} else {
187-
_, _ = buf.WriteString(fmt.Sprintf("%s 0\n", m.Name))
182+
// Write HELP line.
183+
_, _ = fmt.Fprintf(&buf, "# HELP %s %s\n", m.Name, m.Help)
184+
185+
// Write TYPE line.
186+
_, _ = fmt.Fprintf(&buf, "# TYPE %s %s\n", m.Name, m.Type)
187+
188+
// Write a sample metric line with empty label values and zero metric value.
189+
if len(m.Labels) > 0 {
190+
labelPairs := make([]string, len(m.Labels))
191+
for i, l := range m.Labels {
192+
labelPairs[i] = fmt.Sprintf("%s=\"\"", l)
188193
}
194+
_, _ = fmt.Fprintf(&buf, "%s{%s} 0\n", m.Name, strings.Join(labelPairs, ","))
195+
} else {
196+
_, _ = fmt.Fprintf(&buf, "%s 0\n", m.Name)
189197
}
190198

191-
// #nosec G306 - metrics file needs to be readable
192-
return os.WriteFile(path, []byte(buf.String()), 0o644)
199+
return buf.String()
200+
}
201+
202+
// writeMetrics writes all metrics in Prometheus text exposition format.
203+
func writeMetrics(metrics []Metric, w io.Writer) {
204+
for _, m := range metrics {
205+
_, _ = fmt.Fprint(w, m.String())
206+
}
193207
}

0 commit comments

Comments
 (0)