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

Add metricsmd package #3

Merged
merged 8 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .gitattributes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think GitHub automatically does this appropriately?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, does it? That's cool.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah go.sum is always displayed but vendored stuff is redacted except for appropriate stuff like vendor/modules.txt. See your PR for example https://github.com/cilium/tetragon/pull/2164/files.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
go.sum linguist-generated
vendor/* linguist-vendored
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
GO := go

.PHONY: all
all:

.PHONY: vendor
vendor:
$(GO) mod tidy
$(GO) mod vendor
$(GO) mod verify
39 changes: 39 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module github.com/isovalent/metricstool

go 1.22.0

require (
github.com/prometheus/client_golang v1.18.0
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
95 changes: 95 additions & 0 deletions go.sum

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

43 changes: 43 additions & 0 deletions pkg/metricsmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Isovalent Inc.

package metricsmd

import (
"fmt"
"log/slog"

"github.com/prometheus/client_golang/prometheus"
)

type LabelValues struct {
Label string
Values []string
}

type LabelOverrides struct {
Metric string
Overrides []LabelValues
}

type Config struct {
CobraAnnotations map[string]string
Targets map[string]string // cli argument -> docs header
LabelOverrides []LabelOverrides
InitMetrics func(target string, reg *prometheus.Registry, log *slog.Logger) error
AutogeneratedComment bool
HeadingLevel int // must be between 0 and 4
}

func validateConfig(config *Config) error {
if config.Targets == nil {
return fmt.Errorf("config.Targets must be set")
}
if config.InitMetrics == nil {
return fmt.Errorf("config.InitMetrics must be set")
}
if config.HeadingLevel < 0 || config.HeadingLevel > 4 {
return fmt.Errorf("config.HeadingLevel must be between 0 and 4")
}
return nil
}
99 changes: 99 additions & 0 deletions pkg/metricsmd/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Isovalent Inc.

package metricsmd

import (
"fmt"
"io"
"sort"
"strings"

"github.com/prometheus/client_golang/prometheus"
)

// Generate
func Generate(reg *prometheus.Registry, w io.Writer, config *Config) error {
metricsFamilies, err := reg.Gather()
if err != nil {
return err
}
sort.Slice(metricsFamilies, func(i, j int) bool {
return metricsFamilies[i].GetName() < metricsFamilies[j].GetName()
})

for _, metric := range metricsFamilies {
// Include the metric name and help text.
h := "##"
for i := 0; i < config.HeadingLevel; i++ {
h += "#"
}
io.WriteString(w, fmt.Sprintf("%s `%s`\n\n", h, metric.GetName()))
io.WriteString(w, fmt.Sprintf("%s\n\n", metric.GetHelp()))
// The rest is generating a list of label names and values

// map of "label_name" -> set([label_values...])
labelsToValues := make(map[string]map[string]struct{})

// Iterate over the series
series := metric.GetMetric()
for _, m := range series {
for _, label := range m.GetLabel() {
// Check if the map entry exists
_, ok := labelsToValues[label.GetName()]
if !ok {
// Initialize it
labelsToValues[label.GetName()] = make(map[string]struct{})
}
// Add the value to the set of values for this label
if val := label.GetValue(); val != "" {
labelsToValues[label.GetName()][val] = struct{}{}
}
}
}

// Support overriding the values of labels, in case they're not suitable for docs.
for _, override := range config.LabelOverrides {
if override.Metric != metric.GetName() {
continue
}
for _, o := range override.Overrides {
// Erase the current labels if any exist
labelsToValues[o.Label] = make(map[string]struct{})
for _, overrideVal := range o.Values {
labelsToValues[o.Label][overrideVal] = struct{}{}
}
}
}

// Generate a list of labels and their values
var finalLabels []LabelValues
for label, valuesMap := range labelsToValues {
var vals []string
for val := range valuesMap {
vals = append(vals, val)
}
// Sort the values
sort.Strings(vals)
finalLabels = append(finalLabels, LabelValues{
Label: label,
Values: vals,
})
}

// Write out the labels out if there are any
if len(finalLabels) > 0 {
sort.Slice(finalLabels, func(i, j int) bool {
return finalLabels[i].Label < finalLabels[j].Label
})
io.WriteString(w, "| label | values |\n")
io.WriteString(w, "| ----- | ------ |\n")
for _, labelVal := range finalLabels {
row := fmt.Sprintf("| `%-5s` | `%5s` |\n", labelVal.Label, strings.Join(labelVal.Values, ", "))
io.WriteString(w, row)
}
io.WriteString(w, "\n")
}
}
return nil
}
73 changes: 73 additions & 0 deletions pkg/metricsmd/print.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Isovalent Inc.

package metricsmd

import (
"bytes"
"fmt"
"io"
"log/slog"

"golang.org/x/exp/maps"

"github.com/prometheus/client_golang/prometheus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// NewCmd creates a Cobra command for generating metrics reference docs.
// It's intended to be used as an add-on to applications that have a Cobra CLI
// and expose Prometheus metrics.
func NewCmd(vp *viper.Viper, log *slog.Logger, config *Config) (*cobra.Command, error) {
err := validateConfig(config)
if err != nil {
return nil, err
}
cmd := &cobra.Command{
Use: "metrics-docs",
Hidden: true,
Annotations: config.CobraAnnotations,
ValidArgs: maps.Keys(config.Targets),
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
PreRunE: func(cmd *cobra.Command, _ []string) error {
if vp != nil {
flags := cmd.Flags()
if err := vp.BindPFlags(flags); err != nil {
return err
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
target := args[0]
reg := prometheus.NewRegistry()

err := config.InitMetrics(target, reg, log)
if err != nil {
return err
}

var b bytes.Buffer
if config.AutogeneratedComment {
// Comment to inform people this file is autogenerated.
b.WriteString("<!-- This file is autogenerated via https://github.com/isovalent/metricstool -->\n\n")
}
// Document title
h := "#"
for i := 0; i < config.HeadingLevel; i++ {
h += "#"
}
b.WriteString(fmt.Sprintf("%s %s Metrics\n\n", h, config.Targets[target]))
// Generate metrics reference
err = Generate(reg, &b, config)
if err != nil {
return err
}
io.Copy(cmd.OutOrStdout(), &b)

return nil
},
}
return cmd, nil
}
20 changes: 20 additions & 0 deletions vendor/github.com/beorn7/perks/LICENSE

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

Loading