diff --git a/.chloggen/36650.yaml b/.chloggen/36650.yaml new file mode 100644 index 000000000000..7fdb5eab0c99 --- /dev/null +++ b/.chloggen/36650.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: azureeventhubreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: support providing one or more time formats for timestamp parsing + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36650] + +# (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: + +# 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] diff --git a/.chloggen/add-metric-labels-in-googlecloudmonitoringreceiver.yaml b/.chloggen/add-metric-labels-in-googlecloudmonitoringreceiver.yaml new file mode 100644 index 000000000000..6d6f98d22ffa --- /dev/null +++ b/.chloggen/add-metric-labels-in-googlecloudmonitoringreceiver.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: googlecloudmonitoringreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add metric-specific labels to googlecloudmonitoringreceiver component + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35711] + +# (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: + +# 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: [] diff --git a/.chloggen/adjuster-resource-fix.yaml b/.chloggen/adjuster-resource-fix.yaml new file mode 100644 index 000000000000..8fb0802b035b --- /dev/null +++ b/.chloggen/adjuster-resource-fix.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: receiver/prometheusreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Metric adjuster no longer assumes that all metrics from a scrape come from the same resource + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36477] + +# (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: + +# 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: [] diff --git a/.chloggen/gh-semconv-1.28plus.yaml b/.chloggen/gh-semconv-1.28plus.yaml new file mode 100644 index 000000000000..7f6e5f68b086 --- /dev/null +++ b/.chloggen/gh-semconv-1.28plus.yaml @@ -0,0 +1,47 @@ +# 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: githubreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Update metric names to match VCS Metric Semantic Conventions and scraper key name. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36714] + +# (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: | + * Change the `github` scraper key to `scraper` + * Add `vcs.repository.url.full` attribute + * Change attribute `repository.name` to `vcs.repository.name` + * Change attribute `ref.name` to `vcs.ref.head.name` + * Change attribute `ref.type` to `vcs.ref.head.type` + * Change attribute `change.state` to `vcs.change.state` + * Add attribute `vcs.revision_delta.direction` with `ahead` and `behind` values + * Change metric `vcs.repository.ref.revisions_ahead` to `vcs.ref.revisions_delta` with `vcs.revision_delta.direction=ahead` + * Change metric `vcs.repository.ref.revisions_behind` to `vcs.ref.revisions_delta` with `vcs.revision_delta.direction=behind` + * Change metric `vcs.repository.ref.count` to `vcs.ref.count` + * Change metric `vcs.repository.ref.time` to `vcs.ref.time` + * Add attribute `vcs.line_change.type` with `added` and `removed` values + * Change metric `vcs.repository.ref.lines_added` to `vcs.ref.lines_delta` with `vcs.line_change.type=added` + * Change metric `vcs.repository.ref.lines_removed` to `vcs.ref.lines_delta` with `vcs.line_change.type=removed` + * Change metric `vcs.repository.contributor.count` to `vcs.contributor.count` + * Change metric `vcs.repository.change.time_open` to `vcs.change.duration` with `vcs.change.state=open` + * Change metric `vcs.repository.change.time_to_approval` to `vcs.change.time_to_approval` + * Change metric `vcs.repository.change.time_to_merge` to `vcs.change.time_to_merge` + * Change metric `vcs.repository.change.count` to `vcs.change.count` + + +# 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] diff --git a/.chloggen/k8s-override-empty-attributes.yaml b/.chloggen/k8s-override-empty-attributes.yaml new file mode 100644 index 000000000000..2503ca4156dd --- /dev/null +++ b/.chloggen/k8s-override-empty-attributes.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: k8sattributesprocessor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Override extracted k8s attributes if original value has been empty + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36373] + +# (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: + +# 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: [] diff --git a/.chloggen/signaltometrics-config-validation.yaml b/.chloggen/signaltometrics-config-validation.yaml new file mode 100644 index 000000000000..73dbc3439327 --- /dev/null +++ b/.chloggen/signaltometrics-config-validation.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: signaltometrics + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add config validation and custom OTTL functions + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35930] + +# (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: Adds config validation for the signal to metrics connector. Also introduces `AdjustedCount` OTTL function. + +# 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] diff --git a/connector/signaltometricsconnector/README.md b/connector/signaltometricsconnector/README.md index 9e0851209780..be38b041b716 100644 --- a/connector/signaltometricsconnector/README.md +++ b/connector/signaltometricsconnector/README.md @@ -103,9 +103,9 @@ histogram: - [**Optional**] `count` represents an OTTL expression to extract the count to be recorded in the histogram from the incoming data. If no expression is provided - then it defaults to the count of the signal i.e. [adjusted count](https://opentelemetry.io/docs/specs/otel/trace/tracestate-probability-sampling-experimental/#adjusted-count) - for spans and count for others. [OTTL converters](https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs#readme-converters) - can be used to transform the data. + then it defaults to the count of the signal. [OTTL converters](https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs#readme-converters) + can be used to transform the data. For spans, a special converter [adjusted count](#custom-ottl-functions), + is provided to help calculte the span's [adjusted count](https://opentelemetry.io/docs/specs/otel/trace/tracestate-probability-sampling-experimental/#adjusted-count). - [**Required**] `value` represents an OTTL expression to extract the value to be recorded in the histogram from the incoming data. [OTTL converters](https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs#readme-converters) can be used to transform the data. @@ -124,13 +124,13 @@ exponential_histogram: - [**Optional**] `max_size` represents the maximum number of buckets per positive or negative number range. Defaults to `160`. - [**Optional**] `count` represents an OTTL expression to extract the count to be - recorded in the exponential histogram from the incoming data. If no expression - is provided then it defaults to the count of the signal i.e. [adjusted count](https://opentelemetry.io/docs/specs/otel/trace/tracestate-probability-sampling-experimental/#adjusted-count) - for spans and count for others. - [OTTL converters](https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs#readme-converters) can be used to transform the data. -- [**Required**] `value` represents an OTTL expression to extract the value to be recorded - in the exponential histogram from the incoming data. - [OTTL converters](https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs#readme-converters) can be used to transform the data. + recorded in the expoential histogram from the incoming data. If no expression + is provided then it defaults to the count of the signal. [OTTL converters](https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs#readme-converters) + can be used to transform the data. For spans, a special converter [adjusted count](#custom-ottl-functions), + is provided to help calculte the span's [adjusted count](https://opentelemetry.io/docs/specs/otel/trace/tracestate-probability-sampling-experimental/#adjusted-count). +- [**Required**] `value` represents an OTTL expression to extract the value to be + recorded in the exponential histogram from the incoming data. [OTTL converters](https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs#readme-converters) + can be used to transform the data. ### Attributes @@ -225,3 +225,12 @@ signaltometrics.service.name: signaltometrics.service.namespace: signaltometrics.service.instance.id: ``` + +### Custom OTTL functions + +The component implements a couple of custom OTTL functions: + +1. `AdjustedCount`: a converter capable of calculating [adjusted count for a span](https://github.com/open-telemetry/oteps/blob/main/text/trace/0235-sampling-threshold-in-trace-state.md). +2. `get`: a temporary solution to parse OTTL expressions with only values. This is +only for internal usage and MUST NOT be used explicitly as it is a stopgap measure +([see this for more details](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35621)). diff --git a/connector/signaltometricsconnector/config/config.go b/connector/signaltometricsconnector/config/config.go index 7a2beb6787c8..582afd1b7a2f 100644 --- a/connector/signaltometricsconnector/config/config.go +++ b/connector/signaltometricsconnector/config/config.go @@ -3,7 +3,38 @@ package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/config" -import "fmt" +import ( + "errors" + "fmt" + + "github.com/lightstep/go-expohisto/structure" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/internal/customottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" +) + +const ( + // defaultExponentialHistogramMaxSize is the default maximum number + // of buckets per positive or negative number range. 160 buckets + // default supports a high-resolution histogram able to cover a + // long-tail latency distribution from 1ms to 100s with a relative + // error of less than 5%. + // Ref: https://opentelemetry.io/docs/specs/otel/metrics/sdk/#base2-exponential-bucket-histogram-aggregation + defaultExponentialHistogramMaxSize = 160 +) + +var defaultHistogramBuckets = []float64{ + 2, 4, 6, 8, 10, 50, 100, 200, 400, 800, 1000, 1400, 2000, 5000, 10_000, 15_000, +} + +var _ confmap.Unmarshaler = (*Config)(nil) // Config for the connector. Each configuration field describes the metrics // to produce from a specific signal. @@ -17,9 +48,98 @@ func (c *Config) Validate() error { if len(c.Spans) == 0 && len(c.Datapoints) == 0 && len(c.Logs) == 0 { return fmt.Errorf("no configuration provided, at least one should be specified") } + var multiError error // collect all errors at once + if len(c.Spans) > 0 { + parser, err := ottlspan.NewParser( + customottl.SpanFuncs(), + component.TelemetrySettings{Logger: zap.NewNop()}, + ) + if err != nil { + return fmt.Errorf("failed to create parser for OTTL spans: %w", err) + } + for _, span := range c.Spans { + if err := validateMetricInfo(span, parser); err != nil { + multiError = errors.Join(multiError, fmt.Errorf("failed to validate spans configuration: %w", err)) + } + } + } + if len(c.Datapoints) > 0 { + parser, err := ottldatapoint.NewParser( + customottl.DatapointFuncs(), + component.TelemetrySettings{Logger: zap.NewNop()}, + ) + if err != nil { + return fmt.Errorf("failed to create parser for OTTL datapoints: %w", err) + } + for _, dp := range c.Datapoints { + if err := validateMetricInfo(dp, parser); err != nil { + multiError = errors.Join(multiError, fmt.Errorf("failed to validate datapoints configuration: %w", err)) + } + } + } + if len(c.Logs) > 0 { + parser, err := ottllog.NewParser( + customottl.LogFuncs(), + component.TelemetrySettings{Logger: zap.NewNop()}, + ) + if err != nil { + return fmt.Errorf("failed to create parser for OTTL logs: %w", err) + } + for _, log := range c.Logs { + if err := validateMetricInfo(log, parser); err != nil { + multiError = errors.Join(multiError, fmt.Errorf("failed to validate logs configuration: %w", err)) + } + } + } + return multiError +} + +// Unmarshal implements the confmap.Unmarshaler interface. It allows +// unmarshaling the config with a custom logic to allow setting +// default values when/if required. +func (c *Config) Unmarshal(collectorCfg *confmap.Conf) error { + if collectorCfg == nil { + return nil + } + if err := collectorCfg.Unmarshal(c, confmap.WithIgnoreUnused()); err != nil { + return err + } + for i, info := range c.Spans { + info.ensureDefaults() + c.Spans[i] = info + } + for i, info := range c.Datapoints { + info.ensureDefaults() + c.Datapoints[i] = info + } + for i, info := range c.Logs { + info.ensureDefaults() + c.Logs[i] = info + } return nil } +type Attribute struct { + Key string `mapstructure:"key"` + DefaultValue any `mapstructure:"default_value"` +} + +type Histogram struct { + Buckets []float64 `mapstructure:"buckets"` + Count string `mapstructure:"count"` + Value string `mapstructure:"value"` +} + +type ExponentialHistogram struct { + MaxSize int32 `mapstructure:"max_size"` + Count string `mapstructure:"count"` + Value string `mapstructure:"value"` +} + +type Sum struct { + Value string `mapstructure:"value"` +} + // MetricInfo defines the structure of the metric produced by the connector. type MetricInfo struct { Name string `mapstructure:"name"` @@ -40,23 +160,120 @@ type MetricInfo struct { Sum *Sum `mapstructure:"sum"` } -type Attribute struct { - Key string `mapstructure:"key"` - DefaultValue any `mapstructure:"default_value"` +func (mi *MetricInfo) ensureDefaults() { + if mi.Histogram != nil { + // Add default buckets if explicit histogram is defined + if len(mi.Histogram.Buckets) == 0 { + mi.Histogram.Buckets = defaultHistogramBuckets + } + } + if mi.ExponentialHistogram != nil { + if mi.ExponentialHistogram.MaxSize == 0 { + mi.ExponentialHistogram.MaxSize = defaultExponentialHistogramMaxSize + } + } } -type Histogram struct { - Buckets []float64 `mapstructure:"buckets"` - Count string `mapstructure:"count"` - Value string `mapstructure:"value"` +func (mi *MetricInfo) validateAttributes() error { + tmp := pcommon.NewValueEmpty() + duplicate := map[string]struct{}{} + for _, attr := range mi.Attributes { + if attr.Key == "" { + return fmt.Errorf("attribute key missing") + } + if _, ok := duplicate[attr.Key]; ok { + return fmt.Errorf("duplicate key found in attributes config: %s", attr.Key) + } + if err := tmp.FromRaw(attr.DefaultValue); err != nil { + return fmt.Errorf("invalid default value specified for attribute %s", attr.Key) + } + duplicate[attr.Key] = struct{}{} + } + return nil } -type ExponentialHistogram struct { - MaxSize int32 `mapstructure:"max_size"` - Count string `mapstructure:"count"` - Value string `mapstructure:"value"` +func (mi *MetricInfo) validateHistogram() error { + if mi.Histogram != nil { + if len(mi.Histogram.Buckets) == 0 { + return errors.New("histogram buckets missing") + } + if mi.Histogram.Value == "" { + return errors.New("value OTTL statement is required") + } + } + if mi.ExponentialHistogram != nil { + if _, err := structure.NewConfig( + structure.WithMaxSize(mi.ExponentialHistogram.MaxSize), + ).Validate(); err != nil { + return err + } + if mi.ExponentialHistogram.Value == "" { + return errors.New("value OTTL statement is required") + } + } + return nil } -type Sum struct { - Value string `mapstructure:"value"` +func (mi *MetricInfo) validateSum() error { + if mi.Sum != nil { + if mi.Sum.Value == "" { + return errors.New("value must be defined for sum metrics") + } + } + return nil +} + +// validateMetricInfo is an utility method validate all supported metric +// types defined for the metric info including any ottl expressions. +func validateMetricInfo[K any](mi MetricInfo, parser ottl.Parser[K]) error { + if mi.Name == "" { + return errors.New("missing required metric name configuration") + } + if err := mi.validateAttributes(); err != nil { + return fmt.Errorf("attributes validation failed: %w", err) + } + if err := mi.validateHistogram(); err != nil { + return fmt.Errorf("histogram validation failed: %w", err) + } + if err := mi.validateSum(); err != nil { + return fmt.Errorf("sum validation failed: %w", err) + } + + // Exactly one metric should be defined + var ( + metricsDefinedCount int + statements []string + ) + if mi.Histogram != nil { + metricsDefinedCount++ + if mi.Histogram.Count != "" { + statements = append(statements, customottl.ConvertToStatement(mi.Histogram.Count)) + } + statements = append(statements, customottl.ConvertToStatement(mi.Histogram.Value)) + } + if mi.ExponentialHistogram != nil { + metricsDefinedCount++ + if mi.ExponentialHistogram.Count != "" { + statements = append(statements, customottl.ConvertToStatement(mi.ExponentialHistogram.Count)) + } + statements = append(statements, customottl.ConvertToStatement(mi.ExponentialHistogram.Value)) + } + if mi.Sum != nil { + metricsDefinedCount++ + statements = append(statements, customottl.ConvertToStatement(mi.Sum.Value)) + } + if metricsDefinedCount != 1 { + return fmt.Errorf("exactly one of the metrics must be defined, %d found", metricsDefinedCount) + } + + // validate OTTL statements, note that, here we only evalaute if statements + // are valid. Check for required statements is left to the other validations. + if _, err := parser.ParseStatements(statements); err != nil { + return fmt.Errorf("failed to parse OTTL statements: %w", err) + } + // validate OTTL conditions + if _, err := parser.ParseConditions(mi.Conditions); err != nil { + return fmt.Errorf("failed to parse OTTL conditions: %w", err) + } + return nil } diff --git a/connector/signaltometricsconnector/config/config_test.go b/connector/signaltometricsconnector/config/config_test.go index a542a189df5e..202fa4f70a3b 100644 --- a/connector/signaltometricsconnector/config/config_test.go +++ b/connector/signaltometricsconnector/config/config_test.go @@ -4,6 +4,7 @@ package config import ( + "fmt" "path/filepath" "testing" @@ -25,6 +26,148 @@ func TestConfig(t *testing.T) { path: "empty", errorMsgs: []string{"no configuration provided"}, }, + { + path: "without_name", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "missing required metric name"), + fullErrorForSignal(t, "datapoints", "missing required metric name"), + fullErrorForSignal(t, "logs", "missing required metric name"), + }, + }, + { + path: "no_key_attributes", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "attributes validation failed"), + fullErrorForSignal(t, "datapoints", "attributes validation failed"), + fullErrorForSignal(t, "logs", "attributes validation failed"), + }, + }, + { + path: "duplicate_attributes", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "attributes validation failed"), + fullErrorForSignal(t, "datapoints", "attributes validation failed"), + fullErrorForSignal(t, "logs", "attributes validation failed"), + }, + }, + { + path: "invalid_histogram", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "histogram validation failed"), + fullErrorForSignal(t, "datapoints", "histogram validation failed"), + fullErrorForSignal(t, "logs", "histogram validation failed"), + }, + }, + { + path: "invalid_exponential_histogram", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "histogram validation failed"), + fullErrorForSignal(t, "datapoints", "histogram validation failed"), + fullErrorForSignal(t, "logs", "histogram validation failed"), + }, + }, + { + path: "invalid_sum", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "sum validation failed"), + fullErrorForSignal(t, "datapoints", "sum validation failed"), + fullErrorForSignal(t, "logs", "sum validation failed"), + }, + }, + { + path: "multiple_metric", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "exactly one of the metrics must be defined"), + fullErrorForSignal(t, "datapoints", "exactly one of the metrics must be defined"), + fullErrorForSignal(t, "logs", "exactly one of the metrics must be defined"), + }, + }, + { + path: "invalid_ottl_statements", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "failed to parse OTTL statements"), + fullErrorForSignal(t, "datapoints", "failed to parse OTTL statements"), + fullErrorForSignal(t, "logs", "failed to parse OTTL statements"), + }, + }, + { + path: "invalid_ottl_conditions", + errorMsgs: []string{ + fullErrorForSignal(t, "spans", "failed to parse OTTL conditions"), + fullErrorForSignal(t, "datapoints", "failed to parse OTTL conditions"), + fullErrorForSignal(t, "logs", "failed to parse OTTL conditions"), + }, + }, + { + path: "valid_full", + expected: &Config{ + Spans: []MetricInfo{ + { + Name: "span.exp_histogram", + Description: "Exponential histogram", + Unit: "us", + IncludeResourceAttributes: []Attribute{{Key: "key.1", DefaultValue: "foo"}}, + Attributes: []Attribute{{Key: "key.2", DefaultValue: "bar"}}, + Conditions: []string{ + `attributes["some.optional.1"] != nil`, + `resource.attributes["some.optional.2"] != nil`, + }, + ExponentialHistogram: &ExponentialHistogram{ + MaxSize: 10, + Count: "1", + Value: "Microseconds(end_time - start_time)", + }, + }, + { + Name: "span.histogram", + Description: "Histogram", + Unit: "us", + IncludeResourceAttributes: []Attribute{{Key: "key.1", DefaultValue: "foo"}}, + Attributes: []Attribute{{Key: "key.2", DefaultValue: "bar"}}, + Conditions: []string{ + `attributes["some.optional.1"] != nil`, + `resource.attributes["some.optional.2"] != nil`, + }, + Histogram: &Histogram{ + Buckets: []float64{1.1, 11.1, 111.1}, + Count: "1", + Value: "Microseconds(end_time - start_time)", + }, + }, + }, + Datapoints: []MetricInfo{ + { + Name: "dp.sum", + Description: "Sum", + Unit: "ms", + IncludeResourceAttributes: []Attribute{{Key: "key.1", DefaultValue: "foo"}}, + Attributes: []Attribute{{Key: "key.2", DefaultValue: "bar"}}, + Conditions: []string{ + `attributes["some.optional.1"] != nil`, + `IsDouble(attributes["some.optional.1"])`, + }, + Sum: &Sum{ + Value: `attributes["some.optional.1"]`, + }, + }, + }, + Logs: []MetricInfo{ + { + Name: "log.sum", + Description: "Sum", + Unit: "1", + IncludeResourceAttributes: []Attribute{{Key: "key.1", DefaultValue: "foo"}}, + Attributes: []Attribute{{Key: "key.2", DefaultValue: "bar"}}, + Conditions: []string{ + `attributes["some.optional.1"] != nil`, + }, + Sum: &Sum{ + Value: "1", + }, + }, + }, + }, + }, } { t.Run(tc.path, func(t *testing.T) { dir := filepath.Join("..", "testdata", "configs") @@ -49,3 +192,16 @@ func TestConfig(t *testing.T) { }) } } + +const validationMsgFormat = "failed to validate %s configuration: %s" + +func fullErrorForSignal(t *testing.T, signal, errMsg string) string { + t.Helper() + + switch signal { + case "spans", "datapoints", "logs": + return fmt.Sprintf(validationMsgFormat, signal, errMsg) + default: + panic("unhandled signal type") + } +} diff --git a/connector/signaltometricsconnector/go.mod b/connector/signaltometricsconnector/go.mod index aafb7d98f0ea..250acc98656f 100644 --- a/connector/signaltometricsconnector/go.mod +++ b/connector/signaltometricsconnector/go.mod @@ -3,6 +3,9 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/connector/signa go 1.22.0 require ( + github.com/lightstep/go-expohisto v1.0.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl v0.115.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.115.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector/component v0.115.1-0.20241206185113-3f3e208e71b8 go.opentelemetry.io/collector/component/componenttest v0.115.1-0.20241206185113-3f3e208e71b8 @@ -18,38 +21,66 @@ require ( ) require ( + github.com/alecthomas/participle/v2 v2.1.1 // indirect + github.com/antchfx/xmlquery v1.4.2 // indirect + github.com/antchfx/xpath v1.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/elastic/go-grok v0.3.1 // indirect + github.com/elastic/lunes v0.1.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect github.com/knadh/koanf/providers/confmap v0.1.0 // indirect github.com/knadh/koanf/v2 v2.1.2 // indirect + github.com/magefile/mage v1.15.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.115.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.115.1-0.20241206185113-3f3e208e71b8 // indirect go.opentelemetry.io/collector/connector/connectorprofiles v0.115.1-0.20241206185113-3f3e208e71b8 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.115.1-0.20241206185113-3f3e208e71b8 // indirect go.opentelemetry.io/collector/internal/fanoutconsumer v0.115.1-0.20241206185113-3f3e208e71b8 // indirect go.opentelemetry.io/collector/pdata/pprofile v0.115.1-0.20241206185113-3f3e208e71b8 // indirect go.opentelemetry.io/collector/pipeline/pipelineprofiles v0.115.1-0.20241206185113-3f3e208e71b8 // indirect + go.opentelemetry.io/collector/semconv v0.115.1-0.20241206185113-3f3e208e71b8 // indirect go.opentelemetry.io/otel v1.32.0 // indirect go.opentelemetry.io/otel/metric v1.32.0 // indirect go.opentelemetry.io/otel/sdk v1.32.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.18.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect - google.golang.org/grpc v1.67.1 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/grpc v1.68.1 // indirect google.golang.org/protobuf v1.35.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl => ../../pkg/ottl + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => ../../internal/coreinternal + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil => ../../pkg/pdatautil + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest => ../../pkg/pdatatest + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden => ../../pkg/golden + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling => ../../pkg/sampling diff --git a/connector/signaltometricsconnector/go.sum b/connector/signaltometricsconnector/go.sum index e4c10db098d8..87c044bc0c9e 100644 --- a/connector/signaltometricsconnector/go.sum +++ b/connector/signaltometricsconnector/go.sum @@ -1,6 +1,20 @@ +github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= +github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= +github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/antchfx/xmlquery v1.4.2 h1:MZKd9+wblwxfQ1zd1AdrTsqVaMjMCwow3IqkCSe00KA= +github.com/antchfx/xmlquery v1.4.2/go.mod h1:QXhvf5ldTuGqhd1SHNvvtlhhdQLks4dD0awIVhXIDTA= +github.com/antchfx/xpath v1.3.2 h1:LNjzlsSjinu3bQpw9hWMY9ocB80oLOWuQqFvO6xt51U= +github.com/antchfx/xpath v1.3.2/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elastic/go-grok v0.3.1 h1:WEhUxe2KrwycMnlvMimJXvzRa7DoByJB4PVUIE1ZD/U= +github.com/elastic/go-grok v0.3.1/go.mod h1:n38ls8ZgOboZRgKcjMY8eFeZFMmcL9n2lP0iHhIDk64= +github.com/elastic/lunes v0.1.0 h1:amRtLPjwkWtzDF/RKzcEPMvSsSseLDLW+bnhfNSLRe4= +github.com/elastic/lunes v0.1.0/go.mod h1:xGphYIt3XdZRtyWosHQTErsQTd4OP1p9wsbVoHelrd4= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -8,13 +22,27 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -29,6 +57,10 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lightstep/go-expohisto v1.0.0 h1:UPtTS1rGdtehbbAF7o/dhkWLTDI73UifG8LbfQI7cA4= +github.com/lightstep/go-expohisto v1.0.0/go.mod h1:xDXD0++Mu2FOaItXtdDfksfgxfV0z1TMPa+e/EUd0cs= +github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= +github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -46,8 +78,11 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6 h1:SIKIoA4e/5Y9ZOl0DCe3eVMLPOQzJxgZpfdHHeauNTM= +github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6/go.mod h1:BUbeWZiieNxAuuADTBNb3/aeje6on3DhU3rpWsQSB1E= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/collector/component v0.115.1-0.20241206185113-3f3e208e71b8 h1:PtCINrFFDFi6aJRv8toOvLoKzu4qtz389PVcFlP7ydE= go.opentelemetry.io/collector/component v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:oIUFiH7w1eOimdeYhFI+gAIxYSiLDocKVJ0PTvX7d6s= go.opentelemetry.io/collector/component/componenttest v0.115.1-0.20241206185113-3f3e208e71b8 h1:Bic9twYk1GtkTNvzlt9rPCJEavRc5QYdSTN6Ug3hi9Q= @@ -80,6 +115,8 @@ go.opentelemetry.io/collector/pipeline v0.115.1-0.20241206185113-3f3e208e71b8 h1 go.opentelemetry.io/collector/pipeline v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:qE3DmoB05AW0C3lmPvdxZqd/H4po84NPzd5MrqgtL74= go.opentelemetry.io/collector/pipeline/pipelineprofiles v0.115.1-0.20241206185113-3f3e208e71b8 h1:tQ1srHl0/Y4yBL0LnJi9sRd6FDdonqdQNjrsBX/IY2w= go.opentelemetry.io/collector/pipeline/pipelineprofiles v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:2Myg+law/5lcezo9PhhZ0wjCaLYdGK24s1jDWbSW9VY= +go.opentelemetry.io/collector/semconv v0.115.1-0.20241206185113-3f3e208e71b8 h1:+vUVC+FHqapool6OqgMQgc4oEjXrHyvDsvi4hEpBVLE= +go.opentelemetry.io/collector/semconv v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:N6XE8Q0JKgBN2fAhkUQtqK9LT7rEGR6+Wu/Rtbal1iI= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= @@ -99,42 +136,64 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/connector/signaltometricsconnector/internal/customottl/adjustedcount.go b/connector/signaltometricsconnector/internal/customottl/adjustedcount.go new file mode 100644 index 000000000000..84c08441964d --- /dev/null +++ b/connector/signaltometricsconnector/internal/customottl/adjustedcount.go @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package customottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/internal/customottl" + +import ( + "context" + "fmt" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling" +) + +func NewAdjustedCountFactory() ottl.Factory[ottlspan.TransformContext] { + return ottl.NewFactory("AdjustedCount", nil, createAdjustedCountFunction) +} + +func createAdjustedCountFunction(_ ottl.FunctionContext, _ ottl.Arguments) (ottl.ExprFunc[ottlspan.TransformContext], error) { + return adjustedCount() +} + +func adjustedCount() (ottl.ExprFunc[ottlspan.TransformContext], error) { + return func(_ context.Context, tCtx ottlspan.TransformContext) (any, error) { + tracestate := tCtx.GetSpan().TraceState().AsRaw() + w3cTraceState, err := sampling.NewW3CTraceState(tracestate) + if err != nil { + return float64(0), fmt.Errorf("failed to parse w3c tracestate: %w", err) + } + otTraceState := w3cTraceState.OTelValue() + if otTraceState == nil { + // If otel trace state is missing, default to 1 + return float64(1), nil + } + if len(otTraceState.TValue()) == 0 { + // For non-probabilistic sampler OR always sampling threshold, default to 1 + return float64(1), nil + } + return otTraceState.AdjustedCount(), nil + }, nil +} diff --git a/connector/signaltometricsconnector/internal/customottl/adjustedcount_test.go b/connector/signaltometricsconnector/internal/customottl/adjustedcount_test.go new file mode 100644 index 000000000000..63cb778564db --- /dev/null +++ b/connector/signaltometricsconnector/internal/customottl/adjustedcount_test.go @@ -0,0 +1,51 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package customottl + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/ptrace" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" +) + +func Test_AdjustedCount(t *testing.T) { + for _, tc := range []struct { + tracestate string + want float64 + errMsg string + }{ + {tracestate: "", want: 1}, + {tracestate: "invalid=p:8;th:8", want: 1}, // otel trace state nil, default to 1 + {tracestate: "ot=notfound:8", want: 1}, // otel tvalue 0, default to 1 + {tracestate: "ot=404:0", errMsg: "failed to parse"}, // invalid syntax + {tracestate: "ot=th:0", want: 1}, // 100% sampling + {tracestate: "ot=th:8", want: 2}, // 50% sampling + {tracestate: "ot=th:c", want: 4}, // 25% sampling + } { + t.Run("tracestate/"+tc.tracestate, func(t *testing.T) { + exprFunc, err := adjustedCount() + require.NoError(t, err) + span := ptrace.NewSpan() + span.TraceState().FromRaw(tc.tracestate) + result, err := exprFunc(nil, ottlspan.NewTransformContext( + span, + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + ptrace.NewScopeSpans(), + ptrace.NewResourceSpans(), + )) + if tc.errMsg != "" { + require.ErrorContains(t, err, tc.errMsg) + return + } + require.NoError(t, err) + assert.Equal(t, tc.want, result) + }) + } +} diff --git a/connector/signaltometricsconnector/internal/customottl/funcs.go b/connector/signaltometricsconnector/internal/customottl/funcs.go new file mode 100644 index 000000000000..6e75edf7b94e --- /dev/null +++ b/connector/signaltometricsconnector/internal/customottl/funcs.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package customottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/internal/customottl" + +import ( + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottldatapoint" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottllog" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlspan" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs" +) + +func SpanFuncs() map[string]ottl.Factory[ottlspan.TransformContext] { + common := commonFuncs[ottlspan.TransformContext]() + adjustedCountFactory := NewAdjustedCountFactory() + common[adjustedCountFactory.Name()] = adjustedCountFactory + return common +} + +func DatapointFuncs() map[string]ottl.Factory[ottldatapoint.TransformContext] { + return commonFuncs[ottldatapoint.TransformContext]() +} + +func LogFuncs() map[string]ottl.Factory[ottllog.TransformContext] { + return commonFuncs[ottllog.TransformContext]() +} + +func commonFuncs[K any]() map[string]ottl.Factory[K] { + getFactory := NewGetFactory[K]() + standard := ottlfuncs.StandardFuncs[K]() + standard[getFactory.Name()] = getFactory + return standard +} diff --git a/connector/signaltometricsconnector/internal/customottl/get.go b/connector/signaltometricsconnector/internal/customottl/get.go new file mode 100644 index 000000000000..811196b7ba0d --- /dev/null +++ b/connector/signaltometricsconnector/internal/customottl/get.go @@ -0,0 +1,46 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package customottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/internal/customottl" + +import ( + "context" + "fmt" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" +) + +// Get is a temporary OTTL editor to allow statements to return values. This +// will be removed after OTTL can parse data retrival expressions: +// See: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35621 + +type GetArguments[K any] struct { + Value ottl.Getter[K] +} + +func NewGetFactory[K any]() ottl.Factory[K] { + return ottl.NewFactory("get", &GetArguments[K]{}, createGetFunction[K]) +} + +func createGetFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) (ottl.ExprFunc[K], error) { + args, ok := oArgs.(*GetArguments[K]) + + if !ok { + return nil, fmt.Errorf("GetFactory args must be of type *GetArguments[K]") + } + + return get(args.Value), nil +} + +func get[K any](value ottl.Getter[K]) ottl.ExprFunc[K] { + return func(ctx context.Context, tCtx K) (any, error) { + return value.Get(ctx, tCtx) + } +} + +// ConvertToStatement converts ottl.Value to an OTTL statement. To do +// this, it uses a custom `get` editor. This is expected to be a +// temporary measure until value parsing is allowed by the OTTL pkg. +func ConvertToStatement(s string) string { + return fmt.Sprintf("get(%s)", s) +} diff --git a/connector/signaltometricsconnector/testdata/configs/duplicate_attributes.yaml b/connector/signaltometricsconnector/testdata/configs/duplicate_attributes.yaml new file mode 100644 index 000000000000..567631081e85 --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/duplicate_attributes.yaml @@ -0,0 +1,25 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - key: key.1 + - key: key.1 + - key: key.2 + sum: + value: "1" + datapoints: + - name: dp.sum + attributes: + - key: key.1 + - key: key.1 + - key: key.2 + sum: + value: "1" + logs: + - name: log.sum + attributes: + - key: key.1 + - key: key.1 + - key: key.2 + sum: + value: "1" diff --git a/connector/signaltometricsconnector/testdata/configs/invalid_exponential_histogram.yaml b/connector/signaltometricsconnector/testdata/configs/invalid_exponential_histogram.yaml new file mode 100644 index 000000000000..fdc5fba7b1bf --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/invalid_exponential_histogram.yaml @@ -0,0 +1,16 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - key: key.1 + exponential_histogram: {} + datapoints: + - name: dp.sum + attributes: + - key: key.1 + exponential_histogram: {} + logs: + - name: log.sum + attributes: + - key: key.1 + exponential_histogram: {} diff --git a/connector/signaltometricsconnector/testdata/configs/invalid_histogram.yaml b/connector/signaltometricsconnector/testdata/configs/invalid_histogram.yaml new file mode 100644 index 000000000000..f4dbf6feca55 --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/invalid_histogram.yaml @@ -0,0 +1,16 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - key: key.1 + histogram: {} + datapoints: + - name: dp.sum + attributes: + - key: key.1 + histogram: {} + logs: + - name: log.sum + attributes: + - key: key.1 + histogram: {} diff --git a/connector/signaltometricsconnector/testdata/configs/invalid_ottl_conditions.yaml b/connector/signaltometricsconnector/testdata/configs/invalid_ottl_conditions.yaml new file mode 100644 index 000000000000..8963f831a39b --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/invalid_ottl_conditions.yaml @@ -0,0 +1,25 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - key: key.1 + conditions: + - bad_condition + sum: + value: "1" + datapoints: + - name: dp.sum + attributes: + - key: key.1 + conditions: + - bad_condition + sum: + value: "1" + logs: + - name: log.sum + attributes: + - key: key.1 + conditions: + - bad_condition + sum: + value: "1" diff --git a/connector/signaltometricsconnector/testdata/configs/invalid_ottl_statements.yaml b/connector/signaltometricsconnector/testdata/configs/invalid_ottl_statements.yaml new file mode 100644 index 000000000000..9be4e452c90c --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/invalid_ottl_statements.yaml @@ -0,0 +1,19 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - key: key.1 + sum: + value: bad_statement(1) + datapoints: + - name: dp.sum + attributes: + - key: key.1 + sum: + value: bad_statement(1) + logs: + - name: log.sum + attributes: + - key: key.1 + sum: + value: bad_statement(1) diff --git a/connector/signaltometricsconnector/testdata/configs/invalid_sum.yaml b/connector/signaltometricsconnector/testdata/configs/invalid_sum.yaml new file mode 100644 index 000000000000..391aba74d883 --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/invalid_sum.yaml @@ -0,0 +1,16 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - key: key.1 + sum: {} + datapoints: + - name: dp.sum + attributes: + - key: key.1 + sum: {} + logs: + - name: log.sum + attributes: + - key: key.1 + sum: {} diff --git a/connector/signaltometricsconnector/testdata/configs/multiple_metric.yaml b/connector/signaltometricsconnector/testdata/configs/multiple_metric.yaml new file mode 100644 index 000000000000..a030275ba2e9 --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/multiple_metric.yaml @@ -0,0 +1,31 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - key: key.1 + sum: + value: "1" + histogram: + value: "1" + exponential_histogram: + value: "1" + datapoints: + - name: dp.sum + attributes: + - key: key.1 + sum: + value: "1" + histogram: + value: "1" + exponential_histogram: + value: "1" + logs: + - name: log.sum + attributes: + - key: key.1 + sum: + value: "1" + histogram: + value: "1" + exponential_histogram: + value: "1" diff --git a/connector/signaltometricsconnector/testdata/configs/no_key_attributes.yaml b/connector/signaltometricsconnector/testdata/configs/no_key_attributes.yaml new file mode 100644 index 000000000000..a6522505d3ae --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/no_key_attributes.yaml @@ -0,0 +1,19 @@ +signaltometrics: + spans: + - name: span.sum + attributes: + - default_value: key.1 + sum: + value: "1" + datapoints: + - name: dp.sum + attributes: + - default_value: key.1 + sum: + value: "1" + logs: + - name: log.sum + attributes: + - default_value: key.1 + sum: + value: "1" diff --git a/connector/signaltometricsconnector/testdata/configs/valid_full.yaml b/connector/signaltometricsconnector/testdata/configs/valid_full.yaml new file mode 100644 index 000000000000..a200ab878b11 --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/valid_full.yaml @@ -0,0 +1,63 @@ +signaltometrics: + spans: + - name: span.exp_histogram + description: Exponential histogram + unit: us + include_resource_attributes: + - key: key.1 + default_value: foo + attributes: + - key: key.2 + default_value: bar + conditions: + - attributes["some.optional.1"] != nil + - resource.attributes["some.optional.2"] != nil + exponential_histogram: + max_size: 10 + value: Microseconds(end_time - start_time) + count: "1" + - name: span.histogram + description: Histogram + unit: us + include_resource_attributes: + - key: key.1 + default_value: foo + attributes: + - key: key.2 + default_value: bar + conditions: + - attributes["some.optional.1"] != nil + - resource.attributes["some.optional.2"] != nil + histogram: + buckets: [1.1, 11.1, 111.1] + value: Microseconds(end_time - start_time) + count: "1" + datapoints: + - name: dp.sum + description: Sum + unit: ms + include_resource_attributes: + - key: key.1 + default_value: foo + attributes: + - key: key.2 + default_value: bar + conditions: + - attributes["some.optional.1"] != nil + - IsDouble(attributes["some.optional.1"]) + sum: + value: attributes["some.optional.1"] + logs: + - name: log.sum + description: Sum + unit: "1" + include_resource_attributes: + - key: key.1 + default_value: foo + attributes: + - key: key.2 + default_value: bar + conditions: + - attributes["some.optional.1"] != nil + sum: + value: "1" diff --git a/connector/signaltometricsconnector/testdata/configs/without_name.yaml b/connector/signaltometricsconnector/testdata/configs/without_name.yaml new file mode 100644 index 000000000000..92f92ede0864 --- /dev/null +++ b/connector/signaltometricsconnector/testdata/configs/without_name.yaml @@ -0,0 +1,10 @@ +signaltometrics: + spans: + - sum: + value: "1" + datapoints: + - sum: + value: "1" + logs: + - sum: + value: "1" diff --git a/connector/signaltometricsconnector/testdata/traces/traces.yaml b/connector/signaltometricsconnector/testdata/traces/traces.yaml new file mode 100644 index 000000000000..bbf84f43b5fd --- /dev/null +++ b/connector/signaltometricsconnector/testdata/traces/traces.yaml @@ -0,0 +1,96 @@ +resourceSpans: + - resource: + attributes: + - key: resource.foo + value: + stringValue: foo + - key: resource.bar + value: + stringValue: bar + scopeSpans: + - scope: {} + spans: + - attributes: + - key: db.name + value: + stringValue: main + - key: db.system + value: + stringValue: mysql + endTimeUnixNano: "1581452772500000789" + name: db-span + parentSpanId: "" + startTimeUnixNano: "1581452772000000321" + - attributes: + - key: http.request.method + value: + stringValue: POST + - key: url.full + value: + stringValue: https://www.foo.bar/search?q=OpenTelemetry#SemConv + - key: http.response.status_code + value: + intValue: 201 + endTimeUnixNano: "1581452772900000789" + name: http-span + parentSpanId: "" + startTimeUnixNano: "1581452772000000321" + - attributes: + - key: messaging.system + value: + stringValue: kafka + - key: messaging.destination.name + value: + stringValue: TestTopic + endTimeUnixNano: "1581452772002000789" + name: msg-span + parentSpanId: "" + startTimeUnixNano: "1581452772000000321" + - attributes: + - key: db.name + value: + stringValue: main + - key: db.system + value: + stringValue: mysql + endTimeUnixNano: "1581452773000000789" + name: db-span-2 + parentSpanId: "bcff497b5a47310f" + startTimeUnixNano: "1581452772000000321" + - attributes: + - key: http.request.method + value: + stringValue: POST + - key: url.full + value: + stringValue: https://www.foo.bar/search?q=OpenTelemetry#SemConv + - key: http.response.status_code + value: + intValue: 201 + endTimeUnixNano: "1581452783000000789" + name: http-span-2 + parentSpanId: "bcff497b5a47310f" + startTimeUnixNano: "1581452772000000321" + - attributes: + - key: messaging.system + value: + stringValue: kafka + - key: messaging.destination.name + value: + stringValue: TestTopic + endTimeUnixNano: "1581452789000000789" + name: msg-span-2 + parentSpanId: "bcff497b5a47310f" + startTimeUnixNano: "1581452772000000321" + - attributes: + - key: db.name + value: + stringValue: main + - key: db.system + value: + stringValue: mysql + endTimeUnixNano: "1581452772500000804" + name: th-value-8 # represents 2 sampled spans + parentSpanId: "" + startTimeUnixNano: "1581452772000000381" + traceState: "ot=th:8" diff --git a/extension/awsproxy/factory.go b/extension/awsproxy/factory.go index 3330f319f757..3c35cd3f4cba 100644 --- a/extension/awsproxy/factory.go +++ b/extension/awsproxy/factory.go @@ -36,9 +36,7 @@ func createDefaultConfig() component.Config { TCPAddrConfig: confignet.TCPAddrConfig{ Endpoint: testutil.EndpointForPort(defaultPort), }, - TLSSetting: configtls.ClientConfig{ - Insecure: false, - }, + TLSSetting: configtls.NewDefaultClientConfig(), }, } } diff --git a/pkg/translator/azure/resourcelogs_to_logs.go b/pkg/translator/azure/resourcelogs_to_logs.go index 57fd4de3025d..5b608570612a 100644 --- a/pkg/translator/azure/resourcelogs_to_logs.go +++ b/pkg/translator/azure/resourcelogs_to_logs.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "strconv" + "time" jsoniter "github.com/json-iterator/go" "github.com/relvacode/iso8601" @@ -71,8 +72,9 @@ type azureLogRecord struct { var _ plog.Unmarshaler = (*ResourceLogsUnmarshaler)(nil) type ResourceLogsUnmarshaler struct { - Version string - Logger *zap.Logger + Version string + Logger *zap.Logger + TimeFormats []string } func (r ResourceLogsUnmarshaler) UnmarshalLogs(buf []byte) (plog.Logs, error) { @@ -105,7 +107,7 @@ func (r ResourceLogsUnmarshaler) UnmarshalLogs(buf []byte) (plog.Logs, error) { for i := 0; i < len(logs); i++ { log := logs[i] - nanos, err := getTimestamp(log) + nanos, err := getTimestamp(log, r.TimeFormats...) if err != nil { r.Logger.Warn("Unable to convert timestamp from log", zap.String("timestamp", log.Time)) continue @@ -129,11 +131,11 @@ func (r ResourceLogsUnmarshaler) UnmarshalLogs(buf []byte) (plog.Logs, error) { return l, nil } -func getTimestamp(record azureLogRecord) (pcommon.Timestamp, error) { +func getTimestamp(record azureLogRecord, formats ...string) (pcommon.Timestamp, error) { if record.Time != "" { - return asTimestamp(record.Time) + return asTimestamp(record.Time, formats...) } else if record.Timestamp != "" { - return asTimestamp(record.Timestamp) + return asTimestamp(record.Timestamp, formats...) } return 0, errMissingTimestamp @@ -142,13 +144,21 @@ func getTimestamp(record azureLogRecord) (pcommon.Timestamp, error) { // asTimestamp will parse an ISO8601 string into an OpenTelemetry // nanosecond timestamp. If the string cannot be parsed, it will // return zero and the error. -func asTimestamp(s string) (pcommon.Timestamp, error) { - t, err := iso8601.ParseString(s) - if err != nil { - return 0, err +func asTimestamp(s string, formats ...string) (pcommon.Timestamp, error) { + var err error + var t time.Time + // Try parsing with provided formats first + for _, format := range formats { + if t, err = time.Parse(format, s); err == nil { + return pcommon.Timestamp(t.UnixNano()), nil + } } - return pcommon.Timestamp(t.UnixNano()), nil + // Fallback to ISO 8601 parsing if no format matches + if t, err = iso8601.ParseString(s); err == nil { + return pcommon.Timestamp(t.UnixNano()), nil + } + return 0, err } // asSeverity converts the Azure log level to equivalent diff --git a/pkg/translator/azure/resourcelogs_to_logs_test.go b/pkg/translator/azure/resourcelogs_to_logs_test.go index 6a02dd187793..83a9540bce2a 100644 --- a/pkg/translator/azure/resourcelogs_to_logs_test.go +++ b/pkg/translator/azure/resourcelogs_to_logs_test.go @@ -217,8 +217,25 @@ func TestAsTimestamp(t *testing.T) { assert.NoError(t, err) assert.Less(t, pcommon.Timestamp(0), nanos) + timestamp = "11/20/2024 13:57:18" + nanos, err = asTimestamp(timestamp, "01/02/2006 15:04:05") + assert.NoError(t, err) + assert.Less(t, pcommon.Timestamp(0), nanos) + + // time_format set, but fallback to iso8601 and succeeded to parse + timestamp = "2022-11-11T04:48:27.6767145Z" + nanos, err = asTimestamp(timestamp, "01/02/2006 15:04:05") + assert.NoError(t, err) + assert.Less(t, pcommon.Timestamp(0), nanos) + + // time_format set, but all failed to parse + timestamp = "11/20/2024 13:57:18" + nanos, err = asTimestamp(timestamp, "2006-01-02 15:04:05") + assert.Error(t, err) + assert.Equal(t, pcommon.Timestamp(0), nanos) + timestamp = "invalid-time" - nanos, err = asTimestamp(timestamp) + nanos, err = asTimestamp(timestamp, nil...) assert.Error(t, err) assert.Equal(t, pcommon.Timestamp(0), nanos) } diff --git a/pkg/translator/azure/resources_to_traces.go b/pkg/translator/azure/resources_to_traces.go index 2e9f214f389d..2f92d013248c 100644 --- a/pkg/translator/azure/resources_to_traces.go +++ b/pkg/translator/azure/resources_to_traces.go @@ -62,8 +62,9 @@ type azureTracesRecord struct { var _ ptrace.Unmarshaler = (*TracesUnmarshaler)(nil) type TracesUnmarshaler struct { - Version string - Logger *zap.Logger + Version string + Logger *zap.Logger + TimeFormats []string } func (r TracesUnmarshaler) UnmarshalTraces(buf []byte) (ptrace.Traces, error) { @@ -95,7 +96,7 @@ func (r TracesUnmarshaler) UnmarshalTraces(buf []byte) (ptrace.Traces, error) { resource.Attributes().PutStr("service.name", azureTrace.AppRoleName) - nanos, err := asTimestamp(azureTrace.Time) + nanos, err := asTimestamp(azureTrace.Time, r.TimeFormats...) if err != nil { r.Logger.Warn("Invalid Timestamp", zap.String("time", azureTrace.Time)) continue diff --git a/pkg/translator/azurelogs/resourcelogs_to_logs.go b/pkg/translator/azurelogs/resourcelogs_to_logs.go index 15c0ef59e531..437df4096163 100644 --- a/pkg/translator/azurelogs/resourcelogs_to_logs.go +++ b/pkg/translator/azurelogs/resourcelogs_to_logs.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "strconv" + "time" jsoniter "github.com/json-iterator/go" "github.com/relvacode/iso8601" @@ -44,7 +45,6 @@ const ( var errMissingTimestamp = errors.New("missing timestamp") -// azureRecords represents an array of Azure log records // as exported via an Azure Event Hub type azureRecords struct { Records []azureLogRecord `json:"records"` @@ -76,8 +76,9 @@ type azureLogRecord struct { var _ plog.Unmarshaler = (*ResourceLogsUnmarshaler)(nil) type ResourceLogsUnmarshaler struct { - Version string - Logger *zap.Logger + Version string + Logger *zap.Logger + TimeFormats []string } func (r ResourceLogsUnmarshaler) UnmarshalLogs(buf []byte) (plog.Logs, error) { @@ -109,7 +110,7 @@ func (r ResourceLogsUnmarshaler) UnmarshalLogs(buf []byte) (plog.Logs, error) { for i := 0; i < len(logs); i++ { log := logs[i] - nanos, err := getTimestamp(log) + nanos, err := getTimestamp(log, r.TimeFormats...) if err != nil { r.Logger.Warn("Unable to convert timestamp from log", zap.String("timestamp", log.Time)) continue @@ -137,11 +138,11 @@ func (r ResourceLogsUnmarshaler) UnmarshalLogs(buf []byte) (plog.Logs, error) { return l, nil } -func getTimestamp(record azureLogRecord) (pcommon.Timestamp, error) { +func getTimestamp(record azureLogRecord, formats ...string) (pcommon.Timestamp, error) { if record.Time != "" { - return asTimestamp(record.Time) + return asTimestamp(record.Time, formats...) } else if record.Timestamp != "" { - return asTimestamp(record.Timestamp) + return asTimestamp(record.Timestamp, formats...) } return 0, errMissingTimestamp @@ -150,13 +151,21 @@ func getTimestamp(record azureLogRecord) (pcommon.Timestamp, error) { // asTimestamp will parse an ISO8601 string into an OpenTelemetry // nanosecond timestamp. If the string cannot be parsed, it will // return zero and the error. -func asTimestamp(s string) (pcommon.Timestamp, error) { - t, err := iso8601.ParseString(s) - if err != nil { - return 0, err +func asTimestamp(s string, formats ...string) (pcommon.Timestamp, error) { + var err error + var t time.Time + // Try parsing with provided formats first + for _, format := range formats { + if t, err = time.Parse(format, s); err == nil { + return pcommon.Timestamp(t.UnixNano()), nil + } } - return pcommon.Timestamp(t.UnixNano()), nil + // Fallback to ISO 8601 parsing if no format matches + if t, err = iso8601.ParseString(s); err == nil { + return pcommon.Timestamp(t.UnixNano()), nil + } + return 0, err } // asSeverity converts the Azure log level to equivalent diff --git a/pkg/translator/azurelogs/resourcelogs_to_logs_test.go b/pkg/translator/azurelogs/resourcelogs_to_logs_test.go index 4f4f17dbc0a2..9e0fbcf05f28 100644 --- a/pkg/translator/azurelogs/resourcelogs_to_logs_test.go +++ b/pkg/translator/azurelogs/resourcelogs_to_logs_test.go @@ -243,6 +243,23 @@ func TestAsTimestamp(t *testing.T) { assert.NoError(t, err) assert.Less(t, pcommon.Timestamp(0), nanos) + timestamp = "11/20/2024 13:57:18" + nanos, err = asTimestamp(timestamp, "01/02/2006 15:04:05") + assert.NoError(t, err) + assert.Less(t, pcommon.Timestamp(0), nanos) + + // time_format set, but fallback to iso8601 and succeeded to parse + timestamp = "2022-11-11T04:48:27.6767145Z" + nanos, err = asTimestamp(timestamp, "01/02/2006 15:04:05") + assert.NoError(t, err) + assert.Less(t, pcommon.Timestamp(0), nanos) + + // time_format set, but all failed to parse + timestamp = "11/20/2024 13:57:18" + nanos, err = asTimestamp(timestamp, "2006-01-02 15:04:05") + assert.Error(t, err) + assert.Equal(t, pcommon.Timestamp(0), nanos) + timestamp = "invalid-time" nanos, err = asTimestamp(timestamp) assert.Error(t, err) diff --git a/processor/k8sattributesprocessor/processor.go b/processor/k8sattributesprocessor/processor.go index 9afd118f4762..a93cafa52403 100644 --- a/processor/k8sattributesprocessor/processor.go +++ b/processor/k8sattributesprocessor/processor.go @@ -144,9 +144,7 @@ func (kp *kubernetesprocessor) processResource(ctx context.Context, resource pco for i := range podIdentifierValue { if podIdentifierValue[i].Source.From == kube.ConnectionSource && podIdentifierValue[i].Value != "" { - if _, found := resource.Attributes().Get(kube.K8sIPLabelName); !found { - resource.Attributes().PutStr(kube.K8sIPLabelName, podIdentifierValue[i].Value) - } + setResourceAttribute(resource.Attributes(), kube.K8sIPLabelName, podIdentifierValue[i].Value) break } } @@ -161,9 +159,7 @@ func (kp *kubernetesprocessor) processResource(ctx context.Context, resource pco kp.logger.Debug("getting the pod", zap.Any("pod", pod)) for key, val := range pod.Attributes { - if _, found := resource.Attributes().Get(key); !found { - resource.Attributes().PutStr(key, val) - } + setResourceAttribute(resource.Attributes(), key, val) } kp.addContainerAttributes(resource.Attributes(), pod) } @@ -173,9 +169,7 @@ func (kp *kubernetesprocessor) processResource(ctx context.Context, resource pco if namespace != "" { attrsToAdd := kp.getAttributesForPodsNamespace(namespace) for key, val := range attrsToAdd { - if _, found := resource.Attributes().Get(key); !found { - resource.Attributes().PutStr(key, val) - } + setResourceAttribute(resource.Attributes(), key, val) } } @@ -183,19 +177,22 @@ func (kp *kubernetesprocessor) processResource(ctx context.Context, resource pco if nodeName != "" { attrsToAdd := kp.getAttributesForPodsNode(nodeName) for key, val := range attrsToAdd { - if _, found := resource.Attributes().Get(key); !found { - resource.Attributes().PutStr(key, val) - } + setResourceAttribute(resource.Attributes(), key, val) } nodeUID := kp.getUIDForPodsNode(nodeName) if nodeUID != "" { - if _, found := resource.Attributes().Get(conventions.AttributeK8SNodeUID); !found { - resource.Attributes().PutStr(conventions.AttributeK8SNodeUID, nodeUID) - } + setResourceAttribute(resource.Attributes(), conventions.AttributeK8SNodeUID, nodeUID) } } } +func setResourceAttribute(attributes pcommon.Map, key string, val string) { + attr, found := attributes.Get(key) + if !found || attr.AsString() == "" { + attributes.PutStr(key, val) + } +} + func getNamespace(pod *kube.Pod, resAttrs pcommon.Map) string { if pod != nil && pod.Namespace != "" { return pod.Namespace @@ -245,19 +242,13 @@ func (kp *kubernetesprocessor) addContainerAttributes(attrs pcommon.Map, pod *ku } } if containerSpec.Name != "" { - if _, found := attrs.Get(conventions.AttributeK8SContainerName); !found { - attrs.PutStr(conventions.AttributeK8SContainerName, containerSpec.Name) - } + setResourceAttribute(attrs, conventions.AttributeK8SContainerName, containerSpec.Name) } if containerSpec.ImageName != "" { - if _, found := attrs.Get(conventions.AttributeContainerImageName); !found { - attrs.PutStr(conventions.AttributeContainerImageName, containerSpec.ImageName) - } + setResourceAttribute(attrs, conventions.AttributeContainerImageName, containerSpec.ImageName) } if containerSpec.ImageTag != "" { - if _, found := attrs.Get(conventions.AttributeContainerImageTag); !found { - attrs.PutStr(conventions.AttributeContainerImageTag, containerSpec.ImageTag) - } + setResourceAttribute(attrs, conventions.AttributeContainerImageTag, containerSpec.ImageTag) } // attempt to get container ID from restart count runID := -1 diff --git a/processor/k8sattributesprocessor/processor_test.go b/processor/k8sattributesprocessor/processor_test.go index d42ea5e555a2..07c6b51b9c53 100644 --- a/processor/k8sattributesprocessor/processor_test.go +++ b/processor/k8sattributesprocessor/processor_test.go @@ -1709,3 +1709,62 @@ func (nh *nopHost) GetExtensions() map[component.ID]component.Component { func (nh *nopHost) Report(event *componentstatus.Event) { nh.reportFunc(event) } + +func Test_setResourceAttribute(t *testing.T) { + tests := []struct { + name string + attributes func() pcommon.Map + key string + val string + wantAttrs func() pcommon.Map + }{ + { + name: "attribute not present - add value", + attributes: pcommon.NewMap, + key: "foo", + val: "bar", + wantAttrs: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("foo", "bar") + return m + }, + }, + { + name: "attribute present with non-empty value - do not overwrite value", + attributes: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("foo", "bar") + return m + }, + key: "foo", + val: "baz", + wantAttrs: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("foo", "bar") + return m + }, + }, + { + name: "attribute present with empty value - set value", + attributes: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("foo", "") + return m + }, + key: "foo", + val: "bar", + wantAttrs: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("foo", "bar") + return m + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + attrs := tt.attributes() + setResourceAttribute(attrs, tt.key, tt.val) + require.Equal(t, tt.wantAttrs(), attrs) + }) + } +} diff --git a/receiver/azureeventhubreceiver/README.md b/receiver/azureeventhubreceiver/README.md index 0d4873d5b6f7..bad2b947a5d8 100644 --- a/receiver/azureeventhubreceiver/README.md +++ b/receiver/azureeventhubreceiver/README.md @@ -50,6 +50,12 @@ attribute names are copied without any changes. Default: `false` (semantic conventions are not applied) +### time_formats (optional) + +All supported time format for logs, metrics and traces. Default is `nil` (unset), which means using the current iso8601 parser. The format is based on https://pkg.go.dev/time#Layout. If no time-zone info, will use UTC time. If all failed, it will use iso8601 format to parse. + +Default: `nil` + ### Example Configuration ```yaml @@ -60,6 +66,11 @@ receivers: group: bar offset: "1234-5566" format: "azure" + # optional + time_formats: + # All supported time format. Default is empty string array, which means using the current iso8601 parser. The format is based on https://pkg.go.dev/time#Layout. If no time-zone info, will use UTC time. + logs: ["01/02/2006 15:04:05","2006-01-02 15:04:05","2006-01-02T15:04:05Z07:00"] + metrics: ["01/02/2006 15:04:05"] ``` This component can persist its state using the [storage extension]. diff --git a/receiver/azureeventhubreceiver/azureresourcelogs_unmarshaler.go b/receiver/azureeventhubreceiver/azureresourcelogs_unmarshaler.go index 3c3c6a04387d..d7845de6201e 100644 --- a/receiver/azureeventhubreceiver/azureresourcelogs_unmarshaler.go +++ b/receiver/azureeventhubreceiver/azureresourcelogs_unmarshaler.go @@ -21,19 +21,21 @@ type AzureResourceLogsEventUnmarshaler struct { unmarshaler logsUnmarshaler } -func newAzureResourceLogsUnmarshaler(buildInfo component.BuildInfo, logger *zap.Logger, applySemanticConventions bool) eventLogsUnmarshaler { +func newAzureResourceLogsUnmarshaler(buildInfo component.BuildInfo, logger *zap.Logger, applySemanticConventions bool, timeFormat []string) eventLogsUnmarshaler { if applySemanticConventions { return AzureResourceLogsEventUnmarshaler{ unmarshaler: &azurelogs.ResourceLogsUnmarshaler{ - Version: buildInfo.Version, - Logger: logger, + Version: buildInfo.Version, + Logger: logger, + TimeFormats: timeFormat, }, } } return AzureResourceLogsEventUnmarshaler{ unmarshaler: &azure.ResourceLogsUnmarshaler{ - Version: buildInfo.Version, - Logger: logger, + Version: buildInfo.Version, + Logger: logger, + TimeFormats: timeFormat, }, } } diff --git a/receiver/azureeventhubreceiver/azureresourcemetrics_unmarshaler.go b/receiver/azureeventhubreceiver/azureresourcemetrics_unmarshaler.go index efef7e72a60a..cce62e907fbf 100644 --- a/receiver/azureeventhubreceiver/azureresourcemetrics_unmarshaler.go +++ b/receiver/azureeventhubreceiver/azureresourcemetrics_unmarshaler.go @@ -21,13 +21,12 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azureeventhubreceiver/internal/metadata" ) -const ( - azureResourceID = "azure.resource.id" -) +const azureResourceID = "azure.resource.id" type azureResourceMetricsUnmarshaler struct { - buildInfo component.BuildInfo - logger *zap.Logger + buildInfo component.BuildInfo + logger *zap.Logger + TimeFormat []string } // azureMetricRecords represents an array of Azure metric records @@ -50,10 +49,11 @@ type azureMetricRecord struct { Average float64 `json:"average"` } -func newAzureResourceMetricsUnmarshaler(buildInfo component.BuildInfo, logger *zap.Logger) eventMetricsUnmarshaler { +func newAzureResourceMetricsUnmarshaler(buildInfo component.BuildInfo, logger *zap.Logger, timeFormat []string) eventMetricsUnmarshaler { return azureResourceMetricsUnmarshaler{ - buildInfo: buildInfo, - logger: logger, + buildInfo: buildInfo, + logger: logger, + TimeFormat: timeFormat, } } @@ -90,7 +90,7 @@ func (r azureResourceMetricsUnmarshaler) UnmarshalMetrics(event *eventhub.Event) resourceID = azureMetric.ResourceID } - nanos, err := asTimestamp(azureMetric.Time) + nanos, err := asTimestamp(azureMetric.Time, r.TimeFormat) if err != nil { r.logger.Warn("Invalid Timestamp", zap.String("time", azureMetric.Time)) continue @@ -152,10 +152,19 @@ func (r azureResourceMetricsUnmarshaler) UnmarshalMetrics(event *eventhub.Event) // asTimestamp will parse an ISO8601 string into an OpenTelemetry // nanosecond timestamp. If the string cannot be parsed, it will // return zero and the error. -func asTimestamp(s string) (pcommon.Timestamp, error) { - t, err := iso8601.ParseString(s) - if err != nil { - return 0, err +func asTimestamp(s string, formats []string) (pcommon.Timestamp, error) { + var err error + var t time.Time + // Try parsing with provided formats first + for _, format := range formats { + if t, err = time.Parse(format, s); err == nil { + return pcommon.Timestamp(t.UnixNano()), nil + } + } + + // Fallback to ISO 8601 parsing if no format matches + if t, err = iso8601.ParseString(s); err == nil { + return pcommon.Timestamp(t.UnixNano()), nil } - return pcommon.Timestamp(t.UnixNano()), nil + return 0, err } diff --git a/receiver/azureeventhubreceiver/azureresourcetraces_unmarshaler.go b/receiver/azureeventhubreceiver/azureresourcetraces_unmarshaler.go index 40fed32a62ff..6ff4213d8874 100755 --- a/receiver/azureeventhubreceiver/azureresourcetraces_unmarshaler.go +++ b/receiver/azureeventhubreceiver/azureresourcetraces_unmarshaler.go @@ -16,11 +16,12 @@ type azureTracesEventUnmarshaler struct { unmarshaler *azure.TracesUnmarshaler } -func newAzureTracesUnmarshaler(buildInfo component.BuildInfo, logger *zap.Logger) eventTracesUnmarshaler { +func newAzureTracesUnmarshaler(buildInfo component.BuildInfo, logger *zap.Logger, timeFormat []string) eventTracesUnmarshaler { return azureTracesEventUnmarshaler{ unmarshaler: &azure.TracesUnmarshaler{ - Version: buildInfo.Version, - Logger: logger, + Version: buildInfo.Version, + Logger: logger, + TimeFormats: timeFormat, }, } } diff --git a/receiver/azureeventhubreceiver/config.go b/receiver/azureeventhubreceiver/config.go index e5e01c98cf87..b7db1541a38d 100644 --- a/receiver/azureeventhubreceiver/config.go +++ b/receiver/azureeventhubreceiver/config.go @@ -32,6 +32,13 @@ type Config struct { Format string `mapstructure:"format"` ConsumerGroup string `mapstructure:"group"` ApplySemanticConventions bool `mapstructure:"apply_semantic_conventions"` + TimeFormats TimeFormat `mapstructure:"time_formats"` +} + +type TimeFormat struct { + Logs []string `mapstructure:"logs"` + Metrics []string `mapstructure:"metrics"` + Traces []string `mapstructure:"traces"` } func isValidFormat(format string) bool { diff --git a/receiver/azureeventhubreceiver/factory.go b/receiver/azureeventhubreceiver/factory.go index 2286cdb952e1..cc0ee1098f1f 100644 --- a/receiver/azureeventhubreceiver/factory.go +++ b/receiver/azureeventhubreceiver/factory.go @@ -110,21 +110,21 @@ func (f *eventhubReceiverFactory) getReceiver( if logFormat(receiverConfig.Format) == rawLogFormat { logsUnmarshaler = newRawLogsUnmarshaler(settings.Logger) } else { - logsUnmarshaler = newAzureResourceLogsUnmarshaler(settings.BuildInfo, settings.Logger, receiverConfig.ApplySemanticConventions) + logsUnmarshaler = newAzureResourceLogsUnmarshaler(settings.BuildInfo, settings.Logger, receiverConfig.ApplySemanticConventions, receiverConfig.TimeFormats.Logs) } case pipeline.SignalMetrics: if logFormat(receiverConfig.Format) == rawLogFormat { metricsUnmarshaler = nil err = errors.New("raw format not supported for Metrics") } else { - metricsUnmarshaler = newAzureResourceMetricsUnmarshaler(settings.BuildInfo, settings.Logger) + metricsUnmarshaler = newAzureResourceMetricsUnmarshaler(settings.BuildInfo, settings.Logger, receiverConfig.TimeFormats.Metrics) } case pipeline.SignalTraces: if logFormat(receiverConfig.Format) == rawLogFormat { tracesUnmarshaler = nil err = errors.New("raw format not supported for Traces") } else { - tracesUnmarshaler = newAzureTracesUnmarshaler(settings.BuildInfo, settings.Logger) + tracesUnmarshaler = newAzureTracesUnmarshaler(settings.BuildInfo, settings.Logger, receiverConfig.TimeFormats.Traces) } } diff --git a/receiver/githubreceiver/README.md b/receiver/githubreceiver/README.md index 48c318f1d064..ecff5133e990 100644 --- a/receiver/githubreceiver/README.md +++ b/receiver/githubreceiver/README.md @@ -38,8 +38,8 @@ The collection interval is common to all scrapers and is set to 30 seconds by de github: collection_interval: #default = 30s recommended 300s scrapers: - : - : + scraper/config-1: + scraper/config-2: ... ``` @@ -55,7 +55,7 @@ receivers: initial_delay: 1s collection_interval: 60s scrapers: - github: + scraper: metrics: vcs.repository.contributor.count: enabled: true diff --git a/receiver/githubreceiver/config_test.go b/receiver/githubreceiver/config_test.go index 2ff23f5c7eaa..5c310c84fe97 100644 --- a/receiver/githubreceiver/config_test.go +++ b/receiver/githubreceiver/config_test.go @@ -41,7 +41,7 @@ func TestLoadConfig(t *testing.T) { defaultConfigGitHubReceiver := factory.CreateDefaultConfig() defaultConfigGitHubReceiver.(*Config).Scrapers = map[string]internal.Config{ - metadata.Type.String(): (&githubscraper.Factory{}).CreateDefaultConfig(), + githubscraper.TypeStr: (&githubscraper.Factory{}).CreateDefaultConfig(), } defaultConfigGitHubReceiver.(*Config).WebHook = WebHook{ @@ -67,7 +67,7 @@ func TestLoadConfig(t *testing.T) { InitialDelay: 1 * time.Second, }, Scrapers: map[string]internal.Config{ - metadata.Type.String(): (&githubscraper.Factory{}).CreateDefaultConfig(), + githubscraper.TypeStr: (&githubscraper.Factory{}).CreateDefaultConfig(), }, WebHook: WebHook{ ServerConfig: confighttp.ServerConfig{ diff --git a/receiver/githubreceiver/documentation.md b/receiver/githubreceiver/documentation.md index a95f684a1fd7..160cb6a90c26 100644 --- a/receiver/githubreceiver/documentation.md +++ b/receiver/githubreceiver/documentation.md @@ -12,7 +12,7 @@ metrics: enabled: false ``` -### vcs.repository.change.count +### vcs.change.count The number of changes (pull requests) in a repository, categorized by their state (either open or merged). @@ -24,12 +24,13 @@ The number of changes (pull requests) in a repository, categorized by their stat | Name | Description | Values | | ---- | ----------- | ------ | -| change.state | The state of a change (pull request) | Str: ``open``, ``merged`` | -| repository.name | The name of a VCS repository | Any Str | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.change.state | The state of a change (pull request) | Str: ``open``, ``merged`` | +| vcs.repository.name | The name of the VCS repository. | Any Str | -### vcs.repository.change.time_open +### vcs.change.duration -The amount of time a change (pull request) has been open. +The time duration a change (pull request/merge request/changelist) has been in an open state. | Unit | Metric Type | Value Type | | ---- | ----------- | ---------- | @@ -39,10 +40,12 @@ The amount of time a change (pull request) has been open. | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | +| vcs.ref.head.name | The name of the VCS head reference (branch). | Any Str | +| vcs.change.state | The state of a change (pull request) | Str: ``open``, ``merged`` | -### vcs.repository.change.time_to_approval +### vcs.change.time_to_approval The amount of time it took a change (pull request) to go from open to approved. @@ -54,10 +57,11 @@ The amount of time it took a change (pull request) to go from open to approved. | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | +| vcs.ref.head.name | The name of the VCS head reference (branch). | Any Str | -### vcs.repository.change.time_to_merge +### vcs.change.time_to_merge The amount of time it took a change (pull request) to go from open to merged. @@ -69,18 +73,11 @@ The amount of time it took a change (pull request) to go from open to merged. | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | +| vcs.ref.head.name | The name of the VCS head reference (branch). | Any Str | -### vcs.repository.count - -The number of repositories in an organization. - -| Unit | Metric Type | Value Type | -| ---- | ----------- | ---------- | -| {repository} | Gauge | Int | - -### vcs.repository.ref.count +### vcs.ref.count The number of refs of type branch in a repository. @@ -92,28 +89,13 @@ The number of refs of type branch in a repository. | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.type | The type of ref (branch, tag). | Str: ``branch``, ``tag`` | - -### vcs.repository.ref.lines_added - -The number of lines added in a ref (branch) relative to the default branch (trunk). - -| Unit | Metric Type | Value Type | -| ---- | ----------- | ---------- | -| {line} | Gauge | Int | - -#### Attributes - -| Name | Description | Values | -| ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | -| ref.type | The type of ref (branch, tag). | Str: ``branch``, ``tag`` | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | +| vcs.ref.head.type | The type of the head reference (branch, tag). | Str: ``branch``, ``tag`` | -### vcs.repository.ref.lines_deleted +### vcs.ref.lines_delta -The number of lines deleted in a ref (branch) relative to the default branch (trunk). +The number of lines added/removed in a ref (branch) relative to the default branch (trunk). | Unit | Metric Type | Value Type | | ---- | ----------- | ---------- | @@ -123,13 +105,15 @@ The number of lines deleted in a ref (branch) relative to the default branch (tr | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | -| ref.type | The type of ref (branch, tag). | Str: ``branch``, ``tag`` | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | +| vcs.ref.head.name | The name of the VCS head reference (branch). | Any Str | +| vcs.ref.head.type | The type of the head reference (branch, tag). | Str: ``branch``, ``tag`` | +| vcs.line_change.type | The type of line change being measured on a ref (branch). | Str: ``added``, ``removed`` | -### vcs.repository.ref.revisions_ahead +### vcs.ref.revisions_delta -The number of revisions (commits) a ref (branch) is ahead of the default branch (trunk). +The number of revisions (commits) a ref (branch) is ahead/behind the branch from trunk (default). | Unit | Metric Type | Value Type | | ---- | ----------- | ---------- | @@ -139,41 +123,36 @@ The number of revisions (commits) a ref (branch) is ahead of the default branch | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | -| ref.type | The type of ref (branch, tag). | Str: ``branch``, ``tag`` | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | +| vcs.ref.head.name | The name of the VCS head reference (branch). | Any Str | +| vcs.ref.head.type | The type of the head reference (branch, tag). | Str: ``branch``, ``tag`` | +| vcs.revision_delta.direction | The type of revision comparison. | Str: ``ahead``, ``behind`` | -### vcs.repository.ref.revisions_behind +### vcs.ref.time -The number of revisions (commits) a ref (branch) is behind the default branch (trunk). +Time a ref (branch) created from the default branch (trunk) has existed. The `vcs.ref.head.type` attribute will always be `branch`. | Unit | Metric Type | Value Type | | ---- | ----------- | ---------- | -| {revision} | Gauge | Int | +| s | Gauge | Int | #### Attributes | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | -| ref.type | The type of ref (branch, tag). | Str: ``branch``, ``tag`` | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | +| vcs.ref.head.name | The name of the VCS head reference (branch). | Any Str | +| vcs.ref.head.type | The type of the head reference (branch, tag). | Str: ``branch``, ``tag`` | -### vcs.repository.ref.time +### vcs.repository.count -Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`. +The number of repositories in an organization. | Unit | Metric Type | Value Type | | ---- | ----------- | ---------- | -| s | Gauge | Int | - -#### Attributes - -| Name | Description | Values | -| ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | -| ref.name | The name of a VCS branch | Any Str | -| ref.type | The type of ref (branch, tag). | Str: ``branch``, ``tag`` | +| {repository} | Gauge | Int | ## Optional Metrics @@ -185,7 +164,7 @@ metrics: enabled: true ``` -### vcs.repository.contributor.count +### vcs.contributor.count The number of unique contributors to a repository. @@ -197,7 +176,8 @@ The number of unique contributors to a repository. | Name | Description | Values | | ---- | ----------- | ------ | -| repository.name | The name of a VCS repository | Any Str | +| vcs.repository.url.full | The canonical URL of the repository providing the complete HTTPS address. | Any Str | +| vcs.repository.name | The name of the VCS repository. | Any Str | ## Resource Attributes diff --git a/receiver/githubreceiver/factory.go b/receiver/githubreceiver/factory.go index fa15220b890f..285b77a99f07 100644 --- a/receiver/githubreceiver/factory.go +++ b/receiver/githubreceiver/factory.go @@ -33,7 +33,7 @@ const ( var ( scraperFactories = map[string]internal.ScraperFactory{ - metadata.Type.String(): &githubscraper.Factory{}, + githubscraper.TypeStr: &githubscraper.Factory{}, } errConfigNotValid = errors.New("configuration is not valid for the github receiver") diff --git a/receiver/githubreceiver/go.mod b/receiver/githubreceiver/go.mod index f90c112a9a16..47fb7e5851a3 100644 --- a/receiver/githubreceiver/go.mod +++ b/receiver/githubreceiver/go.mod @@ -133,13 +133,13 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/net v0.31.0 // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect gonum.org/v1/gonum v0.15.1 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect - google.golang.org/grpc v1.68.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 // indirect + google.golang.org/grpc v1.68.1 // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/receiver/githubreceiver/go.sum b/receiver/githubreceiver/go.sum index 1c24e95a7d26..0fa8cf1b4329 100644 --- a/receiver/githubreceiver/go.sum +++ b/receiver/githubreceiver/go.sum @@ -295,8 +295,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -324,10 +324,10 @@ gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 h1:IfdSdTcLFy4lqUQrQJLkLt1PB+AsqVz6lwkWPzWEz10= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/receiver/githubreceiver/internal/metadata/generated_config.go b/receiver/githubreceiver/internal/metadata/generated_config.go index d16a2a6316c2..2a44c29d06b2 100644 --- a/receiver/githubreceiver/internal/metadata/generated_config.go +++ b/receiver/githubreceiver/internal/metadata/generated_config.go @@ -28,56 +28,48 @@ func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { // MetricsConfig provides config for github metrics. type MetricsConfig struct { - VcsRepositoryChangeCount MetricConfig `mapstructure:"vcs.repository.change.count"` - VcsRepositoryChangeTimeOpen MetricConfig `mapstructure:"vcs.repository.change.time_open"` - VcsRepositoryChangeTimeToApproval MetricConfig `mapstructure:"vcs.repository.change.time_to_approval"` - VcsRepositoryChangeTimeToMerge MetricConfig `mapstructure:"vcs.repository.change.time_to_merge"` - VcsRepositoryContributorCount MetricConfig `mapstructure:"vcs.repository.contributor.count"` - VcsRepositoryCount MetricConfig `mapstructure:"vcs.repository.count"` - VcsRepositoryRefCount MetricConfig `mapstructure:"vcs.repository.ref.count"` - VcsRepositoryRefLinesAdded MetricConfig `mapstructure:"vcs.repository.ref.lines_added"` - VcsRepositoryRefLinesDeleted MetricConfig `mapstructure:"vcs.repository.ref.lines_deleted"` - VcsRepositoryRefRevisionsAhead MetricConfig `mapstructure:"vcs.repository.ref.revisions_ahead"` - VcsRepositoryRefRevisionsBehind MetricConfig `mapstructure:"vcs.repository.ref.revisions_behind"` - VcsRepositoryRefTime MetricConfig `mapstructure:"vcs.repository.ref.time"` + VcsChangeCount MetricConfig `mapstructure:"vcs.change.count"` + VcsChangeDuration MetricConfig `mapstructure:"vcs.change.duration"` + VcsChangeTimeToApproval MetricConfig `mapstructure:"vcs.change.time_to_approval"` + VcsChangeTimeToMerge MetricConfig `mapstructure:"vcs.change.time_to_merge"` + VcsContributorCount MetricConfig `mapstructure:"vcs.contributor.count"` + VcsRefCount MetricConfig `mapstructure:"vcs.ref.count"` + VcsRefLinesDelta MetricConfig `mapstructure:"vcs.ref.lines_delta"` + VcsRefRevisionsDelta MetricConfig `mapstructure:"vcs.ref.revisions_delta"` + VcsRefTime MetricConfig `mapstructure:"vcs.ref.time"` + VcsRepositoryCount MetricConfig `mapstructure:"vcs.repository.count"` } func DefaultMetricsConfig() MetricsConfig { return MetricsConfig{ - VcsRepositoryChangeCount: MetricConfig{ + VcsChangeCount: MetricConfig{ Enabled: true, }, - VcsRepositoryChangeTimeOpen: MetricConfig{ + VcsChangeDuration: MetricConfig{ Enabled: true, }, - VcsRepositoryChangeTimeToApproval: MetricConfig{ + VcsChangeTimeToApproval: MetricConfig{ Enabled: true, }, - VcsRepositoryChangeTimeToMerge: MetricConfig{ + VcsChangeTimeToMerge: MetricConfig{ Enabled: true, }, - VcsRepositoryContributorCount: MetricConfig{ + VcsContributorCount: MetricConfig{ Enabled: false, }, - VcsRepositoryCount: MetricConfig{ - Enabled: true, - }, - VcsRepositoryRefCount: MetricConfig{ + VcsRefCount: MetricConfig{ Enabled: true, }, - VcsRepositoryRefLinesAdded: MetricConfig{ + VcsRefLinesDelta: MetricConfig{ Enabled: true, }, - VcsRepositoryRefLinesDeleted: MetricConfig{ + VcsRefRevisionsDelta: MetricConfig{ Enabled: true, }, - VcsRepositoryRefRevisionsAhead: MetricConfig{ + VcsRefTime: MetricConfig{ Enabled: true, }, - VcsRepositoryRefRevisionsBehind: MetricConfig{ - Enabled: true, - }, - VcsRepositoryRefTime: MetricConfig{ + VcsRepositoryCount: MetricConfig{ Enabled: true, }, } diff --git a/receiver/githubreceiver/internal/metadata/generated_config_test.go b/receiver/githubreceiver/internal/metadata/generated_config_test.go index 4747288dcaea..ec0861b334e8 100644 --- a/receiver/githubreceiver/internal/metadata/generated_config_test.go +++ b/receiver/githubreceiver/internal/metadata/generated_config_test.go @@ -25,18 +25,16 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "all_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - VcsRepositoryChangeCount: MetricConfig{Enabled: true}, - VcsRepositoryChangeTimeOpen: MetricConfig{Enabled: true}, - VcsRepositoryChangeTimeToApproval: MetricConfig{Enabled: true}, - VcsRepositoryChangeTimeToMerge: MetricConfig{Enabled: true}, - VcsRepositoryContributorCount: MetricConfig{Enabled: true}, - VcsRepositoryCount: MetricConfig{Enabled: true}, - VcsRepositoryRefCount: MetricConfig{Enabled: true}, - VcsRepositoryRefLinesAdded: MetricConfig{Enabled: true}, - VcsRepositoryRefLinesDeleted: MetricConfig{Enabled: true}, - VcsRepositoryRefRevisionsAhead: MetricConfig{Enabled: true}, - VcsRepositoryRefRevisionsBehind: MetricConfig{Enabled: true}, - VcsRepositoryRefTime: MetricConfig{Enabled: true}, + VcsChangeCount: MetricConfig{Enabled: true}, + VcsChangeDuration: MetricConfig{Enabled: true}, + VcsChangeTimeToApproval: MetricConfig{Enabled: true}, + VcsChangeTimeToMerge: MetricConfig{Enabled: true}, + VcsContributorCount: MetricConfig{Enabled: true}, + VcsRefCount: MetricConfig{Enabled: true}, + VcsRefLinesDelta: MetricConfig{Enabled: true}, + VcsRefRevisionsDelta: MetricConfig{Enabled: true}, + VcsRefTime: MetricConfig{Enabled: true}, + VcsRepositoryCount: MetricConfig{Enabled: true}, }, ResourceAttributes: ResourceAttributesConfig{ OrganizationName: ResourceAttributeConfig{Enabled: true}, @@ -48,18 +46,16 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "none_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - VcsRepositoryChangeCount: MetricConfig{Enabled: false}, - VcsRepositoryChangeTimeOpen: MetricConfig{Enabled: false}, - VcsRepositoryChangeTimeToApproval: MetricConfig{Enabled: false}, - VcsRepositoryChangeTimeToMerge: MetricConfig{Enabled: false}, - VcsRepositoryContributorCount: MetricConfig{Enabled: false}, - VcsRepositoryCount: MetricConfig{Enabled: false}, - VcsRepositoryRefCount: MetricConfig{Enabled: false}, - VcsRepositoryRefLinesAdded: MetricConfig{Enabled: false}, - VcsRepositoryRefLinesDeleted: MetricConfig{Enabled: false}, - VcsRepositoryRefRevisionsAhead: MetricConfig{Enabled: false}, - VcsRepositoryRefRevisionsBehind: MetricConfig{Enabled: false}, - VcsRepositoryRefTime: MetricConfig{Enabled: false}, + VcsChangeCount: MetricConfig{Enabled: false}, + VcsChangeDuration: MetricConfig{Enabled: false}, + VcsChangeTimeToApproval: MetricConfig{Enabled: false}, + VcsChangeTimeToMerge: MetricConfig{Enabled: false}, + VcsContributorCount: MetricConfig{Enabled: false}, + VcsRefCount: MetricConfig{Enabled: false}, + VcsRefLinesDelta: MetricConfig{Enabled: false}, + VcsRefRevisionsDelta: MetricConfig{Enabled: false}, + VcsRefTime: MetricConfig{Enabled: false}, + VcsRepositoryCount: MetricConfig{Enabled: false}, }, ResourceAttributes: ResourceAttributesConfig{ OrganizationName: ResourceAttributeConfig{Enabled: false}, diff --git a/receiver/githubreceiver/internal/metadata/generated_metrics.go b/receiver/githubreceiver/internal/metadata/generated_metrics.go index 440151dea48f..0f2537a3068f 100644 --- a/receiver/githubreceiver/internal/metadata/generated_metrics.go +++ b/receiver/githubreceiver/internal/metadata/generated_metrics.go @@ -13,126 +13,126 @@ import ( conventions "go.opentelemetry.io/collector/semconv/v1.27.0" ) -// AttributeChangeState specifies the a value change.state attribute. -type AttributeChangeState int +// AttributeVcsChangeState specifies the a value vcs.change.state attribute. +type AttributeVcsChangeState int const ( - _ AttributeChangeState = iota - AttributeChangeStateOpen - AttributeChangeStateMerged + _ AttributeVcsChangeState = iota + AttributeVcsChangeStateOpen + AttributeVcsChangeStateMerged ) -// String returns the string representation of the AttributeChangeState. -func (av AttributeChangeState) String() string { +// String returns the string representation of the AttributeVcsChangeState. +func (av AttributeVcsChangeState) String() string { switch av { - case AttributeChangeStateOpen: + case AttributeVcsChangeStateOpen: return "open" - case AttributeChangeStateMerged: + case AttributeVcsChangeStateMerged: return "merged" } return "" } -// MapAttributeChangeState is a helper map of string to AttributeChangeState attribute value. -var MapAttributeChangeState = map[string]AttributeChangeState{ - "open": AttributeChangeStateOpen, - "merged": AttributeChangeStateMerged, +// MapAttributeVcsChangeState is a helper map of string to AttributeVcsChangeState attribute value. +var MapAttributeVcsChangeState = map[string]AttributeVcsChangeState{ + "open": AttributeVcsChangeStateOpen, + "merged": AttributeVcsChangeStateMerged, } -// AttributeRefType specifies the a value ref.type attribute. -type AttributeRefType int +// AttributeVcsLineChangeType specifies the a value vcs.line_change.type attribute. +type AttributeVcsLineChangeType int const ( - _ AttributeRefType = iota - AttributeRefTypeBranch - AttributeRefTypeTag + _ AttributeVcsLineChangeType = iota + AttributeVcsLineChangeTypeAdded + AttributeVcsLineChangeTypeRemoved ) -// String returns the string representation of the AttributeRefType. -func (av AttributeRefType) String() string { +// String returns the string representation of the AttributeVcsLineChangeType. +func (av AttributeVcsLineChangeType) String() string { switch av { - case AttributeRefTypeBranch: - return "branch" - case AttributeRefTypeTag: - return "tag" + case AttributeVcsLineChangeTypeAdded: + return "added" + case AttributeVcsLineChangeTypeRemoved: + return "removed" } return "" } -// MapAttributeRefType is a helper map of string to AttributeRefType attribute value. -var MapAttributeRefType = map[string]AttributeRefType{ - "branch": AttributeRefTypeBranch, - "tag": AttributeRefTypeTag, +// MapAttributeVcsLineChangeType is a helper map of string to AttributeVcsLineChangeType attribute value. +var MapAttributeVcsLineChangeType = map[string]AttributeVcsLineChangeType{ + "added": AttributeVcsLineChangeTypeAdded, + "removed": AttributeVcsLineChangeTypeRemoved, } -type metricVcsRepositoryChangeCount struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. -} +// AttributeVcsRefHeadType specifies the a value vcs.ref.head.type attribute. +type AttributeVcsRefHeadType int -// init fills vcs.repository.change.count metric with initial data. -func (m *metricVcsRepositoryChangeCount) init() { - m.data.SetName("vcs.repository.change.count") - m.data.SetDescription("The number of changes (pull requests) in a repository, categorized by their state (either open or merged).") - m.data.SetUnit("{change}") - m.data.SetEmptyGauge() - m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) -} +const ( + _ AttributeVcsRefHeadType = iota + AttributeVcsRefHeadTypeBranch + AttributeVcsRefHeadTypeTag +) -func (m *metricVcsRepositoryChangeCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, changeStateAttributeValue string, repositoryNameAttributeValue string) { - if !m.config.Enabled { - return +// String returns the string representation of the AttributeVcsRefHeadType. +func (av AttributeVcsRefHeadType) String() string { + switch av { + case AttributeVcsRefHeadTypeBranch: + return "branch" + case AttributeVcsRefHeadTypeTag: + return "tag" } - dp := m.data.Gauge().DataPoints().AppendEmpty() - dp.SetStartTimestamp(start) - dp.SetTimestamp(ts) - dp.SetIntValue(val) - dp.Attributes().PutStr("change.state", changeStateAttributeValue) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) + return "" } -// updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryChangeCount) updateCapacity() { - if m.data.Gauge().DataPoints().Len() > m.capacity { - m.capacity = m.data.Gauge().DataPoints().Len() - } +// MapAttributeVcsRefHeadType is a helper map of string to AttributeVcsRefHeadType attribute value. +var MapAttributeVcsRefHeadType = map[string]AttributeVcsRefHeadType{ + "branch": AttributeVcsRefHeadTypeBranch, + "tag": AttributeVcsRefHeadTypeTag, } -// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryChangeCount) emit(metrics pmetric.MetricSlice) { - if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { - m.updateCapacity() - m.data.MoveTo(metrics.AppendEmpty()) - m.init() +// AttributeVcsRevisionDeltaDirection specifies the a value vcs.revision_delta.direction attribute. +type AttributeVcsRevisionDeltaDirection int + +const ( + _ AttributeVcsRevisionDeltaDirection = iota + AttributeVcsRevisionDeltaDirectionAhead + AttributeVcsRevisionDeltaDirectionBehind +) + +// String returns the string representation of the AttributeVcsRevisionDeltaDirection. +func (av AttributeVcsRevisionDeltaDirection) String() string { + switch av { + case AttributeVcsRevisionDeltaDirectionAhead: + return "ahead" + case AttributeVcsRevisionDeltaDirectionBehind: + return "behind" } + return "" } -func newMetricVcsRepositoryChangeCount(cfg MetricConfig) metricVcsRepositoryChangeCount { - m := metricVcsRepositoryChangeCount{config: cfg} - if cfg.Enabled { - m.data = pmetric.NewMetric() - m.init() - } - return m +// MapAttributeVcsRevisionDeltaDirection is a helper map of string to AttributeVcsRevisionDeltaDirection attribute value. +var MapAttributeVcsRevisionDeltaDirection = map[string]AttributeVcsRevisionDeltaDirection{ + "ahead": AttributeVcsRevisionDeltaDirectionAhead, + "behind": AttributeVcsRevisionDeltaDirectionBehind, } -type metricVcsRepositoryChangeTimeOpen struct { +type metricVcsChangeCount struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.change.time_open metric with initial data. -func (m *metricVcsRepositoryChangeTimeOpen) init() { - m.data.SetName("vcs.repository.change.time_open") - m.data.SetDescription("The amount of time a change (pull request) has been open.") - m.data.SetUnit("s") +// init fills vcs.change.count metric with initial data. +func (m *metricVcsChangeCount) init() { + m.data.SetName("vcs.change.count") + m.data.SetDescription("The number of changes (pull requests) in a repository, categorized by their state (either open or merged).") + m.data.SetUnit("{change}") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryChangeTimeOpen) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string) { +func (m *metricVcsChangeCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsChangeStateAttributeValue string, vcsRepositoryNameAttributeValue string) { if !m.config.Enabled { return } @@ -140,19 +140,20 @@ func (m *metricVcsRepositoryChangeTimeOpen) recordDataPoint(start pcommon.Timest dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.change.state", vcsChangeStateAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryChangeTimeOpen) updateCapacity() { +func (m *metricVcsChangeCount) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryChangeTimeOpen) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsChangeCount) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -160,8 +161,8 @@ func (m *metricVcsRepositoryChangeTimeOpen) emit(metrics pmetric.MetricSlice) { } } -func newMetricVcsRepositoryChangeTimeOpen(cfg MetricConfig) metricVcsRepositoryChangeTimeOpen { - m := metricVcsRepositoryChangeTimeOpen{config: cfg} +func newMetricVcsChangeCount(cfg MetricConfig) metricVcsChangeCount { + m := metricVcsChangeCount{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -169,22 +170,22 @@ func newMetricVcsRepositoryChangeTimeOpen(cfg MetricConfig) metricVcsRepositoryC return m } -type metricVcsRepositoryChangeTimeToApproval struct { +type metricVcsChangeDuration struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.change.time_to_approval metric with initial data. -func (m *metricVcsRepositoryChangeTimeToApproval) init() { - m.data.SetName("vcs.repository.change.time_to_approval") - m.data.SetDescription("The amount of time it took a change (pull request) to go from open to approved.") +// init fills vcs.change.duration metric with initial data. +func (m *metricVcsChangeDuration) init() { + m.data.SetName("vcs.change.duration") + m.data.SetDescription("The time duration a change (pull request/merge request/changelist) has been in an open state.") m.data.SetUnit("s") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryChangeTimeToApproval) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string) { +func (m *metricVcsChangeDuration) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsChangeStateAttributeValue string) { if !m.config.Enabled { return } @@ -192,19 +193,21 @@ func (m *metricVcsRepositoryChangeTimeToApproval) recordDataPoint(start pcommon. dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.name", vcsRefHeadNameAttributeValue) + dp.Attributes().PutStr("vcs.change.state", vcsChangeStateAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryChangeTimeToApproval) updateCapacity() { +func (m *metricVcsChangeDuration) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryChangeTimeToApproval) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsChangeDuration) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -212,8 +215,8 @@ func (m *metricVcsRepositoryChangeTimeToApproval) emit(metrics pmetric.MetricSli } } -func newMetricVcsRepositoryChangeTimeToApproval(cfg MetricConfig) metricVcsRepositoryChangeTimeToApproval { - m := metricVcsRepositoryChangeTimeToApproval{config: cfg} +func newMetricVcsChangeDuration(cfg MetricConfig) metricVcsChangeDuration { + m := metricVcsChangeDuration{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -221,22 +224,22 @@ func newMetricVcsRepositoryChangeTimeToApproval(cfg MetricConfig) metricVcsRepos return m } -type metricVcsRepositoryChangeTimeToMerge struct { +type metricVcsChangeTimeToApproval struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.change.time_to_merge metric with initial data. -func (m *metricVcsRepositoryChangeTimeToMerge) init() { - m.data.SetName("vcs.repository.change.time_to_merge") - m.data.SetDescription("The amount of time it took a change (pull request) to go from open to merged.") +// init fills vcs.change.time_to_approval metric with initial data. +func (m *metricVcsChangeTimeToApproval) init() { + m.data.SetName("vcs.change.time_to_approval") + m.data.SetDescription("The amount of time it took a change (pull request) to go from open to approved.") m.data.SetUnit("s") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryChangeTimeToMerge) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string) { +func (m *metricVcsChangeTimeToApproval) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string) { if !m.config.Enabled { return } @@ -244,19 +247,20 @@ func (m *metricVcsRepositoryChangeTimeToMerge) recordDataPoint(start pcommon.Tim dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.name", vcsRefHeadNameAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryChangeTimeToMerge) updateCapacity() { +func (m *metricVcsChangeTimeToApproval) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryChangeTimeToMerge) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsChangeTimeToApproval) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -264,8 +268,8 @@ func (m *metricVcsRepositoryChangeTimeToMerge) emit(metrics pmetric.MetricSlice) } } -func newMetricVcsRepositoryChangeTimeToMerge(cfg MetricConfig) metricVcsRepositoryChangeTimeToMerge { - m := metricVcsRepositoryChangeTimeToMerge{config: cfg} +func newMetricVcsChangeTimeToApproval(cfg MetricConfig) metricVcsChangeTimeToApproval { + m := metricVcsChangeTimeToApproval{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -273,22 +277,22 @@ func newMetricVcsRepositoryChangeTimeToMerge(cfg MetricConfig) metricVcsReposito return m } -type metricVcsRepositoryContributorCount struct { +type metricVcsChangeTimeToMerge struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.contributor.count metric with initial data. -func (m *metricVcsRepositoryContributorCount) init() { - m.data.SetName("vcs.repository.contributor.count") - m.data.SetDescription("The number of unique contributors to a repository.") - m.data.SetUnit("{contributor}") +// init fills vcs.change.time_to_merge metric with initial data. +func (m *metricVcsChangeTimeToMerge) init() { + m.data.SetName("vcs.change.time_to_merge") + m.data.SetDescription("The amount of time it took a change (pull request) to go from open to merged.") + m.data.SetUnit("s") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryContributorCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string) { +func (m *metricVcsChangeTimeToMerge) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string) { if !m.config.Enabled { return } @@ -296,18 +300,20 @@ func (m *metricVcsRepositoryContributorCount) recordDataPoint(start pcommon.Time dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.name", vcsRefHeadNameAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryContributorCount) updateCapacity() { +func (m *metricVcsChangeTimeToMerge) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryContributorCount) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsChangeTimeToMerge) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -315,8 +321,8 @@ func (m *metricVcsRepositoryContributorCount) emit(metrics pmetric.MetricSlice) } } -func newMetricVcsRepositoryContributorCount(cfg MetricConfig) metricVcsRepositoryContributorCount { - m := metricVcsRepositoryContributorCount{config: cfg} +func newMetricVcsChangeTimeToMerge(cfg MetricConfig) metricVcsChangeTimeToMerge { + m := metricVcsChangeTimeToMerge{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -324,21 +330,22 @@ func newMetricVcsRepositoryContributorCount(cfg MetricConfig) metricVcsRepositor return m } -type metricVcsRepositoryCount struct { +type metricVcsContributorCount struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.count metric with initial data. -func (m *metricVcsRepositoryCount) init() { - m.data.SetName("vcs.repository.count") - m.data.SetDescription("The number of repositories in an organization.") - m.data.SetUnit("{repository}") +// init fills vcs.contributor.count metric with initial data. +func (m *metricVcsContributorCount) init() { + m.data.SetName("vcs.contributor.count") + m.data.SetDescription("The number of unique contributors to a repository.") + m.data.SetUnit("{contributor}") m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { +func (m *metricVcsContributorCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string) { if !m.config.Enabled { return } @@ -346,17 +353,19 @@ func (m *metricVcsRepositoryCount) recordDataPoint(start pcommon.Timestamp, ts p dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryCount) updateCapacity() { +func (m *metricVcsContributorCount) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryCount) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsContributorCount) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -364,8 +373,8 @@ func (m *metricVcsRepositoryCount) emit(metrics pmetric.MetricSlice) { } } -func newMetricVcsRepositoryCount(cfg MetricConfig) metricVcsRepositoryCount { - m := metricVcsRepositoryCount{config: cfg} +func newMetricVcsContributorCount(cfg MetricConfig) metricVcsContributorCount { + m := metricVcsContributorCount{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -373,74 +382,22 @@ func newMetricVcsRepositoryCount(cfg MetricConfig) metricVcsRepositoryCount { return m } -type metricVcsRepositoryRefCount struct { +type metricVcsRefCount struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.ref.count metric with initial data. -func (m *metricVcsRepositoryRefCount) init() { - m.data.SetName("vcs.repository.ref.count") +// init fills vcs.ref.count metric with initial data. +func (m *metricVcsRefCount) init() { + m.data.SetName("vcs.ref.count") m.data.SetDescription("The number of refs of type branch in a repository.") m.data.SetUnit("{ref}") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryRefCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refTypeAttributeValue string) { - if !m.config.Enabled { - return - } - dp := m.data.Gauge().DataPoints().AppendEmpty() - dp.SetStartTimestamp(start) - dp.SetTimestamp(ts) - dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.type", refTypeAttributeValue) -} - -// updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryRefCount) updateCapacity() { - if m.data.Gauge().DataPoints().Len() > m.capacity { - m.capacity = m.data.Gauge().DataPoints().Len() - } -} - -// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryRefCount) emit(metrics pmetric.MetricSlice) { - if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { - m.updateCapacity() - m.data.MoveTo(metrics.AppendEmpty()) - m.init() - } -} - -func newMetricVcsRepositoryRefCount(cfg MetricConfig) metricVcsRepositoryRefCount { - m := metricVcsRepositoryRefCount{config: cfg} - if cfg.Enabled { - m.data = pmetric.NewMetric() - m.init() - } - return m -} - -type metricVcsRepositoryRefLinesAdded struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. -} - -// init fills vcs.repository.ref.lines_added metric with initial data. -func (m *metricVcsRepositoryRefLinesAdded) init() { - m.data.SetName("vcs.repository.ref.lines_added") - m.data.SetDescription("The number of lines added in a ref (branch) relative to the default branch (trunk).") - m.data.SetUnit("{line}") - m.data.SetEmptyGauge() - m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) -} - -func (m *metricVcsRepositoryRefLinesAdded) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue string) { +func (m *metricVcsRefCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadTypeAttributeValue string) { if !m.config.Enabled { return } @@ -448,20 +405,20 @@ func (m *metricVcsRepositoryRefLinesAdded) recordDataPoint(start pcommon.Timesta dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) - dp.Attributes().PutStr("ref.type", refTypeAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.type", vcsRefHeadTypeAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryRefLinesAdded) updateCapacity() { +func (m *metricVcsRefCount) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryRefLinesAdded) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsRefCount) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -469,8 +426,8 @@ func (m *metricVcsRepositoryRefLinesAdded) emit(metrics pmetric.MetricSlice) { } } -func newMetricVcsRepositoryRefLinesAdded(cfg MetricConfig) metricVcsRepositoryRefLinesAdded { - m := metricVcsRepositoryRefLinesAdded{config: cfg} +func newMetricVcsRefCount(cfg MetricConfig) metricVcsRefCount { + m := metricVcsRefCount{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -478,22 +435,22 @@ func newMetricVcsRepositoryRefLinesAdded(cfg MetricConfig) metricVcsRepositoryRe return m } -type metricVcsRepositoryRefLinesDeleted struct { +type metricVcsRefLinesDelta struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.ref.lines_deleted metric with initial data. -func (m *metricVcsRepositoryRefLinesDeleted) init() { - m.data.SetName("vcs.repository.ref.lines_deleted") - m.data.SetDescription("The number of lines deleted in a ref (branch) relative to the default branch (trunk).") +// init fills vcs.ref.lines_delta metric with initial data. +func (m *metricVcsRefLinesDelta) init() { + m.data.SetName("vcs.ref.lines_delta") + m.data.SetDescription("The number of lines added/removed in a ref (branch) relative to the default branch (trunk).") m.data.SetUnit("{line}") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryRefLinesDeleted) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue string) { +func (m *metricVcsRefLinesDelta) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsRefHeadTypeAttributeValue string, vcsLineChangeTypeAttributeValue string) { if !m.config.Enabled { return } @@ -501,20 +458,22 @@ func (m *metricVcsRepositoryRefLinesDeleted) recordDataPoint(start pcommon.Times dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) - dp.Attributes().PutStr("ref.type", refTypeAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.name", vcsRefHeadNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.type", vcsRefHeadTypeAttributeValue) + dp.Attributes().PutStr("vcs.line_change.type", vcsLineChangeTypeAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryRefLinesDeleted) updateCapacity() { +func (m *metricVcsRefLinesDelta) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryRefLinesDeleted) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsRefLinesDelta) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -522,8 +481,8 @@ func (m *metricVcsRepositoryRefLinesDeleted) emit(metrics pmetric.MetricSlice) { } } -func newMetricVcsRepositoryRefLinesDeleted(cfg MetricConfig) metricVcsRepositoryRefLinesDeleted { - m := metricVcsRepositoryRefLinesDeleted{config: cfg} +func newMetricVcsRefLinesDelta(cfg MetricConfig) metricVcsRefLinesDelta { + m := metricVcsRefLinesDelta{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -531,22 +490,22 @@ func newMetricVcsRepositoryRefLinesDeleted(cfg MetricConfig) metricVcsRepository return m } -type metricVcsRepositoryRefRevisionsAhead struct { +type metricVcsRefRevisionsDelta struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.ref.revisions_ahead metric with initial data. -func (m *metricVcsRepositoryRefRevisionsAhead) init() { - m.data.SetName("vcs.repository.ref.revisions_ahead") - m.data.SetDescription("The number of revisions (commits) a ref (branch) is ahead of the default branch (trunk).") +// init fills vcs.ref.revisions_delta metric with initial data. +func (m *metricVcsRefRevisionsDelta) init() { + m.data.SetName("vcs.ref.revisions_delta") + m.data.SetDescription("The number of revisions (commits) a ref (branch) is ahead/behind the branch from trunk (default).") m.data.SetUnit("{revision}") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryRefRevisionsAhead) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue string) { +func (m *metricVcsRefRevisionsDelta) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsRefHeadTypeAttributeValue string, vcsRevisionDeltaDirectionAttributeValue string) { if !m.config.Enabled { return } @@ -554,20 +513,22 @@ func (m *metricVcsRepositoryRefRevisionsAhead) recordDataPoint(start pcommon.Tim dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) - dp.Attributes().PutStr("ref.type", refTypeAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.name", vcsRefHeadNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.type", vcsRefHeadTypeAttributeValue) + dp.Attributes().PutStr("vcs.revision_delta.direction", vcsRevisionDeltaDirectionAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryRefRevisionsAhead) updateCapacity() { +func (m *metricVcsRefRevisionsDelta) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryRefRevisionsAhead) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsRefRevisionsDelta) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -575,8 +536,8 @@ func (m *metricVcsRepositoryRefRevisionsAhead) emit(metrics pmetric.MetricSlice) } } -func newMetricVcsRepositoryRefRevisionsAhead(cfg MetricConfig) metricVcsRepositoryRefRevisionsAhead { - m := metricVcsRepositoryRefRevisionsAhead{config: cfg} +func newMetricVcsRefRevisionsDelta(cfg MetricConfig) metricVcsRefRevisionsDelta { + m := metricVcsRefRevisionsDelta{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -584,22 +545,22 @@ func newMetricVcsRepositoryRefRevisionsAhead(cfg MetricConfig) metricVcsReposito return m } -type metricVcsRepositoryRefRevisionsBehind struct { +type metricVcsRefTime struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.ref.revisions_behind metric with initial data. -func (m *metricVcsRepositoryRefRevisionsBehind) init() { - m.data.SetName("vcs.repository.ref.revisions_behind") - m.data.SetDescription("The number of revisions (commits) a ref (branch) is behind the default branch (trunk).") - m.data.SetUnit("{revision}") +// init fills vcs.ref.time metric with initial data. +func (m *metricVcsRefTime) init() { + m.data.SetName("vcs.ref.time") + m.data.SetDescription("Time a ref (branch) created from the default branch (trunk) has existed. The `vcs.ref.head.type` attribute will always be `branch`.") + m.data.SetUnit("s") m.data.SetEmptyGauge() m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryRefRevisionsBehind) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue string) { +func (m *metricVcsRefTime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsRefHeadTypeAttributeValue string) { if !m.config.Enabled { return } @@ -607,20 +568,21 @@ func (m *metricVcsRepositoryRefRevisionsBehind) recordDataPoint(start pcommon.Ti dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) - dp.Attributes().PutStr("ref.type", refTypeAttributeValue) + dp.Attributes().PutStr("vcs.repository.url.full", vcsRepositoryURLFullAttributeValue) + dp.Attributes().PutStr("vcs.repository.name", vcsRepositoryNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.name", vcsRefHeadNameAttributeValue) + dp.Attributes().PutStr("vcs.ref.head.type", vcsRefHeadTypeAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryRefRevisionsBehind) updateCapacity() { +func (m *metricVcsRefTime) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryRefRevisionsBehind) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsRefTime) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -628,8 +590,8 @@ func (m *metricVcsRepositoryRefRevisionsBehind) emit(metrics pmetric.MetricSlice } } -func newMetricVcsRepositoryRefRevisionsBehind(cfg MetricConfig) metricVcsRepositoryRefRevisionsBehind { - m := metricVcsRepositoryRefRevisionsBehind{config: cfg} +func newMetricVcsRefTime(cfg MetricConfig) metricVcsRefTime { + m := metricVcsRefTime{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -637,22 +599,21 @@ func newMetricVcsRepositoryRefRevisionsBehind(cfg MetricConfig) metricVcsReposit return m } -type metricVcsRepositoryRefTime struct { +type metricVcsRepositoryCount struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. capacity int // max observed number of data points added to the metric. } -// init fills vcs.repository.ref.time metric with initial data. -func (m *metricVcsRepositoryRefTime) init() { - m.data.SetName("vcs.repository.ref.time") - m.data.SetDescription("Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`.") - m.data.SetUnit("s") +// init fills vcs.repository.count metric with initial data. +func (m *metricVcsRepositoryCount) init() { + m.data.SetName("vcs.repository.count") + m.data.SetDescription("The number of repositories in an organization.") + m.data.SetUnit("{repository}") m.data.SetEmptyGauge() - m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricVcsRepositoryRefTime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue string) { +func (m *metricVcsRepositoryCount) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { if !m.config.Enabled { return } @@ -660,20 +621,17 @@ func (m *metricVcsRepositoryRefTime) recordDataPoint(start pcommon.Timestamp, ts dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) - dp.Attributes().PutStr("repository.name", repositoryNameAttributeValue) - dp.Attributes().PutStr("ref.name", refNameAttributeValue) - dp.Attributes().PutStr("ref.type", refTypeAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. -func (m *metricVcsRepositoryRefTime) updateCapacity() { +func (m *metricVcsRepositoryCount) updateCapacity() { if m.data.Gauge().DataPoints().Len() > m.capacity { m.capacity = m.data.Gauge().DataPoints().Len() } } // emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricVcsRepositoryRefTime) emit(metrics pmetric.MetricSlice) { +func (m *metricVcsRepositoryCount) emit(metrics pmetric.MetricSlice) { if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { m.updateCapacity() m.data.MoveTo(metrics.AppendEmpty()) @@ -681,8 +639,8 @@ func (m *metricVcsRepositoryRefTime) emit(metrics pmetric.MetricSlice) { } } -func newMetricVcsRepositoryRefTime(cfg MetricConfig) metricVcsRepositoryRefTime { - m := metricVcsRepositoryRefTime{config: cfg} +func newMetricVcsRepositoryCount(cfg MetricConfig) metricVcsRepositoryCount { + m := metricVcsRepositoryCount{config: cfg} if cfg.Enabled { m.data = pmetric.NewMetric() m.init() @@ -693,25 +651,23 @@ func newMetricVcsRepositoryRefTime(cfg MetricConfig) metricVcsRepositoryRefTime // MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations // required to produce metric representation defined in metadata and user config. type MetricsBuilder struct { - config MetricsBuilderConfig // config of the metrics builder. - startTime pcommon.Timestamp // start time that will be applied to all recorded data points. - metricsCapacity int // maximum observed number of metrics per resource. - metricsBuffer pmetric.Metrics // accumulates metrics data before emitting. - buildInfo component.BuildInfo // contains version information. - resourceAttributeIncludeFilter map[string]filter.Filter - resourceAttributeExcludeFilter map[string]filter.Filter - metricVcsRepositoryChangeCount metricVcsRepositoryChangeCount - metricVcsRepositoryChangeTimeOpen metricVcsRepositoryChangeTimeOpen - metricVcsRepositoryChangeTimeToApproval metricVcsRepositoryChangeTimeToApproval - metricVcsRepositoryChangeTimeToMerge metricVcsRepositoryChangeTimeToMerge - metricVcsRepositoryContributorCount metricVcsRepositoryContributorCount - metricVcsRepositoryCount metricVcsRepositoryCount - metricVcsRepositoryRefCount metricVcsRepositoryRefCount - metricVcsRepositoryRefLinesAdded metricVcsRepositoryRefLinesAdded - metricVcsRepositoryRefLinesDeleted metricVcsRepositoryRefLinesDeleted - metricVcsRepositoryRefRevisionsAhead metricVcsRepositoryRefRevisionsAhead - metricVcsRepositoryRefRevisionsBehind metricVcsRepositoryRefRevisionsBehind - metricVcsRepositoryRefTime metricVcsRepositoryRefTime + config MetricsBuilderConfig // config of the metrics builder. + startTime pcommon.Timestamp // start time that will be applied to all recorded data points. + metricsCapacity int // maximum observed number of metrics per resource. + metricsBuffer pmetric.Metrics // accumulates metrics data before emitting. + buildInfo component.BuildInfo // contains version information. + resourceAttributeIncludeFilter map[string]filter.Filter + resourceAttributeExcludeFilter map[string]filter.Filter + metricVcsChangeCount metricVcsChangeCount + metricVcsChangeDuration metricVcsChangeDuration + metricVcsChangeTimeToApproval metricVcsChangeTimeToApproval + metricVcsChangeTimeToMerge metricVcsChangeTimeToMerge + metricVcsContributorCount metricVcsContributorCount + metricVcsRefCount metricVcsRefCount + metricVcsRefLinesDelta metricVcsRefLinesDelta + metricVcsRefRevisionsDelta metricVcsRefRevisionsDelta + metricVcsRefTime metricVcsRefTime + metricVcsRepositoryCount metricVcsRepositoryCount } // MetricBuilderOption applies changes to default metrics builder. @@ -734,24 +690,22 @@ func WithStartTime(startTime pcommon.Timestamp) MetricBuilderOption { func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, options ...MetricBuilderOption) *MetricsBuilder { mb := &MetricsBuilder{ - config: mbc, - startTime: pcommon.NewTimestampFromTime(time.Now()), - metricsBuffer: pmetric.NewMetrics(), - buildInfo: settings.BuildInfo, - metricVcsRepositoryChangeCount: newMetricVcsRepositoryChangeCount(mbc.Metrics.VcsRepositoryChangeCount), - metricVcsRepositoryChangeTimeOpen: newMetricVcsRepositoryChangeTimeOpen(mbc.Metrics.VcsRepositoryChangeTimeOpen), - metricVcsRepositoryChangeTimeToApproval: newMetricVcsRepositoryChangeTimeToApproval(mbc.Metrics.VcsRepositoryChangeTimeToApproval), - metricVcsRepositoryChangeTimeToMerge: newMetricVcsRepositoryChangeTimeToMerge(mbc.Metrics.VcsRepositoryChangeTimeToMerge), - metricVcsRepositoryContributorCount: newMetricVcsRepositoryContributorCount(mbc.Metrics.VcsRepositoryContributorCount), - metricVcsRepositoryCount: newMetricVcsRepositoryCount(mbc.Metrics.VcsRepositoryCount), - metricVcsRepositoryRefCount: newMetricVcsRepositoryRefCount(mbc.Metrics.VcsRepositoryRefCount), - metricVcsRepositoryRefLinesAdded: newMetricVcsRepositoryRefLinesAdded(mbc.Metrics.VcsRepositoryRefLinesAdded), - metricVcsRepositoryRefLinesDeleted: newMetricVcsRepositoryRefLinesDeleted(mbc.Metrics.VcsRepositoryRefLinesDeleted), - metricVcsRepositoryRefRevisionsAhead: newMetricVcsRepositoryRefRevisionsAhead(mbc.Metrics.VcsRepositoryRefRevisionsAhead), - metricVcsRepositoryRefRevisionsBehind: newMetricVcsRepositoryRefRevisionsBehind(mbc.Metrics.VcsRepositoryRefRevisionsBehind), - metricVcsRepositoryRefTime: newMetricVcsRepositoryRefTime(mbc.Metrics.VcsRepositoryRefTime), - resourceAttributeIncludeFilter: make(map[string]filter.Filter), - resourceAttributeExcludeFilter: make(map[string]filter.Filter), + config: mbc, + startTime: pcommon.NewTimestampFromTime(time.Now()), + metricsBuffer: pmetric.NewMetrics(), + buildInfo: settings.BuildInfo, + metricVcsChangeCount: newMetricVcsChangeCount(mbc.Metrics.VcsChangeCount), + metricVcsChangeDuration: newMetricVcsChangeDuration(mbc.Metrics.VcsChangeDuration), + metricVcsChangeTimeToApproval: newMetricVcsChangeTimeToApproval(mbc.Metrics.VcsChangeTimeToApproval), + metricVcsChangeTimeToMerge: newMetricVcsChangeTimeToMerge(mbc.Metrics.VcsChangeTimeToMerge), + metricVcsContributorCount: newMetricVcsContributorCount(mbc.Metrics.VcsContributorCount), + metricVcsRefCount: newMetricVcsRefCount(mbc.Metrics.VcsRefCount), + metricVcsRefLinesDelta: newMetricVcsRefLinesDelta(mbc.Metrics.VcsRefLinesDelta), + metricVcsRefRevisionsDelta: newMetricVcsRefRevisionsDelta(mbc.Metrics.VcsRefRevisionsDelta), + metricVcsRefTime: newMetricVcsRefTime(mbc.Metrics.VcsRefTime), + metricVcsRepositoryCount: newMetricVcsRepositoryCount(mbc.Metrics.VcsRepositoryCount), + resourceAttributeIncludeFilter: make(map[string]filter.Filter), + resourceAttributeExcludeFilter: make(map[string]filter.Filter), } if mbc.ResourceAttributes.OrganizationName.MetricsInclude != nil { mb.resourceAttributeIncludeFilter["organization.name"] = filter.CreateFilter(mbc.ResourceAttributes.OrganizationName.MetricsInclude) @@ -835,18 +789,16 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { ils.Scope().SetName("github.com/open-telemetry/opentelemetry-collector-contrib/receiver/githubreceiver") ils.Scope().SetVersion(mb.buildInfo.Version) ils.Metrics().EnsureCapacity(mb.metricsCapacity) - mb.metricVcsRepositoryChangeCount.emit(ils.Metrics()) - mb.metricVcsRepositoryChangeTimeOpen.emit(ils.Metrics()) - mb.metricVcsRepositoryChangeTimeToApproval.emit(ils.Metrics()) - mb.metricVcsRepositoryChangeTimeToMerge.emit(ils.Metrics()) - mb.metricVcsRepositoryContributorCount.emit(ils.Metrics()) + mb.metricVcsChangeCount.emit(ils.Metrics()) + mb.metricVcsChangeDuration.emit(ils.Metrics()) + mb.metricVcsChangeTimeToApproval.emit(ils.Metrics()) + mb.metricVcsChangeTimeToMerge.emit(ils.Metrics()) + mb.metricVcsContributorCount.emit(ils.Metrics()) + mb.metricVcsRefCount.emit(ils.Metrics()) + mb.metricVcsRefLinesDelta.emit(ils.Metrics()) + mb.metricVcsRefRevisionsDelta.emit(ils.Metrics()) + mb.metricVcsRefTime.emit(ils.Metrics()) mb.metricVcsRepositoryCount.emit(ils.Metrics()) - mb.metricVcsRepositoryRefCount.emit(ils.Metrics()) - mb.metricVcsRepositoryRefLinesAdded.emit(ils.Metrics()) - mb.metricVcsRepositoryRefLinesDeleted.emit(ils.Metrics()) - mb.metricVcsRepositoryRefRevisionsAhead.emit(ils.Metrics()) - mb.metricVcsRepositoryRefRevisionsBehind.emit(ils.Metrics()) - mb.metricVcsRepositoryRefTime.emit(ils.Metrics()) for _, op := range options { op.apply(rm) @@ -878,64 +830,54 @@ func (mb *MetricsBuilder) Emit(options ...ResourceMetricsOption) pmetric.Metrics return metrics } -// RecordVcsRepositoryChangeCountDataPoint adds a data point to vcs.repository.change.count metric. -func (mb *MetricsBuilder) RecordVcsRepositoryChangeCountDataPoint(ts pcommon.Timestamp, val int64, changeStateAttributeValue AttributeChangeState, repositoryNameAttributeValue string) { - mb.metricVcsRepositoryChangeCount.recordDataPoint(mb.startTime, ts, val, changeStateAttributeValue.String(), repositoryNameAttributeValue) -} - -// RecordVcsRepositoryChangeTimeOpenDataPoint adds a data point to vcs.repository.change.time_open metric. -func (mb *MetricsBuilder) RecordVcsRepositoryChangeTimeOpenDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string) { - mb.metricVcsRepositoryChangeTimeOpen.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue) +// RecordVcsChangeCountDataPoint adds a data point to vcs.change.count metric. +func (mb *MetricsBuilder) RecordVcsChangeCountDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsChangeStateAttributeValue AttributeVcsChangeState, vcsRepositoryNameAttributeValue string) { + mb.metricVcsChangeCount.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsChangeStateAttributeValue.String(), vcsRepositoryNameAttributeValue) } -// RecordVcsRepositoryChangeTimeToApprovalDataPoint adds a data point to vcs.repository.change.time_to_approval metric. -func (mb *MetricsBuilder) RecordVcsRepositoryChangeTimeToApprovalDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string) { - mb.metricVcsRepositoryChangeTimeToApproval.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue) +// RecordVcsChangeDurationDataPoint adds a data point to vcs.change.duration metric. +func (mb *MetricsBuilder) RecordVcsChangeDurationDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsChangeStateAttributeValue AttributeVcsChangeState) { + mb.metricVcsChangeDuration.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue, vcsRefHeadNameAttributeValue, vcsChangeStateAttributeValue.String()) } -// RecordVcsRepositoryChangeTimeToMergeDataPoint adds a data point to vcs.repository.change.time_to_merge metric. -func (mb *MetricsBuilder) RecordVcsRepositoryChangeTimeToMergeDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string) { - mb.metricVcsRepositoryChangeTimeToMerge.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue) +// RecordVcsChangeTimeToApprovalDataPoint adds a data point to vcs.change.time_to_approval metric. +func (mb *MetricsBuilder) RecordVcsChangeTimeToApprovalDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string) { + mb.metricVcsChangeTimeToApproval.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue, vcsRefHeadNameAttributeValue) } -// RecordVcsRepositoryContributorCountDataPoint adds a data point to vcs.repository.contributor.count metric. -func (mb *MetricsBuilder) RecordVcsRepositoryContributorCountDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string) { - mb.metricVcsRepositoryContributorCount.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue) +// RecordVcsChangeTimeToMergeDataPoint adds a data point to vcs.change.time_to_merge metric. +func (mb *MetricsBuilder) RecordVcsChangeTimeToMergeDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string) { + mb.metricVcsChangeTimeToMerge.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue, vcsRefHeadNameAttributeValue) } -// RecordVcsRepositoryCountDataPoint adds a data point to vcs.repository.count metric. -func (mb *MetricsBuilder) RecordVcsRepositoryCountDataPoint(ts pcommon.Timestamp, val int64) { - mb.metricVcsRepositoryCount.recordDataPoint(mb.startTime, ts, val) -} - -// RecordVcsRepositoryRefCountDataPoint adds a data point to vcs.repository.ref.count metric. -func (mb *MetricsBuilder) RecordVcsRepositoryRefCountDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refTypeAttributeValue AttributeRefType) { - mb.metricVcsRepositoryRefCount.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refTypeAttributeValue.String()) +// RecordVcsContributorCountDataPoint adds a data point to vcs.contributor.count metric. +func (mb *MetricsBuilder) RecordVcsContributorCountDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string) { + mb.metricVcsContributorCount.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue) } -// RecordVcsRepositoryRefLinesAddedDataPoint adds a data point to vcs.repository.ref.lines_added metric. -func (mb *MetricsBuilder) RecordVcsRepositoryRefLinesAddedDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue AttributeRefType) { - mb.metricVcsRepositoryRefLinesAdded.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue, refTypeAttributeValue.String()) +// RecordVcsRefCountDataPoint adds a data point to vcs.ref.count metric. +func (mb *MetricsBuilder) RecordVcsRefCountDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadTypeAttributeValue AttributeVcsRefHeadType) { + mb.metricVcsRefCount.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue, vcsRefHeadTypeAttributeValue.String()) } -// RecordVcsRepositoryRefLinesDeletedDataPoint adds a data point to vcs.repository.ref.lines_deleted metric. -func (mb *MetricsBuilder) RecordVcsRepositoryRefLinesDeletedDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue AttributeRefType) { - mb.metricVcsRepositoryRefLinesDeleted.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue, refTypeAttributeValue.String()) +// RecordVcsRefLinesDeltaDataPoint adds a data point to vcs.ref.lines_delta metric. +func (mb *MetricsBuilder) RecordVcsRefLinesDeltaDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsRefHeadTypeAttributeValue AttributeVcsRefHeadType, vcsLineChangeTypeAttributeValue AttributeVcsLineChangeType) { + mb.metricVcsRefLinesDelta.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue, vcsRefHeadNameAttributeValue, vcsRefHeadTypeAttributeValue.String(), vcsLineChangeTypeAttributeValue.String()) } -// RecordVcsRepositoryRefRevisionsAheadDataPoint adds a data point to vcs.repository.ref.revisions_ahead metric. -func (mb *MetricsBuilder) RecordVcsRepositoryRefRevisionsAheadDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue AttributeRefType) { - mb.metricVcsRepositoryRefRevisionsAhead.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue, refTypeAttributeValue.String()) +// RecordVcsRefRevisionsDeltaDataPoint adds a data point to vcs.ref.revisions_delta metric. +func (mb *MetricsBuilder) RecordVcsRefRevisionsDeltaDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsRefHeadTypeAttributeValue AttributeVcsRefHeadType, vcsRevisionDeltaDirectionAttributeValue AttributeVcsRevisionDeltaDirection) { + mb.metricVcsRefRevisionsDelta.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue, vcsRefHeadNameAttributeValue, vcsRefHeadTypeAttributeValue.String(), vcsRevisionDeltaDirectionAttributeValue.String()) } -// RecordVcsRepositoryRefRevisionsBehindDataPoint adds a data point to vcs.repository.ref.revisions_behind metric. -func (mb *MetricsBuilder) RecordVcsRepositoryRefRevisionsBehindDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue AttributeRefType) { - mb.metricVcsRepositoryRefRevisionsBehind.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue, refTypeAttributeValue.String()) +// RecordVcsRefTimeDataPoint adds a data point to vcs.ref.time metric. +func (mb *MetricsBuilder) RecordVcsRefTimeDataPoint(ts pcommon.Timestamp, val int64, vcsRepositoryURLFullAttributeValue string, vcsRepositoryNameAttributeValue string, vcsRefHeadNameAttributeValue string, vcsRefHeadTypeAttributeValue AttributeVcsRefHeadType) { + mb.metricVcsRefTime.recordDataPoint(mb.startTime, ts, val, vcsRepositoryURLFullAttributeValue, vcsRepositoryNameAttributeValue, vcsRefHeadNameAttributeValue, vcsRefHeadTypeAttributeValue.String()) } -// RecordVcsRepositoryRefTimeDataPoint adds a data point to vcs.repository.ref.time metric. -func (mb *MetricsBuilder) RecordVcsRepositoryRefTimeDataPoint(ts pcommon.Timestamp, val int64, repositoryNameAttributeValue string, refNameAttributeValue string, refTypeAttributeValue AttributeRefType) { - mb.metricVcsRepositoryRefTime.recordDataPoint(mb.startTime, ts, val, repositoryNameAttributeValue, refNameAttributeValue, refTypeAttributeValue.String()) +// RecordVcsRepositoryCountDataPoint adds a data point to vcs.repository.count metric. +func (mb *MetricsBuilder) RecordVcsRepositoryCountDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricVcsRepositoryCount.recordDataPoint(mb.startTime, ts, val) } // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, diff --git a/receiver/githubreceiver/internal/metadata/generated_metrics_test.go b/receiver/githubreceiver/internal/metadata/generated_metrics_test.go index cde077b95c6b..34a1f3a1f72e 100644 --- a/receiver/githubreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/githubreceiver/internal/metadata/generated_metrics_test.go @@ -70,50 +70,42 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryChangeCountDataPoint(ts, 1, AttributeChangeStateOpen, "repository.name-val") + mb.RecordVcsChangeCountDataPoint(ts, 1, "vcs.repository.url.full-val", AttributeVcsChangeStateOpen, "vcs.repository.name-val") defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryChangeTimeOpenDataPoint(ts, 1, "repository.name-val", "ref.name-val") + mb.RecordVcsChangeDurationDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val", "vcs.ref.head.name-val", AttributeVcsChangeStateOpen) defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryChangeTimeToApprovalDataPoint(ts, 1, "repository.name-val", "ref.name-val") + mb.RecordVcsChangeTimeToApprovalDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val", "vcs.ref.head.name-val") defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryChangeTimeToMergeDataPoint(ts, 1, "repository.name-val", "ref.name-val") + mb.RecordVcsChangeTimeToMergeDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val", "vcs.ref.head.name-val") allMetricsCount++ - mb.RecordVcsRepositoryContributorCountDataPoint(ts, 1, "repository.name-val") + mb.RecordVcsContributorCountDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val") defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryCountDataPoint(ts, 1) - - defaultMetricsCount++ - allMetricsCount++ - mb.RecordVcsRepositoryRefCountDataPoint(ts, 1, "repository.name-val", AttributeRefTypeBranch) - - defaultMetricsCount++ - allMetricsCount++ - mb.RecordVcsRepositoryRefLinesAddedDataPoint(ts, 1, "repository.name-val", "ref.name-val", AttributeRefTypeBranch) + mb.RecordVcsRefCountDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val", AttributeVcsRefHeadTypeBranch) defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryRefLinesDeletedDataPoint(ts, 1, "repository.name-val", "ref.name-val", AttributeRefTypeBranch) + mb.RecordVcsRefLinesDeltaDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val", "vcs.ref.head.name-val", AttributeVcsRefHeadTypeBranch, AttributeVcsLineChangeTypeAdded) defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryRefRevisionsAheadDataPoint(ts, 1, "repository.name-val", "ref.name-val", AttributeRefTypeBranch) + mb.RecordVcsRefRevisionsDeltaDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val", "vcs.ref.head.name-val", AttributeVcsRefHeadTypeBranch, AttributeVcsRevisionDeltaDirectionAhead) defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryRefRevisionsBehindDataPoint(ts, 1, "repository.name-val", "ref.name-val", AttributeRefTypeBranch) + mb.RecordVcsRefTimeDataPoint(ts, 1, "vcs.repository.url.full-val", "vcs.repository.name-val", "vcs.ref.head.name-val", AttributeVcsRefHeadTypeBranch) defaultMetricsCount++ allMetricsCount++ - mb.RecordVcsRepositoryRefTimeDataPoint(ts, 1, "repository.name-val", "ref.name-val", AttributeRefTypeBranch) + mb.RecordVcsRepositoryCountDataPoint(ts, 1) rb := mb.NewResourceBuilder() rb.SetOrganizationName("organization.name-val") @@ -140,9 +132,9 @@ func TestMetricsBuilder(t *testing.T) { validatedMetrics := make(map[string]bool) for i := 0; i < ms.Len(); i++ { switch ms.At(i).Name() { - case "vcs.repository.change.count": - assert.False(t, validatedMetrics["vcs.repository.change.count"], "Found a duplicate in the metrics slice: vcs.repository.change.count") - validatedMetrics["vcs.repository.change.count"] = true + case "vcs.change.count": + assert.False(t, validatedMetrics["vcs.change.count"], "Found a duplicate in the metrics slice: vcs.change.count") + validatedMetrics["vcs.change.count"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) assert.Equal(t, "The number of changes (pull requests) in a repository, categorized by their state (either open or merged).", ms.At(i).Description()) @@ -152,33 +144,42 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("change.state") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.change.state") assert.True(t, ok) assert.EqualValues(t, "open", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("repository.name") + attrVal, ok = dp.Attributes().Get("vcs.repository.name") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - case "vcs.repository.change.time_open": - assert.False(t, validatedMetrics["vcs.repository.change.time_open"], "Found a duplicate in the metrics slice: vcs.repository.change.time_open") - validatedMetrics["vcs.repository.change.time_open"] = true + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + case "vcs.change.duration": + assert.False(t, validatedMetrics["vcs.change.duration"], "Found a duplicate in the metrics slice: vcs.change.duration") + validatedMetrics["vcs.change.duration"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) - assert.Equal(t, "The amount of time a change (pull request) has been open.", ms.At(i).Description()) + assert.Equal(t, "The time duration a change (pull request/merge request/changelist) has been in an open state.", ms.At(i).Description()) assert.Equal(t, "s", ms.At(i).Unit()) dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.name") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") + assert.EqualValues(t, "vcs.ref.head.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.change.state") assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - case "vcs.repository.change.time_to_approval": - assert.False(t, validatedMetrics["vcs.repository.change.time_to_approval"], "Found a duplicate in the metrics slice: vcs.repository.change.time_to_approval") - validatedMetrics["vcs.repository.change.time_to_approval"] = true + assert.EqualValues(t, "open", attrVal.Str()) + case "vcs.change.time_to_approval": + assert.False(t, validatedMetrics["vcs.change.time_to_approval"], "Found a duplicate in the metrics slice: vcs.change.time_to_approval") + validatedMetrics["vcs.change.time_to_approval"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) assert.Equal(t, "The amount of time it took a change (pull request) to go from open to approved.", ms.At(i).Description()) @@ -188,15 +189,18 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.name") assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - case "vcs.repository.change.time_to_merge": - assert.False(t, validatedMetrics["vcs.repository.change.time_to_merge"], "Found a duplicate in the metrics slice: vcs.repository.change.time_to_merge") - validatedMetrics["vcs.repository.change.time_to_merge"] = true + assert.EqualValues(t, "vcs.ref.head.name-val", attrVal.Str()) + case "vcs.change.time_to_merge": + assert.False(t, validatedMetrics["vcs.change.time_to_merge"], "Found a duplicate in the metrics slice: vcs.change.time_to_merge") + validatedMetrics["vcs.change.time_to_merge"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) assert.Equal(t, "The amount of time it took a change (pull request) to go from open to merged.", ms.At(i).Description()) @@ -206,15 +210,18 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.name") assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - case "vcs.repository.contributor.count": - assert.False(t, validatedMetrics["vcs.repository.contributor.count"], "Found a duplicate in the metrics slice: vcs.repository.contributor.count") - validatedMetrics["vcs.repository.contributor.count"] = true + assert.EqualValues(t, "vcs.ref.head.name-val", attrVal.Str()) + case "vcs.contributor.count": + assert.False(t, validatedMetrics["vcs.contributor.count"], "Found a duplicate in the metrics slice: vcs.contributor.count") + validatedMetrics["vcs.contributor.count"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) assert.Equal(t, "The number of unique contributors to a repository.", ms.At(i).Description()) @@ -224,24 +231,15 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - case "vcs.repository.count": - assert.False(t, validatedMetrics["vcs.repository.count"], "Found a duplicate in the metrics slice: vcs.repository.count") - validatedMetrics["vcs.repository.count"] = true - assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) - assert.Equal(t, "The number of repositories in an organization.", ms.At(i).Description()) - assert.Equal(t, "{repository}", ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) - assert.Equal(t, start, dp.StartTimestamp()) - assert.Equal(t, ts, dp.Timestamp()) - assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) - case "vcs.repository.ref.count": - assert.False(t, validatedMetrics["vcs.repository.ref.count"], "Found a duplicate in the metrics slice: vcs.repository.ref.count") - validatedMetrics["vcs.repository.ref.count"] = true + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + case "vcs.ref.count": + assert.False(t, validatedMetrics["vcs.ref.count"], "Found a duplicate in the metrics slice: vcs.ref.count") + validatedMetrics["vcs.ref.count"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) assert.Equal(t, "The number of refs of type branch in a repository.", ms.At(i).Description()) @@ -251,117 +249,105 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.type") + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") assert.True(t, ok) - assert.EqualValues(t, "branch", attrVal.Str()) - case "vcs.repository.ref.lines_added": - assert.False(t, validatedMetrics["vcs.repository.ref.lines_added"], "Found a duplicate in the metrics slice: vcs.repository.ref.lines_added") - validatedMetrics["vcs.repository.ref.lines_added"] = true - assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) - assert.Equal(t, "The number of lines added in a ref (branch) relative to the default branch (trunk).", ms.At(i).Description()) - assert.Equal(t, "{line}", ms.At(i).Unit()) - dp := ms.At(i).Gauge().DataPoints().At(0) - assert.Equal(t, start, dp.StartTimestamp()) - assert.Equal(t, ts, dp.Timestamp()) - assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) - assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") - assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") - assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.type") + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.type") assert.True(t, ok) assert.EqualValues(t, "branch", attrVal.Str()) - case "vcs.repository.ref.lines_deleted": - assert.False(t, validatedMetrics["vcs.repository.ref.lines_deleted"], "Found a duplicate in the metrics slice: vcs.repository.ref.lines_deleted") - validatedMetrics["vcs.repository.ref.lines_deleted"] = true + case "vcs.ref.lines_delta": + assert.False(t, validatedMetrics["vcs.ref.lines_delta"], "Found a duplicate in the metrics slice: vcs.ref.lines_delta") + validatedMetrics["vcs.ref.lines_delta"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) - assert.Equal(t, "The number of lines deleted in a ref (branch) relative to the default branch (trunk).", ms.At(i).Description()) + assert.Equal(t, "The number of lines added/removed in a ref (branch) relative to the default branch (trunk).", ms.At(i).Description()) assert.Equal(t, "{line}", ms.At(i).Unit()) dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.name") assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.type") + assert.EqualValues(t, "vcs.ref.head.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.type") assert.True(t, ok) assert.EqualValues(t, "branch", attrVal.Str()) - case "vcs.repository.ref.revisions_ahead": - assert.False(t, validatedMetrics["vcs.repository.ref.revisions_ahead"], "Found a duplicate in the metrics slice: vcs.repository.ref.revisions_ahead") - validatedMetrics["vcs.repository.ref.revisions_ahead"] = true + attrVal, ok = dp.Attributes().Get("vcs.line_change.type") + assert.True(t, ok) + assert.EqualValues(t, "added", attrVal.Str()) + case "vcs.ref.revisions_delta": + assert.False(t, validatedMetrics["vcs.ref.revisions_delta"], "Found a duplicate in the metrics slice: vcs.ref.revisions_delta") + validatedMetrics["vcs.ref.revisions_delta"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) - assert.Equal(t, "The number of revisions (commits) a ref (branch) is ahead of the default branch (trunk).", ms.At(i).Description()) + assert.Equal(t, "The number of revisions (commits) a ref (branch) is ahead/behind the branch from trunk (default).", ms.At(i).Description()) assert.Equal(t, "{revision}", ms.At(i).Unit()) dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") + assert.True(t, ok) + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.name") assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.type") + assert.EqualValues(t, "vcs.ref.head.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.type") assert.True(t, ok) assert.EqualValues(t, "branch", attrVal.Str()) - case "vcs.repository.ref.revisions_behind": - assert.False(t, validatedMetrics["vcs.repository.ref.revisions_behind"], "Found a duplicate in the metrics slice: vcs.repository.ref.revisions_behind") - validatedMetrics["vcs.repository.ref.revisions_behind"] = true + attrVal, ok = dp.Attributes().Get("vcs.revision_delta.direction") + assert.True(t, ok) + assert.EqualValues(t, "ahead", attrVal.Str()) + case "vcs.ref.time": + assert.False(t, validatedMetrics["vcs.ref.time"], "Found a duplicate in the metrics slice: vcs.ref.time") + validatedMetrics["vcs.ref.time"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) - assert.Equal(t, "The number of revisions (commits) a ref (branch) is behind the default branch (trunk).", ms.At(i).Description()) - assert.Equal(t, "{revision}", ms.At(i).Unit()) + assert.Equal(t, "Time a ref (branch) created from the default branch (trunk) has existed. The `vcs.ref.head.type` attribute will always be `branch`.", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") + attrVal, ok := dp.Attributes().Get("vcs.repository.url.full") assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") + assert.EqualValues(t, "vcs.repository.url.full-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.repository.name") assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.type") + assert.EqualValues(t, "vcs.repository.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.name") + assert.True(t, ok) + assert.EqualValues(t, "vcs.ref.head.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("vcs.ref.head.type") assert.True(t, ok) assert.EqualValues(t, "branch", attrVal.Str()) - case "vcs.repository.ref.time": - assert.False(t, validatedMetrics["vcs.repository.ref.time"], "Found a duplicate in the metrics slice: vcs.repository.ref.time") - validatedMetrics["vcs.repository.ref.time"] = true + case "vcs.repository.count": + assert.False(t, validatedMetrics["vcs.repository.count"], "Found a duplicate in the metrics slice: vcs.repository.count") + validatedMetrics["vcs.repository.count"] = true assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) - assert.Equal(t, "Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`.", ms.At(i).Description()) - assert.Equal(t, "s", ms.At(i).Unit()) + assert.Equal(t, "The number of repositories in an organization.", ms.At(i).Description()) + assert.Equal(t, "{repository}", ms.At(i).Unit()) dp := ms.At(i).Gauge().DataPoints().At(0) assert.Equal(t, start, dp.StartTimestamp()) assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - attrVal, ok := dp.Attributes().Get("repository.name") - assert.True(t, ok) - assert.EqualValues(t, "repository.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.name") - assert.True(t, ok) - assert.EqualValues(t, "ref.name-val", attrVal.Str()) - attrVal, ok = dp.Attributes().Get("ref.type") - assert.True(t, ok) - assert.EqualValues(t, "branch", attrVal.Str()) } } }) diff --git a/receiver/githubreceiver/internal/metadata/testdata/config.yaml b/receiver/githubreceiver/internal/metadata/testdata/config.yaml index 8dd9bc3c44a3..87fe68de5cf0 100644 --- a/receiver/githubreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/githubreceiver/internal/metadata/testdata/config.yaml @@ -1,29 +1,25 @@ default: all_set: metrics: - vcs.repository.change.count: + vcs.change.count: enabled: true - vcs.repository.change.time_open: + vcs.change.duration: enabled: true - vcs.repository.change.time_to_approval: + vcs.change.time_to_approval: enabled: true - vcs.repository.change.time_to_merge: + vcs.change.time_to_merge: enabled: true - vcs.repository.contributor.count: + vcs.contributor.count: enabled: true - vcs.repository.count: - enabled: true - vcs.repository.ref.count: + vcs.ref.count: enabled: true - vcs.repository.ref.lines_added: + vcs.ref.lines_delta: enabled: true - vcs.repository.ref.lines_deleted: + vcs.ref.revisions_delta: enabled: true - vcs.repository.ref.revisions_ahead: + vcs.ref.time: enabled: true - vcs.repository.ref.revisions_behind: - enabled: true - vcs.repository.ref.time: + vcs.repository.count: enabled: true resource_attributes: organization.name: @@ -32,29 +28,25 @@ all_set: enabled: true none_set: metrics: - vcs.repository.change.count: + vcs.change.count: enabled: false - vcs.repository.change.time_open: + vcs.change.duration: enabled: false - vcs.repository.change.time_to_approval: + vcs.change.time_to_approval: enabled: false - vcs.repository.change.time_to_merge: + vcs.change.time_to_merge: enabled: false - vcs.repository.contributor.count: + vcs.contributor.count: enabled: false - vcs.repository.count: - enabled: false - vcs.repository.ref.count: + vcs.ref.count: enabled: false - vcs.repository.ref.lines_added: + vcs.ref.lines_delta: enabled: false - vcs.repository.ref.lines_deleted: + vcs.ref.revisions_delta: enabled: false - vcs.repository.ref.revisions_ahead: + vcs.ref.time: enabled: false - vcs.repository.ref.revisions_behind: - enabled: false - vcs.repository.ref.time: + vcs.repository.count: enabled: false resource_attributes: organization.name: diff --git a/receiver/githubreceiver/internal/scraper/githubscraper/factory.go b/receiver/githubreceiver/internal/scraper/githubscraper/factory.go index eb82e1c5c6f4..101a0ef64e31 100644 --- a/receiver/githubreceiver/internal/scraper/githubscraper/factory.go +++ b/receiver/githubreceiver/internal/scraper/githubscraper/factory.go @@ -18,6 +18,7 @@ import ( // This file implements factory for the GitHub Scraper as part of the GitHub Receiver const ( + TypeStr = "scraper" defaultHTTPTimeout = 15 * time.Second ) diff --git a/receiver/githubreceiver/internal/scraper/githubscraper/generated_graphql.go b/receiver/githubreceiver/internal/scraper/githubscraper/generated_graphql.go index db5f5271fb75..6965cbd66422 100644 --- a/receiver/githubreceiver/internal/scraper/githubscraper/generated_graphql.go +++ b/receiver/githubreceiver/internal/scraper/githubscraper/generated_graphql.go @@ -749,6 +749,8 @@ type SearchNodeRepository struct { Name string `json:"name"` // The Ref associated with the repository's default branch. DefaultBranchRef SearchNodeDefaultBranchRef `json:"defaultBranchRef"` + // The HTTP URL for this repository + Url string `json:"url"` } // GetTypename returns SearchNodeRepository.Typename, and is useful for accessing the field via an interface. @@ -765,6 +767,9 @@ func (v *SearchNodeRepository) GetDefaultBranchRef() SearchNodeDefaultBranchRef return v.DefaultBranchRef } +// GetUrl returns SearchNodeRepository.Url, and is useful for accessing the field via an interface. +func (v *SearchNodeRepository) GetUrl() string { return v.Url } + // SearchNodeUser includes the requested fields of the GraphQL type User. // The GraphQL type's documentation follows. // @@ -1463,6 +1468,7 @@ query getRepoDataBySearch ($searchQuery: String!, $repoCursor: String) { defaultBranchRef { name } + url } } pageInfo { diff --git a/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.graphql b/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.graphql index 7a66c245fbcb..7fd04b129c98 100644 --- a/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.graphql +++ b/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.graphql @@ -18,6 +18,7 @@ query getRepoDataBySearch( defaultBranchRef { name } + url } } pageInfo { @@ -130,6 +131,7 @@ query getPullRequestData( } } headRefName + # last: 1 returns the very first review reviews(states: APPROVED, last: 1) { totalCount nodes { diff --git a/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.yaml b/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.yaml index fc6183da61bf..63726bd18a53 100644 --- a/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.yaml +++ b/receiver/githubreceiver/internal/scraper/githubscraper/genqlient.yaml @@ -11,4 +11,6 @@ generated: generated_graphql.go bindings: DateTime: type: time.Time + URI: + type: string diff --git a/receiver/githubreceiver/internal/scraper/githubscraper/github_scraper.go b/receiver/githubreceiver/internal/scraper/githubscraper/github_scraper.go index 70bf35f059fa..10b9ee8434f4 100644 --- a/receiver/githubreceiver/internal/scraper/githubscraper/github_scraper.go +++ b/receiver/githubreceiver/internal/scraper/githubscraper/github_scraper.go @@ -104,6 +104,7 @@ func (ghs *githubScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { for _, repo := range repos { repo := repo name := repo.Name + url := repo.Url trunk := repo.DefaultBranchRef.Name now := now @@ -119,8 +120,8 @@ func (ghs *githubScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { // SetStartTimestamp call from having a nil pointer panic mux.Lock() - refType := metadata.AttributeRefTypeBranch - ghs.mb.RecordVcsRepositoryRefCountDataPoint(now, int64(count), name, refType) + refType := metadata.AttributeVcsRefHeadTypeBranch + ghs.mb.RecordVcsRefCountDataPoint(now, int64(count), url, name, refType) // Iterate through the refs (branches) populating the Branch focused // metrics @@ -136,8 +137,8 @@ func (ghs *githubScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { // See https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/githubreceiver/internal/scraper/githubscraper/README.md#github-limitations // for more information as to why `BehindBy` and `AheadBy` are // swapped. - ghs.mb.RecordVcsRepositoryRefRevisionsAheadDataPoint(now, int64(branch.Compare.BehindBy), branch.Repository.Name, branch.Name, refType) - ghs.mb.RecordVcsRepositoryRefRevisionsBehindDataPoint(now, int64(branch.Compare.AheadBy), branch.Repository.Name, branch.Name, refType) + ghs.mb.RecordVcsRefRevisionsDeltaDataPoint(now, int64(branch.Compare.BehindBy), url, branch.Repository.Name, branch.Name, refType, metadata.AttributeVcsRevisionDeltaDirectionAhead) + ghs.mb.RecordVcsRefRevisionsDeltaDataPoint(now, int64(branch.Compare.AheadBy), url, branch.Repository.Name, branch.Name, refType, metadata.AttributeVcsRevisionDeltaDirectionBehind) var additions int var deletions int @@ -149,9 +150,9 @@ func (ghs *githubScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { continue } - ghs.mb.RecordVcsRepositoryRefTimeDataPoint(now, age, branch.Repository.Name, branch.Name, refType) - ghs.mb.RecordVcsRepositoryRefLinesAddedDataPoint(now, int64(additions), branch.Repository.Name, branch.Name, refType) - ghs.mb.RecordVcsRepositoryRefLinesDeletedDataPoint(now, int64(deletions), branch.Repository.Name, branch.Name, refType) + ghs.mb.RecordVcsRefTimeDataPoint(now, age, url, branch.Repository.Name, branch.Name, refType) + ghs.mb.RecordVcsRefLinesDeltaDataPoint(now, int64(additions), url, branch.Repository.Name, branch.Name, refType, metadata.AttributeVcsLineChangeTypeAdded) + ghs.mb.RecordVcsRefLinesDeltaDataPoint(now, int64(deletions), url, branch.Repository.Name, branch.Name, refType, metadata.AttributeVcsLineChangeTypeRemoved) } // Get the contributor count for each of the repositories @@ -159,7 +160,7 @@ func (ghs *githubScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { if err != nil { ghs.logger.Sugar().Errorf("error getting contributor count: %v", zap.Error(err)) } - ghs.mb.RecordVcsRepositoryContributorCountDataPoint(now, int64(contribs), name) + ghs.mb.RecordVcsContributorCountDataPoint(now, int64(contribs), url, name) // Get change (pull request) data prs, err := ghs.getPullRequests(ctx, genClient, name) @@ -176,24 +177,24 @@ func (ghs *githubScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { age := getAge(pr.CreatedAt, pr.MergedAt) - ghs.mb.RecordVcsRepositoryChangeTimeToMergeDataPoint(now, age, name, pr.HeadRefName) + ghs.mb.RecordVcsChangeTimeToMergeDataPoint(now, age, url, name, pr.HeadRefName) } else { open++ age := getAge(pr.CreatedAt, now.AsTime()) - ghs.mb.RecordVcsRepositoryChangeTimeOpenDataPoint(now, age, name, pr.HeadRefName) + ghs.mb.RecordVcsChangeDurationDataPoint(now, age, url, name, pr.HeadRefName, metadata.AttributeVcsChangeStateOpen) if pr.Reviews.TotalCount > 0 { age := getAge(pr.CreatedAt, pr.Reviews.Nodes[0].CreatedAt) - ghs.mb.RecordVcsRepositoryChangeTimeToApprovalDataPoint(now, age, name, pr.HeadRefName) + ghs.mb.RecordVcsChangeTimeToApprovalDataPoint(now, age, url, name, pr.HeadRefName) } } } - ghs.mb.RecordVcsRepositoryChangeCountDataPoint(now, int64(open), metadata.AttributeChangeStateOpen, name) - ghs.mb.RecordVcsRepositoryChangeCountDataPoint(now, int64(merged), metadata.AttributeChangeStateMerged, name) + ghs.mb.RecordVcsChangeCountDataPoint(now, int64(open), url, metadata.AttributeVcsChangeStateOpen, name) + ghs.mb.RecordVcsChangeCountDataPoint(now, int64(merged), url, metadata.AttributeVcsChangeStateMerged, name) mux.Unlock() }() } diff --git a/receiver/githubreceiver/internal/scraper/githubscraper/testdata/scraper/expected_happy_path.yaml b/receiver/githubreceiver/internal/scraper/githubscraper/testdata/scraper/expected_happy_path.yaml index 45cc52ce905d..5eca16f1a549 100644 --- a/receiver/githubreceiver/internal/scraper/githubscraper/testdata/scraper/expected_happy_path.yaml +++ b/receiver/githubreceiver/internal/scraper/githubscraper/testdata/scraper/expected_happy_path.yaml @@ -15,169 +15,204 @@ resourceMetrics: dataPoints: - asInt: "1" attributes: - - key: change.state + - key: vcs.change.state value: stringValue: merged - - key: repository.name + - key: vcs.repository.name value: stringValue: repo1 + - key: vcs.repository.url.full + value: + stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - asInt: "1" attributes: - - key: change.state + - key: vcs.change.state value: stringValue: open - - key: repository.name + - key: vcs.repository.name value: stringValue: repo1 + - key: vcs.repository.url.full + value: + stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.change.count + name: vcs.change.count unit: '{change}' - - description: The amount of time a change (pull request) has been open. + - description: The time duration a change (pull request/merge request/changelist) has been in an open state. gauge: dataPoints: - asInt: "9223372036" attributes: - - key: ref.name + - key: vcs.change.state + value: + stringValue: open + - key: vcs.ref.head.name value: stringValue: "" - - key: repository.name + - key: vcs.repository.name value: stringValue: repo1 + - key: vcs.repository.url.full + value: + stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.change.time_open + name: vcs.change.duration unit: s - description: The amount of time it took a change (pull request) to go from open to merged. gauge: dataPoints: - asInt: "0" attributes: - - key: ref.name + - key: vcs.ref.head.name value: stringValue: "" - - key: repository.name + - key: vcs.repository.name value: stringValue: repo1 + - key: vcs.repository.url.full + value: + stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.change.time_to_merge + name: vcs.change.time_to_merge unit: s - - description: The number of repositories in an organization. - gauge: - dataPoints: - - asInt: "1" - startTimeUnixNano: "1000000" - timeUnixNano: "2000000" - name: vcs.repository.count - unit: '{repository}' - description: The number of refs of type branch in a repository. gauge: dataPoints: - asInt: "1" attributes: - - key: ref.type + - key: vcs.ref.head.type value: stringValue: branch - - key: repository.name + - key: vcs.repository.name value: stringValue: repo1 + - key: vcs.repository.url.full + value: + stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.ref.count + name: vcs.ref.count unit: '{ref}' - - description: The number of lines added in a ref (branch) relative to the default branch (trunk). + - description: The number of lines added/removed in a ref (branch) relative to the default branch (trunk). gauge: dataPoints: - asInt: "10" attributes: - - key: ref.name + - key: vcs.line_change.type + value: + stringValue: added + - key: vcs.ref.head.name value: stringValue: main - - key: ref.type + - key: vcs.ref.head.type value: stringValue: branch - - key: repository.name + - key: vcs.repository.name + value: + stringValue: "" + - key: vcs.repository.url.full value: stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.ref.lines_added - unit: '{line}' - - description: The number of lines deleted in a ref (branch) relative to the default branch (trunk). - gauge: - dataPoints: - asInt: "9" attributes: - - key: ref.name + - key: vcs.line_change.type + value: + stringValue: removed + - key: vcs.ref.head.name value: stringValue: main - - key: ref.type + - key: vcs.ref.head.type value: stringValue: branch - - key: repository.name + - key: vcs.repository.name + value: + stringValue: "" + - key: vcs.repository.url.full value: stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.ref.lines_deleted + name: vcs.ref.lines_delta unit: '{line}' - - description: The number of revisions (commits) a ref (branch) is ahead of the default branch (trunk). + - description: The number of revisions (commits) a ref (branch) is ahead/behind the branch from trunk (default). gauge: dataPoints: - asInt: "1" attributes: - - key: ref.name + - key: vcs.ref.head.name value: stringValue: main - - key: ref.type + - key: vcs.ref.head.type value: stringValue: branch - - key: repository.name + - key: vcs.repository.name + value: + stringValue: "" + - key: vcs.repository.url.full value: stringValue: "" + - key: vcs.revision_delta.direction + value: + stringValue: ahead startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.ref.revisions_ahead - unit: '{revision}' - - description: The number of revisions (commits) a ref (branch) is behind the default branch (trunk). - gauge: - dataPoints: - asInt: "0" attributes: - - key: ref.name + - key: vcs.ref.head.name value: stringValue: main - - key: ref.type + - key: vcs.ref.head.type value: stringValue: branch - - key: repository.name + - key: vcs.repository.name + value: + stringValue: "" + - key: vcs.repository.url.full value: stringValue: "" + - key: vcs.revision_delta.direction + value: + stringValue: behind startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.ref.revisions_behind + name: vcs.ref.revisions_delta unit: '{revision}' - - description: Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`. + - description: Time a ref (branch) created from the default branch (trunk) has existed. The `vcs.ref.head.type` attribute will always be `branch`. gauge: dataPoints: - asInt: "86400" attributes: - - key: ref.name + - key: vcs.ref.head.name value: stringValue: main - - key: ref.type + - key: vcs.ref.head.type value: stringValue: branch - - key: repository.name + - key: vcs.repository.name + value: + stringValue: "" + - key: vcs.repository.url.full value: stringValue: "" startTimeUnixNano: "1000000" timeUnixNano: "2000000" - name: vcs.repository.ref.time + name: vcs.ref.time unit: s + - description: The number of repositories in an organization. + gauge: + dataPoints: + - asInt: "1" + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + name: vcs.repository.count + unit: '{repository}' scope: name: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/githubreceiver version: latest diff --git a/receiver/githubreceiver/metadata.yaml b/receiver/githubreceiver/metadata.yaml index 4a626b887216..687b547fcb08 100644 --- a/receiver/githubreceiver/metadata.yaml +++ b/receiver/githubreceiver/metadata.yaml @@ -22,24 +22,39 @@ resource_attributes: type: string attributes: - repository.name: - description: The name of a VCS repository + vcs.repository.url.full: + description: The canonical URL of the repository providing the complete HTTPS address. type: string - ref.name: - description: The name of a VCS branch + vcs.repository.name: + description: The name of the VCS repository. type: string - ref.type: - description: The type of ref (branch, tag). + vcs.ref.head.name: + description: The name of the VCS head reference (branch). + type: string + vcs.ref.head.type: + description: The type of the head reference (branch, tag). type: string enum: - branch - tag - change.state: + vcs.change.state: description: The state of a change (pull request) type: string enum: - open - merged + vcs.revision_delta.direction: + description: The type of revision comparison. + type: string + enum: + - ahead + - behind + vcs.line_change.type: + description: The type of line change being measured on a ref (branch). + type: string + enum: + - added + - removed metrics: vcs.repository.count: @@ -49,83 +64,69 @@ metrics: gauge: value_type: int attributes: [] - vcs.repository.ref.count: + vcs.ref.count: enabled: true description: The number of refs of type branch in a repository. unit: "{ref}" gauge: value_type: int - attributes: [repository.name, ref.type] - vcs.repository.ref.time: + attributes: [vcs.repository.url.full, vcs.repository.name, vcs.ref.head.type] + vcs.ref.time: enabled: true - description: Time a ref (branch) created from the default branch (trunk) has existed. The `ref.type` attribute will always be `branch`. + description: Time a ref (branch) created from the default branch (trunk) has existed. The `vcs.ref.head.type` attribute will always be `branch`. unit: s gauge: value_type: int - attributes: [repository.name, ref.name, ref.type] - vcs.repository.ref.revisions_ahead: + attributes: [vcs.repository.url.full, vcs.repository.name, vcs.ref.head.name, vcs.ref.head.type] + vcs.ref.revisions_delta: enabled: true - description: The number of revisions (commits) a ref (branch) is ahead of the default branch (trunk). + description: The number of revisions (commits) a ref (branch) is ahead/behind the branch from trunk (default). unit: "{revision}" gauge: value_type: int - attributes: [repository.name, ref.name, ref.type] - vcs.repository.ref.revisions_behind: - enabled: true - description: The number of revisions (commits) a ref (branch) is behind the default branch (trunk). - unit: "{revision}" - gauge: - value_type: int - attributes: [repository.name, ref.name, ref.type] - vcs.repository.ref.lines_added: - enabled: true - description: The number of lines added in a ref (branch) relative to the default branch (trunk). - unit: "{line}" - gauge: - value_type: int - attributes: [repository.name, ref.name, ref.type] - vcs.repository.ref.lines_deleted: + attributes: [vcs.repository.url.full, vcs.repository.name, vcs.ref.head.name, vcs.ref.head.type, vcs.revision_delta.direction] + vcs.ref.lines_delta: enabled: true - description: The number of lines deleted in a ref (branch) relative to the default branch (trunk). + description: The number of lines added/removed in a ref (branch) relative to the default branch (trunk). unit: "{line}" gauge: value_type: int - attributes: [repository.name, ref.name, ref.type] - vcs.repository.contributor.count: + attributes: [vcs.repository.url.full, vcs.repository.name, vcs.ref.head.name, vcs.ref.head.type, vcs.line_change.type] + vcs.contributor.count: enabled: false description: The number of unique contributors to a repository. unit: "{contributor}" gauge: value_type: int - attributes: [repository.name] - vcs.repository.change.time_open: + attributes: [vcs.repository.url.full, vcs.repository.name] + vcs.change.duration: enabled: true - description: The amount of time a change (pull request) has been open. + description: The time duration a change (pull request/merge request/changelist) has been in an open state. unit: s gauge: value_type: int - attributes: [repository.name, ref.name] - vcs.repository.change.time_to_merge: + attributes: [vcs.repository.url.full, vcs.repository.name, vcs.ref.head.name, vcs.change.state] + vcs.change.time_to_merge: enabled: true description: The amount of time it took a change (pull request) to go from open to merged. unit: s gauge: value_type: int - attributes: [repository.name, ref.name] - vcs.repository.change.time_to_approval: + attributes: [vcs.repository.url.full, vcs.repository.name, vcs.ref.head.name] + vcs.change.time_to_approval: enabled: true description: The amount of time it took a change (pull request) to go from open to approved. unit: s gauge: value_type: int - attributes: [repository.name, ref.name] - vcs.repository.change.count: + attributes: [vcs.repository.url.full, vcs.repository.name, vcs.ref.head.name] + vcs.change.count: description: The number of changes (pull requests) in a repository, categorized by their state (either open or merged). enabled: true gauge: value_type: int unit: "{change}" - attributes: [change.state, repository.name] + attributes: [vcs.repository.url.full, vcs.change.state, vcs.repository.name] tests: config: diff --git a/receiver/githubreceiver/testdata/config.yaml b/receiver/githubreceiver/testdata/config.yaml index 6ab79edfcbd2..612b31b0213a 100644 --- a/receiver/githubreceiver/testdata/config.yaml +++ b/receiver/githubreceiver/testdata/config.yaml @@ -3,7 +3,7 @@ receivers: initial_delay: 1s collection_interval: 60s scrapers: - github: + scraper: webhook: endpoint: localhost:8080 read_timeout: "500ms" @@ -18,7 +18,7 @@ receivers: initial_delay: 1s collection_interval: 30s scrapers: - github: + scraper: webhook: endpoint: localhost:8080 read_timeout: "500ms" diff --git a/receiver/googlecloudmonitoringreceiver/receiver.go b/receiver/googlecloudmonitoringreceiver/receiver.go index 25caf4707915..eb5cedea91b6 100644 --- a/receiver/googlecloudmonitoringreceiver/receiver.go +++ b/receiver/googlecloudmonitoringreceiver/receiver.go @@ -271,17 +271,24 @@ func (mr *monitoringReceiver) convertGCPTimeSeriesToMetrics(metrics pmetric.Metr } // Set metadata (user and system labels) - if timeSeries.Metadata != nil { - for k, v := range timeSeries.Metadata.UserLabels { + if timeSeries.GetMetadata() != nil { + for k, v := range timeSeries.GetMetadata().GetUserLabels() { resource.Attributes().PutStr(k, v) } - if timeSeries.Metadata.SystemLabels != nil { - for k, v := range timeSeries.Metadata.SystemLabels.Fields { + if timeSeries.GetMetadata().GetSystemLabels() != nil { + for k, v := range timeSeries.GetMetadata().GetSystemLabels().GetFields() { resource.Attributes().PutStr(k, fmt.Sprintf("%v", v)) } } } + // Add metric-specific labels if they are present + if len(timeSeries.GetMetric().Labels) > 0 { + for k, v := range timeSeries.GetMetric().GetLabels() { + resource.Attributes().PutStr(k, fmt.Sprintf("%v", v)) + } + } + // Store the newly created ResourceMetrics in the map resourceMetricsMap[resourceKey] = rm } diff --git a/receiver/prometheusreceiver/internal/metrics_adjuster.go b/receiver/prometheusreceiver/internal/metrics_adjuster.go index 060450cc0f53..ebb33bb970b5 100644 --- a/receiver/prometheusreceiver/internal/metrics_adjuster.go +++ b/receiver/prometheusreceiver/internal/metrics_adjuster.go @@ -255,25 +255,28 @@ func NewInitialPointAdjuster(logger *zap.Logger, gcInterval time.Duration, useCr // AdjustMetrics takes a sequence of metrics and adjust their start times based on the initial and // previous points in the timeseriesMap. func (a *initialPointAdjuster) AdjustMetrics(metrics pmetric.Metrics) error { - // By contract metrics will have at least 1 data point, so for sure will have at least one ResourceMetrics. - - job, found := metrics.ResourceMetrics().At(0).Resource().Attributes().Get(semconv.AttributeServiceName) - if !found { - return errors.New("adjusting metrics without job") - } + for i := 0; i < metrics.ResourceMetrics().Len(); i++ { + rm := metrics.ResourceMetrics().At(i) + _, found := rm.Resource().Attributes().Get(semconv.AttributeServiceName) + if !found { + return errors.New("adjusting metrics without job") + } - instance, found := metrics.ResourceMetrics().At(0).Resource().Attributes().Get(semconv.AttributeServiceInstanceID) - if !found { - return errors.New("adjusting metrics without instance") + _, found = rm.Resource().Attributes().Get(semconv.AttributeServiceInstanceID) + if !found { + return errors.New("adjusting metrics without instance") + } } - tsm := a.jobsMap.get(job.Str(), instance.Str()) - // The lock on the relevant timeseriesMap is held throughout the adjustment process to ensure that - // nothing else can modify the data used for adjustment. - tsm.Lock() - defer tsm.Unlock() for i := 0; i < metrics.ResourceMetrics().Len(); i++ { rm := metrics.ResourceMetrics().At(i) + job, _ := rm.Resource().Attributes().Get(semconv.AttributeServiceName) + instance, _ := rm.Resource().Attributes().Get(semconv.AttributeServiceInstanceID) + tsm := a.jobsMap.get(job.Str(), instance.Str()) + + // The lock on the relevant timeseriesMap is held throughout the adjustment process to ensure that + // nothing else can modify the data used for adjustment. + tsm.Lock() for j := 0; j < rm.ScopeMetrics().Len(); j++ { ilm := rm.ScopeMetrics().At(j) for k := 0; k < ilm.Metrics().Len(); k++ { @@ -303,6 +306,7 @@ func (a *initialPointAdjuster) AdjustMetrics(metrics pmetric.Metrics) error { } } } + tsm.Unlock() } return nil } diff --git a/receiver/prometheusreceiver/internal/metrics_adjuster_test.go b/receiver/prometheusreceiver/internal/metrics_adjuster_test.go index fd1f4f8818da..1f7779269556 100644 --- a/receiver/prometheusreceiver/internal/metrics_adjuster_test.go +++ b/receiver/prometheusreceiver/internal/metrics_adjuster_test.go @@ -26,6 +26,7 @@ var ( percent0 = []float64{10, 50, 90} sum1 = "sum1" + sum2 = "sum2" gauge1 = "gauge1" histogram1 = "histogram1" summary1 = "summary1" @@ -103,6 +104,37 @@ func TestSum(t *testing.T) { runScript(t, NewInitialPointAdjuster(zap.NewNop(), time.Minute, true), "job", "0", script) } +func TestSumWithDifferentResources(t *testing.T) { + script := []*metricsAdjusterTest{ + { + description: "Sum: round 1 - initial instance, start time is established", + metrics: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t1, t1, 44))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t1, t1, 44)))), + adjusted: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t1, t1, 44))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t1, t1, 44)))), + }, + { + description: "Sum: round 2 - instance adjusted based on round 1 (metrics in different order)", + metrics: metricsFromResourceMetrics(resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t2, t2, 66))), resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t2, t2, 66)))), + adjusted: metricsFromResourceMetrics(resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t1, t2, 66))), resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t1, t2, 66)))), + }, + { + description: "Sum: round 3 - instance reset (value less than previous value), start time is reset", + metrics: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t3, t3, 55))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t3, t3, 55)))), + adjusted: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t3, t3, 55))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t3, t3, 55)))), + }, + { + description: "Sum: round 4 - instance adjusted based on round 3", + metrics: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t4, t4, 72))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t4, t4, 72)))), + adjusted: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t3, t4, 72))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t3, t4, 72)))), + }, + { + description: "Sum: round 5 - instance adjusted based on round 4, sum2 metric resets but sum1 doesn't", + metrics: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t5, t5, 72))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t5, t5, 10)))), + adjusted: metricsFromResourceMetrics(resourceMetrics("job1", "instance1", sumMetric(sum1, doublePoint(k1v1k2v2, t3, t5, 72))), resourceMetrics("job2", "instance2", sumMetric(sum2, doublePoint(k1v1k2v2, t5, t5, 10)))), + }, + } + runScript(t, NewInitialPointAdjuster(zap.NewNop(), time.Minute, true), "job", "0", script) +} + func TestSummaryNoCount(t *testing.T) { script := []*metricsAdjusterTest{ { @@ -710,14 +742,32 @@ func runScript(t *testing.T, ma MetricsAdjuster, job, instance string, tests []* t.Run(test.description, func(t *testing.T) { adjusted := pmetric.NewMetrics() test.metrics.CopyTo(adjusted) - // Add the instance/job to the input metrics. - adjusted.ResourceMetrics().At(0).Resource().Attributes().PutStr(semconv.AttributeServiceInstanceID, instance) - adjusted.ResourceMetrics().At(0).Resource().Attributes().PutStr(semconv.AttributeServiceName, job) + // Add the instance/job to the input metrics if they aren't already present. + for i := 0; i < adjusted.ResourceMetrics().Len(); i++ { + rm := adjusted.ResourceMetrics().At(i) + _, found := rm.Resource().Attributes().Get(semconv.AttributeServiceName) + if !found { + rm.Resource().Attributes().PutStr(semconv.AttributeServiceName, job) + } + _, found = rm.Resource().Attributes().Get(semconv.AttributeServiceInstanceID) + if !found { + rm.Resource().Attributes().PutStr(semconv.AttributeServiceInstanceID, instance) + } + } assert.NoError(t, ma.AdjustMetrics(adjusted)) - // Add the instance/job to the expected metrics as well. - test.adjusted.ResourceMetrics().At(0).Resource().Attributes().PutStr(semconv.AttributeServiceInstanceID, instance) - test.adjusted.ResourceMetrics().At(0).Resource().Attributes().PutStr(semconv.AttributeServiceName, job) + // Add the instance/job to the expected metrics as well if they aren't already present. + for i := 0; i < test.adjusted.ResourceMetrics().Len(); i++ { + rm := test.adjusted.ResourceMetrics().At(i) + _, found := rm.Resource().Attributes().Get(semconv.AttributeServiceName) + if !found { + rm.Resource().Attributes().PutStr(semconv.AttributeServiceName, job) + } + _, found = rm.Resource().Attributes().Get(semconv.AttributeServiceInstanceID) + if !found { + rm.Resource().Attributes().PutStr(semconv.AttributeServiceInstanceID, instance) + } + } assert.EqualValues(t, test.adjusted, adjusted) }) } diff --git a/receiver/prometheusreceiver/internal/metricsutil_test.go b/receiver/prometheusreceiver/internal/metricsutil_test.go index d9b79ebdbdd9..bc1dabbd696d 100644 --- a/receiver/prometheusreceiver/internal/metricsutil_test.go +++ b/receiver/prometheusreceiver/internal/metricsutil_test.go @@ -6,6 +6,7 @@ package internal import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" + semconv "go.opentelemetry.io/collector/semconv/v1.27.0" ) type kv struct { @@ -23,6 +24,28 @@ func metrics(metrics ...pmetric.Metric) pmetric.Metrics { return md } +func metricsFromResourceMetrics(metrics ...pmetric.ResourceMetrics) pmetric.Metrics { + md := pmetric.NewMetrics() + for _, metric := range metrics { + mr := md.ResourceMetrics().AppendEmpty() + metric.CopyTo(mr) + } + return md +} + +func resourceMetrics(job, instance string, metrics ...pmetric.Metric) pmetric.ResourceMetrics { + mr := pmetric.NewResourceMetrics() + mr.Resource().Attributes().PutStr(semconv.AttributeServiceName, job) + mr.Resource().Attributes().PutStr(semconv.AttributeServiceInstanceID, instance) + ms := mr.ScopeMetrics().AppendEmpty().Metrics() + + for _, metric := range metrics { + destMetric := ms.AppendEmpty() + metric.CopyTo(destMetric) + } + return mr +} + func histogramPointRaw(attributes []*kv, startTimestamp, timestamp pcommon.Timestamp) pmetric.HistogramDataPoint { hdp := pmetric.NewHistogramDataPoint() hdp.SetStartTimestamp(startTimestamp)