Skip to content

Instantly share code, notes, and snippets.

@Daniel-W-Innes
Created September 18, 2025 17:50
Show Gist options
  • Save Daniel-W-Innes/b1e252f95958b9546c1eee935425dd75 to your computer and use it in GitHub Desktop.
Save Daniel-W-Innes/b1e252f95958b9546c1eee935425dd75 to your computer and use it in GitHub Desktop.
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"sort"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
func parseMF(path string) (map[string]*dto.MetricFamily, error) {
reader, err := os.Open(path)
if err != nil {
return nil, err
}
var parser expfmt.TextParser
mf, err := parser.TextToMetricFamilies(reader)
if err != nil {
return nil, err
}
return mf, nil
}
type (
Labels struct {
Count uint
Set map[string]struct{}
}
MetricInfo struct {
Name string
Type string
Zeros uint
ZerosPercent float64
Count uint
CountPercent float64
Labels map[string]Labels
}
Info struct {
TotalCount uint
TotalZeros uint
Metrics []MetricInfo
}
)
func (m *MetricInfo) AddLabels(labels []*dto.LabelPair) {
for _, label := range labels {
if label == nil {
continue
}
if l, ok := m.Labels[label.GetName()]; !ok {
m.Labels[label.GetName()] = Labels{
Count: 1,
Set: map[string]struct{}{label.GetValue(): {}},
}
} else if _, exists := l.Set[label.GetValue()]; !exists {
l.Set[label.GetValue()] = struct{}{}
l.Count++
m.Labels[label.GetName()] = l
}
}
}
func (m *MetricInfo) AddCount(mType dto.MetricType, metric *dto.Metric) error {
switch mType {
case dto.MetricType_COUNTER:
if metric.GetCounter().GetValue() == 0 {
m.Zeros++
}
m.Count++
case dto.MetricType_GAUGE:
if metric.GetGauge().GetValue() == 0 {
m.Zeros++
}
m.Count++
case dto.MetricType_SUMMARY:
for _, quantile := range metric.GetSummary().Quantile {
if quantile == nil {
continue
}
if quantile.GetValue() == 0 {
m.Zeros++
}
m.Count++
}
case dto.MetricType_HISTOGRAM:
for _, bucket := range metric.GetHistogram().Bucket {
if bucket == nil {
continue
}
if bucket.GetCumulativeCount() == 0 {
m.Zeros++
}
m.Count++
}
default:
return fmt.Errorf("unknown metric type %s for metric %s", mType.String(), m.Name)
}
return nil
}
func main() {
f := flag.String("f", "", "path to the Prometheus metrics file")
flag.Parse()
mf, err := parseMF(*f)
if err != nil {
log.Fatalf("failed to parse metrics file: %v", err)
}
out := Info{}
for k, v := range mf {
if v == nil {
continue
}
if len(v.Metric) == 0 {
continue
}
info := MetricInfo{
Name: k,
Type: v.GetType().String(),
Zeros: 0,
Count: 0,
Labels: map[string]Labels{},
}
for _, m := range v.Metric {
if m == nil {
continue
}
info.AddLabels(m.GetLabel())
if err := info.AddCount(v.GetType(), m); err != nil {
log.Printf("failed to add counts for metric %s: %v", k, err)
continue
}
}
info.ZerosPercent = float64(info.Zeros) / float64(info.Count) * 100
out.TotalCount += info.Count
out.TotalZeros += info.Zeros
out.Metrics = append(out.Metrics, info)
}
sort.Slice(out.Metrics, func(i, j int) bool {
if out.Metrics[i].Count == out.Metrics[j].Count {
return out.Metrics[i].Zeros > out.Metrics[j].Zeros
}
return out.Metrics[i].Count > out.Metrics[j].Count
})
for i := range out.Metrics {
out.Metrics[i].CountPercent = float64(out.Metrics[i].Count) / float64(out.TotalCount) * 100
}
b, err := json.MarshalIndent(out, "", " ")
if err != nil {
log.Fatalf("failed to marshal output: %v", err)
}
if err := os.WriteFile("metrics.json", b, 0o644); err != nil {
log.Fatalf("failed to write metrics.json: %v", err)
}
fmt.Println("Metrics written to metrics.json")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment