[chore] service: inject default views via settings (#13775)

#### Description

Instead of updating the telemetry config, pass the default views in via
settings and have the telemetry implementation use it as appropriate.
This is necessary to decouple the service package from the
otelconftelemetry config.

#### Link to tracking issue

Part of
https://github.com/open-telemetry/opentelemetry-collector/issues/4970

#### Testing

N/A, non-functional change.

#### Documentation

N/A

---------

Co-authored-by: Alex Boten <223565+codeboten@users.noreply.github.com>
This commit is contained in:
Andrew Wilkins 2025-09-10 00:02:48 +08:00 committed by GitHub
parent 82f0c8d0c4
commit 01a48d2436
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 110 additions and 7 deletions

View File

@ -124,14 +124,11 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) {
}
mpConfig := &cfg.Telemetry.Metrics.MeterProvider
if mpConfig.Views == nil {
mpConfig.Views = configureViews(cfg.Telemetry.Metrics.Level)
}
telFactory := otelconftelemetry.NewFactory()
telset := telemetry.Settings{
BuildInfo: set.BuildInfo,
ZapOptions: set.LoggingOptions,
BuildInfo: set.BuildInfo,
ZapOptions: set.LoggingOptions,
DefaultViews: configureViews,
}
telProviders, err := telFactory.CreateProviders(ctx, telset, &cfg.Telemetry)

View File

@ -6,7 +6,9 @@ package otelconftelemetry
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
@ -20,6 +22,8 @@ import (
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp"
"go.opentelemetry.io/collector/service/internal/promtest"
"go.opentelemetry.io/collector/service/telemetry"
)
@ -255,3 +259,91 @@ func TestInstrumentEnabled(t *testing.T) {
require.NoError(t, err)
assert.Implements(t, new(enabledInstrument), floatGauge)
}
func TestTelemetryMetrics_DefaultViews(t *testing.T) {
type testcase struct {
configuredViews []config.View
expectedMeterNames []string
}
defaultViews := func(level configtelemetry.Level) []config.View {
assert.Equal(t, configtelemetry.LevelDetailed, level)
return []config.View{{
Selector: &config.ViewSelector{
MeterName: ptr("a"),
},
Stream: &config.ViewStream{
Aggregation: &config.ViewStreamAggregation{
Drop: config.ViewStreamAggregationDrop{},
},
},
}}
}
for name, tc := range map[string]testcase{
"no_configured_views": {
expectedMeterNames: []string{"b", "c"},
},
"configured_views": {
configuredViews: []config.View{{
Selector: &config.ViewSelector{
MeterName: ptr("b"),
},
Stream: &config.ViewStream{
Aggregation: &config.ViewStreamAggregation{
Drop: config.ViewStreamAggregationDrop{},
},
},
}},
expectedMeterNames: []string{"a", "c"},
},
} {
t.Run(name, func(t *testing.T) {
var metrics pmetric.Metrics
srv := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
body, err := io.ReadAll(req.Body)
assert.NoError(t, err)
exportRequest := pmetricotlp.NewExportRequest()
assert.NoError(t, exportRequest.UnmarshalProto(body))
metrics = exportRequest.Metrics()
}))
defer srv.Close()
cfg := createDefaultConfig().(*Config)
cfg.Metrics.Level = configtelemetry.LevelDetailed
cfg.Metrics.Views = tc.configuredViews
cfg.Metrics.Readers = []config.MetricReader{{
Periodic: &config.PeriodicMetricReader{
Exporter: config.PushMetricExporter{
OTLP: &config.OTLPMetric{
Endpoint: ptr(srv.URL),
Protocol: ptr("http/protobuf"),
Insecure: ptr(true),
},
},
},
}}
factory := NewFactory()
settings := telemetry.Settings{DefaultViews: defaultViews}
providers, err := factory.CreateProviders(t.Context(), settings, cfg)
require.NoError(t, err)
mp := providers.MeterProvider()
for _, meterName := range []string{"a", "b", "c"} {
counter, _ := mp.Meter(meterName).Int64Counter(meterName + ".counter")
counter.Add(t.Context(), 1)
}
require.NoError(t, providers.Shutdown(t.Context())) // should flush metrics
var scopes []string
for _, rm := range metrics.ResourceMetrics().All() {
for _, sm := range rm.ScopeMetrics().All() {
scopes = append(scopes, sm.Scope().Name())
}
}
assert.ElementsMatch(t, tc.expectedMeterNames, scopes)
})
}
}

View File

@ -121,10 +121,12 @@ func (t *otelconfTelemetry) Shutdown(ctx context.Context) error {
return nil
}
func newSDK(ctx context.Context, _ telemetry.Settings, cfg *Config, res *sdkresource.Resource) (*config.SDK, error) {
func newSDK(ctx context.Context, set telemetry.Settings, cfg *Config, res *sdkresource.Resource) (*config.SDK, error) {
mpConfig := cfg.Metrics.MeterProvider
if cfg.Metrics.Level == configtelemetry.LevelNone {
mpConfig.Readers = nil
} else if mpConfig.Views == nil && set.DefaultViews != nil {
mpConfig.Views = set.DefaultViews(cfg.Metrics.Level)
}
// Merge the BuildInfo-based resource attributes with the user-defined resource attributes.

View File

@ -6,6 +6,7 @@ package telemetry // import "go.opentelemetry.io/collector/service/telemetry"
import (
"context"
otelconf "go.opentelemetry.io/contrib/otelconf/v0.3.0"
"go.opentelemetry.io/otel/log"
nooplog "go.opentelemetry.io/otel/log/noop"
"go.opentelemetry.io/otel/metric"
@ -15,6 +16,7 @@ import (
"go.uber.org/zap"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/service/telemetry/internal/migration"
)
@ -67,6 +69,16 @@ type Settings struct {
// ZapOptions contains options for creating the zap logger.
ZapOptions []zap.Option
// DefaultViews holds a function that returns default metric
// views for the given internal telemetry metrics level.
//
// The meter provider is expected to use this if no user-provided
// view configuration is supplied.
//
// TODO we should not use otelconf.View directly here, change
// to something independent of otelconf.
DefaultViews func(configtelemetry.Level) []otelconf.View
}
// Factory is a factory interface for internal telemetry.