From 9e789b5f9944e7d2ec169f68a9219f4b3ca4a734 Mon Sep 17 00:00:00 2001 From: Xuewei Zhang Date: Mon, 9 Sep 2019 19:13:07 -0700 Subject: [PATCH] Refactor on metrics so that names for all the views are tracked --- pkg/problemmetrics/problem_metrics.go | 6 +- pkg/systemstatsmonitor/disk_collector.go | 9 +- pkg/systemstatsmonitor/host_collector.go | 11 +- pkg/types/types.go | 16 +++ pkg/util/metrics/helpers.go | 149 ----------------------- pkg/util/metrics/metric.go | 60 +++++++++ pkg/util/metrics/metric_float64.go | 100 +++++++++++++++ pkg/util/metrics/metric_int64.go | 100 +++++++++++++++ 8 files changed, 292 insertions(+), 159 deletions(-) create mode 100644 pkg/util/metrics/metric.go create mode 100644 pkg/util/metrics/metric_float64.go create mode 100644 pkg/util/metrics/metric_int64.go diff --git a/pkg/problemmetrics/problem_metrics.go b/pkg/problemmetrics/problem_metrics.go index 2d82ab04..e6241e6d 100644 --- a/pkg/problemmetrics/problem_metrics.go +++ b/pkg/problemmetrics/problem_metrics.go @@ -49,7 +49,8 @@ func NewProblemMetricsManagerOrDie() *ProblemMetricsManager { var err error pmm.problemCounter, err = metrics.NewInt64Metric( - "problem_counter", + metrics.ProblemCounterID, + string(metrics.ProblemCounterID), "Number of times a specific type of problem have occurred.", "1", metrics.Sum, @@ -59,7 +60,8 @@ func NewProblemMetricsManagerOrDie() *ProblemMetricsManager { } pmm.problemGauge, err = metrics.NewInt64Metric( - "problem_gauge", + metrics.ProblemGaugeID, + string(metrics.ProblemGaugeID), "Whether a specific type of problem is affecting the node or not.", "1", metrics.LastValue, diff --git a/pkg/systemstatsmonitor/disk_collector.go b/pkg/systemstatsmonitor/disk_collector.go index b8328111..6bb07b60 100644 --- a/pkg/systemstatsmonitor/disk_collector.go +++ b/pkg/systemstatsmonitor/disk_collector.go @@ -49,7 +49,8 @@ func NewDiskCollectorOrDie(diskConfig *ssmtypes.DiskStatsConfig) *diskCollector // Use metrics.Sum aggregation method to ensure the metric is a counter/cumulative metric. dc.mIOTime, err = metrics.NewInt64Metric( - diskConfig.MetricsConfigs["disk/io_time"].DisplayName, + metrics.DiskIOTimeID, + diskConfig.MetricsConfigs[string(metrics.DiskIOTimeID)].DisplayName, "The IO time spent on the disk", "second", metrics.Sum, @@ -60,7 +61,8 @@ func NewDiskCollectorOrDie(diskConfig *ssmtypes.DiskStatsConfig) *diskCollector // Use metrics.Sum aggregation method to ensure the metric is a counter/cumulative metric. dc.mWeightedIO, err = metrics.NewInt64Metric( - diskConfig.MetricsConfigs["disk/weighted_io"].DisplayName, + metrics.DiskWeightedIOID, + diskConfig.MetricsConfigs[string(metrics.DiskWeightedIOID)].DisplayName, "The weighted IO on the disk", "second", metrics.Sum, @@ -70,7 +72,8 @@ func NewDiskCollectorOrDie(diskConfig *ssmtypes.DiskStatsConfig) *diskCollector } dc.mAvgQueueLen, err = metrics.NewFloat64Metric( - diskConfig.MetricsConfigs["disk/avg_queue_len"].DisplayName, + metrics.DiskAvgQueueLenID, + diskConfig.MetricsConfigs[string(metrics.DiskAvgQueueLenID)].DisplayName, "The average queue length on the disk", "second", metrics.LastValue, diff --git a/pkg/systemstatsmonitor/host_collector.go b/pkg/systemstatsmonitor/host_collector.go index b02c5d56..b1305aa2 100644 --- a/pkg/systemstatsmonitor/host_collector.go +++ b/pkg/systemstatsmonitor/host_collector.go @@ -26,12 +26,12 @@ import ( ) type hostCollector struct { - tags map[string]string - uptime *metrics.Int64Metric + tags map[string]string + uptime *metrics.Int64Metric } func NewHostCollectorOrDie(hostConfig *ssmtypes.HostStatsConfig) *hostCollector { - hc := hostCollector{map[string]string{}, nil, 0} + hc := hostCollector{map[string]string{}, nil} kernelVersion, err := host.KernelVersion() if err != nil { @@ -48,7 +48,8 @@ func NewHostCollectorOrDie(hostConfig *ssmtypes.HostStatsConfig) *hostCollector // Use metrics.Sum aggregation method to ensure the metric is a counter/cumulative metric. if hostConfig.MetricsConfigs["host/uptime"].DisplayName != "" { hc.uptime, err = metrics.NewInt64Metric( - hostConfig.MetricsConfigs["host/uptime"].DisplayName, + metrics.HostUptimeID, + hostConfig.MetricsConfigs[string(metrics.HostUptimeID)].DisplayName, "The uptime of the operating system", "second", metrics.LastValue, @@ -75,4 +76,4 @@ func (hc *hostCollector) collect() { if hc.uptime != nil { hc.uptime.Record(hc.tags, int64(uptime)) } -} \ No newline at end of file +} diff --git a/pkg/types/types.go b/pkg/types/types.go index b981b282..ee422a36 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -131,3 +131,19 @@ type ProblemDaemonHandler struct { // CmdOptionDescription explains how to configure the problem daemon from command line arguments. CmdOptionDescription string } + +// ExporterType is the type of the exporter. +type ExporterType string + +// ExporterConfigPathMap represents configurations on all types of exporters: +// 1) Each key represents a type of exporter. +// 2) Each value represents the config file path for the exporter. +type ExporterConfigPathMap map[ExporterType]*string + +// ExporterHandler represents the initialization handler for a type of exporter. +type ExporterHandler struct { + // CreateExporterOrDie initializes an exporter, panic if error occurs. + CreateExporterOrDie func(string) Exporter + // CmdOptionDescription explains how to configure the exporter from command line arguments. + CmdOptionDescription string +} diff --git a/pkg/util/metrics/helpers.go b/pkg/util/metrics/helpers.go index 3cc6952f..5e1d79c5 100644 --- a/pkg/util/metrics/helpers.go +++ b/pkg/util/metrics/helpers.go @@ -16,15 +16,12 @@ limitations under the License. package metrics import ( - "context" "fmt" "strings" "sync" pcm "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" - "go.opencensus.io/stats" - "go.opencensus.io/stats/view" "go.opencensus.io/tag" ) @@ -47,152 +44,6 @@ const ( Sum Aggregation = "Sum" ) -// Int64MetricRepresentation represents a snapshot of an int64 metrics. -// This is used for inspecting metric internals. -type Int64MetricRepresentation struct { - // Name is the metric name. - Name string - // Labels contains all metric labels in key-value pair format. - Labels map[string]string - // Value is the value of the metric. - Value int64 -} - -// Int64Metric represents an int64 metric. -type Int64Metric struct { - name string - measure *stats.Int64Measure -} - -// NewInt64Metric create a Int64Metric metric, returns nil when name is empty. -func NewInt64Metric(name string, description string, unit string, aggregation Aggregation, tagNames []string) (*Int64Metric, error) { - if name == "" { - return nil, nil - } - - tagKeys, err := getTagKeysFromNames(tagNames) - if err != nil { - return nil, fmt.Errorf("failed to create metric %q because of tag creation failure: %v", name, err) - } - - var aggregationMethod *view.Aggregation - switch aggregation { - case LastValue: - aggregationMethod = view.LastValue() - case Sum: - aggregationMethod = view.Sum() - default: - return nil, fmt.Errorf("unknown aggregation option %q", aggregation) - } - - measure := stats.Int64(name, description, unit) - newView := &view.View{ - Name: name, - Measure: measure, - Description: description, - Aggregation: aggregationMethod, - TagKeys: tagKeys, - } - view.Register(newView) - - metric := Int64Metric{name, measure} - return &metric, nil -} - -// Record records a measurement for the metric, with provided tags as metric labels. -func (metric *Int64Metric) Record(tags map[string]string, measurement int64) error { - var mutators []tag.Mutator - - tagMapMutex.RLock() - defer tagMapMutex.RUnlock() - - for tagName, tagValue := range tags { - tagKey, ok := tagMap[tagName] - if !ok { - return fmt.Errorf("referencing none existing tag %q in metric %q", tagName, metric.name) - } - mutators = append(mutators, tag.Upsert(tagKey, tagValue)) - } - - return stats.RecordWithTags( - context.Background(), - mutators, - metric.measure.M(measurement)) -} - -// Float64MetricRepresentation represents a snapshot of a float64 metrics. -// This is used for inspecting metric internals. -type Float64MetricRepresentation struct { - // Name is the metric name. - Name string - // Labels contains all metric labels in key-value pair format. - Labels map[string]string - // Value is the value of the metric. - Value float64 -} - -// Float64Metric represents an float64 metric. -type Float64Metric struct { - name string - measure *stats.Float64Measure -} - -// NewFloat64Metric create a Float64Metric metrics, returns nil when name is empty. -func NewFloat64Metric(name string, description string, unit string, aggregation Aggregation, tagNames []string) (*Float64Metric, error) { - if name == "" { - return nil, nil - } - - tagKeys, err := getTagKeysFromNames(tagNames) - if err != nil { - return nil, fmt.Errorf("failed to create metric %q because of tag creation failure: %v", name, err) - } - - var aggregationMethod *view.Aggregation - switch aggregation { - case LastValue: - aggregationMethod = view.LastValue() - case Sum: - aggregationMethod = view.Sum() - default: - return nil, fmt.Errorf("unknown aggregation option %q", aggregation) - } - - measure := stats.Float64(name, description, unit) - newView := &view.View{ - Name: name, - Measure: measure, - Description: description, - Aggregation: aggregationMethod, - TagKeys: tagKeys, - } - view.Register(newView) - - metric := Float64Metric{name, measure} - return &metric, nil -} - -// Record records a measurement for the metric, with provided tags as metric labels. -func (metric *Float64Metric) Record(tags map[string]string, measurement float64) error { - var mutators []tag.Mutator - - tagMapMutex.RLock() - defer tagMapMutex.RUnlock() - - for tagName, tagValue := range tags { - tagKey, ok := tagMap[tagName] - if !ok { - return fmt.Errorf("referencing none existing tag %q in metric %q", tagName, metric.name) - } - mutators = append(mutators, tag.Upsert(tagKey, tagValue)) - } - - return stats.RecordWithTags( - context.Background(), - mutators, - metric.measure.M(measurement)) -} - func getTagKeysFromNames(tagNames []string) ([]tag.Key, error) { tagMapMutex.Lock() defer tagMapMutex.Unlock() diff --git a/pkg/util/metrics/metric.go b/pkg/util/metrics/metric.go new file mode 100644 index 00000000..e19f4461 --- /dev/null +++ b/pkg/util/metrics/metric.go @@ -0,0 +1,60 @@ +/* +Copyright 2019 The Kubernetes Authors All rights reserved. + +Licensed 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 metrics + +import ( + "sync" +) + +const ( + ProblemCounterID MetricID = "problem_counter" + ProblemGaugeID MetricID = "problem_gauge" + DiskIOTimeID MetricID = "disk/io_time" + DiskWeightedIOID MetricID = "disk/weighted_io" + DiskAvgQueueLenID MetricID = "disk/avg_queue_len" + HostUptimeID MetricID = "host/uptime" +) + +var MetricMap MetricMapping + +func init() { + MetricMap.mapMutex.Lock() + defer MetricMap.mapMutex.Unlock() + + MetricMap.viewNameToMetricIDMap = make(map[string]MetricID) +} + +type MetricID string + +type MetricMapping struct { + viewNameToMetricIDMap map[string]MetricID + mapMutex sync.RWMutex +} + +func (mm *MetricMapping) AddMapping(metricID MetricID, viewName string) { + mm.mapMutex.Lock() + defer mm.mapMutex.Unlock() + + mm.viewNameToMetricIDMap[viewName] = metricID +} + +func (mm *MetricMapping) ViewNameToMetricID(viewName string) (MetricID, bool) { + mm.mapMutex.RLock() + defer mm.mapMutex.RUnlock() + + id, ok := mm.viewNameToMetricIDMap[viewName] + return id, ok +} diff --git a/pkg/util/metrics/metric_float64.go b/pkg/util/metrics/metric_float64.go new file mode 100644 index 00000000..fab1ec01 --- /dev/null +++ b/pkg/util/metrics/metric_float64.go @@ -0,0 +1,100 @@ +/* +Copyright 2019 The Kubernetes Authors All rights reserved. + +Licensed 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 metrics + +import ( + "context" + "fmt" + + "go.opencensus.io/stats" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" +) + +// Float64MetricRepresentation represents a snapshot of a float64 metrics. +// This is used for inspecting metric internals. +type Float64MetricRepresentation struct { + // Name is the metric name. + Name string + // Labels contains all metric labels in key-value pair format. + Labels map[string]string + // Value is the value of the metric. + Value float64 +} + +// Float64Metric represents an float64 metric. +type Float64Metric struct { + name string + measure *stats.Float64Measure +} + +// NewFloat64Metric create a Float64Metric metrics, returns nil when viewName is empty. +func NewFloat64Metric(metricID MetricID, viewName string, description string, unit string, aggregation Aggregation, tagNames []string) (*Float64Metric, error) { + if viewName == "" { + return nil, nil + } + + MetricMap.AddMapping(metricID, viewName) + + tagKeys, err := getTagKeysFromNames(tagNames) + if err != nil { + return nil, fmt.Errorf("failed to create metric %q because of tag creation failure: %v", viewName, err) + } + + var aggregationMethod *view.Aggregation + switch aggregation { + case LastValue: + aggregationMethod = view.LastValue() + case Sum: + aggregationMethod = view.Sum() + default: + return nil, fmt.Errorf("unknown aggregation option %q", aggregation) + } + + measure := stats.Float64(viewName, description, unit) + newView := &view.View{ + Name: viewName, + Measure: measure, + Description: description, + Aggregation: aggregationMethod, + TagKeys: tagKeys, + } + view.Register(newView) + + metric := Float64Metric{viewName, measure} + return &metric, nil +} + +// Record records a measurement for the metric, with provided tags as metric labels. +func (metric *Float64Metric) Record(tags map[string]string, measurement float64) error { + var mutators []tag.Mutator + + tagMapMutex.RLock() + defer tagMapMutex.RUnlock() + + for tagName, tagValue := range tags { + tagKey, ok := tagMap[tagName] + if !ok { + return fmt.Errorf("referencing none existing tag %q in metric %q", tagName, metric.name) + } + mutators = append(mutators, tag.Upsert(tagKey, tagValue)) + } + + return stats.RecordWithTags( + context.Background(), + mutators, + metric.measure.M(measurement)) +} diff --git a/pkg/util/metrics/metric_int64.go b/pkg/util/metrics/metric_int64.go new file mode 100644 index 00000000..a01626e1 --- /dev/null +++ b/pkg/util/metrics/metric_int64.go @@ -0,0 +1,100 @@ +/* +Copyright 2019 The Kubernetes Authors All rights reserved. + +Licensed 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 metrics + +import ( + "context" + "fmt" + + "go.opencensus.io/stats" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" +) + +// Int64MetricRepresentation represents a snapshot of an int64 metrics. +// This is used for inspecting metric internals. +type Int64MetricRepresentation struct { + // Name is the metric name. + Name string + // Labels contains all metric labels in key-value pair format. + Labels map[string]string + // Value is the value of the metric. + Value int64 +} + +// Int64Metric represents an int64 metric. +type Int64Metric struct { + name string + measure *stats.Int64Measure +} + +// NewInt64Metric create a Int64Metric metric, returns nil when viewName is empty. +func NewInt64Metric(metricID MetricID, viewName string, description string, unit string, aggregation Aggregation, tagNames []string) (*Int64Metric, error) { + if viewName == "" { + return nil, nil + } + + MetricMap.AddMapping(metricID, viewName) + + tagKeys, err := getTagKeysFromNames(tagNames) + if err != nil { + return nil, fmt.Errorf("failed to create metric %q because of tag creation failure: %v", viewName, err) + } + + var aggregationMethod *view.Aggregation + switch aggregation { + case LastValue: + aggregationMethod = view.LastValue() + case Sum: + aggregationMethod = view.Sum() + default: + return nil, fmt.Errorf("unknown aggregation option %q", aggregation) + } + + measure := stats.Int64(viewName, description, unit) + newView := &view.View{ + Name: viewName, + Measure: measure, + Description: description, + Aggregation: aggregationMethod, + TagKeys: tagKeys, + } + view.Register(newView) + + metric := Int64Metric{viewName, measure} + return &metric, nil +} + +// Record records a measurement for the metric, with provided tags as metric labels. +func (metric *Int64Metric) Record(tags map[string]string, measurement int64) error { + var mutators []tag.Mutator + + tagMapMutex.RLock() + defer tagMapMutex.RUnlock() + + for tagName, tagValue := range tags { + tagKey, ok := tagMap[tagName] + if !ok { + return fmt.Errorf("referencing none existing tag %q in metric %q", tagName, metric.name) + } + mutators = append(mutators, tag.Upsert(tagKey, tagValue)) + } + + return stats.RecordWithTags( + context.Background(), + mutators, + metric.measure.M(measurement)) +}