exporter/signalfxexporter/config.go (148 lines of code) (raw):

// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 package signalfxexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter" import ( "errors" "fmt" "net/url" "time" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configretry" "go.opentelemetry.io/collector/config/configtls" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/exporter/exporterhelper" "gopkg.in/yaml.v3" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/correlation" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/splunk" ) // This mimics the original translation_rules config option (now deleted), but is not reachable // by user configuration. It's only used by defaultTranslationRules to parse the // YAML into a []translation.Rule object. type translationRulesConfig struct { TranslationRules []translation.Rule `mapstructure:"translation_rules"` } var defaultTranslationRules = func() []translation.Rule { var data map[string]any var defaultRules translationRulesConfig // It is safe to panic since this is deterministic, and will not fail anywhere else if it doesn't fail all the time. if err := yaml.Unmarshal([]byte(translation.DefaultTranslationRulesYaml), &data); err != nil { panic(err) } if err := confmap.NewFromStringMap(data).Unmarshal(&defaultRules); err != nil { panic(fmt.Errorf("failed to load default translation rules: %w", err)) } return defaultRules.TranslationRules }() var defaultExcludeMetrics = func() []dpfilters.MetricFilter { cfg, err := loadConfig([]byte(translation.DefaultExcludeMetricsYaml)) // It is safe to panic since this is deterministic, and will not fail anywhere else if it doesn't fail all the time. if err != nil { panic(err) } return cfg.ExcludeMetrics }() var _ confmap.Unmarshaler = (*Config)(nil) // Config defines configuration for SignalFx exporter. type Config struct { QueueSettings exporterhelper.QueueBatchConfig `mapstructure:"sending_queue"` configretry.BackOffConfig `mapstructure:"retry_on_failure"` confighttp.ClientConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. // AccessToken is the authentication token provided by SignalFx. AccessToken configopaque.String `mapstructure:"access_token"` // Realm is the SignalFx realm where data is going to be sent to. Realm string `mapstructure:"realm"` // IngestURL is the destination to where SignalFx metrics will be sent to, it is // intended for tests and debugging. The value of Realm is ignored if the // URL is specified. The exporter will automatically append the appropriate // path: "/v2/datapoint" for metrics, and "/v2/event" for events. IngestURL string `mapstructure:"ingest_url"` // ingest_tls needs to be set if the exporter's IngestURL is pointing to a signalfx receiver // with TLS enabled and using a self-signed certificate where its CA is not loaded in the system cert pool. IngestTLSSettings configtls.ClientConfig `mapstructure:"ingest_tls,omitempty"` // APIURL is the destination to where SignalFx metadata will be sent. This // value takes precedence over the value of Realm APIURL string `mapstructure:"api_url"` // api_tls needs to be set if the exporter's APIURL is pointing to a httpforwarder extension // with TLS enabled and using a self-signed certificate where its CA is not loaded in the system cert pool. APITLSSettings configtls.ClientConfig `mapstructure:"api_tls,omitempty"` // Whether to log datapoints dispatched to Splunk Observability Cloud LogDataPoints bool `mapstructure:"log_data_points"` // Whether to log dimension updates being sent to SignalFx. LogDimensionUpdates bool `mapstructure:"log_dimension_updates"` // Dimension update client configuration used for metadata updates. DimensionClient DimensionClientConfig `mapstructure:"dimension_client"` splunk.AccessTokenPassthroughConfig `mapstructure:",squash"` DisableDefaultTranslationRules bool `mapstructure:"disable_default_translation_rules"` // DeltaTranslationTTL specifies in seconds the max duration to keep the most recent datapoint for any // `delta_metric` specified in TranslationRules. Default is 3600s. DeltaTranslationTTL int64 `mapstructure:"delta_translation_ttl"` // SyncHostMetadata defines if the exporter should scrape host metadata and // sends it as property updates to SignalFx backend. // IMPORTANT: Host metadata synchronization relies on `resourcedetection` processor. // If this option is enabled make sure that `resourcedetection` processor // is enabled in the pipeline with one of the cloud provider detectors // or environment variable detector setting a unique value to // `host.name` attribute within your k8s cluster. Also keep override // And keep `override=true` in resourcedetection config. SyncHostMetadata bool `mapstructure:"sync_host_metadata"` // ExcludeMetrics defines dpfilter.MetricFilters that will determine metrics to be // excluded from sending to SignalFx backend. If translations enabled with // TranslationRules options, the exclusion will be applied on translated metrics. ExcludeMetrics []dpfilters.MetricFilter `mapstructure:"exclude_metrics"` // IncludeMetrics defines dpfilter.MetricFilters to override exclusion any of metric. // This option can be used to included metrics that are otherwise dropped by default. // See ./translation/default_metrics.go for a list of metrics that are dropped by default. IncludeMetrics []dpfilters.MetricFilter `mapstructure:"include_metrics"` // ExcludeProperties defines dpfilter.PropertyFilters to prevent inclusion of // properties to include with dimension updates to the SignalFx backend. ExcludeProperties []dpfilters.PropertyFilter `mapstructure:"exclude_properties"` // Correlation configuration for syncing traces service and environment to metrics. Correlation *correlation.Config `mapstructure:"correlation"` // NonAlphanumericDimensionChars is a list of allowable characters, in addition to alphanumeric ones, // to be used in a dimension key. NonAlphanumericDimensionChars string `mapstructure:"nonalphanumeric_dimension_chars"` // Whether to drop histogram bucket metrics dispatched to Splunk Observability. // Default value is set to false. DropHistogramBuckets bool `mapstructure:"drop_histogram_buckets"` // Whether to send histogram metrics in OTLP format to Splunk Observability. // Default value is set to false. SendOTLPHistograms bool `mapstructure:"send_otlp_histograms"` } type DimensionClientConfig struct { MaxBuffered int `mapstructure:"max_buffered"` SendDelay time.Duration `mapstructure:"send_delay"` MaxIdleConns int `mapstructure:"max_idle_conns"` MaxIdleConnsPerHost int `mapstructure:"max_idle_conns_per_host"` MaxConnsPerHost int `mapstructure:"max_conns_per_host"` IdleConnTimeout time.Duration `mapstructure:"idle_conn_timeout"` Timeout time.Duration `mapstructure:"timeout"` } func (cfg *Config) getMetricTranslator(done chan struct{}) (*translation.MetricTranslator, error) { var rules []translation.Rule // The new way to disable default translation rules. This override any setting of the default TranslationRules. if cfg.DisableDefaultTranslationRules { rules = []translation.Rule{} } else { rules = defaultTranslationRules } metricTranslator, err := translation.NewMetricTranslator(rules, cfg.DeltaTranslationTTL, done) if err != nil { return nil, fmt.Errorf("invalid default translation rules: %w", err) } return metricTranslator, nil } func (cfg *Config) getIngestURL() (*url.URL, error) { strURL := cfg.IngestURL if cfg.IngestURL == "" { strURL = fmt.Sprintf("https://ingest.%s.signalfx.com", cfg.Realm) } ingestURL, err := url.Parse(strURL) if err != nil { return nil, fmt.Errorf("invalid \"ingest_url\": %w", err) } return ingestURL, nil } func (cfg *Config) getAPIURL() (*url.URL, error) { strURL := cfg.APIURL if cfg.APIURL == "" { strURL = fmt.Sprintf("https://api.%s.signalfx.com", cfg.Realm) } apiURL, err := url.Parse(strURL) if err != nil { return nil, fmt.Errorf("invalid \"api_url\": %w", err) } return apiURL, nil } func (cfg *Config) Unmarshal(componentParser *confmap.Conf) error { if componentParser == nil { // Nothing to do if there is no config given. return nil } if err := componentParser.Unmarshal(cfg, confmap.WithIgnoreUnused()); err != nil { return err } return setDefaultExcludes(cfg) } // Validate checks if the exporter configuration is valid. func (cfg *Config) Validate() error { if cfg.AccessToken == "" { return errors.New(`requires a non-empty "access_token"`) } if cfg.Realm == "" && (cfg.IngestURL == "" || cfg.APIURL == "") { return errors.New(`requires a non-empty "realm", or` + ` "ingest_url" and "api_url" should be explicitly set`) } if cfg.Timeout < 0 { return errors.New(`cannot have a negative "timeout"`) } return nil } func setDefaultExcludes(cfg *Config) error { // If ExcludeMetrics is not set to empty, append defaults. if cfg.ExcludeMetrics == nil || len(cfg.ExcludeMetrics) > 0 { cfg.ExcludeMetrics = append(cfg.ExcludeMetrics, defaultExcludeMetrics...) } return nil } func loadConfig(bytes []byte) (Config, error) { var cfg Config var data map[string]any if err := yaml.Unmarshal(bytes, &data); err != nil { return cfg, err } if err := confmap.NewFromStringMap(data).Unmarshal(&cfg); err != nil { return cfg, fmt.Errorf("failed to load default exclude metrics: %w", err) } return cfg, nil }