Skip to content

Commit 4079d3f

Browse files
authored
[spanmetricsconnector] Add instrumentation scope (#38001)
#### Description Added the instrumentation scope to the metrics generated by the span metrics connector. #### Link to tracking issue Fixes #23662 #### Testing Added a new unit test to cover this feature. #### Documentation Some notes to the `README.md` file explaining the new config files. Signed-off-by: Israel Blancas <[email protected]>
1 parent 9dd6a2a commit 4079d3f

File tree

5 files changed

+164
-9
lines changed

5 files changed

+164
-9
lines changed

.chloggen/23662.yaml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: spanmetricsconnector
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add instrumentation scope to span metrics connector.
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [23662]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: |
19+
This change adds the instrumentation scope to the span metrics connector, which allows users to specify the instrumentation scope for the connector.
20+
Now, the connector has a new configuration option:
21+
- `include_instrumentation_scope`: A list of instrumentation scope names to include from the traces.
22+
23+
The instrumentation scope name is the name of the instrumentation library that collected the span.
24+
25+
# If your change doesn't affect end users or the exported elements of any package,
26+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
27+
# Optional: The change log or logs in which this entry should be included.
28+
# e.g. '[user]' or '[user, api]'
29+
# Include 'user' if the change is relevant to end users.
30+
# Include 'api' if there is a change to a library API.
31+
# Default: '[user]'
32+
change_logs: []

connector/spanmetricsconnector/README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ The following settings can be optionally configured:
106106

107107
If no `default` is provided, this dimension will be **omitted** from the metric.
108108
- `exclude_dimensions`: the list of dimensions to be excluded from the default set of dimensions. Use to exclude unneeded data from metrics.
109-
- `dimensions_cache_size` (default: `1000`): the size of cache for storing Dimensions to improve collectors memory usage. Must be a positive number.
109+
- `dimensions_cache_size` (default: `1000`): the size of cache for storing Dimensions to improve collectors memory usage. Must be a positive number.
110+
- `include_instrumentation_scope`: a list of instrumentation scope names to include from the traces.
110111
- `resource_metrics_cache_size` (default: `1000`): the size of the cache holding metrics for a service. This is mostly relevant for
111112
cumulative temporality to avoid memory leaks and correct metric timestamp resets.
112113
- `aggregation_temporality` (default: `AGGREGATION_TEMPORALITY_CUMULATIVE`): Defines the aggregation temporality of the generated metrics.
@@ -164,6 +165,8 @@ connectors:
164165
- service.name
165166
- telemetry.sdk.language
166167
- telemetry.sdk.name
168+
include_instrumentation_scope:
169+
- express
167170

168171
service:
169172
pipelines:

connector/spanmetricsconnector/config.go

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ type Config struct {
8686

8787
// Events defines the configuration for events section of spans.
8888
Events EventsConfig `mapstructure:"events"`
89+
90+
IncludeInstrumentationScope []string `mapstructure:"include_instrumentation_scope"`
8991
}
9092

9193
type HistogramConfig struct {

connector/spanmetricsconnector/connector.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ import (
2828
)
2929

3030
const (
31-
serviceNameKey = conventions.AttributeServiceName
32-
spanNameKey = "span.name" // OpenTelemetry non-standard constant.
33-
spanKindKey = "span.kind" // OpenTelemetry non-standard constant.
34-
statusCodeKey = "status.code" // OpenTelemetry non-standard constant.
35-
metricKeySeparator = string(byte(0))
31+
serviceNameKey = conventions.AttributeServiceName
32+
spanNameKey = "span.name" // OpenTelemetry non-standard constant.
33+
spanKindKey = "span.kind" // OpenTelemetry non-standard constant.
34+
statusCodeKey = "status.code" // OpenTelemetry non-standard constant.
35+
instrumentationScopeNameKey = "span.instrumentation.scope.name" // OpenTelemetry non-standard constant.
36+
instrumentationScopeVersionKey = "span.instrumentation.scope.version" // OpenTelemetry non-standard constant.
37+
metricKeySeparator = string(byte(0))
3638

3739
defaultDimensionsCacheSize = 1000
3840
defaultResourceMetricsCacheSize = 1000
@@ -401,7 +403,7 @@ func (p *connectorImp) aggregateMetrics(traces ptrace.Traces) {
401403

402404
attributes, ok := p.metricKeyToDimensions.Get(key)
403405
if !ok {
404-
attributes = p.buildAttributes(serviceName, span, resourceAttr, p.dimensions)
406+
attributes = p.buildAttributes(serviceName, span, resourceAttr, p.dimensions, ils.Scope())
405407
p.metricKeyToDimensions.Add(key, attributes)
406408
}
407409
if !p.config.Histogram.Disable {
@@ -432,7 +434,7 @@ func (p *connectorImp) aggregateMetrics(traces ptrace.Traces) {
432434
eKey := p.buildKey(serviceName, span, eDimensions, rscAndEventAttrs)
433435
eAttributes, ok := p.metricKeyToDimensions.Get(eKey)
434436
if !ok {
435-
eAttributes = p.buildAttributes(serviceName, span, rscAndEventAttrs, eDimensions)
437+
eAttributes = p.buildAttributes(serviceName, span, rscAndEventAttrs, eDimensions, ils.Scope())
436438
p.metricKeyToDimensions.Add(eKey, eAttributes)
437439
}
438440
e := events.GetOrCreate(eKey, eAttributes)
@@ -505,7 +507,7 @@ func contains(elements []string, value string) bool {
505507
return false
506508
}
507509

508-
func (p *connectorImp) buildAttributes(serviceName string, span ptrace.Span, resourceAttrs pcommon.Map, dimensions []utilattri.Dimension) pcommon.Map {
510+
func (p *connectorImp) buildAttributes(serviceName string, span ptrace.Span, resourceAttrs pcommon.Map, dimensions []utilattri.Dimension, instrumentationScope pcommon.InstrumentationScope) pcommon.Map {
509511
attr := pcommon.NewMap()
510512
attr.EnsureCapacity(4 + len(dimensions))
511513
if !contains(p.config.ExcludeDimensions, serviceNameKey) {
@@ -520,11 +522,20 @@ func (p *connectorImp) buildAttributes(serviceName string, span ptrace.Span, res
520522
if !contains(p.config.ExcludeDimensions, statusCodeKey) {
521523
attr.PutStr(statusCodeKey, traceutil.StatusCodeStr(span.Status().Code()))
522524
}
525+
523526
for _, d := range dimensions {
524527
if v, ok := utilattri.GetDimensionValue(d, span.Attributes(), resourceAttrs); ok {
525528
v.CopyTo(attr.PutEmpty(d.Name))
526529
}
527530
}
531+
532+
if contains(p.config.IncludeInstrumentationScope, instrumentationScope.Name()) && instrumentationScope.Name() != "" {
533+
attr.PutStr(instrumentationScopeNameKey, instrumentationScope.Name())
534+
if instrumentationScope.Version() != "" {
535+
attr.PutStr(instrumentationScopeVersionKey, instrumentationScope.Version())
536+
}
537+
}
538+
528539
return attr
529540
}
530541

connector/spanmetricsconnector/connector_test.go

+107
Original file line numberDiff line numberDiff line change
@@ -1874,3 +1874,110 @@ func (c alwaysIncreasingClock) Now() time.Time {
18741874
c.Clock.(*clockwork.FakeClock).Advance(time.Millisecond)
18751875
return c.Clock.Now()
18761876
}
1877+
1878+
func TestBuildAttributes_InstrumentationScope(t *testing.T) {
1879+
tests := []struct {
1880+
name string
1881+
instrumentationScope pcommon.InstrumentationScope
1882+
config Config
1883+
want map[string]string
1884+
}{
1885+
{
1886+
name: "with instrumentation scope name and version",
1887+
instrumentationScope: func() pcommon.InstrumentationScope {
1888+
scope := pcommon.NewInstrumentationScope()
1889+
scope.SetName("express")
1890+
scope.SetVersion("1.0.0")
1891+
return scope
1892+
}(),
1893+
config: Config{
1894+
IncludeInstrumentationScope: []string{"express"},
1895+
},
1896+
want: map[string]string{
1897+
serviceNameKey: "test_service",
1898+
spanNameKey: "test_span",
1899+
spanKindKey: "SPAN_KIND_INTERNAL",
1900+
statusCodeKey: "STATUS_CODE_UNSET",
1901+
instrumentationScopeNameKey: "express",
1902+
instrumentationScopeVersionKey: "1.0.0",
1903+
},
1904+
},
1905+
{
1906+
name: "with instrumentation scope but not included",
1907+
instrumentationScope: func() pcommon.InstrumentationScope {
1908+
scope := pcommon.NewInstrumentationScope()
1909+
scope.SetName("express")
1910+
scope.SetVersion("1.0.0")
1911+
return scope
1912+
}(),
1913+
config: Config{},
1914+
want: map[string]string{
1915+
serviceNameKey: "test_service",
1916+
spanNameKey: "test_span",
1917+
spanKindKey: "SPAN_KIND_INTERNAL",
1918+
statusCodeKey: "STATUS_CODE_UNSET",
1919+
},
1920+
},
1921+
{
1922+
name: "without instrumentation scope but version and included in config",
1923+
instrumentationScope: func() pcommon.InstrumentationScope {
1924+
scope := pcommon.NewInstrumentationScope()
1925+
scope.SetVersion("1.0.0")
1926+
return scope
1927+
}(),
1928+
config: Config{
1929+
IncludeInstrumentationScope: []string{"express"},
1930+
},
1931+
want: map[string]string{
1932+
serviceNameKey: "test_service",
1933+
spanNameKey: "test_span",
1934+
spanKindKey: "SPAN_KIND_INTERNAL",
1935+
statusCodeKey: "STATUS_CODE_UNSET",
1936+
},
1937+
},
1938+
1939+
{
1940+
name: "with instrumentation scope and instrumentation scope name but no version and included in config",
1941+
instrumentationScope: func() pcommon.InstrumentationScope {
1942+
scope := pcommon.NewInstrumentationScope()
1943+
scope.SetName("express")
1944+
return scope
1945+
}(),
1946+
config: Config{
1947+
IncludeInstrumentationScope: []string{"express"},
1948+
},
1949+
want: map[string]string{
1950+
serviceNameKey: "test_service",
1951+
spanNameKey: "test_span",
1952+
spanKindKey: "SPAN_KIND_INTERNAL",
1953+
statusCodeKey: "STATUS_CODE_UNSET",
1954+
instrumentationScopeNameKey: "express",
1955+
},
1956+
},
1957+
}
1958+
1959+
for _, tt := range tests {
1960+
t.Run(tt.name, func(t *testing.T) {
1961+
// Create connector
1962+
p := &connectorImp{
1963+
config: tt.config,
1964+
}
1965+
1966+
// Create basic span
1967+
span := ptrace.NewSpan()
1968+
span.SetName("test_span")
1969+
span.SetKind(ptrace.SpanKindInternal)
1970+
1971+
// Build attributes
1972+
attrs := p.buildAttributes("test_service", span, pcommon.NewMap(), nil, tt.instrumentationScope)
1973+
1974+
// Verify results
1975+
assert.Equal(t, len(tt.want), attrs.Len())
1976+
for k, v := range tt.want {
1977+
val, ok := attrs.Get(k)
1978+
assert.True(t, ok)
1979+
assert.Equal(t, v, val.Str())
1980+
}
1981+
})
1982+
}
1983+
}

0 commit comments

Comments
 (0)