From 9c7a9d5157934f090892075cbcb0f9ae5dd580a9 Mon Sep 17 00:00:00 2001 From: Kemal Akkoyun Date: Fri, 5 Aug 2022 18:21:25 +0200 Subject: [PATCH 1/2] Add tests and examples Signed-off-by: Kemal Akkoyun --- prometheus/collector.go | 6 +- prometheus/collectors/go_collector_latest.go | 11 +- .../collectors/go_collector_latest_test.go | 254 ++++++++++++++++++ 3 files changed, 266 insertions(+), 5 deletions(-) diff --git a/prometheus/collector.go b/prometheus/collector.go index ac1ca3cf5..cf05079fb 100644 --- a/prometheus/collector.go +++ b/prometheus/collector.go @@ -69,9 +69,9 @@ type Collector interface { // If a Collector collects the same metrics throughout its lifetime, its // Describe method can simply be implemented as: // -// func (c customCollector) Describe(ch chan<- *Desc) { -// DescribeByCollect(c, ch) -// } +// func (c customCollector) Describe(ch chan<- *Desc) { +// DescribeByCollect(c, ch) +// } // // However, this will not work if the metrics collected change dynamically over // the lifetime of the Collector in a way that their combined set of descriptors diff --git a/prometheus/collectors/go_collector_latest.go b/prometheus/collectors/go_collector_latest.go index 232601002..f686f1aba 100644 --- a/prometheus/collectors/go_collector_latest.go +++ b/prometheus/collectors/go_collector_latest.go @@ -23,7 +23,14 @@ import ( "github.com/prometheus/client_golang/prometheus/internal" ) -// WithGoCollectorMemStatsMetricsDisabled metrics that where representing runtime.MemStats structure such as: +var ( + MetricsAll = regexp.MustCompile("/.*") + MetricsGC = GoRuntimeMetricsRule{regexp.MustCompile(`^/gc/.*`)} + MetricsMemory = GoRuntimeMetricsRule{regexp.MustCompile(`^/memory/.*`)} + MetricsScheduler = GoRuntimeMetricsRule{regexp.MustCompile(`^/sched/.*`)} +) + +// WithGoCollectorMemStatsMetricsDisabled disables metrics that is gathered in runtime.MemStats structure such as: // // go_memstats_alloc_bytes // go_memstats_alloc_bytes_total @@ -90,7 +97,7 @@ func WithGoCollectorRuntimeMetrics(rules ...GoRuntimeMetricsRule) func(options * } return func(o *internal.GoCollectorOptions) { - o.DisableMemStatsLikeMetrics = true + o.RuntimeMetricRules = append(o.RuntimeMetricRules, rs...) } } diff --git a/prometheus/collectors/go_collector_latest_test.go b/prometheus/collectors/go_collector_latest_test.go index 06d9781a1..822f5495f 100644 --- a/prometheus/collectors/go_collector_latest_test.go +++ b/prometheus/collectors/go_collector_latest_test.go @@ -18,12 +18,25 @@ package collectors import ( "encoding/json" + "log" + "net/http" + "reflect" "regexp" + "sort" "testing" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" ) +var baseMetrics = []string{ + "go_gc_duration_seconds", + "go_goroutines", + "go_info", + "go_memstats_last_gc_time_seconds", + "go_threads", +} + func TestGoCollectorMarshalling(t *testing.T) { reg := prometheus.NewRegistry() reg.MustRegister(NewGoCollector( @@ -41,8 +54,249 @@ func TestGoCollectorMarshalling(t *testing.T) { } } +func TestWithGoCollectorMemStatsMetricsDisabled(t *testing.T) { + reg := prometheus.NewRegistry() + reg.MustRegister(NewGoCollector( + WithGoCollectorMemStatsMetricsDisabled(), + )) + result, err := reg.Gather() + if err != nil { + t.Fatal(err) + } + + got := []string{} + for _, r := range result { + got = append(got, r.GetName()) + } + + if !reflect.DeepEqual(got, baseMetrics) { + t.Errorf("got %v, want %v", got, baseMetrics) + } +} + +func TestGoCollectorAllowList(t *testing.T) { + for _, test := range []struct { + name string + rules []GoRuntimeMetricsRule + expected []string + }{ + { + name: "Without any rules", + rules: nil, + expected: baseMetrics, + }, + { + name: "allow all", + rules: []GoRuntimeMetricsRule{{ + Matcher: regexp.MustCompile("/.*"), + }}, + expected: withBaseMetrics([]string{ + "go_gc_cycles_automatic_gc_cycles_total", + "go_gc_cycles_forced_gc_cycles_total", + "go_gc_cycles_total_gc_cycles_total", + "go_gc_heap_allocs_by_size_bytes", + "go_gc_heap_allocs_bytes_total", + "go_gc_heap_allocs_objects_total", + "go_gc_heap_frees_by_size_bytes", + "go_gc_heap_frees_bytes_total", + "go_gc_heap_frees_objects_total", + "go_gc_heap_goal_bytes", + "go_gc_heap_objects_objects", + "go_gc_heap_tiny_allocs_objects_total", + "go_gc_pauses_seconds", + "go_memory_classes_heap_free_bytes", + "go_memory_classes_heap_objects_bytes", + "go_memory_classes_heap_released_bytes", + "go_memory_classes_heap_stacks_bytes", + "go_memory_classes_heap_unused_bytes", + "go_memory_classes_metadata_mcache_free_bytes", + "go_memory_classes_metadata_mcache_inuse_bytes", + "go_memory_classes_metadata_mspan_free_bytes", + "go_memory_classes_metadata_mspan_inuse_bytes", + "go_memory_classes_metadata_other_bytes", + "go_memory_classes_os_stacks_bytes", + "go_memory_classes_other_bytes", + "go_memory_classes_profiling_buckets_bytes", + "go_memory_classes_total_bytes", + "go_sched_goroutines_goroutines", + "go_sched_latencies_seconds", + }), + }, + { + name: "allow GC", + rules: []GoRuntimeMetricsRule{MetricsGC}, + expected: withBaseMetrics([]string{ + "go_gc_cycles_automatic_gc_cycles_total", + "go_gc_cycles_forced_gc_cycles_total", + "go_gc_cycles_total_gc_cycles_total", + "go_gc_heap_allocs_by_size_bytes", + "go_gc_heap_allocs_bytes_total", + "go_gc_heap_allocs_objects_total", + "go_gc_heap_frees_by_size_bytes", + "go_gc_heap_frees_bytes_total", + "go_gc_heap_frees_objects_total", + "go_gc_heap_goal_bytes", + "go_gc_heap_objects_objects", + "go_gc_heap_tiny_allocs_objects_total", + "go_gc_pauses_seconds", + }), + }, + { + name: "allow Memory", + rules: []GoRuntimeMetricsRule{MetricsMemory}, + expected: withBaseMetrics([]string{ + "go_memory_classes_heap_free_bytes", + "go_memory_classes_heap_objects_bytes", + "go_memory_classes_heap_released_bytes", + "go_memory_classes_heap_stacks_bytes", + "go_memory_classes_heap_unused_bytes", + "go_memory_classes_metadata_mcache_free_bytes", + "go_memory_classes_metadata_mcache_inuse_bytes", + "go_memory_classes_metadata_mspan_free_bytes", + "go_memory_classes_metadata_mspan_inuse_bytes", + "go_memory_classes_metadata_other_bytes", + "go_memory_classes_os_stacks_bytes", + "go_memory_classes_other_bytes", + "go_memory_classes_profiling_buckets_bytes", + "go_memory_classes_total_bytes", + }), + }, + { + name: "allow Scheduler", + rules: []GoRuntimeMetricsRule{MetricsScheduler}, + expected: []string{ + "go_gc_duration_seconds", + "go_goroutines", + "go_info", + "go_memstats_last_gc_time_seconds", + "go_sched_goroutines_goroutines", + "go_sched_latencies_seconds", + "go_threads", + }, + }, + } { + t.Run(test.name, func(t *testing.T) { + reg := prometheus.NewRegistry() + reg.MustRegister(NewGoCollector( + WithGoCollectorMemStatsMetricsDisabled(), + WithGoCollectorRuntimeMetrics(test.rules...), + )) + result, err := reg.Gather() + if err != nil { + t.Fatal(err) + } + + got := []string{} + for _, r := range result { + got = append(got, r.GetName()) + } + + if !reflect.DeepEqual(got, test.expected) { + t.Errorf("got %v, want %v", got, test.expected) + } + }) + } +} + +func withBaseMetrics(metricNames []string) []string { + metricNames = append(metricNames, baseMetrics...) + sort.Strings(metricNames) + return metricNames +} + +func TestGoCollectorDenyList(t *testing.T) { + for _, test := range []struct { + name string + matchers []*regexp.Regexp + expected []string + }{ + { + name: "Without any matchers", + matchers: nil, + expected: baseMetrics, + }, + { + name: "deny all", + matchers: []*regexp.Regexp{regexp.MustCompile("/.*")}, + expected: baseMetrics, + }, + { + name: "deny gc and scheduler latency", + matchers: []*regexp.Regexp{ + regexp.MustCompile("^/gc/.*"), + regexp.MustCompile("^/sched/latencies:.*"), + }, + expected: baseMetrics, + }, + } { + t.Run(test.name, func(t *testing.T) { + reg := prometheus.NewRegistry() + reg.MustRegister(NewGoCollector( + WithGoCollectorMemStatsMetricsDisabled(), + WithoutGoCollectorRuntimeMetrics(test.matchers...), + )) + result, err := reg.Gather() + if err != nil { + t.Fatal(err) + } + + got := []string{} + for _, r := range result { + got = append(got, r.GetName()) + } + + if !reflect.DeepEqual(got, test.expected) { + t.Errorf("got %v, want %v", got, test.expected) + } + }) + } +} + func ExampleGoCollector() { + reg := prometheus.NewRegistry() + + // Register the GoCollector with the default options. Only the base metrics will be enabled. + reg.MustRegister(NewGoCollector()) + + http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) + log.Fatal(http.ListenAndServe(":8080", nil)) } func ExampleGoCollector_WithAdvancedGoMetrics() { + reg := prometheus.NewRegistry() + + // Enable Go metrics with pre-defined rules. Or your custom rules. + reg.MustRegister( + NewGoCollector( + WithGoCollectorMemStatsMetricsDisabled(), + WithGoCollectorRuntimeMetrics( + MetricsScheduler, + MetricsMemory, + GoRuntimeMetricsRule{ + Matcher: regexp.MustCompile("^/mycustomrule.*"), + }, + ), + WithoutGoCollectorRuntimeMetrics(regexp.MustCompile("^/gc/.*")), + )) + + http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func ExampleGoCollector_DefaultRegister() { + // Unregister the default GoCollector. + prometheus.Unregister(NewGoCollector()) + + // Register the default GoCollector with a custom config. + prometheus.MustRegister(NewGoCollector(WithGoCollectorRuntimeMetrics( + MetricsScheduler, + MetricsGC, + GoRuntimeMetricsRule{ + Matcher: regexp.MustCompile("^/mycustomrule.*"), + }, + ), + )) + + http.Handle("/metrics", promhttp.Handler()) + log.Fatal(http.ListenAndServe(":8080", nil)) } From 17f6001e5ea21bc2893820130865171f7fd962ad Mon Sep 17 00:00:00 2001 From: Kemal Akkoyun Date: Fri, 5 Aug 2022 19:25:39 +0200 Subject: [PATCH 2/2] Add docs for the presets Signed-off-by: Kemal Akkoyun --- prometheus/collectors/go_collector_latest.go | 13 ++++++++++--- prometheus/collectors/go_collector_latest_test.go | 6 ++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/prometheus/collectors/go_collector_latest.go b/prometheus/collectors/go_collector_latest.go index f686f1aba..246c5ea94 100644 --- a/prometheus/collectors/go_collector_latest.go +++ b/prometheus/collectors/go_collector_latest.go @@ -24,9 +24,16 @@ import ( ) var ( - MetricsAll = regexp.MustCompile("/.*") - MetricsGC = GoRuntimeMetricsRule{regexp.MustCompile(`^/gc/.*`)} - MetricsMemory = GoRuntimeMetricsRule{regexp.MustCompile(`^/memory/.*`)} + // MetricsAll allows all the metrics to be collected from Go runtime. + MetricsAll = GoRuntimeMetricsRule{regexp.MustCompile("/.*")} + // MetricsGC allows only GC metrics to be collected from Go runtime. + // e.g. go_gc_cycles_automatic_gc_cycles_total + MetricsGC = GoRuntimeMetricsRule{regexp.MustCompile(`^/gc/.*`)} + // MetricsMemory allows only memory metrics to be collected from Go runtime. + // e.g. go_memory_classes_heap_free_bytes + MetricsMemory = GoRuntimeMetricsRule{regexp.MustCompile(`^/memory/.*`)} + // MetricsScheduler allows only scheduler metrics to be collected from Go runtime. + // e.g. go_sched_goroutines_goroutines MetricsScheduler = GoRuntimeMetricsRule{regexp.MustCompile(`^/sched/.*`)} ) diff --git a/prometheus/collectors/go_collector_latest_test.go b/prometheus/collectors/go_collector_latest_test.go index 822f5495f..96cdb7183 100644 --- a/prometheus/collectors/go_collector_latest_test.go +++ b/prometheus/collectors/go_collector_latest_test.go @@ -86,10 +86,8 @@ func TestGoCollectorAllowList(t *testing.T) { expected: baseMetrics, }, { - name: "allow all", - rules: []GoRuntimeMetricsRule{{ - Matcher: regexp.MustCompile("/.*"), - }}, + name: "allow all", + rules: []GoRuntimeMetricsRule{MetricsAll}, expected: withBaseMetrics([]string{ "go_gc_cycles_automatic_gc_cycles_total", "go_gc_cycles_forced_gc_cycles_total",