Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[processor/spanmetrics] Support for specifying Resource Attributes on metrics generated from traces #6717

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f5553a1
starts initial work on optional resource attributes config to span me…
Tenaria Dec 13, 2021
f186407
adds test to check metric length when copying resources
Tenaria Dec 13, 2021
3206a11
adds changelog and fixes tests
Tenaria Dec 13, 2021
3da4719
Merge branch 'main' into OBC-256-Resource-Attributes-Merge-Upstream
Tenaria Dec 13, 2021
945a6d1
fix tests
Tenaria Dec 13, 2021
fc62eab
remove empty new line
Tenaria Dec 13, 2021
07b5ad3
updates changelog
Tenaria Dec 13, 2021
5782cb0
add accidentally removed comment back in
Tenaria Dec 13, 2021
e2eb41d
revert variable name change and fix comment
Tenaria Dec 13, 2021
321c6e2
revert variable name cahnge
Tenaria Dec 13, 2021
fb0561c
adds test to ensure ordering of keys used for aggregation
Tenaria Dec 13, 2021
80ebf64
fixes lock held; optimise for loop; add parallel in test; add comments
Tenaria Dec 13, 2021
19775dc
reverse conditions to simplify code
Tenaria Dec 13, 2021
8c2d38e
update tests to use defined constant instrumentationLibraryName
Tenaria Dec 13, 2021
cbbf532
Merge branch 'main' into OBC-256-Resource-Attributes-Merge-Upstream
chenzhihao Jan 4, 2022
91c8b7d
update the logic of method updateCallMetrics
chenzhihao Jan 5, 2022
068a1bd
fix tests
chenzhihao Jan 5, 2022
18f20d0
add todos
chenzhihao Jan 5, 2022
4d370ad
add todos
chenzhihao Jan 5, 2022
024a122
Merge branch 'main' into OBC-256-Resource-Attributes-Merge-Upstream
chenzhihao Jan 5, 2022
7561f97
fix lint
chenzhihao Jan 5, 2022
62e892f
fix lint
chenzhihao Jan 5, 2022
d8edfda
remove resourceAttrList
chenzhihao Jan 5, 2022
d175a4e
Update "resource_attributes"
chenzhihao Jan 6, 2022
608a083
fix lint
chenzhihao Jan 6, 2022
a0519c0
remove duplicate p.resetExemplarData()
chenzhihao Jan 6, 2022
9772056
refactor how processor reset after every ConsumeTraces process
chenzhihao Jan 6, 2022
ae17b37
move keybuilder to a package
chenzhihao Jan 6, 2022
8ea124c
update comments
chenzhihao Jan 6, 2022
78c11f3
add copyright license
chenzhihao Jan 6, 2022
fbea55f
user defer for p.reset() so that downstream components can get data q…
chenzhihao Jan 6, 2022
3d6b4c1
gofmt
chenzhihao Jan 6, 2022
fd42b76
refactor how we use p.lock
chenzhihao Jan 6, 2022
7ed4b1c
gofmt
chenzhihao Jan 6, 2022
f676e46
added a set of unit tests to cover the excessive concurrent usage
chenzhihao Jan 7, 2022
4b71675
change the usage of the lock to make sure the processor can only be e…
chenzhihao Jan 7, 2022
494b483
Merge branch 'main' into OBC-256-Resource-Attributes-Merge-Upstream
chenzhihao Jan 7, 2022
38ebb2a
add comments; lint
chenzhihao Jan 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- `jaeger` receiver/exporter: Parse/set Jaeger status with OTel spec values (#6682)
- `awsecscontainermetricsreceiver`: remove tag from `container.image.name` (#6436)
- `k8sclusterreceiver`: remove tag from `container.image.name` (#6436)
- `spanmetricproccessor`: service.name attribute added as a default resource attribute instead of attribute in generated metrics (#6717)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to update the changelog, the maintainers will do this upon a new release

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw in Zhihao's PR that there was a changelog added (see comment #6503 (review)). @MovieStoreGuy can you confirm whether we update the changelog or the maintainers do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is fine for us to update the changelog, we have the greatest overall idea of what has changed so I think it makes sense to add it. However, If a maintainer says otherwise, I am also fine with it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would definitely prefer that you add a changelog entry, and there is CI automation that will complain if you don't! It generally makes it easier for us to prepare releases when we don't need to track down all the changes that have been made and think about how to describe them.


## 🚀 New components 🚀

Expand All @@ -48,6 +49,7 @@
- `skywalkingexporter`: add skywalking metrics exporter (#6528)
- `deltatorateprocessor`: add int counter support (#6982)
- `filestorageextension`: document default values (#7022)
- `spanmetricproccessor`: Support specifying resource attributes to attach to metrics generated from traces (#6717)
- `redisreceiver`: Migrate the scraper to the mdatagen metrics builder (#6938)

## v0.41.0
Expand Down
21 changes: 18 additions & 3 deletions processor/spanmetricsprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ latency_bucket{http_method="GET",http_status_code="200",label1="value1",operatio
```

Each metric will have _at least_ the following dimensions because they are common across all spans:
- Service name
- Operation
- Span kind
- Status code

Each metric will have _at least_ the following resource attributes because they are common across all spans:
- Service name

This processor lets traces to continue through the pipeline unmodified.

The following settings are required:
Expand All @@ -48,8 +50,7 @@ The following settings can be optionally configured:
- Default: `[2ms, 4ms, 6ms, 8ms, 10ms, 50ms, 100ms, 200ms, 400ms, 800ms, 1s, 1400ms, 2s, 5s, 10s, 15s]`
- `dimensions`: the list of dimensions to add together with the default dimensions defined above.

Each additional dimension is defined with a `name` which is looked up in the span's collection of attributes or
resource attributes (AKA process tags) such as `ip`, `host.name` or `region`.
Each additional dimension is defined with a `name` which is looked up in the span's collection of attributes.

If the `name`d attribute is missing in the span, the optional provided `default` is used.

Expand All @@ -60,6 +61,15 @@ The following settings can be optionally configured:
One of either `AGGREGATION_TEMPORALITY_CUMULATIVE` or `AGGREGATION_TEMPORALITY_DELTA`.
- Default: `AGGREGATION_TEMPORALITY_CUMULATIVE`

- `resource_attributes`: the list of resource attributes to add together with the default resource attributes defined
above. Each additional resource attributes is defined with a `name` which is looked up in the span's collection of
resource attributes. If the `name`d resource attribute is missing in the span, the optional provided `default` is
used. If no `default` is provided, this resource attribute will be **omitted** from the metric.

`service.name` will be automatically added as resource attribute to all the generated metrics.

- `resource_attributes_cache_size`:the max items number of `resouce_key_to_dimensions_cache`. If not provided, will
use default value size `1000`.
## Examples

The following is a simple example usage of the spanmetrics processor.
Expand Down Expand Up @@ -96,6 +106,11 @@ processors:
default: GET
- name: http.status_code
dimensions_cache_size: 1000
resource_attributes:
- name: region
default: us-east-1
- name: host_id
resource_attributes_cache_size: 1000
aggregation_temporality: "AGGREGATION_TEMPORALITY_DELTA"

exporters:
Expand Down
15 changes: 13 additions & 2 deletions processor/spanmetricsprocessor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const (
cumulative = "AGGREGATION_TEMPORALITY_CUMULATIVE"
)

// Dimension defines the dimension name and optional default value if the Dimension is missing from a span attribute.
// Dimension defines the key and optional default value if the key is missing from a span attribute.
type Dimension struct {
Name string `mapstructure:"name"`
Default *string `mapstructure:"default"`
Expand All @@ -44,7 +44,6 @@ type Config struct {
LatencyHistogramBuckets []time.Duration `mapstructure:"latency_histogram_buckets"`

// Dimensions defines the list of additional dimensions on top of the provided:
// - service.name
// - operation
// - span.kind
// - status.code
Expand All @@ -58,6 +57,18 @@ type Config struct {
DimensionsCacheSize int `mapstructure:"dimensions_cache_size"`

AggregationTemporality string `mapstructure:"aggregation_temporality"`

// ResourceAttributes defines the list of additional resource attributes to attach to metrics on top of the provided:
// - service.name
// These will be fetched from the span's resource attributes. For more details, see:
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md
// and https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md.
ResourceAttributes []Dimension `mapstructure:"resource_attributes"`

// ResourceAttributesCacheSize defines the size of cache for storing Resource_Attributes, which helps to avoid cache
// memory growing indefinitely over the lifetime of the collector.
// Optional. See defaultResourceAttributesCacheSize in processor.go for the default value.
ResourceAttributesCacheSize int `mapstructure:"resource_attributes_cache_size"`
}

// GetAggregationTemporality converts the string value given in the config into a MetricAggregationTemporality.
Expand Down
59 changes: 37 additions & 22 deletions processor/spanmetricsprocessor/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,33 @@ import (
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver"
)

// todo - cover resource-attributes
func TestLoadConfig(t *testing.T) {
defaultMethod := "GET"
defaultRegion := "us-east-1"
testcases := []struct {
configFile string
wantMetricsExporter string
wantLatencyHistogramBuckets []time.Duration
wantDimensions []Dimension
wantDimensionsCacheSize int
wantAggregationTemporality string
configFile string
wantMetricsExporter string
wantLatencyHistogramBuckets []time.Duration
wantDimensions []Dimension
wantDimensionsCacheSize int
wantResourceAttributes []Dimension
wantResourceAttributesCacheSize int
wantAggregationTemporality string
}{
{
configFile: "config-2-pipelines.yaml",
wantMetricsExporter: "prometheus",
wantAggregationTemporality: cumulative,
wantDimensionsCacheSize: 500,
configFile: "config-2-pipelines.yaml",
wantMetricsExporter: "prometheus",
wantAggregationTemporality: cumulative,
wantDimensionsCacheSize: 500,
wantResourceAttributesCacheSize: 300,
},
{
configFile: "config-3-pipelines.yaml",
wantMetricsExporter: "otlp/spanmetrics",
wantAggregationTemporality: cumulative,
wantDimensionsCacheSize: defaultDimensionsCacheSize,
configFile: "config-3-pipelines.yaml",
wantMetricsExporter: "otlp/spanmetrics",
wantAggregationTemporality: cumulative,
wantDimensionsCacheSize: defaultDimensionsCacheSize,
wantResourceAttributesCacheSize: defaultResourceAttributesCacheSize,
},
{
configFile: "config-full.yaml",
Expand All @@ -72,12 +78,19 @@ func TestLoadConfig(t *testing.T) {
{"http.method", &defaultMethod},
{"http.status_code", nil},
},
wantDimensionsCacheSize: 1500,
wantAggregationTemporality: delta,
wantDimensionsCacheSize: 1500,
wantResourceAttributes: []Dimension{
{"region", &defaultRegion},
{"host_id", nil},
},
wantResourceAttributesCacheSize: 3000,
wantAggregationTemporality: delta,
},
}
for _, tc := range testcases {
tc := tc
t.Run(tc.configFile, func(t *testing.T) {
t.Parallel()
// Prepare
factories, err := componenttest.NopFactories()
require.NoError(t, err)
Expand All @@ -100,12 +113,14 @@ func TestLoadConfig(t *testing.T) {
require.NotNil(t, cfg)
assert.Equal(t,
&Config{
ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
MetricsExporter: tc.wantMetricsExporter,
LatencyHistogramBuckets: tc.wantLatencyHistogramBuckets,
Dimensions: tc.wantDimensions,
DimensionsCacheSize: tc.wantDimensionsCacheSize,
AggregationTemporality: tc.wantAggregationTemporality,
ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
MetricsExporter: tc.wantMetricsExporter,
LatencyHistogramBuckets: tc.wantLatencyHistogramBuckets,
Dimensions: tc.wantDimensions,
DimensionsCacheSize: tc.wantDimensionsCacheSize,
ResourceAttributes: tc.wantResourceAttributes,
ResourceAttributesCacheSize: tc.wantResourceAttributesCacheSize,
AggregationTemporality: tc.wantAggregationTemporality,
},
cfg.Processors[config.NewComponentID(typeStr)],
)
Expand Down
7 changes: 4 additions & 3 deletions processor/spanmetricsprocessor/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ func NewFactory() component.ProcessorFactory {

func createDefaultConfig() config.Processor {
return &Config{
ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
AggregationTemporality: "AGGREGATION_TEMPORALITY_CUMULATIVE",
DimensionsCacheSize: defaultDimensionsCacheSize,
ProcessorSettings: config.NewProcessorSettings(config.NewComponentID(typeStr)),
AggregationTemporality: "AGGREGATION_TEMPORALITY_CUMULATIVE",
DimensionsCacheSize: defaultDimensionsCacheSize,
ResourceAttributesCacheSize: defaultResourceAttributesCacheSize,
}
}

Expand Down
59 changes: 59 additions & 0 deletions processor/spanmetricsprocessor/keybuilder/keybuilder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package keybuilder // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/spanmetricsprocessor/keybuilder"

import "strings"

const (
separator = string(byte(0))
defaultCapacity = 1024
)

type KeyBuilder interface {
Append(value ...string)
String() string
}

type keyBuilder struct {
sb strings.Builder
separator string
}

func New() KeyBuilder {
b := keyBuilder{
sb: strings.Builder{},
separator: separator,
}
b.sb.Grow(defaultCapacity)
return &b
}

func (mkb *keyBuilder) Append(values ...string) {
for _, value := range values {
if len(value) == 0 {
continue
}
if mkb.sb.Len() != 0 {
mkb.sb.WriteString(mkb.separator)
}
// It's worth noting that from pprof benchmarks, WriteString is the most expensive operation of this processor.
// Specifically, the need to grow the underlying []byte slice to make room for the appended string.
mkb.sb.WriteString(value)
}
}

func (mkb *keyBuilder) String() string {
return mkb.sb.String()
}
58 changes: 58 additions & 0 deletions processor/spanmetricsprocessor/keybuilder/keybuilder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package keybuilder

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNew(t *testing.T) {
t.Run("should create new keybuilder", func(t *testing.T) {
assert.NotPanics(t, func() {
assert.NotNil(t, New())
})
})
}

func Test_metricKeyBuilder_Append(t *testing.T) {
tests := []struct {
name string
args [][]string
result string
}{
{
name: "should skip empty string",
args: [][]string{{"", "abc", "", "def"}},
result: fmt.Sprintf("abc%sdef", separator),
},
{
name: "should concat multiple append",
args: [][]string{{"abc", "def"}, {"", "hij"}},
result: fmt.Sprintf("abc%sdef%shij", separator, separator),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
kb := New()
for _, arg := range tt.args {
kb.Append(arg...)
}
assert.Equal(t, tt.result, kb.String())
})
}
}
Loading