Skip to content

Commit

Permalink
[connector/spanmetrics] Improve consistency between metrics generated…
Browse files Browse the repository at this point in the history
… by spanmetricsconnector (#34485)

**Link to tracking Issue:** #33227 #32818

**Documentation:** added an entry to the changelog explaining the
deprecated metrics.

---------

Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Signed-off-by: Juraci Paixão Kröhling <juraci@kroehling.de>
Co-authored-by: Murphy Chen <minquan.chen@daocloud.io>
Co-authored-by: Rafael Pax <rpax@users.noreply.github.com>
Co-authored-by: Juraci Paixão Kröhling <juraci@kroehling.de>
  • Loading branch information
4 people authored Sep 3, 2024
1 parent a967d83 commit 9545957
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 41 deletions.
29 changes: 29 additions & 0 deletions .chloggen/feat_33227.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: breaking

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: spanmetricsconnector

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Improve consistency between metrics generated by spanmetricsconnector. Added traces.span.metrics as default namespace

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [33227, 32818]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: "Default namespace for the generated metrics is traces.span.metrics now. |
The deprecated metrics are: calls, duration and events. |
The feature flag connector.spanmetrics.legacyLatencyMetricNames was added to revert the behavior."

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
4 changes: 3 additions & 1 deletion connector/spanmetricsconnector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ The following settings can be optionally configured:
cumulative temporality to avoid memory leaks and correct metric timestamp resets.
- `aggregation_temporality` (default: `AGGREGATION_TEMPORALITY_CUMULATIVE`): Defines the aggregation temporality of the generated metrics.
One of either `AGGREGATION_TEMPORALITY_CUMULATIVE` or `AGGREGATION_TEMPORALITY_DELTA`.
- `namespace`: Defines the namespace of the generated metrics. If `namespace` provided, generated metric name will be added `namespace.` prefix.
- `namespace` (default: `traces.span.metrics`): Defines the namespace of the generated metrics. If `namespace` provided, generated metric name will be added `namespace.` prefix.
- `metrics_flush_interval` (default: `60s`): Defines the flush interval of the generated metrics.
- `metrics_expiration` (default: `0`): Defines the expiration time as `time.Duration`, after which, if no new spans are received, metrics will no longer be exported. Setting to `0` means the metrics will never expire (default behavior).
- `metric_timestamp_cache_size` (default `1000`): Only relevant for delta temporality span metrics. Controls the size of the cache used to keep track of a metric's TimestampUnixNano the last time it was flushed. When a metric is evicted from the cache, its next data point will indicate a "reset" in the series. Downstream components converting from delta to cumulative, like `prometheusexporter`, may handle these resets by setting cumulative counters back to 0.
Expand All @@ -122,6 +122,8 @@ The following settings can be optionally configured:
- `dimensions`: (mandatory if `enabled`) the list of the span's event attributes to add as dimensions to the events metric, which will be included _on top of_ the common and configured `dimensions` for span and resource attributes.
- `resource_metrics_key_attributes`: Filter the resource attributes used to produce the resource metrics key map hash. Use this in case changing resource attributes (e.g. process id) are breaking counter metrics.

The feature gate `connector.spanmetrics.legacyMetricNames` (disabled by default) controls the connector to use legacy metric names.

## Examples

The following is a simple example usage of the `spanmetrics` connector.
Expand Down
7 changes: 7 additions & 0 deletions connector/spanmetricsconnector/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func TestLoadConfig(t *testing.T) {
{Name: "http.method", Default: &defaultMethod},
{Name: "http.status_code", Default: (*string)(nil)},
},
Namespace: DefaultNamespace,
DimensionsCacheSize: 1500,
ResourceMetricsCacheSize: 1600,
MetricsFlushInterval: 30 * time.Second,
Expand All @@ -70,6 +71,7 @@ func TestLoadConfig(t *testing.T) {
{
id: component.NewIDWithName(metadata.Type, "exponential_histogram"),
expected: &Config{
Namespace: DefaultNamespace,
AggregationTemporality: cumulative,
DimensionsCacheSize: defaultDimensionsCacheSize,
ResourceMetricsCacheSize: defaultResourceMetricsCacheSize,
Expand Down Expand Up @@ -103,6 +105,7 @@ func TestLoadConfig(t *testing.T) {
MetricsFlushInterval: 60 * time.Second,
Histogram: HistogramConfig{Disable: false, Unit: defaultUnit},
Exemplars: ExemplarsConfig{Enabled: true},
Namespace: DefaultNamespace,
},
},
{
Expand All @@ -114,6 +117,7 @@ func TestLoadConfig(t *testing.T) {
MetricsFlushInterval: 60 * time.Second,
Histogram: HistogramConfig{Disable: false, Unit: defaultUnit},
Exemplars: ExemplarsConfig{Enabled: true, MaxPerDataPoint: &defaultMaxPerDatapoint},
Namespace: DefaultNamespace,
},
},
{
Expand All @@ -125,6 +129,7 @@ func TestLoadConfig(t *testing.T) {
ResourceMetricsKeyAttributes: []string{"service.name", "telemetry.sdk.language", "telemetry.sdk.name"},
MetricsFlushInterval: 60 * time.Second,
Histogram: HistogramConfig{Disable: false, Unit: defaultUnit},
Namespace: DefaultNamespace,
},
},
{
Expand All @@ -136,6 +141,7 @@ func TestLoadConfig(t *testing.T) {
ResourceMetricsCacheSize: defaultResourceMetricsCacheSize,
MetricsFlushInterval: 60 * time.Second,
Histogram: HistogramConfig{Disable: false, Unit: defaultUnit},
Namespace: DefaultNamespace,
},
},
{
Expand All @@ -146,6 +152,7 @@ func TestLoadConfig(t *testing.T) {
ResourceMetricsCacheSize: defaultResourceMetricsCacheSize,
MetricsFlushInterval: 60 * time.Second,
Histogram: HistogramConfig{Disable: false, Unit: defaultUnit},
Namespace: DefaultNamespace,
},
extraAssertions: func(config *Config) {
assert.Equal(t, defaultDeltaTimestampCacheSize, config.GetDeltaTimestampCacheSize())
Expand Down
12 changes: 9 additions & 3 deletions connector/spanmetricsconnector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,22 +295,28 @@ func (p *connectorImp) buildMetrics() pmetric.Metrics {
return startTime
}

metricsNamespace := p.config.Namespace
if legacyMetricNamesFeatureGate.IsEnabled() && metricsNamespace == DefaultNamespace {
metricsNamespace = ""
}

sums := rawMetrics.sums
metric := sm.Metrics().AppendEmpty()
metric.SetName(buildMetricName(p.config.Namespace, metricNameCalls))
metric.SetName(buildMetricName(metricsNamespace, metricNameCalls))
sums.BuildMetrics(metric, startTimeGenerator, timestamp, p.config.GetAggregationTemporality())

if !p.config.Histogram.Disable {
histograms := rawMetrics.histograms
metric = sm.Metrics().AppendEmpty()
metric.SetName(buildMetricName(p.config.Namespace, metricNameDuration))
metric.SetName(buildMetricName(metricsNamespace, metricNameDuration))
metric.SetUnit(p.config.Histogram.Unit.String())
histograms.BuildMetrics(metric, startTimeGenerator, timestamp, p.config.GetAggregationTemporality())
}

events := rawMetrics.events
if p.events.Enabled {
metric = sm.Metrics().AppendEmpty()
metric.SetName(buildMetricName(p.config.Namespace, metricNameEvents))
metric.SetName(buildMetricName(metricsNamespace, metricNameEvents))
events.BuildMetrics(metric, startTimeGenerator, timestamp, p.config.GetAggregationTemporality())
}

Expand Down
102 changes: 65 additions & 37 deletions connector/spanmetricsconnector/connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go.opentelemetry.io/collector/connector/connectortest"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/consumer/consumertest"
"go.opentelemetry.io/collector/featuregate"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/pdata/ptrace"
Expand Down Expand Up @@ -1053,53 +1054,80 @@ func BenchmarkConnectorConsumeTraces(b *testing.B) {
}

func TestExcludeDimensionsConsumeTraces(t *testing.T) {
excludeDimensions := []string{"span.kind", "span.name", "totallyWrongNameDoesNotAffectAnything"}
p, err := newConnectorImp(stringp("defaultNullValue"), explicitHistogramsConfig, disabledExemplarsConfig, disabledEventsConfig, cumulative, 0, []string{}, 1000, clockwork.NewFakeClock(), excludeDimensions...)
require.NoError(t, err)
traces := buildSampleTrace()

// Test
ctx := metadata.NewIncomingContext(context.Background(), nil)
testcases := []struct {
dsc string
featureGateEnabled bool
}{
{
dsc: fmt.Sprintf("%s enabled", legacyMetricNamesFeatureGateID),
featureGateEnabled: true,
},
{
dsc: fmt.Sprintf("%s disabled", legacyMetricNamesFeatureGateID),
featureGateEnabled: false,
},
}

err = p.ConsumeTraces(ctx, traces)
require.NoError(t, err)
metrics := p.buildMetrics()
excludeDimensions := []string{"span.kind", "span.name", "totallyWrongNameDoesNotAffectAnything"}
for _, tc := range testcases {
tc := tc
t.Run(tc.dsc, func(t *testing.T) {
// Set feature gate value
previousValue := legacyMetricNamesFeatureGate.IsEnabled()
require.NoError(t, featuregate.GlobalRegistry().Set(legacyMetricNamesFeatureGate.ID(), tc.featureGateEnabled))
defer func() {
require.NoError(t, featuregate.GlobalRegistry().Set(legacyMetricNamesFeatureGate.ID(), previousValue))
}()

p, err := newConnectorImp(stringp("defaultNullValue"), explicitHistogramsConfig, disabledExemplarsConfig, disabledEventsConfig, cumulative, 0, []string{}, 1000, clockwork.NewFakeClock(), excludeDimensions...)
require.NoError(t, err)
traces := buildSampleTrace()

for i := 0; i < metrics.ResourceMetrics().Len(); i++ {
rm := metrics.ResourceMetrics().At(i)
ism := rm.ScopeMetrics()
// Checking all metrics, naming notice: ilmC/mC - C here is for Counter.
for ilmC := 0; ilmC < ism.Len(); ilmC++ {
m := ism.At(ilmC).Metrics()
for mC := 0; mC < m.Len(); mC++ {
metric := m.At(mC)
// We check only sum and histogram metrics here, because for now only they are present in this module.
ctx := metadata.NewIncomingContext(context.Background(), nil)

switch metric.Type() {
case pmetric.MetricTypeExponentialHistogram, pmetric.MetricTypeHistogram:
{
dp := metric.Histogram().DataPoints()
for dpi := 0; dpi < dp.Len(); dpi++ {
for attributeKey := range dp.At(dpi).Attributes().AsRaw() {
assert.NotContains(t, excludeDimensions, attributeKey)
}
err = p.ConsumeTraces(ctx, traces)
require.NoError(t, err)
metrics := p.buildMetrics()

}
}
case pmetric.MetricTypeEmpty, pmetric.MetricTypeGauge, pmetric.MetricTypeSum, pmetric.MetricTypeSummary:
{
dp := metric.Sum().DataPoints()
for dpi := 0; dpi < dp.Len(); dpi++ {
for attributeKey := range dp.At(dpi).Attributes().AsRaw() {
assert.NotContains(t, excludeDimensions, attributeKey)
for i := 0; i < metrics.ResourceMetrics().Len(); i++ {
rm := metrics.ResourceMetrics().At(i)
ism := rm.ScopeMetrics()
// Checking all metrics, naming notice: ilmC/mC - C here is for Counter.
for ilmC := 0; ilmC < ism.Len(); ilmC++ {
m := ism.At(ilmC).Metrics()
for mC := 0; mC < m.Len(); mC++ {
metric := m.At(mC)
// We check only sum and histogram metrics here, because for now only they are present in this module.

switch metric.Type() {
case pmetric.MetricTypeExponentialHistogram, pmetric.MetricTypeHistogram:
{
dp := metric.Histogram().DataPoints()
for dpi := 0; dpi < dp.Len(); dpi++ {
for attributeKey := range dp.At(dpi).Attributes().AsRaw() {
assert.NotContains(t, excludeDimensions, attributeKey)
}

}
}
case pmetric.MetricTypeEmpty, pmetric.MetricTypeGauge, pmetric.MetricTypeSum, pmetric.MetricTypeSummary:
{
dp := metric.Sum().DataPoints()
for dpi := 0; dpi < dp.Len(); dpi++ {
for attributeKey := range dp.At(dpi).Attributes().AsRaw() {
assert.NotContains(t, excludeDimensions, attributeKey)
}
}
}

}
}

}
}

}
}

})
}

}
Expand Down
19 changes: 19 additions & 0 deletions connector/spanmetricsconnector/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,28 @@ import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/connector"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/featuregate"

"github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector/internal/metadata"
)

const (
DefaultNamespace = "traces.span.metrics"
legacyMetricNamesFeatureGateID = "connector.spanmetrics.legacyMetricNames"
)

var legacyMetricNamesFeatureGate *featuregate.Gate

func init() {
// TODO: Remove this feature gate when the legacy metric names are removed.
legacyMetricNamesFeatureGate = featuregate.GlobalRegistry().MustRegister(
legacyMetricNamesFeatureGateID,
featuregate.StageAlpha, // Alpha because we want it disabled by default.
featuregate.WithRegisterDescription("When enabled, connector uses legacy metric names."),
featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/33227"),
)
}

// NewFactory creates a factory for the spanmetrics connector.
func NewFactory() connector.Factory {
return connector.NewFactory(
Expand All @@ -33,6 +51,7 @@ func createDefaultConfig() component.Config {
ResourceMetricsCacheSize: defaultResourceMetricsCacheSize,
MetricsFlushInterval: 60 * time.Second,
Histogram: HistogramConfig{Disable: false, Unit: defaultUnit},
Namespace: DefaultNamespace,
}
}

Expand Down
2 changes: 2 additions & 0 deletions connector/spanmetricsconnector/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
go.opentelemetry.io/collector/connector v0.108.2-0.20240829190554-7da6b618a7ee
go.opentelemetry.io/collector/consumer v0.108.2-0.20240829190554-7da6b618a7ee
go.opentelemetry.io/collector/consumer/consumertest v0.108.2-0.20240829190554-7da6b618a7ee
go.opentelemetry.io/collector/featuregate v1.14.2-0.20240829190554-7da6b618a7ee
go.opentelemetry.io/collector/pdata v1.14.2-0.20240829190554-7da6b618a7ee
go.opentelemetry.io/collector/semconv v0.108.2-0.20240829190554-7da6b618a7ee
go.uber.org/goleak v1.3.0
Expand All @@ -31,6 +32,7 @@ require (
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
Expand Down
4 changes: 4 additions & 0 deletions connector/spanmetricsconnector/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 9545957

Please sign in to comment.