Skip to content

Commit cbfc2bc

Browse files
authored
Upgrade to OTEL v1.11 and drop .NET6 Support (#202)
1 parent 40d1d9d commit cbfc2bc

File tree

43 files changed

+143
-2853
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+143
-2853
lines changed

.github/workflows/application-signals-e2e-test.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,6 @@ jobs:
139139
staging_distro_name: ${{ inputs.ec2-default-image-name }}
140140
dotnet-version: '8.0'
141141

142-
default-v6:
143-
needs: [ upload-main-build ]
144-
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/dotnet-ec2-default-test.yml@main
145-
secrets: inherit
146-
with:
147-
aws-region: us-east-1
148-
caller-workflow-name: 'main-build'
149-
staging_distro_name: ${{ inputs.ec2-default-image-name }}
150-
dotnet-version: '6.0'
151-
152142
dotnet-ec2-windows-test:
153143
needs: [ upload-main-build ]
154144
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/dotnet-ec2-windows-test.yml@main

.github/workflows/release_lambda.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ jobs:
9898
aws lambda publish-layer-version \
9999
--layer-name ${{ env.LAYER_NAME }} \
100100
--content S3Bucket=${{ env.BUCKET_NAME }},S3Key=aws-distro-opentelemetry-dotnet-instrumentation-linux-glibc-x64.zip \
101-
--compatible-runtimes dotnet6 dotnet8 \
101+
--compatible-runtimes dotnet8 \
102102
--compatible-architectures "x86_64" \
103103
--license-info "Apache-2.0" \
104104
--description "AWS Distro of OpenTelemetry Lambda Layer for .Net Runtime v${{ github.event.inputs.version }}" \

build/Build.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
internal partial class Build : NukeBuild
1414
{
15-
private const string OpenTelemetryAutoInstrumentationDefaultVersion = "v1.9.0";
15+
private const string OpenTelemetryAutoInstrumentationDefaultVersion = "v1.11.0";
1616
private static readonly AbsolutePath TestNuGetPackageApps = NukeBuild.RootDirectory / "test" / "test-applications" / "nuget-package";
1717

1818
[Solution("AWS.Distro.OpenTelemetry.AutoInstrumentation.sln")]
@@ -121,7 +121,7 @@ private static string GetOTelAutoInstrumentationFileName()
121121
{
122122
FileSystemTasks.CopyDirectoryRecursively(
123123
RootDirectory / "src" / "AWS.Distro.OpenTelemetry.AutoInstrumentation" / "bin" / this.configuration /
124-
"net6.0",
124+
"net8.0",
125125
this.openTelemetryDistributionFolder / "net",
126126
DirectoryExistsPolicy.Merge,
127127
FileExistsPolicy.Skip);

exporters/AWS.Distro.OpenTelemetry.Exporter.Xray.Udp/AWS.Distro.OpenTelemetry.Exporter.Xray.Udp.csproj

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<Version>0.1.0</Version>
44
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
5-
<TargetFrameworks>net6.0</TargetFrameworks>
5+
<TargetFrameworks>net8.0</TargetFrameworks>
66
<TargetFrameworks Condition="$(OS) == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
77
<ImplicitUsings>enable</ImplicitUsings>
88
<Nullable>enable</Nullable>
@@ -16,19 +16,10 @@
1616

1717
<ItemGroup>
1818
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
19-
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
20-
<PackageReference Include="OpenTelemetry.Api" Version="1.9.0" />
21-
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
22-
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
23-
<PackageReference Include="Google.Protobuf" Version="3.28.2" />
24-
<PackageReference Include="Grpc.Tools" Version="2.65.0" PrivateAssets="all" />
25-
</ItemGroup>
26-
27-
<ItemGroup>
28-
<Protobuf Include="opentelemetry\proto\collector\trace\v1\trace_service.proto" GrpcServices="none" ProtoRoot="." />
29-
<Protobuf Include="opentelemetry\proto\trace\v1\trace.proto" GrpcServices="none" ProtoRoot="." />
30-
<Protobuf Include="opentelemetry\proto\resource\v1\resource.proto" GrpcServices="none" ProtoRoot="." />
31-
<Protobuf Include="opentelemetry\proto\common\v1\common.proto" GrpcServices="none" ProtoRoot="." />
19+
<PackageReference Include="OpenTelemetry" Version="1.11.2" />
20+
<PackageReference Include="OpenTelemetry.Api" Version="1.11.2" />
21+
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.11.2" />
22+
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
3223
</ItemGroup>
3324

3425
<!-- Items that are only added when building the NuGet package -->

exporters/AWS.Distro.OpenTelemetry.Exporter.Xray.Udp/OtlpExporterUtils.cs

Lines changed: 44 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,9 @@
44
using System.Diagnostics;
55
using System.Reflection;
66
using AWS.Distro.OpenTelemetry.AutoInstrumentation.Logging;
7-
using Google.Protobuf;
87
using Microsoft.Extensions.Logging;
9-
using Newtonsoft.Json;
108
using OpenTelemetry;
11-
using OpenTelemetry.Proto.Collector.Trace.V1;
12-
using OpenTelemetry.Proto.Trace.V1;
139
using OpenTelemetry.Resources;
14-
using OtlpResource = OpenTelemetry.Proto.Resource.V1;
1510

1611
namespace AWS.Distro.OpenTelemetry.Exporter.Xray.Udp;
1712

@@ -20,152 +15,69 @@ public class OtlpExporterUtils
2015
private static readonly ILoggerFactory Factory = LoggerFactory.Create(builder => builder.AddProvider(new ConsoleLoggerProvider()));
2116
private static readonly ILogger Logger = Factory.CreateLogger<OtlpExporterUtils>();
2217

23-
// The SerializeSpans function builds a ExportTraceServiceRequest object by calling private "ToOtlpSpan" function
24-
// using reflection. "ToOtlpSpan" converts an Activity object into an OpenTelemetry.Proto.Trace.V1.Span object.
25-
// With the conversion above, the Activity object is converted to an Otel span object to be exported using the
26-
// UDP exporter. The "ToOtlpSpan" function can be found here:
27-
// https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ActivityExtensions.cs#L136
28-
public static byte[]? SerializeSpans(Batch<Activity> batch, Resource processResource)
29-
{
30-
Type? activityExtensionsType = Type.GetType("OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ActivityExtensions, OpenTelemetry.Exporter.OpenTelemetryProtocol");
18+
private static readonly MethodInfo? WriteTraceDataMethod;
19+
private static readonly object? SdkLimitOptions;
3120

21+
static OtlpExporterUtils() {
22+
Type? otlpSerializerType = Type.GetType("OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer.ProtobufOtlpTraceSerializer, OpenTelemetry.Exporter.OpenTelemetryProtocol");
3223
Type? sdkLimitOptionsType = Type.GetType("OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.SdkLimitOptions, OpenTelemetry.Exporter.OpenTelemetryProtocol");
3324

3425
if (sdkLimitOptionsType == null)
3526
{
3627
Logger.LogTrace("SdkLimitOptions Type was not found");
37-
return null;
28+
return;
3829
}
3930

40-
MethodInfo? toOtlpSpanMethod = activityExtensionsType?.GetMethod(
41-
"ToOtlpSpan",
42-
BindingFlags.Static | BindingFlags.NonPublic,
43-
null,
44-
new[] { typeof(Activity), sdkLimitOptionsType },
45-
null);
46-
47-
var request = new ExportTraceServiceRequest();
48-
var sdkLimitOptions = OtlpExporterUtils.GetSdkLimitOptions();
49-
50-
if (sdkLimitOptions == null)
31+
if (otlpSerializerType == null)
5132
{
52-
Logger.LogTrace("SdkLimitOptions Object was not found/created properly using the default parameterless constructor");
53-
return null;
33+
Logger.LogTrace("OtlpSerializer Type was not found");
34+
return;
5435
}
5536

56-
var otlpResource = OtlpExporterUtils.ToOtlpResource(processResource);
57-
58-
// Create a ResourceSpans instance to hold the span and the otlpResource
59-
ResourceSpans resourceSpans = new ResourceSpans
60-
{
61-
Resource = otlpResource,
62-
};
63-
var scopeSpans = new ScopeSpans();
64-
65-
if (toOtlpSpanMethod != null)
66-
{
67-
foreach (var activity in batch)
37+
WriteTraceDataMethod = otlpSerializerType.GetMethod(
38+
"WriteTraceData",
39+
BindingFlags.NonPublic | BindingFlags.Static,
40+
null,
41+
new[]
6842
{
69-
var otlpSpan = toOtlpSpanMethod.Invoke(null, new object[] { activity, sdkLimitOptions });
70-
71-
// The converters below are required since the the JsonConvert.DeserializeObject doesn't
72-
// know how to deserialize a BytesString or SpanKinds from otlp proto json object.
73-
var settings = new Newtonsoft.Json.JsonSerializerSettings();
74-
settings.Converters.Add(new ByteStringConverter());
75-
settings.Converters.Add(new SpanKindConverter());
76-
settings.Converters.Add(new StatusCodeConverter());
77-
78-
// Below is a workaround to casting and works by converting an object into JSON then converting the
79-
// JSON string back into the required object type. The reason casting isn't working is because of different
80-
// assemblies being used. To use the protobuf library, we need to have a local copy of the protobuf assembly.
81-
// Since upstream also has their own copy of the protobuf library, casting is not possible since the complier
82-
// is recognizing them as two different types.
83-
try
84-
{
85-
var otlpSpanJson = otlpSpan?.ToString();
86-
if (otlpSpanJson == null)
87-
{
88-
continue;
89-
}
90-
91-
var otlpSpanConverted = JsonConvert.DeserializeObject<Span>(otlpSpanJson, settings);
92-
scopeSpans.Spans.Add(otlpSpanConverted);
93-
}
94-
catch (Exception ex)
95-
{
96-
Logger.LogError($"Error converting OtlpSpan to/from JSON: {ex.Message}");
97-
}
98-
}
99-
100-
resourceSpans.ScopeSpans.Add(scopeSpans);
101-
request.ResourceSpans.Add(resourceSpans);
102-
}
103-
else
104-
{
105-
Logger.LogTrace("ActivityExtensions.ToOtlpSpan method is not found");
106-
}
107-
108-
return request.ToByteArray();
43+
typeof(byte[]).MakeByRefType(), // ref byte[] buffer
44+
typeof(int), // int writePosition
45+
sdkLimitOptionsType, // SdkLimitOptions
46+
typeof(Resource), // Resource?
47+
typeof(Batch<Activity>).MakeByRefType() // in Batch<Activity>
48+
},
49+
null)
50+
?? throw new MissingMethodException("WriteTraceData not found"); // :contentReference[oaicite:1]{index=1}
51+
52+
SdkLimitOptions = GetSdkLimitOptions();
10953
}
11054

111-
// Function that uses reflection to call ResourceExtensions.ToOtlpResource function.
112-
// This functions converts from an OpenTelemetry.Resources.Resource to
113-
// OpenTelemetry.Proto.Resource.V1.Resource (protobuf resource to be exported)
114-
private static OtlpResource.Resource? ToOtlpResource(Resource processResource)
55+
// The WriteTraceData function builds writes data to the buffer byte[] object by calling private "WriteTraceData" function
56+
// using reflection. "WriteTraceData" is based on the latest v1.11.2 version of the OpenTelemetry.Exporter.OpenTelemetryProtocol
57+
// depedency specifically found at https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs#L23
58+
// and used the by the OTLP Exporters.
59+
public static int WriteTraceData(
60+
ref byte[] buffer,
61+
int writePosition,
62+
Resource? resource,
63+
in Batch<Activity> batch)
11564
{
116-
Type? resourceExtensionsType = Type.GetType("OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ResourceExtensions, OpenTelemetry.Exporter.OpenTelemetryProtocol");
117-
118-
if (resourceExtensionsType == null)
119-
{
120-
Logger.LogTrace("ResourceExtensions Type was not found");
121-
return null;
122-
}
123-
124-
MethodInfo? toOtlpResourceMethod = resourceExtensionsType.GetMethod(
125-
"ToOtlpResource",
126-
BindingFlags.Static | BindingFlags.Public,
127-
null,
128-
new[] { typeof(Resource) },
129-
null);
130-
131-
if (toOtlpResourceMethod == null)
65+
if (SdkLimitOptions == null)
13266
{
133-
Logger.LogTrace("ResourceExtensions.ToOtlpResource Method was not found");
134-
return null;
67+
Logger.LogTrace("SdkLimitOptions Object was not found/created properly using the default parameterless constructor");
68+
return -1;
13569
}
70+
71+
// Pack arguments (ref/in remain by-ref in the args array)
72+
object[] args = { buffer, writePosition, SdkLimitOptions, resource!, batch! };
13673

137-
var otlpResource = toOtlpResourceMethod.Invoke(null, new object[] { processResource });
138-
139-
if (otlpResource == null)
140-
{
141-
Logger.LogTrace("OtlpResource object cannot be converted from OpenTelemetry.Resources");
142-
return null;
143-
}
74+
// Invoke static method (null target) :contentReference[oaicite:2]{index=2}
75+
var result = (int)WriteTraceDataMethod?.Invoke(obj: null, parameters: args)!;
14476

145-
// Below is a workaround to casting and works by converting an object into JSON then converting the
146-
// JSON string back into the required object type. The reason casting isn't working is because of different
147-
// assemblies being used. To use the protobuf library, we need to have a local copy of the protobuf assembly.
148-
// Since upstream also has their own copy of the protobuf library, casting is not possible since the complier
149-
// is recognizing them as two different types.
150-
try
151-
{
152-
// ToString method from OpenTelemetry.Proto.Resource.V1.Resource already converts the object into
153-
// Json using the proper converters.
154-
string? otlpResourceJson = otlpResource.ToString();
155-
if (otlpResourceJson == null)
156-
{
157-
Logger.LogTrace("OtlpResource object cannot be converted to JSON");
158-
return null;
159-
}
77+
// Unpack ref-buffer
78+
buffer = (byte[])args[0];
16079

161-
var otlpResourceConverted = JsonConvert.DeserializeObject<OtlpResource.Resource>(otlpResourceJson);
162-
return otlpResourceConverted;
163-
}
164-
catch (Exception ex)
165-
{
166-
Logger.LogError($"Error converting OtlpResource to/from JSON: {ex.Message}");
167-
return null;
168-
}
80+
return result;
16981
}
17082

17183
// Uses reflection to the get the SdkLimitOptions required to invoke the ToOtlpSpan function used in the
@@ -185,84 +97,4 @@ public class OtlpExporterUtils
18597
object? sdkLimitOptionsInstance = Activator.CreateInstance(sdkLimitOptionsType);
18698
return sdkLimitOptionsInstance;
18799
}
188-
}
189-
190-
internal class ByteStringConverter : JsonConverter<ByteString>
191-
{
192-
/// <inheritdoc/>
193-
public override ByteString? ReadJson(JsonReader reader, Type objectType, ByteString? existingValue, bool hasExistingValue, JsonSerializer serializer)
194-
{
195-
var base64String = (string?)reader.Value;
196-
return ByteString.FromBase64(base64String);
197-
}
198-
199-
/// <inheritdoc/>
200-
public override void WriteJson(JsonWriter writer, ByteString? value, JsonSerializer serializer)
201-
{
202-
writer.WriteValue(value?.ToBase64());
203-
}
204-
}
205-
206-
internal class SpanKindConverter : JsonConverter<Span.Types.SpanKind>
207-
{
208-
/// <inheritdoc/>
209-
public override Span.Types.SpanKind ReadJson(JsonReader reader, Type objectType, Span.Types.SpanKind existingValue, bool hasExistingValue, JsonSerializer serializer)
210-
{
211-
// Handle the string to enum conversion
212-
string? enumString = reader.Value?.ToString();
213-
214-
// Convert the string representation to the corresponding enum value
215-
switch (enumString)
216-
{
217-
case "SPAN_KIND_CLIENT":
218-
return Span.Types.SpanKind.Client;
219-
case "SPAN_KIND_SERVER":
220-
return Span.Types.SpanKind.Server;
221-
case "SPAN_KIND_INTERNAL":
222-
return Span.Types.SpanKind.Internal;
223-
case "SPAN_KIND_PRODUCER":
224-
return Span.Types.SpanKind.Producer;
225-
case "SPAN_KIND_CONSUMER":
226-
return Span.Types.SpanKind.Consumer;
227-
default:
228-
throw new JsonSerializationException($"Unknown SpanKind: {enumString}");
229-
}
230-
}
231-
232-
/// <inheritdoc/>
233-
public override void WriteJson(JsonWriter writer, Span.Types.SpanKind value, JsonSerializer serializer)
234-
{
235-
// Write the string representation of the enum
236-
writer.WriteValue(value.ToString());
237-
}
238-
}
239-
240-
internal class StatusCodeConverter : JsonConverter<Status.Types.StatusCode>
241-
{
242-
/// <inheritdoc/>
243-
public override Status.Types.StatusCode ReadJson(JsonReader reader, Type objectType, Status.Types.StatusCode existingValue, bool hasExistingValue, JsonSerializer serializer)
244-
{
245-
// Handle the string to enum conversion
246-
string? enumString = reader.Value?.ToString();
247-
248-
// Convert the string representation to the corresponding enum value
249-
switch (enumString)
250-
{
251-
case "STATUS_CODE_UNSET":
252-
return Status.Types.StatusCode.Unset;
253-
case "STATUS_CODE_OK":
254-
return Status.Types.StatusCode.Ok;
255-
case "STATUS_CODE_ERROR":
256-
return Status.Types.StatusCode.Error;
257-
default:
258-
throw new JsonSerializationException($"Unknown StatusCode: {enumString}");
259-
}
260-
}
261-
262-
/// <inheritdoc/>
263-
public override void WriteJson(JsonWriter writer, Status.Types.StatusCode value, JsonSerializer serializer)
264-
{
265-
// Write the string representation of the enum
266-
writer.WriteValue(value.ToString());
267-
}
268-
}
100+
}

0 commit comments

Comments
 (0)