diff --git a/CHANGELOG.md b/CHANGELOG.md index 410d2d87013a..3313e51a6836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ in 0.11.0 are no longer converted to the messages and fields that replaced the deprecated ones. Received deprecated messages and fields will be now ignored. In OTLP/JSON in the instrumentationLibraryLogs object the "logs" field is now named "logRecords" (#4724) +- Move `config.Config` and `config.Service` to `serviceconfig` (#4608) ### 💡 Enhancements 💡 diff --git a/config/config.go b/config/config.go index 4e595e8cb0ba..3a19ecdbb0f4 100644 --- a/config/config.go +++ b/config/config.go @@ -14,141 +14,24 @@ package config // import "go.opentelemetry.io/collector/config" -import ( - "errors" - "fmt" -) - -var ( - errMissingExporters = errors.New("no enabled exporters specified in config") - errMissingReceivers = errors.New("no enabled receivers specified in config") - errMissingServicePipelines = errors.New("service must have at least one pipeline") -) - -// Config defines the configuration for the various elements of collector or agent. -type Config struct { - // Receivers is a map of ComponentID to Receivers. - Receivers map[ComponentID]Receiver - - // Exporters is a map of ComponentID to Exporters. - Exporters map[ComponentID]Exporter - - // Processors is a map of ComponentID to Processors. - Processors map[ComponentID]Processor - - // Extensions is a map of ComponentID to extensions. - Extensions map[ComponentID]Extension - - Service -} - -var _ validatable = (*Config)(nil) - -// Validate returns an error if the config is invalid. -// -// This function performs basic validation of configuration. There may be more subtle -// invalid cases that we currently don't check for but which we may want to add in -// the future (e.g. disallowing receiving and exporting on the same endpoint). -func (cfg *Config) Validate() error { - // Currently, there is no default receiver enabled. - // The configuration must specify at least one receiver to be valid. - if len(cfg.Receivers) == 0 { - return errMissingReceivers - } - - // Validate the receiver configuration. - for recvID, recvCfg := range cfg.Receivers { - if err := recvCfg.Validate(); err != nil { - return fmt.Errorf("receiver %q has invalid configuration: %w", recvID, err) - } - } - - // Currently, there is no default exporter enabled. - // The configuration must specify at least one exporter to be valid. - if len(cfg.Exporters) == 0 { - return errMissingExporters - } - - // Validate the exporter configuration. - for expID, expCfg := range cfg.Exporters { - if err := expCfg.Validate(); err != nil { - return fmt.Errorf("exporter %q has invalid configuration: %w", expID, err) - } - } - - // Validate the processor configuration. - for procID, procCfg := range cfg.Processors { - if err := procCfg.Validate(); err != nil { - return fmt.Errorf("processor %q has invalid configuration: %w", procID, err) - } - } - - // Validate the extension configuration. - for extID, extCfg := range cfg.Extensions { - if err := extCfg.Validate(); err != nil { - return fmt.Errorf("extension %q has invalid configuration: %w", extID, err) - } - } - - return cfg.validateService() -} - -func (cfg *Config) validateService() error { - // Check that all enabled extensions in the service are configured. - for _, ref := range cfg.Service.Extensions { - // Check that the name referenced in the Service extensions exists in the top-level extensions. - if cfg.Extensions[ref] == nil { - return fmt.Errorf("service references extension %q which does not exist", ref) - } - } - - // Must have at least one pipeline. - if len(cfg.Service.Pipelines) == 0 { - return errMissingServicePipelines - } - - // Check that all pipelines have at least one receiver and one exporter, and they reference - // only configured components. - for pipelineID, pipeline := range cfg.Service.Pipelines { - // Validate pipeline has at least one receiver. - if len(pipeline.Receivers) == 0 { - return fmt.Errorf("pipeline %q must have at least one receiver", pipelineID) - } - - // Validate pipeline receiver name references. - for _, ref := range pipeline.Receivers { - // Check that the name referenced in the pipeline's receivers exists in the top-level receivers. - if cfg.Receivers[ref] == nil { - return fmt.Errorf("pipeline %q references receiver %q which does not exist", pipelineID, ref) - } - } +// Type is the component type as it is used in the config. +type Type string - // Validate pipeline processor name references. - for _, ref := range pipeline.Processors { - // Check that the name referenced in the pipeline's processors exists in the top-level processors. - if cfg.Processors[ref] == nil { - return fmt.Errorf("pipeline %q references processor %q which does not exist", pipelineID, ref) - } - } +// DataType is a special Type that represents the data types supported by the collector. We currently support +// collecting metrics, traces and logs, this can expand in the future. +type DataType = Type - // Validate pipeline has at least one exporter. - if len(pipeline.Exporters) == 0 { - return fmt.Errorf("pipeline %q must have at least one exporter", pipelineID) - } +// Currently supported data types. Add new data types here when new types are supported in the future. +const ( + // TracesDataType is the data type tag for traces. + TracesDataType DataType = "traces" - // Validate pipeline exporter name references. - for _, ref := range pipeline.Exporters { - // Check that the name referenced in the pipeline's Exporters exists in the top-level Exporters. - if cfg.Exporters[ref] == nil { - return fmt.Errorf("pipeline %q references exporter %q which does not exist", pipelineID, ref) - } - } - } - return nil -} + // MetricsDataType is the data type tag for metrics. + MetricsDataType DataType = "metrics" -// Type is the component type as it is used in the config. -type Type string + // LogsDataType is the data type tag for logs. + LogsDataType DataType = "logs" +) // validatable defines the interface for the configuration validation. type validatable interface { diff --git a/config/configunmarshaler/defaultunmarshaler.go b/config/configunmarshaler/defaultunmarshaler.go index c0bb55f1b75e..1271826b6f52 100644 --- a/config/configunmarshaler/defaultunmarshaler.go +++ b/config/configunmarshaler/defaultunmarshaler.go @@ -23,6 +23,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/service/serviceconfig" ) // These are errors that can be returned by Unmarshal(). Note that error codes are not part @@ -85,8 +86,8 @@ func NewDefault() ConfigUnmarshaler { // Unmarshal the Config from a config.Map. // After the config is unmarshalled, `Validate()` must be called to validate. -func (*defaultUnmarshaler) Unmarshal(v *config.Map, factories component.Factories) (*config.Config, error) { - var cfg config.Config +func (*defaultUnmarshaler) Unmarshal(v *config.Map, factories component.Factories) (*serviceconfig.Config, error) { + var cfg serviceconfig.Config // Unmarshal top level sections and validate. rawCfg := configSettings{} @@ -164,12 +165,12 @@ func unmarshalExtensions(exts map[config.ComponentID]map[string]interface{}, fac return extensions, nil } -func unmarshalService(srvRaw map[string]interface{}) (config.Service, error) { +func unmarshalService(srvRaw map[string]interface{}) (serviceconfig.Service, error) { // Setup default telemetry values as in service/logger.go. // TODO: Add a component.ServiceFactory to allow this to be defined by the Service. - srv := config.Service{ - Telemetry: config.ServiceTelemetry{ - Logs: config.ServiceTelemetryLogs{ + srv := serviceconfig.Service{ + Telemetry: serviceconfig.ServiceTelemetry{ + Logs: serviceconfig.ServiceTelemetryLogs{ Level: zapcore.InfoLevel, Development: false, Encoding: "console", @@ -195,8 +196,8 @@ func unmarshalService(srvRaw map[string]interface{}) (config.Service, error) { return srv, nil } -func defaultServiceTelemetryMetricsSettings() config.ServiceTelemetryMetrics { - return config.ServiceTelemetryMetrics{ +func defaultServiceTelemetryMetricsSettings() serviceconfig.ServiceTelemetryMetrics { + return serviceconfig.ServiceTelemetryMetrics{ Level: configtelemetry.LevelBasic, //nolint:staticcheck Address: ":8888", } diff --git a/config/configunmarshaler/defaultunmarshaler_test.go b/config/configunmarshaler/defaultunmarshaler_test.go index 55a6d8c8071b..e7228364cfa2 100644 --- a/config/configunmarshaler/defaultunmarshaler_test.go +++ b/config/configunmarshaler/defaultunmarshaler_test.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/collector/config/configtelemetry" "go.opentelemetry.io/collector/config/configtest" "go.opentelemetry.io/collector/internal/testcomponents" + "go.opentelemetry.io/collector/service/serviceconfig" ) func TestDecodeConfig(t *testing.T) { @@ -100,8 +101,8 @@ func TestDecodeConfig(t *testing.T) { // Verify Service Telemetry assert.Equal(t, - config.ServiceTelemetry{ - Logs: config.ServiceTelemetryLogs{ + serviceconfig.ServiceTelemetry{ + Logs: serviceconfig.ServiceTelemetryLogs{ Level: zapcore.DebugLevel, Development: true, Encoding: "console", @@ -111,7 +112,7 @@ func TestDecodeConfig(t *testing.T) { ErrorOutputPaths: []string{"stderr", "./error-output-logs"}, InitialFields: map[string]interface{}{"field_key": "filed_value"}, }, - Metrics: config.ServiceTelemetryMetrics{ + Metrics: serviceconfig.ServiceTelemetryMetrics{ Level: configtelemetry.LevelNormal, Address: ":8081", }, @@ -126,7 +127,7 @@ func TestDecodeConfig(t *testing.T) { assert.Equal(t, 1, len(cfg.Service.Pipelines), "Incorrect pipelines count") assert.Equal(t, - &config.Pipeline{ + &serviceconfig.Pipeline{ Receivers: []config.ComponentID{config.NewComponentID("examplereceiver")}, Processors: []config.ComponentID{config.NewComponentID("exampleprocessor")}, Exporters: []config.ComponentID{config.NewComponentID("exampleexporter")}, @@ -225,7 +226,7 @@ func TestLoadEmptyAllSections(t *testing.T) { assert.NoError(t, err) } -func loadConfigFile(t *testing.T, fileName string, factories component.Factories) (*config.Config, error) { +func loadConfigFile(t *testing.T, fileName string, factories component.Factories) (*serviceconfig.Config, error) { cm, err := configtest.LoadConfigMap(fileName) require.NoError(t, err) @@ -242,7 +243,7 @@ func TestDefaultLoggerConfig(t *testing.T) { zapProdCfg := zap.NewProductionConfig() assert.Equal(t, - config.ServiceTelemetryLogs{ + serviceconfig.ServiceTelemetryLogs{ Level: zapProdCfg.Level.Level(), Development: zapProdCfg.Development, Encoding: "console", diff --git a/config/configunmarshaler/unmarshaler.go b/config/configunmarshaler/unmarshaler.go index 7c649e70440a..d2ef218295e6 100644 --- a/config/configunmarshaler/unmarshaler.go +++ b/config/configunmarshaler/unmarshaler.go @@ -17,10 +17,11 @@ package configunmarshaler // import "go.opentelemetry.io/collector/config/config import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/service/serviceconfig" ) // ConfigUnmarshaler is the interface that unmarshalls the collector configuration from the config.Map. type ConfigUnmarshaler interface { // Unmarshal the configuration from the given parser and factories. - Unmarshal(v *config.Map, factories component.Factories) (*config.Config, error) + Unmarshal(v *config.Map, factories component.Factories) (*serviceconfig.Config, error) } diff --git a/service/config_provider.go b/service/config_provider.go index 6ac4541fe6cf..8db528b4f03c 100644 --- a/service/config_provider.go +++ b/service/config_provider.go @@ -28,6 +28,7 @@ import ( "go.opentelemetry.io/collector/config/configmapprovider" "go.opentelemetry.io/collector/config/configunmarshaler" "go.opentelemetry.io/collector/config/experimental/configsource" + "go.opentelemetry.io/collector/service/serviceconfig" ) // ConfigProvider provides the service configuration. @@ -44,7 +45,7 @@ type ConfigProvider interface { // Get returns the service configuration, or error otherwise. // // Should never be called concurrently with itself, Watch or Shutdown. - Get(ctx context.Context, factories component.Factories) (*config.Config, error) + Get(ctx context.Context, factories component.Factories) (*serviceconfig.Config, error) // Watch blocks until any configuration change was detected or an unrecoverable error // happened during monitoring the configuration changes. @@ -140,7 +141,7 @@ func NewDefaultConfigProvider(configLocations []string, properties []string) Con configunmarshaler.NewDefault()) } -func (cm *configProvider) Get(ctx context.Context, factories component.Factories) (*config.Config, error) { +func (cm *configProvider) Get(ctx context.Context, factories component.Factories) (*serviceconfig.Config, error) { // First check if already an active watching, close that if any. if err := cm.closeIfNeeded(ctx); err != nil { return nil, fmt.Errorf("cannot close previous watch: %w", err) @@ -159,7 +160,7 @@ func (cm *configProvider) Get(ctx context.Context, factories component.Factories } } - var cfg *config.Config + var cfg *serviceconfig.Config if cfg, err = cm.configUnmarshaler.Unmarshal(ret.Map, factories); err != nil { return nil, fmt.Errorf("cannot unmarshal the configuration: %w", err) } diff --git a/service/config_provider_test.go b/service/config_provider_test.go index 6ad14a55624c..a3c3099cb8a5 100644 --- a/service/config_provider_test.go +++ b/service/config_provider_test.go @@ -31,6 +31,7 @@ import ( "go.opentelemetry.io/collector/config/configtest" "go.opentelemetry.io/collector/config/configunmarshaler" "go.opentelemetry.io/collector/config/experimental/configsource" + "go.opentelemetry.io/collector/service/serviceconfig" ) type mockProvider struct { @@ -66,7 +67,7 @@ type errConfigUnmarshaler struct { err error } -func (ecu *errConfigUnmarshaler) Unmarshal(*config.Map, component.Factories) (*config.Config, error) { +func (ecu *errConfigUnmarshaler) Unmarshal(*config.Map, component.Factories) (*serviceconfig.Config, error) { return nil, ecu.err } diff --git a/service/internal/builder/exporters_builder.go b/service/internal/builder/exporters_builder.go index 5576ac4009a7..a97be3e9a959 100644 --- a/service/internal/builder/exporters_builder.go +++ b/service/internal/builder/exporters_builder.go @@ -25,6 +25,7 @@ import ( "go.opentelemetry.io/collector/component/componenterror" "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/service/internal/components" + "go.opentelemetry.io/collector/service/serviceconfig" ) // builtExporter is an exporter that is built based on a config. It can have @@ -133,7 +134,7 @@ type exportersRequiredDataTypes map[config.ComponentID]dataTypeRequirements func BuildExporters( settings component.TelemetrySettings, buildInfo component.BuildInfo, - cfg *config.Config, + cfg *serviceconfig.Config, factories map[config.Type]component.ExporterFactory, ) (Exporters, error) { logger := settings.Logger.With(zap.String(components.ZapKindKey, components.ZapKindLogExporter)) @@ -172,7 +173,7 @@ func BuildExporters( return exporters, nil } -func calcExportersRequiredDataTypes(cfg *config.Config) exportersRequiredDataTypes { +func calcExportersRequiredDataTypes(cfg *serviceconfig.Config) exportersRequiredDataTypes { // Go over all pipelines. The data type of the pipeline defines what data type // each exporter is expected to receive. Collect all required types for each // exporter. diff --git a/service/internal/builder/exporters_builder_test.go b/service/internal/builder/exporters_builder_test.go index abb93d3d0e66..7afac1da6a36 100644 --- a/service/internal/builder/exporters_builder_test.go +++ b/service/internal/builder/exporters_builder_test.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/collector/config/configgrpc" "go.opentelemetry.io/collector/exporter/otlpexporter" "go.opentelemetry.io/collector/internal/testcomponents" + "go.opentelemetry.io/collector/service/serviceconfig" "go.opentelemetry.io/collector/service/servicetest" ) @@ -38,7 +39,7 @@ func TestBuildExporters(t *testing.T) { otlpFactory := otlpexporter.NewFactory() factories.Exporters[otlpFactory.Type()] = otlpFactory - cfg := &config.Config{ + cfg := &serviceconfig.Config{ Exporters: map[config.ComponentID]config.Exporter{ config.NewComponentID("otlp"): &otlpexporter.Config{ ExporterSettings: config.NewExporterSettings(config.NewComponentID("otlp")), @@ -48,8 +49,8 @@ func TestBuildExporters(t *testing.T) { }, }, - Service: config.Service{ - Pipelines: map[config.ComponentID]*config.Pipeline{ + Service: serviceconfig.Service{ + Pipelines: map[config.ComponentID]*serviceconfig.Pipeline{ config.NewComponentID("traces"): { Exporters: []config.ComponentID{config.NewComponentID("otlp")}, }, @@ -105,15 +106,15 @@ func TestBuildExporters_BuildLogs(t *testing.T) { factories, err := testcomponents.ExampleComponents() assert.Nil(t, err) - cfg := &config.Config{ + cfg := &serviceconfig.Config{ Exporters: map[config.ComponentID]config.Exporter{ config.NewComponentID("exampleexporter"): &testcomponents.ExampleExporter{ ExporterSettings: config.NewExporterSettings(config.NewComponentID("exampleexporter")), }, }, - Service: config.Service{ - Pipelines: map[config.ComponentID]*config.Pipeline{ + Service: serviceconfig.Service{ + Pipelines: map[config.ComponentID]*serviceconfig.Pipeline{ config.NewComponentID("logs"): { Exporters: []config.ComponentID{config.NewComponentID("exampleexporter")}, }, diff --git a/service/internal/builder/pipelines_builder.go b/service/internal/builder/pipelines_builder.go index 3f488bb195ea..4110a167fdc1 100644 --- a/service/internal/builder/pipelines_builder.go +++ b/service/internal/builder/pipelines_builder.go @@ -26,6 +26,7 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/service/internal/components" "go.opentelemetry.io/collector/service/internal/fanoutconsumer" + "go.opentelemetry.io/collector/service/serviceconfig" ) // builtPipeline is a pipeline that is built based on a config. @@ -38,7 +39,7 @@ type builtPipeline struct { firstLC consumer.Logs // Config is the configuration of this Pipeline. - Config *config.Pipeline + Config *serviceconfig.Pipeline // MutatesData is set to true if any processors in the pipeline // can mutate the TraceData or MetricsData input argument. MutatesData bool @@ -84,7 +85,7 @@ func (bps BuiltPipelines) ShutdownProcessors(ctx context.Context) error { type pipelinesBuilder struct { settings component.TelemetrySettings buildInfo component.BuildInfo - config *config.Config + config *serviceconfig.Config exporters Exporters factories map[config.Type]component.ProcessorFactory } @@ -94,7 +95,7 @@ type pipelinesBuilder struct { func BuildPipelines( settings component.TelemetrySettings, buildInfo component.BuildInfo, - config *config.Config, + config *serviceconfig.Config, exporters Exporters, factories map[config.Type]component.ProcessorFactory, ) (BuiltPipelines, error) { @@ -115,7 +116,7 @@ func BuildPipelines( // Builds a pipeline of processors. Returns the first processor in the pipeline. // The last processor in the pipeline will be plugged to fan out the data into exporters // that are configured for this pipeline. -func (pb *pipelinesBuilder) buildPipeline(ctx context.Context, pipelineID config.ComponentID, pipelineCfg *config.Pipeline) (*builtPipeline, error) { +func (pb *pipelinesBuilder) buildPipeline(ctx context.Context, pipelineID config.ComponentID, pipelineCfg *serviceconfig.Pipeline) (*builtPipeline, error) { // BuildProcessors the pipeline backwards. diff --git a/service/internal/builder/pipelines_builder_test.go b/service/internal/builder/pipelines_builder_test.go index e67c1ce94871..4ebb7a0c9b68 100644 --- a/service/internal/builder/pipelines_builder_test.go +++ b/service/internal/builder/pipelines_builder_test.go @@ -28,6 +28,7 @@ import ( "go.opentelemetry.io/collector/internal/testcomponents" "go.opentelemetry.io/collector/internal/testdata" "go.opentelemetry.io/collector/model/pdata" + "go.opentelemetry.io/collector/service/serviceconfig" "go.opentelemetry.io/collector/service/servicetest" ) @@ -56,12 +57,12 @@ func TestBuildPipelines(t *testing.T) { } } -func createExampleConfig(dataType string) *config.Config { +func createExampleConfig(dataType string) *serviceconfig.Config { exampleReceiverFactory := testcomponents.ExampleReceiverFactory exampleProcessorFactory := testcomponents.ExampleProcessorFactory exampleExporterFactory := testcomponents.ExampleExporterFactory - cfg := &config.Config{ + cfg := &serviceconfig.Config{ Receivers: map[config.ComponentID]config.Receiver{ config.NewComponentID(exampleReceiverFactory.Type()): exampleReceiverFactory.CreateDefaultConfig(), }, @@ -71,8 +72,8 @@ func createExampleConfig(dataType string) *config.Config { Exporters: map[config.ComponentID]config.Exporter{ config.NewComponentID(exampleExporterFactory.Type()): exampleExporterFactory.CreateDefaultConfig(), }, - Service: config.Service{ - Pipelines: map[config.ComponentID]*config.Pipeline{ + Service: serviceconfig.Service{ + Pipelines: map[config.ComponentID]*serviceconfig.Pipeline{ config.NewComponentID(config.Type(dataType)): { Receivers: []config.ComponentID{config.NewComponentID(exampleReceiverFactory.Type())}, Processors: []config.ComponentID{config.NewComponentID(exampleProcessorFactory.Type())}, diff --git a/service/internal/builder/receivers_builder.go b/service/internal/builder/receivers_builder.go index 777aafe8b79f..14a2ae243766 100644 --- a/service/internal/builder/receivers_builder.go +++ b/service/internal/builder/receivers_builder.go @@ -28,6 +28,7 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/service/internal/components" "go.opentelemetry.io/collector/service/internal/fanoutconsumer" + "go.opentelemetry.io/collector/service/serviceconfig" ) var errUnusedReceiver = errors.New("receiver defined but not used by any pipeline") @@ -77,7 +78,7 @@ func (rcvs Receivers) StartAll(ctx context.Context, host component.Host) error { // receiversBuilder builds receivers from config. type receiversBuilder struct { - config *config.Config + config *serviceconfig.Config builtPipelines BuiltPipelines factories map[config.Type]component.ReceiverFactory } @@ -86,7 +87,7 @@ type receiversBuilder struct { func BuildReceivers( settings component.TelemetrySettings, buildInfo component.BuildInfo, - cfg *config.Config, + cfg *serviceconfig.Config, builtPipelines BuiltPipelines, factories map[config.Type]component.ReceiverFactory, ) (Receivers, error) { @@ -121,7 +122,7 @@ func BuildReceivers( } // hasReceiver returns true if the pipeline is attached to specified receiver. -func hasReceiver(pipeline *config.Pipeline, receiverID config.ComponentID) bool { +func hasReceiver(pipeline *serviceconfig.Pipeline, receiverID config.ComponentID) bool { for _, id := range pipeline.Receivers { if id == receiverID { return true diff --git a/service/internal/extensions/extensions.go b/service/internal/extensions/extensions.go index 060a24b643be..6dfc37b8f767 100644 --- a/service/internal/extensions/extensions.go +++ b/service/internal/extensions/extensions.go @@ -24,6 +24,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/service/internal/components" + "go.opentelemetry.io/collector/service/serviceconfig" ) // builtExporter is an exporter that is built based on a config. It can have @@ -113,7 +114,7 @@ func (exts Extensions) ToMap() map[config.ComponentID]component.Extension { func Build( settings component.TelemetrySettings, buildInfo component.BuildInfo, - config *config.Config, + config *serviceconfig.Config, factories map[config.Type]component.ExtensionFactory, ) (Extensions, error) { extensions := make(Extensions) diff --git a/service/internal/extensions/extensions_test.go b/service/internal/extensions/extensions_test.go index 3c0420efaed6..1001588d7429 100644 --- a/service/internal/extensions/extensions_test.go +++ b/service/internal/extensions/extensions_test.go @@ -25,6 +25,7 @@ import ( "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/extension/extensionhelper" + "go.opentelemetry.io/collector/service/serviceconfig" ) func TestService_setupExtensions(t *testing.T) { @@ -45,13 +46,13 @@ func TestService_setupExtensions(t *testing.T) { tests := []struct { name string factories component.Factories - config *config.Config + config *serviceconfig.Config wantErrMsg string }{ { name: "extension_not_configured", - config: &config.Config{ - Service: config.Service{ + config: &serviceconfig.Config{ + Service: serviceconfig.Service{ Extensions: []config.ComponentID{ config.NewComponentID("myextension"), }, @@ -61,11 +62,11 @@ func TestService_setupExtensions(t *testing.T) { }, { name: "missing_extension_factory", - config: &config.Config{ + config: &serviceconfig.Config{ Extensions: map[config.ComponentID]config.Extension{ config.NewComponentID(errExtensionFactory.Type()): errExtensionConfig, }, - Service: config.Service{ + Service: serviceconfig.Service{ Extensions: []config.ComponentID{ config.NewComponentID(errExtensionFactory.Type()), }, @@ -80,11 +81,11 @@ func TestService_setupExtensions(t *testing.T) { errExtensionFactory.Type(): errExtensionFactory, }, }, - config: &config.Config{ + config: &serviceconfig.Config{ Extensions: map[config.ComponentID]config.Extension{ config.NewComponentID(errExtensionFactory.Type()): errExtensionConfig, }, - Service: config.Service{ + Service: serviceconfig.Service{ Extensions: []config.ComponentID{ config.NewComponentID(errExtensionFactory.Type()), }, @@ -99,11 +100,11 @@ func TestService_setupExtensions(t *testing.T) { badExtensionFactory.Type(): badExtensionFactory, }, }, - config: &config.Config{ + config: &serviceconfig.Config{ Extensions: map[config.ComponentID]config.Extension{ config.NewComponentID(badExtensionFactory.Type()): badExtensionCfg, }, - Service: config.Service{ + Service: serviceconfig.Service{ Extensions: []config.ComponentID{ config.NewComponentID(badExtensionFactory.Type()), }, diff --git a/service/internal/telemetrylogs/logger.go b/service/internal/telemetrylogs/logger.go index 08ad6d39d7f3..b4e7edec5be8 100644 --- a/service/internal/telemetrylogs/logger.go +++ b/service/internal/telemetrylogs/logger.go @@ -20,10 +20,10 @@ import ( "go.uber.org/zap/zapgrpc" "google.golang.org/grpc/grpclog" - "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/service/serviceconfig" ) -func NewLogger(cfg config.ServiceTelemetryLogs, options []zap.Option) (*zap.Logger, error) { +func NewLogger(cfg serviceconfig.ServiceTelemetryLogs, options []zap.Option) (*zap.Logger, error) { // Copied from NewProductionConfig. zapCfg := &zap.Config{ Level: zap.NewAtomicLevelAt(cfg.Level), diff --git a/service/internal/telemetrylogs/logger_test.go b/service/internal/telemetrylogs/logger_test.go index 152d999963f7..a03af758bb30 100644 --- a/service/internal/telemetrylogs/logger_test.go +++ b/service/internal/telemetrylogs/logger_test.go @@ -21,19 +21,19 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" - "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/service/serviceconfig" ) func TestGRPCLogger(t *testing.T) { tests := []struct { name string - cfg config.ServiceTelemetryLogs + cfg serviceconfig.ServiceTelemetryLogs infoLogged bool warnLogged bool }{ { "collector_info_level_grpc_log_warn", - config.ServiceTelemetryLogs{ + serviceconfig.ServiceTelemetryLogs{ Level: zapcore.InfoLevel, Encoding: "console", }, @@ -42,7 +42,7 @@ func TestGRPCLogger(t *testing.T) { }, { "collector_debug_level_grpc_log_debug", - config.ServiceTelemetryLogs{ + serviceconfig.ServiceTelemetryLogs{ Level: zapcore.DebugLevel, Encoding: "console", }, @@ -51,7 +51,7 @@ func TestGRPCLogger(t *testing.T) { }, { "collector_warn_level_grpc_log_warn", - config.ServiceTelemetryLogs{ + serviceconfig.ServiceTelemetryLogs{ Development: false, // this must set the grpc loggerV2 to loggerV2 Level: zapcore.WarnLevel, Encoding: "console", diff --git a/service/service.go b/service/service.go index 526cbc16ef93..29d85ab30d5d 100644 --- a/service/service.go +++ b/service/service.go @@ -25,13 +25,14 @@ import ( "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/service/internal/builder" "go.opentelemetry.io/collector/service/internal/extensions" + "go.opentelemetry.io/collector/service/serviceconfig" ) // service represents the implementation of a component.Host. type service struct { factories component.Factories buildInfo component.BuildInfo - config *config.Config + config *serviceconfig.Config telemetry component.TelemetrySettings zPagesSpanProcessor *zpages.SpanProcessor asyncErrorChannel chan error diff --git a/service/serviceconfig/config.go b/service/serviceconfig/config.go new file mode 100644 index 000000000000..4c10209f7ed2 --- /dev/null +++ b/service/serviceconfig/config.go @@ -0,0 +1,148 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package serviceconfig // import "go.opentelemetry.io/collector/service/serviceconfig" + +import ( + "errors" + "fmt" + + "go.opentelemetry.io/collector/config" +) + +var ( + errMissingExporters = errors.New("no enabled exporters specified in config") + errMissingReceivers = errors.New("no enabled receivers specified in config") + errMissingServicePipelines = errors.New("service must have at least one pipeline") +) + +// Config defines the configuration for the various elements of collector or agent. +type Config struct { + // Receivers is a map of ComponentID to Receivers. + Receivers map[config.ComponentID]config.Receiver + + // Exporters is a map of ComponentID to Exporters. + Exporters map[config.ComponentID]config.Exporter + + // Processors is a map of ComponentID to Processors. + Processors map[config.ComponentID]config.Processor + + // Extensions is a map of ComponentID to extensions. + Extensions map[config.ComponentID]config.Extension + + Service +} + +// Validate returns an error if the config is invalid. +// +// This function performs basic validation of configuration. There may be more subtle +// invalid cases that we currently don't check for but which we may want to add in +// the future (e.g. disallowing receiving and exporting on the same endpoint). +func (cfg *Config) Validate() error { + // Currently, there is no default receiver enabled. + // The configuration must specify at least one receiver to be valid. + if len(cfg.Receivers) == 0 { + return errMissingReceivers + } + + // Validate the receiver configuration. + for recvID, recvCfg := range cfg.Receivers { + if err := recvCfg.Validate(); err != nil { + return fmt.Errorf("receiver %q has invalid configuration: %w", recvID, err) + } + } + + // Currently, there is no default exporter enabled. + // The configuration must specify at least one exporter to be valid. + if len(cfg.Exporters) == 0 { + return errMissingExporters + } + + // Validate the exporter configuration. + for expID, expCfg := range cfg.Exporters { + if err := expCfg.Validate(); err != nil { + return fmt.Errorf("exporter %q has invalid configuration: %w", expID, err) + } + } + + // Validate the processor configuration. + for procID, procCfg := range cfg.Processors { + if err := procCfg.Validate(); err != nil { + return fmt.Errorf("processor %q has invalid configuration: %w", procID, err) + } + } + + // Validate the extension configuration. + for extID, extCfg := range cfg.Extensions { + if err := extCfg.Validate(); err != nil { + return fmt.Errorf("extension %q has invalid configuration: %w", extID, err) + } + } + + return cfg.validateService() +} + +func (cfg *Config) validateService() error { + // Check that all enabled extensions in the service are configured. + for _, ref := range cfg.Service.Extensions { + // Check that the name referenced in the Service extensions exists in the top-level extensions. + if cfg.Extensions[ref] == nil { + return fmt.Errorf("service references extension %q which does not exist", ref) + } + } + + // Must have at least one pipeline. + if len(cfg.Service.Pipelines) == 0 { + return errMissingServicePipelines + } + + // Check that all pipelines have at least one receiver and one exporter, and they reference + // only configured components. + for pipelineID, pipeline := range cfg.Service.Pipelines { + // Validate pipeline has at least one receiver. + if len(pipeline.Receivers) == 0 { + return fmt.Errorf("pipeline %q must have at least one receiver", pipelineID) + } + + // Validate pipeline receiver name references. + for _, ref := range pipeline.Receivers { + // Check that the name referenced in the pipeline's receivers exists in the top-level receivers. + if cfg.Receivers[ref] == nil { + return fmt.Errorf("pipeline %q references receiver %q which does not exist", pipelineID, ref) + } + } + + // Validate pipeline processor name references. + for _, ref := range pipeline.Processors { + // Check that the name referenced in the pipeline's processors exists in the top-level processors. + if cfg.Processors[ref] == nil { + return fmt.Errorf("pipeline %q references processor %q which does not exist", pipelineID, ref) + } + } + + // Validate pipeline has at least one exporter. + if len(pipeline.Exporters) == 0 { + return fmt.Errorf("pipeline %q must have at least one exporter", pipelineID) + } + + // Validate pipeline exporter name references. + for _, ref := range pipeline.Exporters { + // Check that the name referenced in the pipeline's Exporters exists in the top-level Exporters. + if cfg.Exporters[ref] == nil { + return fmt.Errorf("pipeline %q references exporter %q which does not exist", pipelineID, ref) + } + } + } + return nil +} diff --git a/config/config_test.go b/service/serviceconfig/config_test.go similarity index 67% rename from config/config_test.go rename to service/serviceconfig/config_test.go index c9d6e33ca22d..cf7ac79f08c5 100644 --- a/config/config_test.go +++ b/service/serviceconfig/config_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package config +package serviceconfig import ( "errors" @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/zap/zapcore" + "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/config/configtelemetry" ) @@ -31,44 +32,44 @@ var errInvalidProcConfig = errors.New("invalid processor config") var errInvalidExtConfig = errors.New("invalid extension config") type nopRecvConfig struct { - ReceiverSettings + config.ReceiverSettings } func (nc *nopRecvConfig) Validate() error { - if nc.ID() != NewComponentID("nop") { + if nc.ID() != config.NewComponentID("nop") { return errInvalidRecvConfig } return nil } type nopExpConfig struct { - ExporterSettings + config.ExporterSettings } func (nc *nopExpConfig) Validate() error { - if nc.ID() != NewComponentID("nop") { + if nc.ID() != config.NewComponentID("nop") { return errInvalidExpConfig } return nil } type nopProcConfig struct { - ProcessorSettings + config.ProcessorSettings } func (nc *nopProcConfig) Validate() error { - if nc.ID() != NewComponentID("nop") { + if nc.ID() != config.NewComponentID("nop") { return errInvalidProcConfig } return nil } type nopExtConfig struct { - ExtensionSettings + config.ExtensionSettings } func (nc *nopExtConfig) Validate() error { - if nc.ID() != NewComponentID("nop") { + if nc.ID() != config.NewComponentID("nop") { return errInvalidExtConfig } return nil @@ -116,7 +117,7 @@ func TestConfigValidate(t *testing.T) { name: "invalid-extension-reference", cfgFn: func() *Config { cfg := generateConfig() - cfg.Service.Extensions = append(cfg.Service.Extensions, NewComponentIDWithName("nop", "2")) + cfg.Service.Extensions = append(cfg.Service.Extensions, config.NewComponentIDWithName("nop", "2")) return cfg }, expected: errors.New(`service references extension "nop/2" which does not exist`), @@ -125,8 +126,8 @@ func TestConfigValidate(t *testing.T) { name: "invalid-receiver-reference", cfgFn: func() *Config { cfg := generateConfig() - pipe := cfg.Service.Pipelines[NewComponentID("traces")] - pipe.Receivers = append(pipe.Receivers, NewComponentIDWithName("nop", "2")) + pipe := cfg.Service.Pipelines[config.NewComponentID("traces")] + pipe.Receivers = append(pipe.Receivers, config.NewComponentIDWithName("nop", "2")) return cfg }, expected: errors.New(`pipeline "traces" references receiver "nop/2" which does not exist`), @@ -135,8 +136,8 @@ func TestConfigValidate(t *testing.T) { name: "invalid-processor-reference", cfgFn: func() *Config { cfg := generateConfig() - pipe := cfg.Service.Pipelines[NewComponentID("traces")] - pipe.Processors = append(pipe.Processors, NewComponentIDWithName("nop", "2")) + pipe := cfg.Service.Pipelines[config.NewComponentID("traces")] + pipe.Processors = append(pipe.Processors, config.NewComponentIDWithName("nop", "2")) return cfg }, expected: errors.New(`pipeline "traces" references processor "nop/2" which does not exist`), @@ -145,8 +146,8 @@ func TestConfigValidate(t *testing.T) { name: "invalid-exporter-reference", cfgFn: func() *Config { cfg := generateConfig() - pipe := cfg.Service.Pipelines[NewComponentID("traces")] - pipe.Exporters = append(pipe.Exporters, NewComponentIDWithName("nop", "2")) + pipe := cfg.Service.Pipelines[config.NewComponentID("traces")] + pipe.Exporters = append(pipe.Exporters, config.NewComponentIDWithName("nop", "2")) return cfg }, expected: errors.New(`pipeline "traces" references exporter "nop/2" which does not exist`), @@ -155,7 +156,7 @@ func TestConfigValidate(t *testing.T) { name: "missing-pipeline-receivers", cfgFn: func() *Config { cfg := generateConfig() - pipe := cfg.Service.Pipelines[NewComponentID("traces")] + pipe := cfg.Service.Pipelines[config.NewComponentID("traces")] pipe.Receivers = nil return cfg }, @@ -165,7 +166,7 @@ func TestConfigValidate(t *testing.T) { name: "missing-pipeline-exporters", cfgFn: func() *Config { cfg := generateConfig() - pipe := cfg.Service.Pipelines[NewComponentID("traces")] + pipe := cfg.Service.Pipelines[config.NewComponentID("traces")] pipe.Exporters = nil return cfg }, @@ -184,8 +185,8 @@ func TestConfigValidate(t *testing.T) { name: "invalid-receiver-config", cfgFn: func() *Config { cfg := generateConfig() - cfg.Receivers[NewComponentID("nop")] = &nopRecvConfig{ - ReceiverSettings: NewReceiverSettings(NewComponentID("invalid_rec_type")), + cfg.Receivers[config.NewComponentID("nop")] = &nopRecvConfig{ + ReceiverSettings: config.NewReceiverSettings(config.NewComponentID("invalid_rec_type")), } return cfg }, @@ -195,8 +196,8 @@ func TestConfigValidate(t *testing.T) { name: "invalid-exporter-config", cfgFn: func() *Config { cfg := generateConfig() - cfg.Exporters[NewComponentID("nop")] = &nopExpConfig{ - ExporterSettings: NewExporterSettings(NewComponentID("invalid_rec_type")), + cfg.Exporters[config.NewComponentID("nop")] = &nopExpConfig{ + ExporterSettings: config.NewExporterSettings(config.NewComponentID("invalid_rec_type")), } return cfg }, @@ -206,8 +207,8 @@ func TestConfigValidate(t *testing.T) { name: "invalid-processor-config", cfgFn: func() *Config { cfg := generateConfig() - cfg.Processors[NewComponentID("nop")] = &nopProcConfig{ - ProcessorSettings: NewProcessorSettings(NewComponentID("invalid_rec_type")), + cfg.Processors[config.NewComponentID("nop")] = &nopProcConfig{ + ProcessorSettings: config.NewProcessorSettings(config.NewComponentID("invalid_rec_type")), } return cfg }, @@ -217,8 +218,8 @@ func TestConfigValidate(t *testing.T) { name: "invalid-extension-config", cfgFn: func() *Config { cfg := generateConfig() - cfg.Extensions[NewComponentID("nop")] = &nopExtConfig{ - ExtensionSettings: NewExtensionSettings(NewComponentID("invalid_rec_type")), + cfg.Extensions[config.NewComponentID("nop")] = &nopExtConfig{ + ExtensionSettings: config.NewExtensionSettings(config.NewComponentID("invalid_rec_type")), } return cfg }, @@ -236,24 +237,24 @@ func TestConfigValidate(t *testing.T) { func generateConfig() *Config { return &Config{ - Receivers: map[ComponentID]Receiver{ - NewComponentID("nop"): &nopRecvConfig{ - ReceiverSettings: NewReceiverSettings(NewComponentID("nop")), + Receivers: map[config.ComponentID]config.Receiver{ + config.NewComponentID("nop"): &nopRecvConfig{ + ReceiverSettings: config.NewReceiverSettings(config.NewComponentID("nop")), }, }, - Exporters: map[ComponentID]Exporter{ - NewComponentID("nop"): &nopExpConfig{ - ExporterSettings: NewExporterSettings(NewComponentID("nop")), + Exporters: map[config.ComponentID]config.Exporter{ + config.NewComponentID("nop"): &nopExpConfig{ + ExporterSettings: config.NewExporterSettings(config.NewComponentID("nop")), }, }, - Processors: map[ComponentID]Processor{ - NewComponentID("nop"): &nopProcConfig{ - ProcessorSettings: NewProcessorSettings(NewComponentID("nop")), + Processors: map[config.ComponentID]config.Processor{ + config.NewComponentID("nop"): &nopProcConfig{ + ProcessorSettings: config.NewProcessorSettings(config.NewComponentID("nop")), }, }, - Extensions: map[ComponentID]Extension{ - NewComponentID("nop"): &nopExtConfig{ - ExtensionSettings: NewExtensionSettings(NewComponentID("nop")), + Extensions: map[config.ComponentID]config.Extension{ + config.NewComponentID("nop"): &nopExtConfig{ + ExtensionSettings: config.NewExtensionSettings(config.NewComponentID("nop")), }, }, Service: Service{ @@ -273,12 +274,12 @@ func generateConfig() *Config { Address: ":8080", }, }, - Extensions: []ComponentID{NewComponentID("nop")}, - Pipelines: map[ComponentID]*Pipeline{ - NewComponentID("traces"): { - Receivers: []ComponentID{NewComponentID("nop")}, - Processors: []ComponentID{NewComponentID("nop")}, - Exporters: []ComponentID{NewComponentID("nop")}, + Extensions: []config.ComponentID{config.NewComponentID("nop")}, + Pipelines: map[config.ComponentID]*Pipeline{ + config.NewComponentID("traces"): { + Receivers: []config.ComponentID{config.NewComponentID("nop")}, + Processors: []config.ComponentID{config.NewComponentID("nop")}, + Exporters: []config.ComponentID{config.NewComponentID("nop")}, }, }, }, diff --git a/config/service.go b/service/serviceconfig/service.go similarity index 83% rename from config/service.go rename to service/serviceconfig/service.go index d53fcf7b14d8..1550f46a1638 100644 --- a/config/service.go +++ b/service/serviceconfig/service.go @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -package config // import "go.opentelemetry.io/collector/config" +package serviceconfig // import "go.opentelemetry.io/collector/service/serviceconfig" import ( "go.uber.org/zap/zapcore" + "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/config/configtelemetry" ) @@ -26,7 +27,7 @@ type Service struct { Telemetry ServiceTelemetry `mapstructure:"telemetry"` // Extensions are the ordered list of extensions configured for the service. - Extensions []ComponentID `mapstructure:"extensions"` + Extensions []config.ComponentID `mapstructure:"extensions"` // Pipelines are the set of data pipelines configured for the service. Pipelines Pipelines `mapstructure:"pipelines"` @@ -110,28 +111,12 @@ type ServiceTelemetryMetrics struct { Address string `mapstructure:"address"` } -// DataType is a special Type that represents the data types supported by the collector. We currently support -// collecting metrics, traces and logs, this can expand in the future. -type DataType = Type - -// Currently supported data types. Add new data types here when new types are supported in the future. -const ( - // TracesDataType is the data type tag for traces. - TracesDataType DataType = "traces" - - // MetricsDataType is the data type tag for metrics. - MetricsDataType DataType = "metrics" - - // LogsDataType is the data type tag for logs. - LogsDataType DataType = "logs" -) - // Pipeline defines a single pipeline. type Pipeline struct { - Receivers []ComponentID `mapstructure:"receivers"` - Processors []ComponentID `mapstructure:"processors"` - Exporters []ComponentID `mapstructure:"exporters"` + Receivers []config.ComponentID `mapstructure:"receivers"` + Processors []config.ComponentID `mapstructure:"processors"` + Exporters []config.ComponentID `mapstructure:"exporters"` } // Pipelines is a map of names to Pipelines. -type Pipelines map[ComponentID]*Pipeline +type Pipelines map[config.ComponentID]*Pipeline diff --git a/service/servicetest/configprovider.go b/service/servicetest/configprovider.go index 9bd6a258ce36..a325b517ab96 100644 --- a/service/servicetest/configprovider.go +++ b/service/servicetest/configprovider.go @@ -16,13 +16,13 @@ package servicetest // import "go.opentelemetry.io/collector/service/servicetest import ( "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/config/configtest" "go.opentelemetry.io/collector/config/configunmarshaler" + "go.opentelemetry.io/collector/service/serviceconfig" ) -// LoadConfig loads a config.Config from file, and does NOT validate the configuration. -func LoadConfig(fileName string, factories component.Factories) (*config.Config, error) { +// LoadConfig loads a serviceconfig.Config from file, and does NOT validate the configuration. +func LoadConfig(fileName string, factories component.Factories) (*serviceconfig.Config, error) { // Read yaml config from file cfgMap, err := configtest.LoadConfigMap(fileName) if err != nil { @@ -32,7 +32,7 @@ func LoadConfig(fileName string, factories component.Factories) (*config.Config, } // LoadConfigAndValidate loads a config from the file, and validates the configuration. -func LoadConfigAndValidate(fileName string, factories component.Factories) (*config.Config, error) { +func LoadConfigAndValidate(fileName string, factories component.Factories) (*serviceconfig.Config, error) { cfg, err := LoadConfig(fileName, factories) if err != nil { return nil, err diff --git a/service/servicetest/configprovider_test.go b/service/servicetest/configprovider_test.go index 87f37d1d4f06..09fe49c27f0c 100644 --- a/service/servicetest/configprovider_test.go +++ b/service/servicetest/configprovider_test.go @@ -23,6 +23,7 @@ import ( "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/service/serviceconfig" ) func TestLoadConfig(t *testing.T) { @@ -57,7 +58,7 @@ func TestLoadConfig(t *testing.T) { assert.Contains(t, cfg.Service.Extensions, config.NewComponentID("nop")) require.Len(t, cfg.Service.Pipelines, 1) assert.Equal(t, - &config.Pipeline{ + &serviceconfig.Pipeline{ Receivers: []config.ComponentID{config.NewComponentID("nop")}, Processors: []config.ComponentID{config.NewComponentID("nop")}, Exporters: []config.ComponentID{config.NewComponentID("nop")}, diff --git a/service/settings.go b/service/settings.go index 9a8f6ff8be8b..845a8d3927bb 100644 --- a/service/settings.go +++ b/service/settings.go @@ -19,7 +19,7 @@ import ( "go.uber.org/zap" "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config" + "go.opentelemetry.io/collector/service/serviceconfig" ) // svcSettings holds configuration for building a new service. @@ -31,7 +31,7 @@ type svcSettings struct { BuildInfo component.BuildInfo // Config represents the configuration of the service. - Config *config.Config + Config *serviceconfig.Config // Telemetry represents the service configured telemetry for all the components. Telemetry component.TelemetrySettings