Skip to content

Commit

Permalink
refactor: autogenerate metrics docs (#13943)
Browse files Browse the repository at this point in the history
Signed-off-by: Alan Clucas <[email protected]>
  • Loading branch information
Joibel authored Dec 12, 2024
1 parent ad994b4 commit 9518252
Show file tree
Hide file tree
Showing 33 changed files with 1,578 additions and 442 deletions.
21 changes: 17 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ lint: ui/dist/app/index.html $(GOPATH)/bin/golangci-lint

# for local we have a faster target that prints to stdout, does not use json, and can cache because it has no coverage
.PHONY: test
test: ui/dist/app/index.html
test: ui/dist/app/index.html util/telemetry/metrics_list.go util/telemetry/attributes.go
go build ./...
env KUBECONFIG=/dev/null $(GOTEST) ./...
# marker file, based on it's modification time, we know how long ago this target was run
Expand Down Expand Up @@ -621,8 +621,21 @@ clean:
go clean
rm -Rf test-results node_modules vendor v2 v3 argoexec-linux-amd64 dist/* ui/dist

# swagger
# Build telemetry files
TELEMETRY_BUILDER := $(shell find util/telemetry/builder -type f -name '*.go')
docs/metrics.md: $(TELEMETRY_BUILDER) util/telemetry/builder/values.yaml
@echo Rebuilding $@
go run ./util/telemetry/builder --metricsDocs $@

util/telemetry/metrics_list.go: $(TELEMETRY_BUILDER) util/telemetry/builder/values.yaml
@echo Rebuilding $@
go run ./util/telemetry/builder --metricsListGo $@

util/telemetry/attributes.go: $(TELEMETRY_BUILDER) util/telemetry/builder/values.yaml
@echo Rebuilding $@
go run ./util/telemetry/builder --attributesGo $@

# swagger
pkg/apis/workflow/v1alpha1/openapi_generated.go: $(GOPATH)/bin/openapi-gen $(TYPES)
# These files are generated on a v3/ folder by the tool. Link them to the root folder
[ -e ./v3 ] || ln -s . v3
Expand Down Expand Up @@ -707,7 +720,7 @@ ifneq ($(USE_NIX), true)
endif

.PHONY: docs-spellcheck
docs-spellcheck: /usr/local/bin/mdspell
docs-spellcheck: /usr/local/bin/mdspell docs/metrics.md
# check docs for spelling mistakes
mdspell --ignore-numbers --ignore-acronyms --en-us --no-suggestions --report $(shell find docs -name '*.md' -not -name upgrading.md -not -name README.md -not -name fields.md -not -name upgrading.md -not -name executor_swagger.md -not -path '*/cli/*')
# alphabetize spelling file -- ignore first line (comment), then sort the rest case-sensitive and remove duplicates
Expand All @@ -732,7 +745,7 @@ endif


.PHONY: docs-lint
docs-lint: /usr/local/bin/markdownlint
docs-lint: /usr/local/bin/markdownlint docs/metrics.md
# lint docs
markdownlint docs --fix --ignore docs/fields.md --ignore docs/executor_swagger.md --ignore docs/cli --ignore docs/walk-through/the-structure-of-workflow-specs.md

Expand Down
307 changes: 186 additions & 121 deletions docs/metrics.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ require (
github.com/jcmturner/gokrb5/v8 v8.4.4
github.com/klauspost/pgzip v1.2.6
github.com/minio/minio-go/v7 v7.0.77
github.com/nao1215/markdown v0.6.0
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/common v0.55.0
github.com/robfig/cron/v3 v3.0.1
Expand Down Expand Up @@ -117,13 +118,17 @@ require (
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/karrick/godirwalk v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/segmentio/fasthash v1.0.3 // indirect
Expand Down
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/karrick/godirwalk v1.7.8/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI=
github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
Expand Down Expand Up @@ -613,6 +615,9 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
Expand Down Expand Up @@ -653,13 +658,17 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nao1215/markdown v0.6.0 h1:kqhrC47K434YA1jMTUwJwSV/hla8ifN3NzehMEffI/E=
github.com/nao1215/markdown v0.6.0/go.mod h1:ObBhnNduWwPN+bu4dtv4JoLRt57ONla7l//03iHIVhY=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand Down Expand Up @@ -710,6 +719,9 @@ github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qq
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
Expand Down
58 changes: 24 additions & 34 deletions util/telemetry/attributes.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

192 changes: 192 additions & 0 deletions util/telemetry/builder/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package main

import (
_ "embed"
"errors"
"flag"
"fmt"
"os"
"regexp"
"slices"
"strings"
"unicode"

"sigs.k8s.io/yaml"
)

const generatedBanner string = "// Code generated by util/telemetry/builder. DO NOT EDIT."

//go:embed values.yaml
var valuesYaml []byte

type attribute struct {
Name string `json:"name"`
DisplayName string `json:"displayName,omitempty"`
// Description is a markdown explanation for the documentation. One line only.
Description string `json:"description"`
}

type allowedAttribute struct {
Name string `json:"name"`
Optional bool `json:"optional,omitempty"`
}

type metric struct {
// Name: Metric name, in CamelCaps
// Will be snake cased for display purposes
Name string `json:"name"`
// Description: short description, emitted on the metrics endpoint and added to the documentation. Do not use marrkdown here.
Description string `json:"description"`
// ExtendedDescription: Markdown capable further description added to the documentation before attributes
ExtendedDescription string `json:"extendedDescription,omitempty"`
// Notes: Markdown capable further description added to the documentation after attributes
Notes string `json:"notes,omitempty"`
Attributes []allowedAttribute `json:"attributes,omitempty"`
// Unit: OpenTelemetry unit of measurement https://opentelemetry.io/docs/specs/otel/metrics/api/#instrument-unit
Unit string `json:"unit"`
Type string `json:"type"`
DefaultBuckets []float64 `json:"defaultBuckets,omitempty"`
}

type attributesList []attribute
type metricsList []metric

type values struct {
Attributes attributesList `json:"attributes"`
Metrics metricsList `json:"metrics"`
}

func load() values {
var vals values
err := yaml.UnmarshalStrict(valuesYaml, &vals)
if err != nil {
panic(err)
}
return vals
}

var collectedErrors []error

func recordErrorString(err string) {
collectedErrors = append(collectedErrors, errors.New(err))
}
func recordError(err error) {
collectedErrors = append(collectedErrors, err)
}

func main() {
metricsDocs := flag.String("metricsDocs", "", "Path to metrics.md in the docs")
attributesGo := flag.String("attributesGo", "", "Path to attributes.go in util/telemetry")
metricsListGo := flag.String("metricsListGo", "", "Path to metrics_list.go in util/telemetry")
flag.Parse()
vals := load()
validate(&vals)
if len(collectedErrors) == 0 {
if metricsDocs != nil && *metricsDocs != "" {
createMetricsDocs(*metricsDocs, &vals.Metrics, &vals.Attributes)
}
if attributesGo != nil && *attributesGo != "" {
createAttributesGo(*attributesGo, &vals.Attributes)
}
if metricsListGo != nil && *metricsListGo != "" {
createMetricsListGo(*metricsListGo, &vals.Metrics)
}
}
if len(collectedErrors) > 0 {
for _, err := range collectedErrors {
fmt.Println(err)
}
os.Exit(1)
}
}

func upperToSnake(in string) string {
runes := []rune(in)
in = string(append([]rune{unicode.ToLower(runes[0])}, runes[1:]...))
re := regexp.MustCompile(`[A-Z]`)
return string(re.ReplaceAllFunc([]byte(in), func(in []byte) []byte {
return []byte(fmt.Sprintf("_%s", strings.ToLower(string(in[0]))))
}))
}

func (a *attribute) displayName() string {
name := a.Name
if a.DisplayName != "" {
name = a.DisplayName
}
return upperToSnake(name)
}

func validateMetricsAttributes(metrics *metricsList, attributes *attributesList) {
for _, metric := range *metrics {
for _, attribute := range metric.Attributes {
if getAttribByName(attribute.Name, attributes) == nil {
recordErrorString(fmt.Sprintf("Metric %s: attribute %s not defined", metric.Name, attribute.Name))
}
}
}
}

func validateAttributes(attributes *attributesList) {
if !slices.IsSortedFunc(*attributes, func(a, b attribute) int {
return strings.Compare(a.Name, b.Name)
}) {
recordErrorString("Attributes must be alphabetically sorted by Name")
}
for _, attribute := range *attributes {
if strings.Contains(attribute.Description, "\n") {
recordErrorString(fmt.Sprintf("%s: Description must be a single line", attribute.Name))
}
}
}

func validateMetrics(metrics *metricsList) {
if !slices.IsSortedFunc(*metrics, func(a, b metric) int {
return strings.Compare(a.Name, b.Name)
}) {
recordErrorString("Metrics must be alphabetically sorted by Name")
}
for _, metric := range *metrics {
// This is easier than enum+custom JSON unmarshall as this is not critical code
switch metric.Type {
case "Float64Histogram":
case "Float64ObservableGauge":
case "Int64Counter":
case "Int64UpDownCounter":
case "Int64ObservableGauge":
break
default:
recordErrorString(fmt.Sprintf("%s: Invalid metric type %s", metric.Name, metric.Type))
}
if strings.Contains(metric.Description, "\n") {
recordErrorString(fmt.Sprintf("%s: Description must be a single line", metric.Name))
}
if strings.HasSuffix(metric.Description, ".") {
recordErrorString(fmt.Sprintf("%s: Description must not have a trailing period", metric.Name))
}
}
}

func validate(vals *values) {
validateAttributes(&vals.Attributes)
validateMetrics(&vals.Metrics)
validateMetricsAttributes(&vals.Metrics, &vals.Attributes)
}

func (m *metric) instrumentType() string {
return m.Type
}

func (m *metric) displayName() string {
name := m.Name
return upperToSnake(name)
}

func getAttribByName(name string, attribs *attributesList) *attribute {
for _, attrib := range *attribs {
if name == attrib.Name {
return &attrib
}
}
return nil
}
Loading

0 comments on commit 9518252

Please sign in to comment.