Skip to content

Commit cddc091

Browse files
[otlp] Export instrumentation scope attributes from ActivitySource.Tags (open-telemetry#5897)
Co-authored-by: Mikel Blanchard <[email protected]>
1 parent 963ec41 commit cddc091

File tree

4 files changed

+234
-44
lines changed

4 files changed

+234
-44
lines changed

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Notes](../../RELEASENOTES.md).
77

88
## Unreleased
99

10+
* Added support for exporting instrumentation scope attributes from
11+
`ActivitySource.Tags`.
12+
([#5897](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5897))
13+
1014
## 1.10.0-beta.1
1115

1216
Released 2024-Sep-30

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ internal static void AddBatch(
3232
};
3333
request.ResourceSpans.Add(resourceSpans);
3434

35+
var maxTags = sdkLimitOptions.AttributeCountLimit ?? int.MaxValue;
36+
3537
foreach (var activity in activityBatch)
3638
{
3739
Span? span = activity.ToOtlpSpan(sdkLimitOptions);
@@ -44,15 +46,15 @@ internal static void AddBatch(
4446
}
4547

4648
var activitySourceName = activity.Source.Name;
47-
if (!spansByLibrary.TryGetValue(activitySourceName, out var spans))
49+
if (!spansByLibrary.TryGetValue(activitySourceName, out var scopeSpans))
4850
{
49-
spans = GetSpanListFromPool(activitySourceName, activity.Source.Version);
51+
scopeSpans = GetSpanListFromPool(activity.Source, maxTags, sdkLimitOptions.AttributeValueLengthLimit);
5052

51-
spansByLibrary.Add(activitySourceName, spans);
52-
resourceSpans.ScopeSpans.Add(spans);
53+
spansByLibrary.Add(activitySourceName, scopeSpans);
54+
resourceSpans.ScopeSpans.Add(scopeSpans);
5355
}
5456

55-
spans.Spans.Add(span);
57+
scopeSpans.Spans.Add(span);
5658
}
5759
}
5860

@@ -65,34 +67,69 @@ internal static void Return(this ExportTraceServiceRequest request)
6567
return;
6668
}
6769

68-
foreach (var scope in resourceSpans.ScopeSpans)
70+
foreach (var scopeSpan in resourceSpans.ScopeSpans)
6971
{
70-
scope.Spans.Clear();
71-
SpanListPool.Add(scope);
72+
scopeSpan.Spans.Clear();
73+
scopeSpan.Scope.Attributes.Clear();
74+
SpanListPool.Add(scopeSpan);
7275
}
7376
}
7477

7578
[MethodImpl(MethodImplOptions.AggressiveInlining)]
76-
internal static ScopeSpans GetSpanListFromPool(string name, string? version)
79+
internal static ScopeSpans GetSpanListFromPool(ActivitySource activitySource, int maxTags, int? attributeValueLengthLimit)
7780
{
78-
if (!SpanListPool.TryTake(out var spans))
81+
if (!SpanListPool.TryTake(out var scopeSpans))
7982
{
80-
spans = new ScopeSpans
83+
scopeSpans = new ScopeSpans
8184
{
8285
Scope = new InstrumentationScope
8386
{
84-
Name = name, // Name is enforced to not be null, but it can be empty.
85-
Version = version ?? string.Empty, // NRE throw by proto
87+
Name = activitySource.Name, // Name is enforced to not be null, but it can be empty.
88+
Version = activitySource.Version ?? string.Empty, // NRE throw by proto
8689
},
8790
};
8891
}
8992
else
9093
{
91-
spans.Scope.Name = name;
92-
spans.Scope.Version = version ?? string.Empty;
94+
scopeSpans.Scope.Name = activitySource.Name; // Name is enforced to not be null, but it can be empty.
95+
scopeSpans.Scope.Version = activitySource.Version ?? string.Empty; // NRE throw by proto
96+
}
97+
98+
if (activitySource.Tags != null)
99+
{
100+
var scopeAttributes = scopeSpans.Scope.Attributes;
101+
102+
if (activitySource.Tags is IReadOnlyList<KeyValuePair<string, object?>> activitySourceTagsList)
103+
{
104+
for (int i = 0; i < activitySourceTagsList.Count; i++)
105+
{
106+
if (scopeAttributes.Count < maxTags)
107+
{
108+
OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, activitySourceTagsList[i], attributeValueLengthLimit);
109+
}
110+
else
111+
{
112+
scopeSpans.Scope.DroppedAttributesCount++;
113+
}
114+
}
115+
}
116+
else
117+
{
118+
foreach (var tag in activitySource.Tags)
119+
{
120+
if (scopeAttributes.Count < maxTags)
121+
{
122+
OtlpTagWriter.Instance.TryWriteTag(ref scopeAttributes, tag, attributeValueLengthLimit);
123+
}
124+
else
125+
{
126+
scopeSpans.Scope.DroppedAttributesCount++;
127+
}
128+
}
129+
}
93130
}
94131

