mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-22 07:06:14 +00:00
scripts/metricsgen: add the initial version of metricsgen
This commit is contained in:
@@ -251,6 +251,14 @@ mockery:
|
||||
go generate -run="./scripts/mockery_generate.sh" ./...
|
||||
.PHONY: mockery
|
||||
|
||||
###############################################################################
|
||||
### Metrics ###
|
||||
###############################################################################
|
||||
|
||||
metrics:
|
||||
go generate -run="go run github.com/tendermint/tendermint/scripts/metricsgen" ./...
|
||||
.PHONY: mockery
|
||||
|
||||
###############################################################################
|
||||
### Local testnet using docker ###
|
||||
###############################################################################
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_events_seconds",
|
||||
Help: "Latency for indexing block events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TxEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "tx_events_seconds",
|
||||
Help: "Latency for indexing transaction events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlocksIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "blocks_indexed",
|
||||
Help: "Number of complete blocks indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TransactionsIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "transactions_indexed",
|
||||
Help: "Number of transactions indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: discard.NewHistogram(),
|
||||
TxEventsSeconds: discard.NewHistogram(),
|
||||
BlocksIndexed: discard.NewCounter(),
|
||||
TransactionsIndexed: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,10 @@ package indexer
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/tendermint/tendermint/scripts/metricsgen -struct=Metrics
|
||||
|
||||
// MetricsSubsystem is a the subsystem label for the indexer package.
|
||||
const MetricsSubsystem = "indexer"
|
||||
|
||||
@@ -25,49 +23,3 @@ type Metrics struct {
|
||||
// Number of transactions indexed.
|
||||
TransactionsIndexed metrics.Counter
|
||||
}
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
// Optionally, labels can be provided along with their values ("foo",
|
||||
// "fooValue").
|
||||
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_events_seconds",
|
||||
Help: "Latency for indexing block events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TxEventsSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "tx_events_seconds",
|
||||
Help: "Latency for indexing transaction events.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
BlocksIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "blocks_indexed",
|
||||
Help: "Number of complete blocks indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
TransactionsIndexed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "transactions_indexed",
|
||||
Help: "Number of transactions indexed.",
|
||||
}, labels).With(labelsAndValues...),
|
||||
}
|
||||
}
|
||||
|
||||
// NopMetrics returns an indexer metrics stub that discards all samples.
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
BlockEventsSeconds: discard.NewHistogram(),
|
||||
TxEventsSeconds: discard.NewHistogram(),
|
||||
BlocksIndexed: discard.NewCounter(),
|
||||
TransactionsIndexed: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
// metricsgen is a code generation tool for creating constructors for Tendermint
|
||||
// metrics types.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, `Usage: %[1]s -dir <dir> -struct <struct>
|
||||
|
||||
Generate constructors for the metrics type contained in the specified -dir
|
||||
Go directory. The tool creates a new file in the same directory as the specified directory
|
||||
containing the generated code.
|
||||
|
||||
Options:
|
||||
`, filepath.Base(os.Args[0]))
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
metricNameTag = "metricsgen_name"
|
||||
labelsTag = "metricsgen_labels"
|
||||
bucketTypeTag = "metricsgen_bucketsType"
|
||||
bucketSizeTag = "metricsgen_bucketSizes"
|
||||
)
|
||||
|
||||
var (
|
||||
dir = flag.String("dir", ".", "Path to the directory containing the target package")
|
||||
strct = flag.String("struct", "Metrics", "Struct to parse for metrics")
|
||||
)
|
||||
|
||||
var bucketType = map[string]string{
|
||||
"exprange": "stdprometheus.ExponentialBucketsRange",
|
||||
"exp": "stdprometheus.ExponentialBuckets",
|
||||
"lin": "stdprometheus.LinearBuckets",
|
||||
}
|
||||
|
||||
var tmpl = template.Must(template.New("tmpl").Parse(`// Code generated by metricsgen. DO NOT EDIT.
|
||||
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/metrics/discard"
|
||||
prometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func PrometheusMetrics(namespace string, labelsAndValues...string) *Metrics {
|
||||
labels := []string{}
|
||||
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||
labels = append(labels, labelsAndValues[i])
|
||||
}
|
||||
return &Metrics{
|
||||
{{ range $metric := .ParsedMetrics }}
|
||||
{{- $metric.FieldName }}: prometheus.New{{ $metric.TypeName }}From(stdprometheus.{{$metric.TypeName }}Opts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "{{$metric.MetricName }}",
|
||||
Help: "{{ $metric.Description }}",
|
||||
{{ if ne $metric.HistogramOptions.BucketType "" }}
|
||||
Buckets: {{ $metric.HistogramOptions.BucketType }}({{ $metric.HistogramOptions.BucketSizes }}),
|
||||
{{ else if ne $metric.HistogramOptions.BucketSizes "" }}
|
||||
Buckets: []float64{ {{ $metric.HistogramOptions.BucketSizes }} },
|
||||
{{ end }}
|
||||
{{- if eq (len $metric.Labels) 0 }}
|
||||
}, labels).With(labelsAndValues...),
|
||||
{{ else }}
|
||||
}, append(labels, {{$metric.Labels | printf "%q" }})).With(labelsAndValues...),
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func NopMetrics() *Metrics {
|
||||
return &Metrics{
|
||||
{{- range $metric := .ParsedMetrics }}
|
||||
{{ $metric.FieldName }}: discard.New{{ $metric.TypeName }}(),
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
`))
|
||||
|
||||
// ParsedMetricField is the data parsed for a single field of a metric struct.
|
||||
type ParsedMetricField struct {
|
||||
TypeName string
|
||||
FieldName string
|
||||
MetricName string
|
||||
Description string
|
||||
Labels string
|
||||
|
||||
HistogramOptions HistogramOpts
|
||||
}
|
||||
|
||||
type HistogramOpts struct {
|
||||
BucketType string
|
||||
BucketSizes string
|
||||
}
|
||||
|
||||
// TemplateData is all of the data required for rendering a metric file template.
|
||||
type TemplateData struct {
|
||||
Package string
|
||||
ParsedMetrics []ParsedMetricField
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *strct == "" {
|
||||
log.Fatal("You must specify a non-empty -struct")
|
||||
}
|
||||
if *dir == "" {
|
||||
log.Fatal("You must specify a non-empty -dir")
|
||||
}
|
||||
td, err := ParseMetricsDir(*dir, *strct)
|
||||
if err != nil {
|
||||
log.Fatalf("Parsing file: %v", err)
|
||||
}
|
||||
out := filepath.Join(*dir, "metrics.gen.go")
|
||||
f, err := os.Create(out)
|
||||
if err != nil {
|
||||
log.Fatalf("Opening file: %v", err)
|
||||
}
|
||||
err = GenerateMetricsFile(f, td)
|
||||
if err != nil {
|
||||
log.Fatalf("Generating code: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseMetricsDir parses the dir and scans for a struct matching structName,
|
||||
// ignoring all test files. ParseMetricsDir iterates the fields of the metrics
|
||||
// struct and builds a TemplateData using the data obtained from the abstract syntax tree.
|
||||
func ParseMetricsDir(dir string, structName string) (TemplateData, error) {
|
||||
ignoreTestFiles := func(f fs.FileInfo) bool {
|
||||
return !strings.Contains(f.Name(), "_test.go")
|
||||
}
|
||||
fs := token.NewFileSet()
|
||||
d, err := parser.ParseDir(fs, dir, ignoreTestFiles, parser.ParseComments)
|
||||
if err != nil {
|
||||
return TemplateData{}, err
|
||||
}
|
||||
if len(d) > 1 {
|
||||
return TemplateData{}, fmt.Errorf("multiple packages found in %s", dir)
|
||||
}
|
||||
if len(d) == 0 {
|
||||
return TemplateData{}, fmt.Errorf("no go pacakges found in %s", dir)
|
||||
}
|
||||
|
||||
// Grab the package name.
|
||||
var pkgName string
|
||||
var pkg *ast.Package
|
||||
for pkgName, pkg = range d {
|
||||
}
|
||||
td := TemplateData{
|
||||
Package: pkgName,
|
||||
}
|
||||
// Grab the metrics struct
|
||||
m, err := fetchMetricsStruct(pkg.Files, structName)
|
||||
if err != nil {
|
||||
return TemplateData{}, err
|
||||
}
|
||||
for _, f := range m.Fields.List {
|
||||
if !isMetric(f.Type) {
|
||||
continue
|
||||
}
|
||||
pmf := parseMetricField(f)
|
||||
td.ParsedMetrics = append(td.ParsedMetrics, pmf)
|
||||
}
|
||||
|
||||
return td, err
|
||||
}
|
||||
|
||||
// GenerateMetricsFile executes the metrics file template, writing the result
|
||||
// into the io.Writer.
|
||||
func GenerateMetricsFile(w io.Writer, td TemplateData) error {
|
||||
b := []byte{}
|
||||
buf := bytes.NewBuffer(b)
|
||||
err := tmpl.Execute(buf, td)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err = format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(w, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchMetricsStruct(files map[string]*ast.File, structName string) (*ast.StructType, error) {
|
||||
var (
|
||||
err error
|
||||
st *ast.StructType
|
||||
)
|
||||
for _, file := range files {
|
||||
if !ast.FilterFile(file, func(name string) bool {
|
||||
return name == structName
|
||||
}) {
|
||||
continue
|
||||
}
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
switch f := n.(type) {
|
||||
case *ast.TypeSpec:
|
||||
if f.Name.Name == structName {
|
||||
var ok bool
|
||||
st, ok = f.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
err = fmt.Errorf("found identifier for %q of wrong type", structName)
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st != nil {
|
||||
return st, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("target struct %q not found in dir", structName)
|
||||
}
|
||||
|
||||
func parseMetricField(f *ast.Field) ParsedMetricField {
|
||||
var comment string
|
||||
if f.Doc != nil {
|
||||
for _, c := range f.Doc.List {
|
||||
comment += strings.TrimPrefix(c.Text, "// ")
|
||||
}
|
||||
}
|
||||
pmf := ParsedMetricField{
|
||||
Description: comment,
|
||||
MetricName: extractFieldName(f.Names[0].String(), f.Tag),
|
||||
FieldName: f.Names[0].String(),
|
||||
TypeName: extractTypeName(f.Type),
|
||||
Labels: extractLabels(f.Tag),
|
||||
}
|
||||
if pmf.TypeName == "Histogram" {
|
||||
pmf.HistogramOptions = extractHistogramOptions(f.Tag)
|
||||
}
|
||||
return pmf
|
||||
}
|
||||
|
||||
func extractTypeName(e ast.Expr) string {
|
||||
return strings.TrimPrefix(types.ExprString(e), "metrics.")
|
||||
}
|
||||
|
||||
func isMetric(e ast.Expr) bool {
|
||||
return strings.Contains(types.ExprString(e), "metrics.")
|
||||
}
|
||||
|
||||
func extractLabels(bl *ast.BasicLit) string {
|
||||
if bl != nil {
|
||||
t := reflect.StructTag(strings.Trim(bl.Value, "`"))
|
||||
if v := t.Get(labelsTag); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractFieldName(name string, tag *ast.BasicLit) string {
|
||||
if tag != nil {
|
||||
t := reflect.StructTag(strings.Trim(tag.Value, "`"))
|
||||
if v := t.Get(metricNameTag); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return toSnakeCase(name)
|
||||
}
|
||||
|
||||
func extractHistogramOptions(tag *ast.BasicLit) HistogramOpts {
|
||||
h := HistogramOpts{}
|
||||
if tag != nil {
|
||||
t := reflect.StructTag(strings.Trim(tag.Value, "`"))
|
||||
if v := t.Get(bucketTypeTag); v != "" {
|
||||
h.BucketType = bucketType[v]
|
||||
}
|
||||
if v := t.Get(bucketSizeTag); v != "" {
|
||||
h.BucketSizes = v
|
||||
}
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
var capitalChange = regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||
|
||||
func toSnakeCase(str string) string {
|
||||
snake := capitalChange.ReplaceAllString(str, "${1}_${2}")
|
||||
return strings.ToLower(snake)
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metricsgen "github.com/tendermint/tendermint/scripts/metricsgen"
|
||||
)
|
||||
|
||||
const testDataDir = "./testdata"
|
||||
|
||||
func TestSimpleTemplate(t *testing.T) {
|
||||
m := metricsgen.ParsedMetricField{
|
||||
TypeName: "Histogram",
|
||||
FieldName: "MyMetric",
|
||||
MetricName: "request_count",
|
||||
Description: "how many requests were made since the start of the process",
|
||||
Labels: "first, second, third",
|
||||
}
|
||||
td := metricsgen.TemplateData{
|
||||
Package: "mypack",
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{m},
|
||||
}
|
||||
b := bytes.NewBuffer([]byte{})
|
||||
err := metricsgen.GenerateMetricsFile(b, td)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse template %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromData(t *testing.T) {
|
||||
infos, err := ioutil.ReadDir(testDataDir)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file %v", err)
|
||||
}
|
||||
for _, dir := range infos {
|
||||
t.Run(dir.Name(), func(t *testing.T) {
|
||||
if !dir.IsDir() {
|
||||
t.Fatalf("expected file %s to be directory", dir.Name())
|
||||
}
|
||||
dirName := path.Join(testDataDir, dir.Name())
|
||||
pt, err := metricsgen.ParseMetricsDir(dirName, "Metrics")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to parse from dir %q: %v", dir, err)
|
||||
}
|
||||
outFile := path.Join(dirName, "out.go")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file %s: %v", outFile, err)
|
||||
}
|
||||
of, err := os.Create(outFile)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file %s: %v", outFile, err)
|
||||
}
|
||||
defer os.Remove(outFile)
|
||||
if err := metricsgen.GenerateMetricsFile(of, pt); err != nil {
|
||||
t.Fatalf("unable to generate metrics file %s: %v", outFile, err)
|
||||
}
|
||||
if _, err := parser.ParseFile(token.NewFileSet(), outFile, nil, parser.AllErrors); err != nil {
|
||||
t.Fatalf("unable to parse generated file %s: %v", outFile, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMetricsStruct(t *testing.T) {
|
||||
const pkgName = "mypkg"
|
||||
metricsTests := []struct {
|
||||
name string
|
||||
shouldError bool
|
||||
metricsStruct string
|
||||
expected metricsgen.TemplateData
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
metricsStruct: `type Metrics struct {
|
||||
myGauge metrics.Gauge
|
||||
}`,
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Gauge",
|
||||
FieldName: "myGauge",
|
||||
MetricName: "my_gauge",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "histogram",
|
||||
metricsStruct: "type Metrics struct {\n" +
|
||||
"myHistogram metrics.Histogram `metricsgen_bucketsType:\"exp\" metricsgen_bucketSizes:\"1, 100, .8\"`\n" +
|
||||
"}",
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Histogram",
|
||||
FieldName: "myHistogram",
|
||||
MetricName: "my_histogram",
|
||||
|
||||
HistogramOptions: metricsgen.HistogramOpts{
|
||||
BucketType: "stdprometheus.ExponentialBuckets",
|
||||
BucketSizes: "1, 100, .8",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "labeled name",
|
||||
metricsStruct: "type Metrics struct {\n" +
|
||||
"myCounter metrics.Counter `metricsgen_name:\"new_name\"`\n" +
|
||||
"}",
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Counter",
|
||||
FieldName: "myCounter",
|
||||
MetricName: "new_name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "metric labels",
|
||||
metricsStruct: "type Metrics struct {\n" +
|
||||
"myCounter metrics.Counter `metricsgen_labels:\"label1, label2\"`\n" +
|
||||
"}",
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Counter",
|
||||
FieldName: "myCounter",
|
||||
MetricName: "my_counter",
|
||||
Labels: "label1, label2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignore non-metric field",
|
||||
metricsStruct: `type Metrics struct {
|
||||
myCounter metrics.Counter
|
||||
nonMetric string
|
||||
}`,
|
||||
expected: metricsgen.TemplateData{
|
||||
Package: pkgName,
|
||||
ParsedMetrics: []metricsgen.ParsedMetricField{
|
||||
{
|
||||
TypeName: "Counter",
|
||||
FieldName: "myCounter",
|
||||
MetricName: "my_counter",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, testCase := range metricsTests {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
dir, err := os.MkdirTemp(os.TempDir(), "metricsdir")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create directory: %v", err)
|
||||
}
|
||||
defer os.Remove(dir)
|
||||
f, err := os.Create(filepath.Join(dir, "metrics.go"))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to open file: %v", err)
|
||||
}
|
||||
pkgLine := fmt.Sprintf("package %s\n", pkgName)
|
||||
importClause := `
|
||||
import(
|
||||
"github.com/go-kit/kit/metrics"
|
||||
)
|
||||
`
|
||||
|
||||
f.Write([]byte(pkgLine))
|
||||
f.Write([]byte(importClause))
|
||||
f.Write([]byte(testCase.metricsStruct))
|
||||
|
||||
td, err := metricsgen.ParseMetricsDir(dir, "Metrics")
|
||||
if testCase.shouldError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testCase.expected, td)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package basic
|
||||
|
||||
import "github.com/go-kit/kit/metrics"
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
type Metrics struct {
|
||||
// simple metric that tracks the height of the chain.
|
||||
Height metrics.Gauge
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package commented
|
||||
|
||||
import "github.com/go-kit/kit/metrics"
|
||||
|
||||
type Metrics struct {
|
||||
// Height of the chain.
|
||||
// We expect multi-line comments to parse correctly.
|
||||
Field metrics.Gauge
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package tags
|
||||
|
||||
import "github.com/go-kit/kit/metrics"
|
||||
|
||||
type Metrics struct {
|
||||
WithLabels metrics.Counter `metricsgen_labels:"step,time"`
|
||||
WithExpBuckets metrics.Histogram `metricsgen_bucketsType:"exp" metricsgen_bucketSizes:".1,100,8"`
|
||||
WithBuckets metrics.Histogram `metricsgen_bucketSizes:"1, 2, 3, 4, 5"`
|
||||
Named metrics.Counter `metricsgen_name:"metric_with_name"`
|
||||
}
|
||||
Reference in New Issue
Block a user