plugins/runtimemetrics/registers.go (217 lines of code) (raw):

// Licensed to Apache Software Foundation (ASF) under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Apache Software Foundation (ASF) licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package runtimemetrics import ( "math" original_metrics "runtime/metrics" "github.com/apache/skywalking-go/plugins/core/metrics" ) //nolint var nameReplacing = map[string]*meterInfo{ // GC counts "/gc/cycles/automatic:gc-cycles": newMetricsGaugeReplaceInfo("instance_golang_gc_count_labeled", "type", "automatic"), "/gc/cycles/forced:gc-cycles": newMetricsGaugeReplaceInfo("instance_golang_gc_count_labeled", "type", "forced"), "/gc/cycles/total:gc-cycles": newMetricsGaugeReplaceInfo("instance_golang_gc_count_labeled", "type", "total"), // Heap allocs "/gc/heap/allocs:bytes": newMetricsGaugeReplaceInfo("instance_golang_heap_alloc_size"), "/gc/heap/allocs:objects": newMetricsGaugeReplaceInfo("instance_golang_heap_alloc_objects"), // Heap frees "/gc/heap/frees:bytes": newMetricsGaugeReplaceInfo("instance_golang_heap_frees"), "/gc/heap/frees:objects": newMetricsGaugeReplaceInfo("instance_golang_heap_frees_objects"), // Memory heap "/memory/classes/heap/free:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_heap_labeled", "type", "free"), "/memory/classes/heap/objects:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_heap_labeled", "type", "objects"), "/memory/classes/heap/released:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_heap_labeled", "type", "released"), "/memory/classes/heap/stacks:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_heap_labeled", "type", "stacks"), "/memory/classes/heap/unused:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_heap_labeled", "type", "unused"), // Metadata mcache "/memory/classes/metadata/mcache/free:bytes": newMetricsGaugeReplaceInfo("instance_golang_metadata_mcache_labeled", "type", "free"), "/memory/classes/metadata/mcache/inuse:bytes": newMetricsGaugeReplaceInfo("instance_golang_metadata_mcache_labeled", "type", "inuse"), // Metadata mspan "/memory/classes/metadata/mspan/free:bytes": newMetricsGaugeReplaceInfo("instance_golang_metadata_mspan_labeled", "type", "free"), "/memory/classes/metadata/mspan/inuse:bytes": newMetricsGaugeReplaceInfo("instance_golang_metadata_mspan_labeled", "type", "inuse"), // threads "/sched/gomaxprocs:threads": newMetricsGaugeReplaceInfo("instance_golang_os_threads_num"), "/sched/goroutines:goroutines": newMetricsGaugeReplaceInfo("instance_golang_live_goroutines_num"), // Others "/cgo/go-to-c-calls:calls": newMetricsGaugeReplaceInfo("instance_golang_cgo_calls"), "/gc/heap/goal:bytes": newMetricsGaugeReplaceInfo("instance_golang_gc_heap_goal"), "/gc/heap/objects:objects": newMetricsGaugeReplaceInfo("instance_golang_gc_heap_objects"), "/gc/heap/tiny/allocs:objects": newMetricsGaugeReplaceInfo("instance_golang_gc_heap_tiny_allocs"), "/gc/limiter/last-enabled:gc-cycle": newMetricsGaugeReplaceInfo("instance_golang_gc_limiter_last_enabled"), "/gc/stack/starting-size:bytes": newMetricsGaugeReplaceInfo("instance_golang_gc_stack_starting_size"), "/memory/classes/metadata/other:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_metadata_other"), "/memory/classes/os-stacks:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_os_stacks"), "/memory/classes/other:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_other"), "/memory/classes/profiling/buckets:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_profiling_buckets"), "/memory/classes/total:bytes": newMetricsGaugeReplaceInfo("instance_golang_memory_total"), // Histogram "/gc/heap/allocs-by-size:bytes": newMetricsHistogramReplaceInfo("instance_golang_gc_heap_allocs_by_size", 1), "/gc/heap/frees-by-size:bytes": newMetricsHistogramReplaceInfo("instance_golang_gc_heap_frees_by_size", 1), "/gc/pauses:seconds": newMetricsHistogramReplaceInfo("instance_golang_gc_pauses", 1000_000_000), "/sched/latencies:seconds": newMetricsHistogramReplaceInfo("instance_golang_sched_latencies", 1000_000_000), } //nolint var combinedMetrics = []*meterInfo{ newCombinedGaugeInfo("instance_golang_memory_heap_labeled", []string{ "/memory/classes/heap/free:bytes", "/memory/classes/heap/objects:bytes", "/memory/classes/heap/released:bytes", "/memory/classes/heap/stacks:bytes", "/memory/classes/heap/unused:bytes", }, "type", "total"), } //nolint //skywalking:init func registerMetrics() { allMetrics := original_metrics.All() samples := make([]original_metrics.Sample, 0) infos := make(map[string]*meterInfo) combinedInfos := make([]*meterInfo, 0) for _, m := range allMetrics { info := nameReplacing[m.Name] if info == nil { continue } sample := original_metrics.Sample{Name: m.Name} samples = append(samples, sample) info.init(sample) infos[m.Name] = info } for _, info := range combinedMetrics { info.initWithCombined() combinedInfos = append(combinedInfos, info) } metrics.RegisterBeforeCollectHook(func() { // reading all samples original_metrics.Read(samples) // updating metrics for _, sample := range samples { if i := infos[sample.Name]; i != nil { i.updateMetricValue(sample) } } for _, info := range combinedInfos { info.updateCombinedMetricValue(samples) } }) } type meterInfo struct { // basic info name string tagOpts []metrics.Opt isHistogram bool histogramMultiple int // metric value gaugeValue float64 gaugeMetric metrics.Gauge latestHistogramValue []int64 histogramMetric metrics.Histogram histogramStartInx int // combined metrics needsMetricsNames []string combinedGaugeMetric metrics.Gauge combinedGaugeValue float64 } func newMetricsGaugeReplaceInfo(name string, tags ...string) *meterInfo { meter := &meterInfo{name: name, isHistogram: false} if len(tags) > 0 { meter.tagOpts = make([]metrics.Opt, 0) for i := 0; i < len(tags); i += 2 { meter.tagOpts = append(meter.tagOpts, metrics.WithLabel(tags[i], tags[i+1])) } } return meter } func newMetricsHistogramReplaceInfo(name string, multiples int) *meterInfo { meter := &meterInfo{name: name, isHistogram: true, histogramMultiple: multiples} return meter } func newCombinedGaugeInfo(name string, needsMetrics []string, tags ...string) *meterInfo { meter := &meterInfo{name: name, isHistogram: false, needsMetricsNames: needsMetrics} if len(tags) > 0 { meter.tagOpts = make([]metrics.Opt, 0) for i := 0; i < len(tags); i += 2 { meter.tagOpts = append(meter.tagOpts, metrics.WithLabel(tags[i], tags[i+1])) } } return meter } func (m *meterInfo) init(sample original_metrics.Sample) { if !m.isHistogram { m.gaugeMetric = metrics.NewGauge(m.name, func() float64 { return m.gaugeValue }, m.tagOpts...) return } original_metrics.Read([]original_metrics.Sample{sample}) m.initHistogramIfNeeds(sample) } func (m *meterInfo) initWithCombined() { m.combinedGaugeMetric = metrics.NewGauge(m.name, func() float64 { return m.combinedGaugeValue }, m.tagOpts...) } func (m *meterInfo) updateMetricValue(sample original_metrics.Sample) { if !m.isHistogram { if v, ok := m.readingFloat64(sample); ok { m.gaugeValue = v } return } m.initHistogramIfNeeds(sample) if m.histogramMetric == nil || m.latestHistogramValue == nil { return } histogram, ok := m.readingHistogram(sample) if !ok { return } for i, val := range histogram.Counts { if i < m.histogramStartInx { continue } if i >= len(m.latestHistogramValue) { break } newestValue := int64(val) if add := newestValue - m.latestHistogramValue[i]; add > 0 { m.histogramMetric.ObserveWithCount(histogram.Buckets[i]*float64(m.histogramMultiple), add) m.latestHistogramValue[i] = newestValue } } } func (m *meterInfo) initHistogramIfNeeds(sample original_metrics.Sample) { if m.histogramMetric != nil { return } histogram, ok := m.readingHistogram(sample) if !ok { return } float64s := make([]float64, 0, len(histogram.Buckets)) val := make([]int64, 0, len(histogram.Buckets)) var startInx = 0 var hasAdded = false for _, b := range histogram.Buckets { if b > math.MaxFloat64 || b < 0 { if !hasAdded { startInx++ } continue } hasAdded = true float64s = append(float64s, b*float64(m.histogramMultiple)) val = append(val, 0) } m.histogramStartInx = startInx m.histogramMetric = metrics.NewHistogram(m.name, float64s, m.tagOpts...) m.latestHistogramValue = val } func (m *meterInfo) readingFloat64(s original_metrics.Sample) (float64, bool) { switch s.Value.Kind() { case original_metrics.KindUint64: return float64(s.Value.Uint64()), true case original_metrics.KindFloat64: return s.Value.Float64(), true default: return 0, false } } func (m *meterInfo) readingHistogram(s original_metrics.Sample) (*original_metrics.Float64Histogram, bool) { if s.Value.Kind() != original_metrics.KindFloat64Histogram { return nil, false } return s.Value.Float64Histogram(), true } func (m *meterInfo) updateCombinedMetricValue(samples []original_metrics.Sample) { var sum float64 for _, name := range m.needsMetricsNames { for _, sample := range samples { if sample.Name == name { if v, ok := m.readingFloat64(sample); ok { sum += v } break } } } m.combinedGaugeValue = sum }