95-
return spans;
132+
return scopeSpans;
96133
}
97134

98135
[MethodImpl(MethodImplOptions.AggressiveInlining)]

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public async Task TestRecoveryAfterFailedExport()
7575
await httpClient.GetAsync($"/MockCollector/SetResponseCodes/{string.Join(",", codes.Select(x => (int)x))}");
7676

7777
var exportResults = new List<ExportResult>();
78-
var otlpExporter = new OtlpTraceExporter(new OtlpExporterOptions() { Endpoint = new Uri($"http://localhost:{testGrpcPort}") });
78+
using var otlpExporter = new OtlpTraceExporter(new OtlpExporterOptions() { Endpoint = new Uri($"http://localhost:{testGrpcPort}") });
7979
var delegatingExporter = new DelegatingExporter<Activity>
8080
{
8181
OnExportFunc = (batch) =>
@@ -183,7 +183,7 @@ public async Task GrpcRetryTests(bool useRetryTransmissionHandler, ExportResult
183183
.AddInMemoryCollection(new Dictionary<string, string?> { [ExperimentalOptions.OtlpRetryEnvVar] = useRetryTransmissionHandler ? "in_memory" : null })
184184
.Build();
185185

186-
var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration));
186+
using var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration));
187187

188188
var activitySourceName = "otel.grpc.retry.test";
189189
using var source = new ActivitySource(activitySourceName);
@@ -268,7 +268,7 @@ public async Task HttpRetryTests(bool useRetryTransmissionHandler, ExportResult
268268
.AddInMemoryCollection(new Dictionary<string, string?> { [ExperimentalOptions.OtlpRetryEnvVar] = useRetryTransmissionHandler ? "in_memory" : null })
269269
.Build();
270270

271-
var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration));
271+
using var otlpExporter = new OtlpTraceExporter(exporterOptions, new SdkLimitOptions(), new ExperimentalOptions(configuration));
272272

273273
var activitySourceName = "otel.http.retry.test";
274274
using var source = new ActivitySource(activitySourceName);
@@ -371,7 +371,7 @@ public async Task HttpPersistentStorageRetryTests(bool usePersistentStorageTrans
371371
transmissionHandler = new OtlpExporterTransmissionHandler<ExportTraceServiceRequest>(exportClient, exporterOptions.TimeoutMilliseconds);
372372
}
373373

374-
var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler);
374+
using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler);
375375

376376
var activitySourceName = "otel.http.persistent.storage.retry.test";
377377
using var source = new ActivitySource(activitySourceName);
@@ -510,7 +510,7 @@ public async Task GrpcPersistentStorageRetryTests(bool usePersistentStorageTrans
510510
transmissionHandler = new OtlpExporterTransmissionHandler<ExportTraceServiceRequest>(exportClient, exporterOptions.TimeoutMilliseconds);
511511
}
512512

513-
var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler);
513+
using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler);
514514

515515
var activitySourceName = "otel.grpc.persistent.storage.retry.test";
516516
using var source = new ActivitySource(activitySourceName);

0 commit comments

Comments
 (0)