From 17e3529cf704f9ad103c50c52d3b89a9b7504092 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Sat, 18 Dec 2021 09:12:57 -0500 Subject: [PATCH 01/13] Fixes from merge. --- exporter/collector/config.go | 2 + .../internal/integrationtest/testcases.go | 2 +- exporter/collector/metricsexporter.go | 207 +++++++++++++++++- exporter/collector/metricsexporter_test.go | 142 ++++++++++++ 4 files changed, 342 insertions(+), 11 deletions(-) diff --git a/exporter/collector/config.go b/exporter/collector/config.go index 76f5a1d03..c3206b492 100644 --- a/exporter/collector/config.go +++ b/exporter/collector/config.go @@ -47,6 +47,8 @@ type Config struct { type MetricConfig struct { Prefix string `mapstructure:"prefix"` SkipCreateMetricDescriptor bool `mapstructure:"skip_create_descriptor"` + // If a metric belongs to one of these domains it does not get a prefix. + KnownDomains []string `mapstructure:"known_domains"` } // ResourceMapping defines mapping of resources from source (OpenCensus) to target (Google Cloud). diff --git a/exporter/collector/internal/integrationtest/testcases.go b/exporter/collector/internal/integrationtest/testcases.go index 91a9c7567..f1c64eedb 100644 --- a/exporter/collector/internal/integrationtest/testcases.go +++ b/exporter/collector/internal/integrationtest/testcases.go @@ -20,7 +20,7 @@ var ( Name: "Basic Counter", OTLPInputFixturePath: "testdata/fixtures/basic_counter_metrics.json", ExpectFixturePath: "testdata/fixtures/basic_counter_metrics_expect.json", - Skip: true, + Skip: false, }, { Name: "Delta Counter", diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index 78252a225..7dd267cfe 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -19,6 +19,9 @@ package collector import ( "context" + "fmt" + "net/url" + "path" "strings" "unicode" @@ -26,6 +29,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/exporter/exporterhelper" "go.opentelemetry.io/collector/model/pdata" + "google.golang.org/genproto/googleapis/api/metric" metricpb "google.golang.org/genproto/googleapis/api/metric" monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" @@ -34,9 +38,11 @@ import ( // metricsExporter is the GCM exporter that uses pdata directly type metricsExporter struct { - cfg *Config - client *monitoring.MetricClient - mapper metricMapper + cfg *Config + client *monitoring.MetricClient + mapper metricMapper + mds chan *metric.MetricDescriptor + mdshutdown chan bool } // metricMapper is the part that transforms metrics. Separate from metricsExporter since it has @@ -45,32 +51,74 @@ type metricMapper struct { cfg *Config } +// Allowed metric domains +var domains = []string{"googleapis.com", "kubernetes.io", "istio.io", "knative.dev"} + type labels map[string]string func (me *metricsExporter) Shutdown(context.Context) error { + // Kill the metric-descriptor goroutine. + me.mdshutdown <- true return me.client.Close() } +// Updates config object to include all defaults for metric export. +func (cfg *Config) SetMetricDefaults() { + if cfg.MetricConfig.KnownDomains == nil || len(cfg.MetricConfig.KnownDomains) == 0 { + cfg.MetricConfig.KnownDomains = domains + } + // TODO - if in legacy mode, this should be + // external.googleapis.com/OpenCensus/ + if cfg.MetricConfig.Prefix == "" { + cfg.MetricConfig.Prefix = "workload.googleapis.com" + } +} + +func (m *metricMapper) SetMetricDefaults() { + if m.cfg == nil { + m.cfg = &Config{} + } + m.cfg.SetMetricDefaults() +} + +func (me *metricsExporter) SetMetricDefaults() { + if me.cfg == nil { + me.cfg = &Config{} + } + me.cfg.SetMetricDefaults() +} + func newGoogleCloudMetricsExporter( ctx context.Context, cfg *Config, set component.ExporterCreateSettings, ) (component.MetricsExporter, error) { setVersionInUserAgent(cfg, set.BuildInfo.Version) + cfg.SetMetricDefaults() - // TODO: map cfg options into metric service client configuration with + // map cfg options into metric service client configuration with // generateClientOptions() - client, err := monitoring.NewMetricClient(ctx) + clientOpts, err := generateClientOptions(cfg) + if err != nil { + return nil, err + } + + client, err := monitoring.NewMetricClient(ctx, clientOpts...) if err != nil { return nil, err } mExp := &metricsExporter{ - cfg: cfg, - client: client, - mapper: metricMapper{cfg}, + cfg: cfg, + client: client, + mapper: metricMapper{cfg}, + mds: make(chan *metric.MetricDescriptor), + mdshutdown: make(chan bool), } + // Fire up the metric descriptor exporter. + go mExp.exportMetricDescriptorRunner(ctx) + return exporterhelper.NewMetricsExporter( cfg, set, @@ -84,7 +132,6 @@ func newGoogleCloudMetricsExporter( // pushMetrics calls pushes pdata metrics to GCM, creating metric descriptors if necessary func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) error { timeSeries := make([]*monitoringpb.TimeSeries, 0, m.DataPointCount()) - rms := m.ResourceMetrics() for i := 0; i < rms.Len(); i++ { rm := rms.At(i) @@ -97,6 +144,7 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err mes := ilm.Metrics() for k := 0; k < mes.Len(); k++ { metric := mes.At(k) + me.mds <- me.mapper.metricDescriptor(metric) timeSeries = append(timeSeries, me.mapper.metricToTimeSeries(monitoredResource, extraLabels, metric)...) } } @@ -108,12 +156,47 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err return err } +func (me *metricsExporter) exportMetricDescriptorRunner(ctx context.Context) { + mdCache := make(map[string]*metric.MetricDescriptor) + for { + select { + case md := <-me.mds: + // Not yet sent, now we sent it. + if !me.cfg.MetricConfig.SkipCreateMetricDescriptor && md != nil && mdCache[md.Type] == nil { + err := me.exportMetricDescriptor(ctx, md) + // TODO: Log-once on error, per metric descriptor? + if err != nil { + fmt.Printf("Unable to send metric descriptor: %s, %s", md, err) + } + mdCache[md.Type] = md + } + case <-me.mdshutdown: + return + } + + // TODO - check shutdown and kill this. + } +} + +func (me *metricsExporter) exportMetricDescriptor(ctx context.Context, md *metric.MetricDescriptor) error { + // export + req := &monitoringpb.CreateMetricDescriptorRequest{ + // TODO set Name field with project ID from config or ADC + Name: fmt.Sprintf("projects/%s", me.cfg.ProjectID), + MetricDescriptor: md, + } + _, err := me.client.CreateMetricDescriptor(ctx, req) + return err +} + func (me *metricsExporter) createTimeSeries(ctx context.Context, ts []*monitoringpb.TimeSeries) error { - return me.client.CreateServiceTimeSeries( + // TODO: me.client.CreateServiceTimeSeries( + return me.client.CreateTimeSeries( ctx, &monitoringpb.CreateTimeSeriesRequest{ TimeSeries: ts, // TODO set Name field with project ID from config or ADC + Name: fmt.Sprintf("projects/%s", me.cfg.ProjectID), }, ) } @@ -230,8 +313,25 @@ func (m *metricMapper) gaugePointToTimeSeries( } } +// Returns any configured prefix to add to unknown metric name. +func (me *metricMapper) getMetricNamePrefix(name string) *string { + for _, domain := range me.cfg.MetricConfig.KnownDomains { + if strings.Contains(name, domain) { + return nil + } + } + result := me.cfg.MetricConfig.Prefix + return &result +} + // metricNameToType maps OTLP metric name to GCM metric type (aka name) func (m *metricMapper) metricNameToType(name string) string { + prefix := m.getMetricNamePrefix(name) + if prefix != nil { + return path.Join(*prefix, name) + } + return name + // TODO return "workload.googleapis.com/" + name } @@ -298,3 +398,90 @@ func mergeLabels(mergeInto labels, others ...labels) labels { return mergeInto } + +func (me *metricMapper) mapMetricUrlToDisplayName(m string) string { + // TODO - user configuration around display name? + // strip domain, keep path after domain. + u, err := url.Parse(fmt.Sprintf("metrics://%s", m)) + if err != nil { + return m + } + return strings.TrimLeft(u.Path, "/") +} + +// Extract the metric descriptor from a metric data point. +func (me *metricMapper) metricDescriptor(m pdata.Metric) *metric.MetricDescriptor { + kind, typ := mapMetricPointKind(m) + metricUrl := me.metricNameToType(m.Name()) + // Return nil for unsupported types. + if kind == metric.MetricDescriptor_METRIC_KIND_UNSPECIFIED { + return nil + } + return &metric.MetricDescriptor{ + Name: m.Name(), + DisplayName: me.mapMetricUrlToDisplayName(metricUrl), + Type: metricUrl, + MetricKind: kind, + ValueType: typ, + Unit: m.Unit(), + Description: m.Description(), + // TODO: Labels for non-wlm + } +} + +func toMetricPointValueType(pt pdata.MetricValueType) metric.MetricDescriptor_ValueType { + switch pt { + case pdata.MetricValueTypeInt: + return metric.MetricDescriptor_INT64 + case pdata.MetricValueTypeDouble: + return metric.MetricDescriptor_DOUBLE + default: + return metric.MetricDescriptor_VALUE_TYPE_UNSPECIFIED + } +} + +func mapMetricPointKind(m pdata.Metric) (metric.MetricDescriptor_MetricKind, metric.MetricDescriptor_ValueType) { + var kind metric.MetricDescriptor_MetricKind + var typ metric.MetricDescriptor_ValueType + switch m.DataType() { + case pdata.MetricDataTypeGauge: + kind = metric.MetricDescriptor_GAUGE + if m.Gauge().DataPoints().Len() > 0 { + typ = toMetricPointValueType(m.Gauge().DataPoints().At(0).Type()) + } + case pdata.MetricDataTypeSum: + if !m.Sum().IsMonotonic() { + kind = metric.MetricDescriptor_GAUGE + } else if m.Sum().AggregationTemporality() == pdata.MetricAggregationTemporalityDelta { + // We report fake-deltas for now. + kind = metric.MetricDescriptor_CUMULATIVE + } else { + kind = metric.MetricDescriptor_CUMULATIVE + } + if m.Sum().DataPoints().Len() > 0 { + typ = toMetricPointValueType(m.Sum().DataPoints().At(0).Type()) + } + case pdata.MetricDataTypeSummary: + kind = metric.MetricDescriptor_GAUGE + case pdata.MetricDataTypeHistogram: + typ = metric.MetricDescriptor_DISTRIBUTION + if m.Histogram().AggregationTemporality() == pdata.MetricAggregationTemporalityDelta { + // We report fake-deltas for now. + kind = metric.MetricDescriptor_CUMULATIVE + } else { + kind = metric.MetricDescriptor_CUMULATIVE + } + case pdata.MetricDataTypeExponentialHistogram: + typ = metric.MetricDescriptor_DISTRIBUTION + if m.ExponentialHistogram().AggregationTemporality() == pdata.MetricAggregationTemporalityDelta { + // We report fake-deltas for now. + kind = metric.MetricDescriptor_CUMULATIVE + } else { + kind = metric.MetricDescriptor_CUMULATIVE + } + default: + kind = metric.MetricDescriptor_METRIC_KIND_UNSPECIFIED + typ = metric.MetricDescriptor_VALUE_TYPE_UNSPECIFIED + } + return kind, typ +} diff --git a/exporter/collector/metricsexporter_test.go b/exporter/collector/metricsexporter_test.go index 7c816a9dd..2be9e30a5 100644 --- a/exporter/collector/metricsexporter_test.go +++ b/exporter/collector/metricsexporter_test.go @@ -342,3 +342,145 @@ func TestNumberDataPointToValue(t *testing.T) { assert.Equal(t, valueType, metricpb.MetricDescriptor_DOUBLE) assert.EqualValues(t, value.GetDoubleValue(), 12.3) } + +func TestToMetricDescriptorGauge(t *testing.T) { + mapper := &metricMapper{} + mapper.SetMetricDefaults() + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeGauge) + metric.SetName("custom.googleapis.com/test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + gauge := metric.Gauge() + point := gauge.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + md := mapper.metricDescriptor(metric) + assert.Equal(t, md, &metricpb.MetricDescriptor{ + Name: "custom.googleapis.com/test.metric", + DisplayName: "test.metric", + Type: "custom.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }) +} + +func TestToMetricDescriptorCumulativeMontonicSum(t *testing.T) { + mapper := &metricMapper{} + mapper.SetMetricDefaults() + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeSum) + metric.SetName("custom.googleapis.com/test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + sum := metric.Sum() + sum.SetIsMonotonic(true) + sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + point := sum.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + md := mapper.metricDescriptor(metric) + assert.Equal(t, md, &metricpb.MetricDescriptor{ + Name: "custom.googleapis.com/test.metric", + DisplayName: "test.metric", + Type: "custom.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }) +} + +func TestToMetricDescriptorDeltaMontonicSum(t *testing.T) { + mapper := &metricMapper{} + mapper.SetMetricDefaults() + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeSum) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + sum := metric.Sum() + sum.SetIsMonotonic(true) + sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityDelta) + point := sum.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + md := mapper.metricDescriptor(metric) + assert.Equal(t, md, &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }) +} + +func TestToMetricDescriptorNonMontonicSum(t *testing.T) { + mapper := &metricMapper{} + mapper.SetMetricDefaults() + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeSum) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + sum := metric.Sum() + sum.SetIsMonotonic(false) + sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + point := sum.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + md := mapper.metricDescriptor(metric) + assert.Equal(t, md, &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }) +} + +func TestToMetricDescriptorCumulativeHistogram(t *testing.T) { + mapper := &metricMapper{} + mapper.SetMetricDefaults() + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeHistogram) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + histogram := metric.Histogram() + histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + md := mapper.metricDescriptor(metric) + assert.Equal(t, md, &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DISTRIBUTION, + Unit: "1", + Description: "Description", + }) +} + +func TestToMetricDescriptorCumulativeExponentialHistogram(t *testing.T) { + mapper := &metricMapper{} + mapper.SetMetricDefaults() + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeExponentialHistogram) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + histogram := metric.ExponentialHistogram() + histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + md := mapper.metricDescriptor(metric) + assert.Equal(t, md, &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DISTRIBUTION, + Unit: "1", + Description: "Description", + }) +} From bfa7302af332b3b46496eb2963a5ef4487ab75a3 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Sat, 18 Dec 2021 09:29:26 -0500 Subject: [PATCH 02/13] Fix tests. --- exporter/collector/metricsexporter.go | 63 ++++++++++------------ exporter/collector/metricsexporter_test.go | 3 ++ 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index 7dd267cfe..0266e0521 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -38,11 +38,11 @@ import ( // metricsExporter is the GCM exporter that uses pdata directly type metricsExporter struct { - cfg *Config - client *monitoring.MetricClient - mapper metricMapper - mds chan *metric.MetricDescriptor - mdshutdown chan bool + cfg *Config + client *monitoring.MetricClient + mapper metricMapper + // A channel that receives metric descriptor and sends them to GCM once. + mds chan *metric.MetricDescriptor } // metricMapper is the part that transforms metrics. Separate from metricsExporter since it has @@ -51,14 +51,13 @@ type metricMapper struct { cfg *Config } -// Allowed metric domains +// Known metric domains. Note: This is now configurable for advanced usages. var domains = []string{"googleapis.com", "kubernetes.io", "istio.io", "knative.dev"} type labels map[string]string func (me *metricsExporter) Shutdown(context.Context) error { - // Kill the metric-descriptor goroutine. - me.mdshutdown <- true + // TODO - will the context given to us at startup be cancelled already? return me.client.Close() } @@ -74,6 +73,7 @@ func (cfg *Config) SetMetricDefaults() { } } +// Updates configuration to include all defaults. Used in unit testing. func (m *metricMapper) SetMetricDefaults() { if m.cfg == nil { m.cfg = &Config{} @@ -81,13 +81,6 @@ func (m *metricMapper) SetMetricDefaults() { m.cfg.SetMetricDefaults() } -func (me *metricsExporter) SetMetricDefaults() { - if me.cfg == nil { - me.cfg = &Config{} - } - me.cfg.SetMetricDefaults() -} - func newGoogleCloudMetricsExporter( ctx context.Context, cfg *Config, @@ -109,11 +102,10 @@ func newGoogleCloudMetricsExporter( } mExp := &metricsExporter{ - cfg: cfg, - client: client, - mapper: metricMapper{cfg}, - mds: make(chan *metric.MetricDescriptor), - mdshutdown: make(chan bool), + cfg: cfg, + client: client, + mapper: metricMapper{cfg}, + mds: make(chan *metric.MetricDescriptor), } // Fire up the metric descriptor exporter. @@ -156,6 +148,7 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err return err } +// Reads metric descriptors from the md channel, and reports them (once) to GCM. func (me *metricsExporter) exportMetricDescriptorRunner(ctx context.Context) { mdCache := make(map[string]*metric.MetricDescriptor) for { @@ -170,14 +163,14 @@ func (me *metricsExporter) exportMetricDescriptorRunner(ctx context.Context) { } mdCache[md.Type] = md } - case <-me.mdshutdown: + case <-ctx.Done(): + // Kill this exporter when context is cancelled. return } - - // TODO - check shutdown and kill this. } } +// Helper method to send metric descriptors to GCM. func (me *metricsExporter) exportMetricDescriptor(ctx context.Context, md *metric.MetricDescriptor) error { // export req := &monitoringpb.CreateMetricDescriptorRequest{ @@ -331,9 +324,6 @@ func (m *metricMapper) metricNameToType(name string) string { return path.Join(*prefix, name) } return name - - // TODO - return "workload.googleapis.com/" + name } func numberDataPointToValue( @@ -399,12 +389,13 @@ func mergeLabels(mergeInto labels, others ...labels) labels { return mergeInto } -func (me *metricMapper) mapMetricUrlToDisplayName(m string) string { +// Takes a GCM metric type, like (workload.googleapis.com/MyCoolMetric) and returns the display name. +func (m *metricMapper) metricTypeToDisplayName(mUrl string) string { // TODO - user configuration around display name? - // strip domain, keep path after domain. - u, err := url.Parse(fmt.Sprintf("metrics://%s", m)) + // Default: strip domain, keep path after domain. + u, err := url.Parse(fmt.Sprintf("metrics://%s", mUrl)) if err != nil { - return m + return mUrl } return strings.TrimLeft(u.Path, "/") } @@ -412,15 +403,15 @@ func (me *metricMapper) mapMetricUrlToDisplayName(m string) string { // Extract the metric descriptor from a metric data point. func (me *metricMapper) metricDescriptor(m pdata.Metric) *metric.MetricDescriptor { kind, typ := mapMetricPointKind(m) - metricUrl := me.metricNameToType(m.Name()) + metricType := me.metricNameToType(m.Name()) // Return nil for unsupported types. if kind == metric.MetricDescriptor_METRIC_KIND_UNSPECIFIED { return nil } return &metric.MetricDescriptor{ Name: m.Name(), - DisplayName: me.mapMetricUrlToDisplayName(metricUrl), - Type: metricUrl, + DisplayName: me.metricTypeToDisplayName(metricType), + Type: metricType, MetricKind: kind, ValueType: typ, Unit: m.Unit(), @@ -429,7 +420,7 @@ func (me *metricMapper) metricDescriptor(m pdata.Metric) *metric.MetricDescripto } } -func toMetricPointValueType(pt pdata.MetricValueType) metric.MetricDescriptor_ValueType { +func metricPointValueType(pt pdata.MetricValueType) metric.MetricDescriptor_ValueType { switch pt { case pdata.MetricValueTypeInt: return metric.MetricDescriptor_INT64 @@ -447,7 +438,7 @@ func mapMetricPointKind(m pdata.Metric) (metric.MetricDescriptor_MetricKind, met case pdata.MetricDataTypeGauge: kind = metric.MetricDescriptor_GAUGE if m.Gauge().DataPoints().Len() > 0 { - typ = toMetricPointValueType(m.Gauge().DataPoints().At(0).Type()) + typ = metricPointValueType(m.Gauge().DataPoints().At(0).Type()) } case pdata.MetricDataTypeSum: if !m.Sum().IsMonotonic() { @@ -459,7 +450,7 @@ func mapMetricPointKind(m pdata.Metric) (metric.MetricDescriptor_MetricKind, met kind = metric.MetricDescriptor_CUMULATIVE } if m.Sum().DataPoints().Len() > 0 { - typ = toMetricPointValueType(m.Sum().DataPoints().At(0).Type()) + typ = metricPointValueType(m.Sum().DataPoints().At(0).Type()) } case pdata.MetricDataTypeSummary: kind = metric.MetricDescriptor_GAUGE diff --git a/exporter/collector/metricsexporter_test.go b/exporter/collector/metricsexporter_test.go index 2be9e30a5..220030fb3 100644 --- a/exporter/collector/metricsexporter_test.go +++ b/exporter/collector/metricsexporter_test.go @@ -95,6 +95,7 @@ func TestMergeLabels(t *testing.T) { func TestSumPointToTimeSeries(t *testing.T) { mapper := metricMapper{cfg: &Config{}} + mapper.SetMetricDefaults() mr := &monitoredrespb.MonitoredResource{} newCase := func() (pdata.Metric, pdata.Sum, pdata.NumberDataPoint) { @@ -203,6 +204,7 @@ func TestSumPointToTimeSeries(t *testing.T) { func TestGaugePointToTimeSeries(t *testing.T) { mapper := metricMapper{cfg: &Config{}} + mapper.SetMetricDefaults() mr := &monitoredrespb.MonitoredResource{} newCase := func() (pdata.Metric, pdata.Gauge, pdata.NumberDataPoint) { @@ -259,6 +261,7 @@ func TestGaugePointToTimeSeries(t *testing.T) { func TestMetricNameToType(t *testing.T) { mapper := metricMapper{cfg: &Config{}} + mapper.SetMetricDefaults() assert.Equal( t, mapper.metricNameToType("foo"), From 72e67a57169cecbe57d764d66b0816d1ae60d0e3 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 20 Dec 2021 09:27:07 -0500 Subject: [PATCH 03/13] Clean up test cases, re-disable integration tests. --- .../internal/integrationtest/testcases.go | 2 +- exporter/collector/metricsexporter_test.go | 296 ++++++++++-------- 2 files changed, 159 insertions(+), 139 deletions(-) diff --git a/exporter/collector/internal/integrationtest/testcases.go b/exporter/collector/internal/integrationtest/testcases.go index f1c64eedb..91a9c7567 100644 --- a/exporter/collector/internal/integrationtest/testcases.go +++ b/exporter/collector/internal/integrationtest/testcases.go @@ -20,7 +20,7 @@ var ( Name: "Basic Counter", OTLPInputFixturePath: "testdata/fixtures/basic_counter_metrics.json", ExpectFixturePath: "testdata/fixtures/basic_counter_metrics_expect.json", - Skip: false, + Skip: true, }, { Name: "Delta Counter", diff --git a/exporter/collector/metricsexporter_test.go b/exporter/collector/metricsexporter_test.go index 220030fb3..61bc044d8 100644 --- a/exporter/collector/metricsexporter_test.go +++ b/exporter/collector/metricsexporter_test.go @@ -346,144 +346,164 @@ func TestNumberDataPointToValue(t *testing.T) { assert.EqualValues(t, value.GetDoubleValue(), 12.3) } -func TestToMetricDescriptorGauge(t *testing.T) { - mapper := &metricMapper{} - mapper.SetMetricDefaults() - metric := pdata.NewMetric() - metric.SetDataType(pdata.MetricDataTypeGauge) - metric.SetName("custom.googleapis.com/test.metric") - metric.SetDescription("Description") - metric.SetUnit("1") - gauge := metric.Gauge() - point := gauge.DataPoints().AppendEmpty() - point.SetDoubleVal(10) - md := mapper.metricDescriptor(metric) - assert.Equal(t, md, &metricpb.MetricDescriptor{ - Name: "custom.googleapis.com/test.metric", - DisplayName: "test.metric", - Type: "custom.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_GAUGE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", - }) -} - -func TestToMetricDescriptorCumulativeMontonicSum(t *testing.T) { - mapper := &metricMapper{} - mapper.SetMetricDefaults() - metric := pdata.NewMetric() - metric.SetDataType(pdata.MetricDataTypeSum) - metric.SetName("custom.googleapis.com/test.metric") - metric.SetDescription("Description") - metric.SetUnit("1") - sum := metric.Sum() - sum.SetIsMonotonic(true) - sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) - point := sum.DataPoints().AppendEmpty() - point.SetDoubleVal(10) - md := mapper.metricDescriptor(metric) - assert.Equal(t, md, &metricpb.MetricDescriptor{ - Name: "custom.googleapis.com/test.metric", - DisplayName: "test.metric", - Type: "custom.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", - }) +type metricDescriptorTest struct { + name string + metricCreator func() pdata.Metric + expected *metricpb.MetricDescriptor } -func TestToMetricDescriptorDeltaMontonicSum(t *testing.T) { - mapper := &metricMapper{} - mapper.SetMetricDefaults() - metric := pdata.NewMetric() - metric.SetDataType(pdata.MetricDataTypeSum) - metric.SetName("test.metric") - metric.SetDescription("Description") - metric.SetUnit("1") - sum := metric.Sum() - sum.SetIsMonotonic(true) - sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityDelta) - point := sum.DataPoints().AppendEmpty() - point.SetDoubleVal(10) - md := mapper.metricDescriptor(metric) - assert.Equal(t, md, &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", - }) -} - -func TestToMetricDescriptorNonMontonicSum(t *testing.T) { - mapper := &metricMapper{} - mapper.SetMetricDefaults() - metric := pdata.NewMetric() - metric.SetDataType(pdata.MetricDataTypeSum) - metric.SetName("test.metric") - metric.SetDescription("Description") - metric.SetUnit("1") - sum := metric.Sum() - sum.SetIsMonotonic(false) - sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) - point := sum.DataPoints().AppendEmpty() - point.SetDoubleVal(10) - md := mapper.metricDescriptor(metric) - assert.Equal(t, md, &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_GAUGE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", - }) -} - -func TestToMetricDescriptorCumulativeHistogram(t *testing.T) { - mapper := &metricMapper{} - mapper.SetMetricDefaults() - metric := pdata.NewMetric() - metric.SetDataType(pdata.MetricDataTypeHistogram) - metric.SetName("test.metric") - metric.SetDescription("Description") - metric.SetUnit("1") - histogram := metric.Histogram() - histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) - md := mapper.metricDescriptor(metric) - assert.Equal(t, md, &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DISTRIBUTION, - Unit: "1", - Description: "Description", - }) -} - -func TestToMetricDescriptorCumulativeExponentialHistogram(t *testing.T) { - mapper := &metricMapper{} - mapper.SetMetricDefaults() - metric := pdata.NewMetric() - metric.SetDataType(pdata.MetricDataTypeExponentialHistogram) - metric.SetName("test.metric") - metric.SetDescription("Description") - metric.SetUnit("1") - histogram := metric.ExponentialHistogram() - histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) - md := mapper.metricDescriptor(metric) - assert.Equal(t, md, &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DISTRIBUTION, - Unit: "1", - Description: "Description", - }) +func TestMetricDescriptorMapping(t *testing.T) { + tests := []metricDescriptorTest{ + { + name: "Gauge", + metricCreator: func() pdata.Metric { + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeGauge) + metric.SetName("custom.googleapis.com/test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + gauge := metric.Gauge() + point := gauge.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + return metric + }, + expected: &metricpb.MetricDescriptor{ + Name: "custom.googleapis.com/test.metric", + DisplayName: "test.metric", + Type: "custom.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }, + }, + { + name: "Cumulative Monotonic Sum", + metricCreator: func() pdata.Metric { + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeSum) + metric.SetName("custom.googleapis.com/test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + sum := metric.Sum() + sum.SetIsMonotonic(true) + sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + point := sum.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + return metric + }, + expected: &metricpb.MetricDescriptor{ + Name: "custom.googleapis.com/test.metric", + DisplayName: "test.metric", + Type: "custom.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }, + }, + { + name: "Delta Monotonic Sum", + metricCreator: func() pdata.Metric { + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeSum) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + sum := metric.Sum() + sum.SetIsMonotonic(true) + sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityDelta) + point := sum.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + return metric + }, + expected: &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }, + }, + { + name: "Non-Monotonic Sum", + metricCreator: func() pdata.Metric { + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeSum) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + sum := metric.Sum() + sum.SetIsMonotonic(false) + sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + point := sum.DataPoints().AppendEmpty() + point.SetDoubleVal(10) + return metric + }, + expected: &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + }, + }, + { + name: "Cumulative Histogram", + metricCreator: func() pdata.Metric { + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeHistogram) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + histogram := metric.Histogram() + histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + return metric + }, + expected: &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DISTRIBUTION, + Unit: "1", + Description: "Description", + }, + }, + { + name: "Cumulative Exponential Histogram", + metricCreator: func() pdata.Metric { + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeExponentialHistogram) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + histogram := metric.ExponentialHistogram() + histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + return metric + }, + expected: &metricpb.MetricDescriptor{ + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DISTRIBUTION, + Unit: "1", + Description: "Description", + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mapper := &metricMapper{} + mapper.SetMetricDefaults() + metric := test.metricCreator() + md := mapper.metricDescriptor(metric) + assert.Equal(t, md, test.expected) + }) + } } From d7bc0ade45f0af1f3235cfc3e07dcbd9277cbd7b Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 20 Dec 2021 10:22:13 -0500 Subject: [PATCH 04/13] Add summary descriptors and label descriptors. --- exporter/collector/metricsexporter.go | 138 ++++++++++++-- exporter/collector/metricsexporter_test.go | 203 ++++++++++++++++----- 2 files changed, 281 insertions(+), 60 deletions(-) diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index 0266e0521..2e0429d1f 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/exporter/exporterhelper" "go.opentelemetry.io/collector/model/pdata" + "google.golang.org/genproto/googleapis/api/label" "google.golang.org/genproto/googleapis/api/metric" metricpb "google.golang.org/genproto/googleapis/api/metric" monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" @@ -54,6 +55,13 @@ type metricMapper struct { // Known metric domains. Note: This is now configurable for advanced usages. var domains = []string{"googleapis.com", "kubernetes.io", "istio.io", "knative.dev"} +// Constants we use when translating summary metrics into GCP. +const ( + SUMMARY_COUNT_SUFFIX = "_summary_count" + SUMMARY_SUM_SUFFIX = "_summary_sum" + SUMMARY_PERCENTILE_SUFFIX = "_summary_percentile" +) + type labels map[string]string func (me *metricsExporter) Shutdown(context.Context) error { @@ -136,7 +144,9 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err mes := ilm.Metrics() for k := 0; k < mes.Len(); k++ { metric := mes.At(k) - me.mds <- me.mapper.metricDescriptor(metric) + for _, md := range me.mapper.metricDescriptor(metric) { + me.mds <- md + } timeSeries = append(timeSeries, me.mapper.metricToTimeSeries(monitoredResource, extraLabels, metric)...) } } @@ -400,23 +410,129 @@ func (m *metricMapper) metricTypeToDisplayName(mUrl string) string { return strings.TrimLeft(u.Path, "/") } +// Returns label descriptors for a metric. +func (me *metricMapper) labelDescriptors(m pdata.Metric) []*label.LabelDescriptor { + // TODO - allow customization of label descriptions. + result := []*label.LabelDescriptor{} + switch m.DataType() { + case pdata.MetricDataTypeGauge: + points := m.Gauge().DataPoints() + for i := 0; i < points.Len(); i++ { + points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { + result = append(result, &label.LabelDescriptor{ + Key: key, + }) + return true + }) + } + case pdata.MetricDataTypeSum: + points := m.Sum().DataPoints() + for i := 0; i < points.Len(); i++ { + points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { + result = append(result, &label.LabelDescriptor{ + Key: key, + }) + return true + }) + } + case pdata.MetricDataTypeSummary: + points := m.Summary().DataPoints() + for i := 0; i < points.Len(); i++ { + points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { + result = append(result, &label.LabelDescriptor{ + Key: key, + }) + return true + }) + } + case pdata.MetricDataTypeHistogram: + points := m.Histogram().DataPoints() + for i := 0; i < points.Len(); i++ { + points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { + result = append(result, &label.LabelDescriptor{ + Key: key, + }) + return true + }) + } + case pdata.MetricDataTypeExponentialHistogram: + points := m.ExponentialHistogram().DataPoints() + for i := 0; i < points.Len(); i++ { + points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { + result = append(result, &label.LabelDescriptor{ + Key: key, + }) + return true + }) + } + } + return result +} + +func (me *metricMapper) summaryMetricDescriptors(m pdata.Metric) []*metric.MetricDescriptor { + sumName := fmt.Sprintf("%s%s", m.Name(), SUMMARY_SUM_SUFFIX) + countName := fmt.Sprintf("%s%s", m.Name(), SUMMARY_COUNT_SUFFIX) + percentileName := fmt.Sprintf("%s%s", m.Name(), SUMMARY_PERCENTILE_SUFFIX) + labels := me.labelDescriptors(m) + return []*metric.MetricDescriptor{ + { + Type: me.metricNameToType(sumName), + Labels: labels, + MetricKind: metric.MetricDescriptor_CUMULATIVE, + ValueType: metric.MetricDescriptor_DOUBLE, + Unit: m.Unit(), + Description: m.Description(), + DisplayName: sumName, + }, + { + Type: me.metricNameToType(countName), + Labels: labels, + MetricKind: metric.MetricDescriptor_CUMULATIVE, + ValueType: metric.MetricDescriptor_INT64, + Unit: m.Unit(), + Description: m.Description(), + DisplayName: countName, + }, + { + Type: me.metricNameToType(percentileName), + Labels: append( + labels, + &label.LabelDescriptor{ + Key: "percentile", + Description: "the value at a given percentile of a distribution", + }), + MetricKind: metric.MetricDescriptor_GAUGE, + ValueType: metric.MetricDescriptor_DOUBLE, + Unit: m.Unit(), + Description: m.Description(), + DisplayName: percentileName, + }, + } +} + // Extract the metric descriptor from a metric data point. -func (me *metricMapper) metricDescriptor(m pdata.Metric) *metric.MetricDescriptor { +func (me *metricMapper) metricDescriptor(m pdata.Metric) []*metric.MetricDescriptor { + if m.DataType() == pdata.MetricDataTypeSummary { + return me.summaryMetricDescriptors(m) + } kind, typ := mapMetricPointKind(m) metricType := me.metricNameToType(m.Name()) + labels := me.labelDescriptors(m) // Return nil for unsupported types. if kind == metric.MetricDescriptor_METRIC_KIND_UNSPECIFIED { return nil } - return &metric.MetricDescriptor{ - Name: m.Name(), - DisplayName: me.metricTypeToDisplayName(metricType), - Type: metricType, - MetricKind: kind, - ValueType: typ, - Unit: m.Unit(), - Description: m.Description(), - // TODO: Labels for non-wlm + return []*metric.MetricDescriptor{ + { + Name: m.Name(), + DisplayName: me.metricTypeToDisplayName(metricType), + Type: metricType, + MetricKind: kind, + ValueType: typ, + Unit: m.Unit(), + Description: m.Description(), + Labels: labels, + }, } } diff --git a/exporter/collector/metricsexporter_test.go b/exporter/collector/metricsexporter_test.go index 61bc044d8..8d117ff4e 100644 --- a/exporter/collector/metricsexporter_test.go +++ b/exporter/collector/metricsexporter_test.go @@ -21,6 +21,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/model/pdata" + "google.golang.org/genproto/googleapis/api/label" + "google.golang.org/genproto/googleapis/api/metric" metricpb "google.golang.org/genproto/googleapis/api/metric" monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" @@ -349,7 +351,7 @@ func TestNumberDataPointToValue(t *testing.T) { type metricDescriptorTest struct { name string metricCreator func() pdata.Metric - expected *metricpb.MetricDescriptor + expected []*metricpb.MetricDescriptor } func TestMetricDescriptorMapping(t *testing.T) { @@ -365,16 +367,24 @@ func TestMetricDescriptorMapping(t *testing.T) { gauge := metric.Gauge() point := gauge.DataPoints().AppendEmpty() point.SetDoubleVal(10) + point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: &metricpb.MetricDescriptor{ - Name: "custom.googleapis.com/test.metric", - DisplayName: "test.metric", - Type: "custom.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_GAUGE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", + expected: []*metric.MetricDescriptor{ + { + Name: "custom.googleapis.com/test.metric", + DisplayName: "test.metric", + Type: "custom.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + }, + }, }, }, { @@ -390,16 +400,24 @@ func TestMetricDescriptorMapping(t *testing.T) { sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) point := sum.DataPoints().AppendEmpty() point.SetDoubleVal(10) + point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: &metricpb.MetricDescriptor{ - Name: "custom.googleapis.com/test.metric", - DisplayName: "test.metric", - Type: "custom.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", + expected: []*metric.MetricDescriptor{ + { + Name: "custom.googleapis.com/test.metric", + DisplayName: "test.metric", + Type: "custom.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + }, + }, }, }, { @@ -415,16 +433,24 @@ func TestMetricDescriptorMapping(t *testing.T) { sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityDelta) point := sum.DataPoints().AppendEmpty() point.SetDoubleVal(10) + point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", + expected: []*metric.MetricDescriptor{ + { + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + }, + }, }, }, { @@ -440,16 +466,24 @@ func TestMetricDescriptorMapping(t *testing.T) { sum.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) point := sum.DataPoints().AppendEmpty() point.SetDoubleVal(10) + point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_GAUGE, - ValueType: metricpb.MetricDescriptor_DOUBLE, - Unit: "1", - Description: "Description", + expected: []*metric.MetricDescriptor{ + { + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + }, + }, }, }, { @@ -462,16 +496,25 @@ func TestMetricDescriptorMapping(t *testing.T) { metric.SetUnit("1") histogram := metric.Histogram() histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) + point := histogram.DataPoints().AppendEmpty() + point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DISTRIBUTION, - Unit: "1", - Description: "Description", + expected: []*metric.MetricDescriptor{ + { + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DISTRIBUTION, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + }, + }, }, }, { @@ -486,14 +529,76 @@ func TestMetricDescriptorMapping(t *testing.T) { histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) return metric }, - expected: &metricpb.MetricDescriptor{ - Name: "test.metric", - DisplayName: "test.metric", - Type: "workload.googleapis.com/test.metric", - MetricKind: metricpb.MetricDescriptor_CUMULATIVE, - ValueType: metricpb.MetricDescriptor_DISTRIBUTION, - Unit: "1", - Description: "Description", + expected: []*metric.MetricDescriptor{ + { + Name: "test.metric", + DisplayName: "test.metric", + Type: "workload.googleapis.com/test.metric", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DISTRIBUTION, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{}, + }, + }, + }, + { + name: "Summary", + metricCreator: func() pdata.Metric { + metric := pdata.NewMetric() + metric.SetDataType(pdata.MetricDataTypeSummary) + metric.SetName("test.metric") + metric.SetDescription("Description") + metric.SetUnit("1") + summary := metric.Summary() + point := summary.DataPoints().AppendEmpty() + point.Attributes().InsertString("test_label", "value") + return metric + }, + expected: []*metric.MetricDescriptor{ + { + DisplayName: "test.metric_summary_sum", + Type: "workload.googleapis.com/test.metric_summary_sum", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + }, + }, + { + DisplayName: "test.metric_summary_count", + Type: "workload.googleapis.com/test.metric_summary_count", + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_INT64, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + }, + }, + { + Type: "workload.googleapis.com/test.metric_summary_percentile", + DisplayName: "test.metric_summary_percentile", + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, + Unit: "1", + Description: "Description", + Labels: []*label.LabelDescriptor{ + { + Key: "test_label", + }, + { + Key: "percentile", + Description: "the value at a given percentile of a distribution", + }, + }, + }, }, }, } From 0c1cfd22e49a605b399eef6d1957d63da0a0ee56 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 20 Dec 2021 10:33:51 -0500 Subject: [PATCH 05/13] Fix lint issues. --- exporter/collector/metricsexporter.go | 80 +++++++++++++-------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index 2e0429d1f..fd8d253bf 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -57,9 +57,9 @@ var domains = []string{"googleapis.com", "kubernetes.io", "istio.io", "knative.d // Constants we use when translating summary metrics into GCP. const ( - SUMMARY_COUNT_SUFFIX = "_summary_count" - SUMMARY_SUM_SUFFIX = "_summary_sum" - SUMMARY_PERCENTILE_SUFFIX = "_summary_percentile" + SummaryCountPrefix = "_summary_count" + SummarySumSuffix = "_summary_sum" + SummaryPercentileSuffix = "_summary_percentile" ) type labels map[string]string @@ -317,13 +317,13 @@ func (m *metricMapper) gaugePointToTimeSeries( } // Returns any configured prefix to add to unknown metric name. -func (me *metricMapper) getMetricNamePrefix(name string) *string { - for _, domain := range me.cfg.MetricConfig.KnownDomains { +func (m *metricMapper) getMetricNamePrefix(name string) *string { + for _, domain := range m.cfg.MetricConfig.KnownDomains { if strings.Contains(name, domain) { return nil } } - result := me.cfg.MetricConfig.Prefix + result := m.cfg.MetricConfig.Prefix return &result } @@ -400,23 +400,23 @@ func mergeLabels(mergeInto labels, others ...labels) labels { } // Takes a GCM metric type, like (workload.googleapis.com/MyCoolMetric) and returns the display name. -func (m *metricMapper) metricTypeToDisplayName(mUrl string) string { +func (m *metricMapper) metricTypeToDisplayName(mURL string) string { // TODO - user configuration around display name? // Default: strip domain, keep path after domain. - u, err := url.Parse(fmt.Sprintf("metrics://%s", mUrl)) + u, err := url.Parse(fmt.Sprintf("metrics://%s", mURL)) if err != nil { - return mUrl + return mURL } return strings.TrimLeft(u.Path, "/") } // Returns label descriptors for a metric. -func (me *metricMapper) labelDescriptors(m pdata.Metric) []*label.LabelDescriptor { +func (m *metricMapper) labelDescriptors(pm pdata.Metric) []*label.LabelDescriptor { // TODO - allow customization of label descriptions. result := []*label.LabelDescriptor{} - switch m.DataType() { + switch pm.DataType() { case pdata.MetricDataTypeGauge: - points := m.Gauge().DataPoints() + points := pm.Gauge().DataPoints() for i := 0; i < points.Len(); i++ { points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { result = append(result, &label.LabelDescriptor{ @@ -426,7 +426,7 @@ func (me *metricMapper) labelDescriptors(m pdata.Metric) []*label.LabelDescripto }) } case pdata.MetricDataTypeSum: - points := m.Sum().DataPoints() + points := pm.Sum().DataPoints() for i := 0; i < points.Len(); i++ { points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { result = append(result, &label.LabelDescriptor{ @@ -436,7 +436,7 @@ func (me *metricMapper) labelDescriptors(m pdata.Metric) []*label.LabelDescripto }) } case pdata.MetricDataTypeSummary: - points := m.Summary().DataPoints() + points := pm.Summary().DataPoints() for i := 0; i < points.Len(); i++ { points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { result = append(result, &label.LabelDescriptor{ @@ -446,7 +446,7 @@ func (me *metricMapper) labelDescriptors(m pdata.Metric) []*label.LabelDescripto }) } case pdata.MetricDataTypeHistogram: - points := m.Histogram().DataPoints() + points := pm.Histogram().DataPoints() for i := 0; i < points.Len(); i++ { points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { result = append(result, &label.LabelDescriptor{ @@ -456,7 +456,7 @@ func (me *metricMapper) labelDescriptors(m pdata.Metric) []*label.LabelDescripto }) } case pdata.MetricDataTypeExponentialHistogram: - points := m.ExponentialHistogram().DataPoints() + points := pm.ExponentialHistogram().DataPoints() for i := 0; i < points.Len(); i++ { points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { result = append(result, &label.LabelDescriptor{ @@ -469,32 +469,32 @@ func (me *metricMapper) labelDescriptors(m pdata.Metric) []*label.LabelDescripto return result } -func (me *metricMapper) summaryMetricDescriptors(m pdata.Metric) []*metric.MetricDescriptor { - sumName := fmt.Sprintf("%s%s", m.Name(), SUMMARY_SUM_SUFFIX) - countName := fmt.Sprintf("%s%s", m.Name(), SUMMARY_COUNT_SUFFIX) - percentileName := fmt.Sprintf("%s%s", m.Name(), SUMMARY_PERCENTILE_SUFFIX) - labels := me.labelDescriptors(m) +func (m *metricMapper) summaryMetricDescriptors(pm pdata.Metric) []*metric.MetricDescriptor { + sumName := fmt.Sprintf("%s%s", pm.Name(), SummarySumSuffix) + countName := fmt.Sprintf("%s%s", pm.Name(), SummaryCountPrefix) + percentileName := fmt.Sprintf("%s%s", pm.Name(), SummaryPercentileSuffix) + labels := m.labelDescriptors(pm) return []*metric.MetricDescriptor{ { - Type: me.metricNameToType(sumName), + Type: m.metricNameToType(sumName), Labels: labels, MetricKind: metric.MetricDescriptor_CUMULATIVE, ValueType: metric.MetricDescriptor_DOUBLE, - Unit: m.Unit(), - Description: m.Description(), + Unit: pm.Unit(), + Description: pm.Description(), DisplayName: sumName, }, { - Type: me.metricNameToType(countName), + Type: m.metricNameToType(countName), Labels: labels, MetricKind: metric.MetricDescriptor_CUMULATIVE, ValueType: metric.MetricDescriptor_INT64, - Unit: m.Unit(), - Description: m.Description(), + Unit: pm.Unit(), + Description: pm.Description(), DisplayName: countName, }, { - Type: me.metricNameToType(percentileName), + Type: m.metricNameToType(percentileName), Labels: append( labels, &label.LabelDescriptor{ @@ -503,34 +503,34 @@ func (me *metricMapper) summaryMetricDescriptors(m pdata.Metric) []*metric.Metri }), MetricKind: metric.MetricDescriptor_GAUGE, ValueType: metric.MetricDescriptor_DOUBLE, - Unit: m.Unit(), - Description: m.Description(), + Unit: pm.Unit(), + Description: pm.Description(), DisplayName: percentileName, }, } } // Extract the metric descriptor from a metric data point. -func (me *metricMapper) metricDescriptor(m pdata.Metric) []*metric.MetricDescriptor { - if m.DataType() == pdata.MetricDataTypeSummary { - return me.summaryMetricDescriptors(m) +func (m *metricMapper) metricDescriptor(pm pdata.Metric) []*metric.MetricDescriptor { + if pm.DataType() == pdata.MetricDataTypeSummary { + return m.summaryMetricDescriptors(pm) } - kind, typ := mapMetricPointKind(m) - metricType := me.metricNameToType(m.Name()) - labels := me.labelDescriptors(m) + kind, typ := mapMetricPointKind(pm) + metricType := m.metricNameToType(pm.Name()) + labels := m.labelDescriptors(pm) // Return nil for unsupported types. if kind == metric.MetricDescriptor_METRIC_KIND_UNSPECIFIED { return nil } return []*metric.MetricDescriptor{ { - Name: m.Name(), - DisplayName: me.metricTypeToDisplayName(metricType), + Name: pm.Name(), + DisplayName: m.metricTypeToDisplayName(metricType), Type: metricType, MetricKind: kind, ValueType: typ, - Unit: m.Unit(), - Description: m.Description(), + Unit: pm.Unit(), + Description: pm.Description(), Labels: labels, }, } From 968adf3fc69ce1dcb2f7b30bd8bd91ddf1258d80 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 20 Dec 2021 19:57:59 -0500 Subject: [PATCH 06/13] Some fixes from review. --- exporter/collector/metricsexporter.go | 73 +++++++++++++-------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index fd8d253bf..812c2e77d 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -30,7 +30,6 @@ import ( "go.opentelemetry.io/collector/exporter/exporterhelper" "go.opentelemetry.io/collector/model/pdata" "google.golang.org/genproto/googleapis/api/label" - "google.golang.org/genproto/googleapis/api/metric" metricpb "google.golang.org/genproto/googleapis/api/metric" monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" @@ -43,7 +42,7 @@ type metricsExporter struct { client *monitoring.MetricClient mapper metricMapper // A channel that receives metric descriptor and sends them to GCM once. - mds chan *metric.MetricDescriptor + mds chan *metricpb.MetricDescriptor } // metricMapper is the part that transforms metrics. Separate from metricsExporter since it has @@ -71,7 +70,7 @@ func (me *metricsExporter) Shutdown(context.Context) error { // Updates config object to include all defaults for metric export. func (cfg *Config) SetMetricDefaults() { - if cfg.MetricConfig.KnownDomains == nil || len(cfg.MetricConfig.KnownDomains) == 0 { + if len(cfg.MetricConfig.KnownDomains) == 0 { cfg.MetricConfig.KnownDomains = domains } // TODO - if in legacy mode, this should be @@ -113,7 +112,7 @@ func newGoogleCloudMetricsExporter( cfg: cfg, client: client, mapper: metricMapper{cfg}, - mds: make(chan *metric.MetricDescriptor), + mds: make(chan *metricpb.MetricDescriptor), } // Fire up the metric descriptor exporter. @@ -160,7 +159,7 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err // Reads metric descriptors from the md channel, and reports them (once) to GCM. func (me *metricsExporter) exportMetricDescriptorRunner(ctx context.Context) { - mdCache := make(map[string]*metric.MetricDescriptor) + mdCache := make(map[string]*metricpb.MetricDescriptor) for { select { case md := <-me.mds: @@ -181,7 +180,7 @@ func (me *metricsExporter) exportMetricDescriptorRunner(ctx context.Context) { } // Helper method to send metric descriptors to GCM. -func (me *metricsExporter) exportMetricDescriptor(ctx context.Context, md *metric.MetricDescriptor) error { +func (me *metricsExporter) exportMetricDescriptor(ctx context.Context, md *metricpb.MetricDescriptor) error { // export req := &monitoringpb.CreateMetricDescriptorRequest{ // TODO set Name field with project ID from config or ADC @@ -469,17 +468,17 @@ func (m *metricMapper) labelDescriptors(pm pdata.Metric) []*label.LabelDescripto return result } -func (m *metricMapper) summaryMetricDescriptors(pm pdata.Metric) []*metric.MetricDescriptor { +func (m *metricMapper) summaryMetricDescriptors(pm pdata.Metric) []*metricpb.MetricDescriptor { sumName := fmt.Sprintf("%s%s", pm.Name(), SummarySumSuffix) countName := fmt.Sprintf("%s%s", pm.Name(), SummaryCountPrefix) percentileName := fmt.Sprintf("%s%s", pm.Name(), SummaryPercentileSuffix) labels := m.labelDescriptors(pm) - return []*metric.MetricDescriptor{ + return []*metricpb.MetricDescriptor{ { Type: m.metricNameToType(sumName), Labels: labels, - MetricKind: metric.MetricDescriptor_CUMULATIVE, - ValueType: metric.MetricDescriptor_DOUBLE, + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_DOUBLE, Unit: pm.Unit(), Description: pm.Description(), DisplayName: sumName, @@ -487,8 +486,8 @@ func (m *metricMapper) summaryMetricDescriptors(pm pdata.Metric) []*metric.Metri { Type: m.metricNameToType(countName), Labels: labels, - MetricKind: metric.MetricDescriptor_CUMULATIVE, - ValueType: metric.MetricDescriptor_INT64, + MetricKind: metricpb.MetricDescriptor_CUMULATIVE, + ValueType: metricpb.MetricDescriptor_INT64, Unit: pm.Unit(), Description: pm.Description(), DisplayName: countName, @@ -501,8 +500,8 @@ func (m *metricMapper) summaryMetricDescriptors(pm pdata.Metric) []*metric.Metri Key: "percentile", Description: "the value at a given percentile of a distribution", }), - MetricKind: metric.MetricDescriptor_GAUGE, - ValueType: metric.MetricDescriptor_DOUBLE, + MetricKind: metricpb.MetricDescriptor_GAUGE, + ValueType: metricpb.MetricDescriptor_DOUBLE, Unit: pm.Unit(), Description: pm.Description(), DisplayName: percentileName, @@ -511,7 +510,7 @@ func (m *metricMapper) summaryMetricDescriptors(pm pdata.Metric) []*metric.Metri } // Extract the metric descriptor from a metric data point. -func (m *metricMapper) metricDescriptor(pm pdata.Metric) []*metric.MetricDescriptor { +func (m *metricMapper) metricDescriptor(pm pdata.Metric) []*metricpb.MetricDescriptor { if pm.DataType() == pdata.MetricDataTypeSummary { return m.summaryMetricDescriptors(pm) } @@ -519,10 +518,10 @@ func (m *metricMapper) metricDescriptor(pm pdata.Metric) []*metric.MetricDescrip metricType := m.metricNameToType(pm.Name()) labels := m.labelDescriptors(pm) // Return nil for unsupported types. - if kind == metric.MetricDescriptor_METRIC_KIND_UNSPECIFIED { + if kind == metricpb.MetricDescriptor_METRIC_KIND_UNSPECIFIED { return nil } - return []*metric.MetricDescriptor{ + return []*metricpb.MetricDescriptor{ { Name: pm.Name(), DisplayName: m.metricTypeToDisplayName(metricType), @@ -536,59 +535,59 @@ func (m *metricMapper) metricDescriptor(pm pdata.Metric) []*metric.MetricDescrip } } -func metricPointValueType(pt pdata.MetricValueType) metric.MetricDescriptor_ValueType { +func metricPointValueType(pt pdata.MetricValueType) metricpb.MetricDescriptor_ValueType { switch pt { case pdata.MetricValueTypeInt: - return metric.MetricDescriptor_INT64 + return metricpb.MetricDescriptor_INT64 case pdata.MetricValueTypeDouble: - return metric.MetricDescriptor_DOUBLE + return metricpb.MetricDescriptor_DOUBLE default: - return metric.MetricDescriptor_VALUE_TYPE_UNSPECIFIED + return metricpb.MetricDescriptor_VALUE_TYPE_UNSPECIFIED } } -func mapMetricPointKind(m pdata.Metric) (metric.MetricDescriptor_MetricKind, metric.MetricDescriptor_ValueType) { - var kind metric.MetricDescriptor_MetricKind - var typ metric.MetricDescriptor_ValueType +func mapMetricPointKind(m pdata.Metric) (metricpb.MetricDescriptor_MetricKind, metricpb.MetricDescriptor_ValueType) { + var kind metricpb.MetricDescriptor_MetricKind + var typ metricpb.MetricDescriptor_ValueType switch m.DataType() { case pdata.MetricDataTypeGauge: - kind = metric.MetricDescriptor_GAUGE + kind = metricpb.MetricDescriptor_GAUGE if m.Gauge().DataPoints().Len() > 0 { typ = metricPointValueType(m.Gauge().DataPoints().At(0).Type()) } case pdata.MetricDataTypeSum: if !m.Sum().IsMonotonic() { - kind = metric.MetricDescriptor_GAUGE + kind = metricpb.MetricDescriptor_GAUGE } else if m.Sum().AggregationTemporality() == pdata.MetricAggregationTemporalityDelta { // We report fake-deltas for now. - kind = metric.MetricDescriptor_CUMULATIVE + kind = metricpb.MetricDescriptor_CUMULATIVE } else { - kind = metric.MetricDescriptor_CUMULATIVE + kind = metricpb.MetricDescriptor_CUMULATIVE } if m.Sum().DataPoints().Len() > 0 { typ = metricPointValueType(m.Sum().DataPoints().At(0).Type()) } case pdata.MetricDataTypeSummary: - kind = metric.MetricDescriptor_GAUGE + kind = metricpb.MetricDescriptor_GAUGE case pdata.MetricDataTypeHistogram: - typ = metric.MetricDescriptor_DISTRIBUTION + typ = metricpb.MetricDescriptor_DISTRIBUTION if m.Histogram().AggregationTemporality() == pdata.MetricAggregationTemporalityDelta { // We report fake-deltas for now. - kind = metric.MetricDescriptor_CUMULATIVE + kind = metricpb.MetricDescriptor_CUMULATIVE } else { - kind = metric.MetricDescriptor_CUMULATIVE + kind = metricpb.MetricDescriptor_CUMULATIVE } case pdata.MetricDataTypeExponentialHistogram: - typ = metric.MetricDescriptor_DISTRIBUTION + typ = metricpb.MetricDescriptor_DISTRIBUTION if m.ExponentialHistogram().AggregationTemporality() == pdata.MetricAggregationTemporalityDelta { // We report fake-deltas for now. - kind = metric.MetricDescriptor_CUMULATIVE + kind = metricpb.MetricDescriptor_CUMULATIVE } else { - kind = metric.MetricDescriptor_CUMULATIVE + kind = metricpb.MetricDescriptor_CUMULATIVE } default: - kind = metric.MetricDescriptor_METRIC_KIND_UNSPECIFIED - typ = metric.MetricDescriptor_VALUE_TYPE_UNSPECIFIED + kind = metricpb.MetricDescriptor_METRIC_KIND_UNSPECIFIED + typ = metricpb.MetricDescriptor_VALUE_TYPE_UNSPECIFIED } return kind, typ } From 455ec160cada491a722445c0ddf8b2c53fb53975 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 20 Dec 2021 20:00:09 -0500 Subject: [PATCH 07/13] Remove metric import. --- exporter/collector/metricsexporter_test.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/exporter/collector/metricsexporter_test.go b/exporter/collector/metricsexporter_test.go index 8d117ff4e..829b83e85 100644 --- a/exporter/collector/metricsexporter_test.go +++ b/exporter/collector/metricsexporter_test.go @@ -22,7 +22,6 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/model/pdata" "google.golang.org/genproto/googleapis/api/label" - "google.golang.org/genproto/googleapis/api/metric" metricpb "google.golang.org/genproto/googleapis/api/metric" monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" @@ -370,7 +369,7 @@ func TestMetricDescriptorMapping(t *testing.T) { point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: []*metric.MetricDescriptor{ + expected: []*metricpb.MetricDescriptor{ { Name: "custom.googleapis.com/test.metric", DisplayName: "test.metric", @@ -403,7 +402,7 @@ func TestMetricDescriptorMapping(t *testing.T) { point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: []*metric.MetricDescriptor{ + expected: []*metricpb.MetricDescriptor{ { Name: "custom.googleapis.com/test.metric", DisplayName: "test.metric", @@ -436,7 +435,7 @@ func TestMetricDescriptorMapping(t *testing.T) { point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: []*metric.MetricDescriptor{ + expected: []*metricpb.MetricDescriptor{ { Name: "test.metric", DisplayName: "test.metric", @@ -469,7 +468,7 @@ func TestMetricDescriptorMapping(t *testing.T) { point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: []*metric.MetricDescriptor{ + expected: []*metricpb.MetricDescriptor{ { Name: "test.metric", DisplayName: "test.metric", @@ -500,7 +499,7 @@ func TestMetricDescriptorMapping(t *testing.T) { point.Attributes().InsertString("test_label", "test_value") return metric }, - expected: []*metric.MetricDescriptor{ + expected: []*metricpb.MetricDescriptor{ { Name: "test.metric", DisplayName: "test.metric", @@ -529,7 +528,7 @@ func TestMetricDescriptorMapping(t *testing.T) { histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative) return metric }, - expected: []*metric.MetricDescriptor{ + expected: []*metricpb.MetricDescriptor{ { Name: "test.metric", DisplayName: "test.metric", @@ -555,7 +554,7 @@ func TestMetricDescriptorMapping(t *testing.T) { point.Attributes().InsertString("test_label", "value") return metric }, - expected: []*metric.MetricDescriptor{ + expected: []*metricpb.MetricDescriptor{ { DisplayName: "test.metric_summary_sum", Type: "workload.googleapis.com/test.metric_summary_sum", From f3229ee74fa4582491edf800a370b238807a032b Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Tue, 21 Dec 2021 10:44:21 -0500 Subject: [PATCH 08/13] Fixes from review. - Update default config method - Simplify some of my lack-of-go expertise. --- exporter/collector/config_test.go | 3 + exporter/collector/factory.go | 8 ++- exporter/collector/metricsexporter.go | 66 +++++++--------------- exporter/collector/metricsexporter_test.go | 12 ++-- 4 files changed, 32 insertions(+), 57 deletions(-) diff --git a/exporter/collector/config_test.go b/exporter/collector/config_test.go index f424258d5..c7a6d1fd6 100644 --- a/exporter/collector/config_test.go +++ b/exporter/collector/config_test.go @@ -90,6 +90,9 @@ func TestLoadConfig(t *testing.T) { MetricConfig: MetricConfig{ Prefix: "prefix", SkipCreateMetricDescriptor: true, + KnownDomains: []string{ + "googleapis.com", "kubernetes.io", "istio.io", "knative.dev", + }, }, }) } diff --git a/exporter/collector/factory.go b/exporter/collector/factory.go index 346ad989a..8ef887d2e 100644 --- a/exporter/collector/factory.go +++ b/exporter/collector/factory.go @@ -42,20 +42,24 @@ func NewFactory() component.ExporterFactory { return exporterhelper.NewFactory( typeStr, - createDefaultConfig, + func() config.Exporter { return createDefaultConfig() }, exporterhelper.WithTraces(createTracesExporter), exporterhelper.WithMetrics(createMetricsExporter), ) } // createDefaultConfig creates the default configuration for exporter. -func createDefaultConfig() config.Exporter { +func createDefaultConfig() *Config { return &Config{ ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)), TimeoutSettings: exporterhelper.TimeoutSettings{Timeout: defaultTimeout}, RetrySettings: exporterhelper.DefaultRetrySettings(), QueueSettings: exporterhelper.DefaultQueueSettings(), UserAgent: "opentelemetry-collector-contrib {{version}}", + MetricConfig: MetricConfig{ + KnownDomains: domains, + Prefix: "workload.googleapis.com", + }, } } diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index 812c2e77d..f3e4b6289 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -64,37 +64,16 @@ const ( type labels map[string]string func (me *metricsExporter) Shutdown(context.Context) error { - // TODO - will the context given to us at startup be cancelled already? + close(me.mds) return me.client.Close() } -// Updates config object to include all defaults for metric export. -func (cfg *Config) SetMetricDefaults() { - if len(cfg.MetricConfig.KnownDomains) == 0 { - cfg.MetricConfig.KnownDomains = domains - } - // TODO - if in legacy mode, this should be - // external.googleapis.com/OpenCensus/ - if cfg.MetricConfig.Prefix == "" { - cfg.MetricConfig.Prefix = "workload.googleapis.com" - } -} - -// Updates configuration to include all defaults. Used in unit testing. -func (m *metricMapper) SetMetricDefaults() { - if m.cfg == nil { - m.cfg = &Config{} - } - m.cfg.SetMetricDefaults() -} - func newGoogleCloudMetricsExporter( ctx context.Context, cfg *Config, set component.ExporterCreateSettings, ) (component.MetricsExporter, error) { setVersionInUserAgent(cfg, set.BuildInfo.Version) - cfg.SetMetricDefaults() // map cfg options into metric service client configuration with // generateClientOptions() @@ -116,7 +95,7 @@ func newGoogleCloudMetricsExporter( } // Fire up the metric descriptor exporter. - go mExp.exportMetricDescriptorRunner(ctx) + go mExp.exportMetricDescriptorRunner() return exporterhelper.NewMetricsExporter( cfg, @@ -158,24 +137,22 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err } // Reads metric descriptors from the md channel, and reports them (once) to GCM. -func (me *metricsExporter) exportMetricDescriptorRunner(ctx context.Context) { +func (me *metricsExporter) exportMetricDescriptorRunner() { mdCache := make(map[string]*metricpb.MetricDescriptor) - for { - select { - case md := <-me.mds: - // Not yet sent, now we sent it. - if !me.cfg.MetricConfig.SkipCreateMetricDescriptor && md != nil && mdCache[md.Type] == nil { - err := me.exportMetricDescriptor(ctx, md) - // TODO: Log-once on error, per metric descriptor? - if err != nil { - fmt.Printf("Unable to send metric descriptor: %s, %s", md, err) - } - mdCache[md.Type] = md + // We iterate over all metric descritpors until the channel is closed. + // Note: if we get terminated, this will still attempt to export all descriptors + // prior to shutdown. + for md := range me.mds { + // Not yet sent, now we sent it. + if !me.cfg.MetricConfig.SkipCreateMetricDescriptor && md != nil && mdCache[md.Type] == nil { + err := me.exportMetricDescriptor(context.Background(), md) + // TODO: Log-once on error, per metric descriptor? + if err != nil { + fmt.Printf("Unable to send metric descriptor: %s, %s", md, err) } - case <-ctx.Done(): - // Kill this exporter when context is cancelled. - return + mdCache[md.Type] = md } + // TODO: We may want to compare current MD vs. previous and validate no changes. } } @@ -316,23 +293,18 @@ func (m *metricMapper) gaugePointToTimeSeries( } // Returns any configured prefix to add to unknown metric name. -func (m *metricMapper) getMetricNamePrefix(name string) *string { +func (m *metricMapper) getMetricNamePrefix(name string) string { for _, domain := range m.cfg.MetricConfig.KnownDomains { if strings.Contains(name, domain) { - return nil + return "" } } - result := m.cfg.MetricConfig.Prefix - return &result + return m.cfg.MetricConfig.Prefix } // metricNameToType maps OTLP metric name to GCM metric type (aka name) func (m *metricMapper) metricNameToType(name string) string { - prefix := m.getMetricNamePrefix(name) - if prefix != nil { - return path.Join(*prefix, name) - } - return name + return path.Join(m.getMetricNamePrefix(name), name) } func numberDataPointToValue( diff --git a/exporter/collector/metricsexporter_test.go b/exporter/collector/metricsexporter_test.go index 829b83e85..6e99decf1 100644 --- a/exporter/collector/metricsexporter_test.go +++ b/exporter/collector/metricsexporter_test.go @@ -95,8 +95,7 @@ func TestMergeLabels(t *testing.T) { } func TestSumPointToTimeSeries(t *testing.T) { - mapper := metricMapper{cfg: &Config{}} - mapper.SetMetricDefaults() + mapper := metricMapper{cfg: createDefaultConfig()} mr := &monitoredrespb.MonitoredResource{} newCase := func() (pdata.Metric, pdata.Sum, pdata.NumberDataPoint) { @@ -204,8 +203,7 @@ func TestSumPointToTimeSeries(t *testing.T) { } func TestGaugePointToTimeSeries(t *testing.T) { - mapper := metricMapper{cfg: &Config{}} - mapper.SetMetricDefaults() + mapper := metricMapper{cfg: createDefaultConfig()} mr := &monitoredrespb.MonitoredResource{} newCase := func() (pdata.Metric, pdata.Gauge, pdata.NumberDataPoint) { @@ -261,8 +259,7 @@ func TestGaugePointToTimeSeries(t *testing.T) { } func TestMetricNameToType(t *testing.T) { - mapper := metricMapper{cfg: &Config{}} - mapper.SetMetricDefaults() + mapper := metricMapper{cfg: createDefaultConfig()} assert.Equal( t, mapper.metricNameToType("foo"), @@ -603,8 +600,7 @@ func TestMetricDescriptorMapping(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - mapper := &metricMapper{} - mapper.SetMetricDefaults() + mapper := metricMapper{cfg: createDefaultConfig()} metric := test.metricCreator() md := mapper.metricDescriptor(metric) assert.Equal(t, md, test.expected) From a6525282b3404afe381c8d63f6ea2758244b0951 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Tue, 21 Dec 2021 10:54:56 -0500 Subject: [PATCH 09/13] Add unit test for metric domains. --- exporter/collector/metricsexporter_test.go | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/exporter/collector/metricsexporter_test.go b/exporter/collector/metricsexporter_test.go index 6e99decf1..a646731c5 100644 --- a/exporter/collector/metricsexporter_test.go +++ b/exporter/collector/metricsexporter_test.go @@ -15,6 +15,7 @@ package collector import ( + "fmt" "testing" "time" @@ -607,3 +608,55 @@ func TestMetricDescriptorMapping(t *testing.T) { }) } } + +type knownDomainsTest struct { + name string + metricType string + knownDomains []string +} + +func TestKnownDomains(t *testing.T) { + tests := []knownDomainsTest{ + { + name: "test", + metricType: "prefix/test", + }, + { + name: "googleapis.com/test", + metricType: "googleapis.com/test", + }, + { + name: "kubernetes.io/test", + metricType: "kubernetes.io/test", + }, + { + name: "istio.io/test", + metricType: "istio.io/test", + }, + { + name: "knative.dev/test", + metricType: "knative.dev/test", + }, + { + name: "knative.dev/test", + metricType: "prefix/knative.dev/test", + knownDomains: []string{"example.com"}, + }, + { + name: "example.com/test", + metricType: "example.com/test", + knownDomains: []string{"example.com"}, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("%v to %v", test.name, test.metricType), func(t *testing.T) { + config := createDefaultConfig() + config.MetricConfig.Prefix = "prefix" + if len(test.knownDomains) > 0 { + config.MetricConfig.KnownDomains = test.knownDomains + } + mapper := metricMapper{cfg: config} + assert.Equal(t, test.metricType, mapper.metricNameToType(test.name)) + }) + } +} From 5e20b7d729ea890fc9b7cd646b64cb110941cf31 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Tue, 21 Dec 2021 13:00:45 -0500 Subject: [PATCH 10/13] Fixes from review. --- exporter/collector/metricsexporter.go | 78 ++++++++++++++------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index f3e4b6289..3274defef 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -20,6 +20,7 @@ package collector import ( "context" "fmt" + "log" "net/url" "path" "strings" @@ -131,6 +132,12 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err } // TODO: self observability + // TODO: Figure out how to configure service time series calls. + if false { + err := me.createServiceTimeSeries(ctx, timeSeries) + recordPointCount(ctx, len(timeSeries), m.DataPointCount()-len(timeSeries), err) + return err + } err := me.createTimeSeries(ctx, timeSeries) recordPointCount(ctx, len(timeSeries), m.DataPointCount()-len(timeSeries), err) return err @@ -144,11 +151,13 @@ func (me *metricsExporter) exportMetricDescriptorRunner() { // prior to shutdown. for md := range me.mds { // Not yet sent, now we sent it. + // TODO - check to see if this is a service/system metric and doesn't send descriptors. if !me.cfg.MetricConfig.SkipCreateMetricDescriptor && md != nil && mdCache[md.Type] == nil { err := me.exportMetricDescriptor(context.Background(), md) // TODO: Log-once on error, per metric descriptor? + // TODO: Re-use passed-in logger to exporter. if err != nil { - fmt.Printf("Unable to send metric descriptor: %s, %s", md, err) + log.Printf("Unable to send metric descriptor: %s, %s", md, err) } mdCache[md.Type] = md } @@ -156,26 +165,40 @@ func (me *metricsExporter) exportMetricDescriptorRunner() { } } +func (me *metricsExporter) projectName() string { + // TODO set Name field with project ID from config or ADC + return fmt.Sprintf("projects/%s", me.cfg.ProjectID) +} + // Helper method to send metric descriptors to GCM. func (me *metricsExporter) exportMetricDescriptor(ctx context.Context, md *metricpb.MetricDescriptor) error { // export req := &monitoringpb.CreateMetricDescriptorRequest{ - // TODO set Name field with project ID from config or ADC - Name: fmt.Sprintf("projects/%s", me.cfg.ProjectID), + Name: me.projectName(), MetricDescriptor: md, } _, err := me.client.CreateMetricDescriptor(ctx, req) return err } +// Sends a user-custom-metric timeseries. func (me *metricsExporter) createTimeSeries(ctx context.Context, ts []*monitoringpb.TimeSeries) error { - // TODO: me.client.CreateServiceTimeSeries( return me.client.CreateTimeSeries( ctx, &monitoringpb.CreateTimeSeriesRequest{ + Name: me.projectName(), + TimeSeries: ts, + }, + ) +} + +// Sends a service timeseries. +func (me *metricsExporter) createServiceTimeSeries(ctx context.Context, ts []*monitoringpb.TimeSeries) error { + return me.client.CreateServiceTimeSeries( + ctx, + &monitoringpb.CreateTimeSeriesRequest{ + Name: me.projectName(), TimeSeries: ts, - // TODO set Name field with project ID from config or ADC - Name: fmt.Sprintf("projects/%s", me.cfg.ProjectID), }, ) } @@ -385,56 +408,39 @@ func (m *metricMapper) metricTypeToDisplayName(mURL string) string { func (m *metricMapper) labelDescriptors(pm pdata.Metric) []*label.LabelDescriptor { // TODO - allow customization of label descriptions. result := []*label.LabelDescriptor{} + addAttributes := func(attr pdata.AttributeMap) { + attr.Range(func(key string, _ pdata.AttributeValue) bool { + result = append(result, &label.LabelDescriptor{ + Key: key, + }) + return true + }) + } switch pm.DataType() { case pdata.MetricDataTypeGauge: points := pm.Gauge().DataPoints() for i := 0; i < points.Len(); i++ { - points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { - result = append(result, &label.LabelDescriptor{ - Key: key, - }) - return true - }) + addAttributes(points.At(i).Attributes()) } case pdata.MetricDataTypeSum: points := pm.Sum().DataPoints() for i := 0; i < points.Len(); i++ { - points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { - result = append(result, &label.LabelDescriptor{ - Key: key, - }) - return true - }) + addAttributes(points.At(i).Attributes()) } case pdata.MetricDataTypeSummary: points := pm.Summary().DataPoints() for i := 0; i < points.Len(); i++ { - points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { - result = append(result, &label.LabelDescriptor{ - Key: key, - }) - return true - }) + addAttributes(points.At(i).Attributes()) } case pdata.MetricDataTypeHistogram: points := pm.Histogram().DataPoints() for i := 0; i < points.Len(); i++ { - points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { - result = append(result, &label.LabelDescriptor{ - Key: key, - }) - return true - }) + addAttributes(points.At(i).Attributes()) } case pdata.MetricDataTypeExponentialHistogram: points := pm.ExponentialHistogram().DataPoints() for i := 0; i < points.Len(); i++ { - points.At(i).Attributes().Range(func(key string, _ pdata.AttributeValue) bool { - result = append(result, &label.LabelDescriptor{ - Key: key, - }) - return true - }) + addAttributes(points.At(i).Attributes()) } } return result From 3055b17876ea17c1df7986409207549dfe2b8d53 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Tue, 21 Dec 2021 13:40:07 -0500 Subject: [PATCH 11/13] Add breaking changes. --- exporter/collector/breaking-changes.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/exporter/collector/breaking-changes.md b/exporter/collector/breaking-changes.md index 385af454b..f90fbfc83 100644 --- a/exporter/collector/breaking-changes.md +++ b/exporter/collector/breaking-changes.md @@ -3,6 +3,22 @@ The new pdata based exporter has some breaking changes from the original OpenCensus stackdriver based `googlecloud` exporter: +## Metric Names and Descriptors + +The previous collector exporter would default to sending metrics with the type: +`custom.googleapis.com/OpenCensus/{metric_name}`. This has been changed to +`workload.googleapis.com/{metric_name}`. + +Additionally, the previous exporter had a hardcoded list of known metric domains +where this "prefix" would not be used. The new exporter allows full configuration +of this list via the `metric.known_domains` property. + +Additionally, the DisplayName for a metric used to be exactly the +`{metric_name}`. Now, the metric name is chosen as the full-path after the +domain name of the metric type. E.g. if a metric called +`workload.googleapis.com/nginx/latency` is created, the display name will +be `nginx/latency` instead of `workload.googleapis.com/nginx/latency`. + ## Labels Original label key mapping code is @@ -18,3 +34,10 @@ In the old exporter, delta sums were converted into GAUGE points ([see test fixture](https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/blob/9bc1f49ebe000b0b3b1aa5b7f201e7996effdcd8/exporter/collector/testdata/fixtures/delta_counter_metrics_expect.json#L15)). The new pdata exporter sends these as CUMULATIVE points with the same delta time window (reseting at each point) aka pseudo-cumulatives. + +## OTLP Summary + +The old exporter relied on upstream conversion of OTLP Summary into Gauge and +Cumulative points. The new exporter performas this conversion itself, which +means summary metric descriptors will include label description for `percentile` +labels. \ No newline at end of file From d9c14eef76bee6fac7ac215850aaeca55f35df27 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Tue, 21 Dec 2021 14:40:07 -0500 Subject: [PATCH 12/13] Fixes from review. --- exporter/collector/breaking-changes.md | 2 +- exporter/collector/metricsexporter.go | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/exporter/collector/breaking-changes.md b/exporter/collector/breaking-changes.md index f90fbfc83..a6e962af9 100644 --- a/exporter/collector/breaking-changes.md +++ b/exporter/collector/breaking-changes.md @@ -38,6 +38,6 @@ The new pdata exporter sends these as CUMULATIVE points with the same delta time ## OTLP Summary The old exporter relied on upstream conversion of OTLP Summary into Gauge and -Cumulative points. The new exporter performas this conversion itself, which +Cumulative points. The new exporter performs this conversion itself, which means summary metric descriptors will include label description for `percentile` labels. \ No newline at end of file diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index 3274defef..f992f60ea 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -76,8 +76,6 @@ func newGoogleCloudMetricsExporter( ) (component.MetricsExporter, error) { setVersionInUserAgent(cfg, set.BuildInfo.Version) - // map cfg options into metric service client configuration with - // generateClientOptions() clientOpts, err := generateClientOptions(cfg) if err != nil { return nil, err @@ -92,7 +90,11 @@ func newGoogleCloudMetricsExporter( cfg: cfg, client: client, mapper: metricMapper{cfg}, - mds: make(chan *metricpb.MetricDescriptor), + // We create a buffered channel for metric descriptors. + // MetricDescritpors are asycnhronously sent and optimistic. + // We only get Unit/Description/Display name from them, so it's ok + // to drop / conserve resources for sending timeseries. + mds: make(chan *metricpb.MetricDescriptor, 10), } // Fire up the metric descriptor exporter. @@ -123,8 +125,17 @@ func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) err mes := ilm.Metrics() for k := 0; k < mes.Len(); k++ { metric := mes.At(k) - for _, md := range me.mapper.metricDescriptor(metric) { - me.mds <- md + // TODO - check to see if this is a service/system metric and doesn't send descriptors. + if !me.cfg.MetricConfig.SkipCreateMetricDescriptor { + for _, md := range me.mapper.metricDescriptor(metric) { + if md != nil { + select { + case me.mds <- md: + default: + // Ignore drops, we'll catch descriptor next time around. + } + } + } } timeSeries = append(timeSeries, me.mapper.metricToTimeSeries(monitoredResource, extraLabels, metric)...) } @@ -151,13 +162,13 @@ func (me *metricsExporter) exportMetricDescriptorRunner() { // prior to shutdown. for md := range me.mds { // Not yet sent, now we sent it. - // TODO - check to see if this is a service/system metric and doesn't send descriptors. - if !me.cfg.MetricConfig.SkipCreateMetricDescriptor && md != nil && mdCache[md.Type] == nil { + if mdCache[md.Type] == nil { err := me.exportMetricDescriptor(context.Background(), md) // TODO: Log-once on error, per metric descriptor? // TODO: Re-use passed-in logger to exporter. if err != nil { log.Printf("Unable to send metric descriptor: %s, %s", md, err) + continue } mdCache[md.Type] = md } From e4da6ee5d093a23bf632b4177f1ce9d832ae0628 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Tue, 21 Dec 2021 16:45:37 -0500 Subject: [PATCH 13/13] Update context to be TODO. --- exporter/collector/metricsexporter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/collector/metricsexporter.go b/exporter/collector/metricsexporter.go index f992f60ea..45c4f2a89 100644 --- a/exporter/collector/metricsexporter.go +++ b/exporter/collector/metricsexporter.go @@ -163,7 +163,7 @@ func (me *metricsExporter) exportMetricDescriptorRunner() { for md := range me.mds { // Not yet sent, now we sent it. if mdCache[md.Type] == nil { - err := me.exportMetricDescriptor(context.Background(), md) + err := me.exportMetricDescriptor(context.TODO(), md) // TODO: Log-once on error, per metric descriptor? // TODO: Re-use passed-in logger to exporter. if err != nil {