-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
config.go
199 lines (160 loc) · 7.58 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package spanmetricsconnector // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector"
import (
"errors"
"fmt"
"time"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/pdata/pmetric"
"github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector/internal/metrics"
)
const (
delta = "AGGREGATION_TEMPORALITY_DELTA"
cumulative = "AGGREGATION_TEMPORALITY_CUMULATIVE"
)
var defaultHistogramBucketsMs = []float64{
2, 4, 6, 8, 10, 50, 100, 200, 400, 800, 1000, 1400, 2000, 5000, 10_000, 15_000,
}
var defaultDeltaTimestampCacheSize = 1000
// Dimension defines the dimension name and optional default value if the Dimension is missing from a span attribute.
type Dimension struct {
Name string `mapstructure:"name"`
Default *string `mapstructure:"default"`
}
// Config defines the configuration options for spanmetricsconnector.
type Config struct {
// Dimensions defines the list of additional dimensions on top of the provided:
// - service.name
// - span.kind
// - span.kind
// - status.code
// The dimensions will be fetched from the span's attributes. Examples of some conventionally used attributes:
// https://github.com/open-telemetry/opentelemetry-collector/blob/main/model/semconv/opentelemetry.go.
Dimensions []Dimension `mapstructure:"dimensions"`
ExcludeDimensions []string `mapstructure:"exclude_dimensions"`
// DimensionsCacheSize defines the size of cache for storing Dimensions, which helps to avoid cache memory growing
// indefinitely over the lifetime of the collector.
// Optional. See defaultDimensionsCacheSize in connector.go for the default value.
DimensionsCacheSize int `mapstructure:"dimensions_cache_size"`
// ResourceMetricsCacheSize defines the size of the cache holding metrics for a service. This is mostly relevant for
// cumulative temporality to avoid memory leaks and correct metric timestamp resets.
// Optional. See defaultResourceMetricsCacheSize in connector.go for the default value.
ResourceMetricsCacheSize int `mapstructure:"resource_metrics_cache_size"`
// ResourceMetricsKeyAttributes filters the resource attributes used to create the resource metrics key hash.
// This can be used to avoid situations where resource attributes may change across service restarts, causing
// metric counters to break (and duplicate). A resource does not need to have all of the attributes. The list
// must include enough attributes to properly identify unique resources or risk aggregating data from more
// than one service and span.
// e.g. ["service.name", "telemetry.sdk.language", "telemetry.sdk.name"]
// See https://opentelemetry.io/docs/specs/semconv/resource/ for possible attributes.
ResourceMetricsKeyAttributes []string `mapstructure:"resource_metrics_key_attributes"`
AggregationTemporality string `mapstructure:"aggregation_temporality"`
Histogram HistogramConfig `mapstructure:"histogram"`
// MetricsEmitInterval is the time period between when metrics are flushed or emitted to the configured MetricsExporter.
MetricsFlushInterval time.Duration `mapstructure:"metrics_flush_interval"`
// MetricsExpiration is the time period after which, if no new spans are received, metrics are considered stale and will no longer be exported.
// Default value (0) means that the metrics will never expire.
MetricsExpiration time.Duration `mapstructure:"metrics_expiration"`
// TimestampCacheSize controls the size of the cache used to keep track of delta metrics' TimestampUnixNano the last time it was flushed
TimestampCacheSize *int `mapstructure:"metric_timestamp_cache_size"`
// Namespace is the namespace of the metrics emitted by the connector.
Namespace string `mapstructure:"namespace"`
// Exemplars defines the configuration for exemplars.
Exemplars ExemplarsConfig `mapstructure:"exemplars"`
// Events defines the configuration for events section of spans.
Events EventsConfig `mapstructure:"events"`
}
type HistogramConfig struct {
Disable bool `mapstructure:"disable"`
Unit metrics.Unit `mapstructure:"unit"`
Exponential *ExponentialHistogramConfig `mapstructure:"exponential"`
Explicit *ExplicitHistogramConfig `mapstructure:"explicit"`
}
type ExemplarsConfig struct {
Enabled bool `mapstructure:"enabled"`
MaxPerDataPoint *int `mapstructure:"max_per_data_point"`
}
type ExponentialHistogramConfig struct {
MaxSize int32 `mapstructure:"max_size"`
}
type ExplicitHistogramConfig struct {
// Buckets is the list of durations representing explicit histogram buckets.
Buckets []time.Duration `mapstructure:"buckets"`
}
type EventsConfig struct {
// Enabled is a flag to enable events.
Enabled bool `mapstructure:"enabled"`
// Dimensions defines the list of dimensions to add to the events metric.
Dimensions []Dimension `mapstructure:"dimensions"`
}
var _ component.ConfigValidator = (*Config)(nil)
// Validate checks if the processor configuration is valid
func (c Config) Validate() error {
if err := validateDimensions(c.Dimensions); err != nil {
return fmt.Errorf("failed validating dimensions: %w", err)
}
if err := validateEventDimensions(c.Events.Enabled, c.Events.Dimensions); err != nil {
return fmt.Errorf("failed validating event dimensions: %w", err)
}
if c.DimensionsCacheSize <= 0 {
return fmt.Errorf(
"invalid cache size: %v, the maximum number of the items in the cache should be positive",
c.DimensionsCacheSize,
)
}
if c.Histogram.Explicit != nil && c.Histogram.Exponential != nil {
return errors.New("use either `explicit` or `exponential` buckets histogram")
}
if c.MetricsFlushInterval < 0 {
return fmt.Errorf("invalid metrics_flush_interval: %v, the duration should be positive", c.MetricsFlushInterval)
}
if c.MetricsExpiration < 0 {
return fmt.Errorf("invalid metrics_expiration: %v, the duration should be positive", c.MetricsExpiration)
}
if c.GetAggregationTemporality() == pmetric.AggregationTemporalityDelta && c.GetDeltaTimestampCacheSize() <= 0 {
return fmt.Errorf(
"invalid delta timestamp cache size: %v, the maximum number of the items in the cache should be positive",
c.GetDeltaTimestampCacheSize(),
)
}
return nil
}
// GetAggregationTemporality converts the string value given in the config into a AggregationTemporality.
// Returns cumulative, unless delta is correctly specified.
func (c Config) GetAggregationTemporality() pmetric.AggregationTemporality {
if c.AggregationTemporality == delta {
return pmetric.AggregationTemporalityDelta
}
return pmetric.AggregationTemporalityCumulative
}
func (c Config) GetDeltaTimestampCacheSize() int {
if c.TimestampCacheSize != nil {
return *c.TimestampCacheSize
}
return defaultDeltaTimestampCacheSize
}
// validateDimensions checks duplicates for reserved dimensions and additional dimensions.
func validateDimensions(dimensions []Dimension) error {
labelNames := make(map[string]struct{})
for _, key := range []string{serviceNameKey, spanKindKey, statusCodeKey, spanNameKey} {
labelNames[key] = struct{}{}
}
for _, key := range dimensions {
if _, ok := labelNames[key.Name]; ok {
return fmt.Errorf("duplicate dimension name %s", key.Name)
}
labelNames[key.Name] = struct{}{}
}
return nil
}
// validateEventDimensions checks for empty and duplicates for the dimensions configured.
func validateEventDimensions(enabled bool, dimensions []Dimension) error {
if !enabled {
return nil
}
if len(dimensions) == 0 {
return fmt.Errorf("no dimensions configured for events")
}
return validateDimensions(dimensions)
}