Skip to content

Commit

Permalink
Upgrade Semantic Convention handling in AWS libraries. (#2367)
Browse files Browse the repository at this point in the history
Co-authored-by: Alan West <[email protected]>
Co-authored-by: Piotr Kiełkowicz <[email protected]>
  • Loading branch information
3 people authored Dec 20, 2024
1 parent 31cea68 commit c455a85
Show file tree
Hide file tree
Showing 48 changed files with 2,526 additions and 583 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#nullable enable
OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion
OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion.Legacy = -1 -> OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion
OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion.Latest = 0 -> OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion
OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion.V1_29_0 = 2 -> OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion
OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion.V1_28_0 = 1 -> OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion
OpenTelemetry.Instrumentation.AWS.AWSClientInstrumentationOptions
OpenTelemetry.Instrumentation.AWS.AWSClientInstrumentationOptions.AWSClientInstrumentationOptions() -> void
OpenTelemetry.Instrumentation.AWS.AWSClientInstrumentationOptions.SemanticConventionVersion.get -> OpenTelemetry.Instrumentation.AWS.SemanticConventionVersion
OpenTelemetry.Instrumentation.AWS.AWSClientInstrumentationOptions.SemanticConventionVersion.set -> void
OpenTelemetry.Instrumentation.AWS.AWSClientInstrumentationOptions.SuppressDownstreamInstrumentation.get -> bool
OpenTelemetry.Instrumentation.AWS.AWSClientInstrumentationOptions.SuppressDownstreamInstrumentation.set -> void
OpenTelemetry.Trace.TracerProviderBuilderExtensions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using OpenTelemetry.AWS;

namespace OpenTelemetry.Instrumentation.AWS;

/// <summary>
Expand All @@ -12,4 +14,7 @@ public class AWSClientInstrumentationOptions
/// Gets or sets a value indicating whether downstream instrumentation is suppressed.
/// </summary>
public bool SuppressDownstreamInstrumentation { get; set; }

/// <inheritdoc cref="AWS.SemanticConventionVersion"/>
public SemanticConventionVersion SemanticConventionVersion { get; set; } = AWSSemanticConventions.DefaultSemanticConventionVersion;
}
8 changes: 8 additions & 0 deletions src/OpenTelemetry.Instrumentation.AWS/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

* Introduce `AWSClientInstrumentationOptions.SemanticConventionVersion` which
provides a mechanism for developers to opt-in to newer versions of the
of the OpenTelemetry Semantic Conventions. Currently, you need to opt-in
to these new conventions. In the upcoming stable release of this library,
the new conventions will be enabled by default, and the conventions this library
currently emit will no longer be supported.
([#2367](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2367))

## 1.10.0-beta.2

Released 2024-Dec-12
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,28 @@
// SPDX-License-Identifier: Apache-2.0

using Amazon.Runtime;
using OpenTelemetry.AWS;

namespace OpenTelemetry.Instrumentation.AWS.Implementation;

internal class AWSServiceHelper
{
internal static IReadOnlyDictionary<string, List<string>> ServiceRequestParameterMap = new Dictionary<string, List<string>>()
public AWSServiceHelper(AWSSemanticConventions semanticConventions)
{
this.ParameterAttributeMap =
semanticConventions
.ParameterMappingBuilder
.AddAttributeAWSDynamoTableName("TableName")
.AddAttributeAWSSQSQueueUrl("QueueUrl")
.AddAttributeGenAiModelId("ModelId")
.AddAttributeAWSBedrockAgentId("AgentId")
.AddAttributeAWSBedrockDataSourceId("DataSourceId")
.AddAttributeAWSBedrockGuardrailId("GuardrailId")
.AddAttributeAWSBedrockKnowledgeBaseId("KnowledgeBaseId")
.Build();
}

internal static IReadOnlyDictionary<string, List<string>> ServiceRequestParameterMap { get; } = new Dictionary<string, List<string>>()
{
{ AWSServiceType.DynamoDbService, ["TableName"] },
{ AWSServiceType.SQSService, ["QueueUrl"] },
Expand All @@ -16,25 +32,14 @@ internal class AWSServiceHelper
{ AWSServiceType.BedrockRuntimeService, ["ModelId"] },
};

internal static IReadOnlyDictionary<string, List<string>> ServiceResponseParameterMap = new Dictionary<string, List<string>>()
internal static IReadOnlyDictionary<string, List<string>> ServiceResponseParameterMap { get; } = new Dictionary<string, List<string>>()
{
{ AWSServiceType.BedrockService, ["GuardrailId"] },
{ AWSServiceType.BedrockAgentService, ["AgentId", "DataSourceId"] },
};

internal static IReadOnlyDictionary<string, string> ParameterAttributeMap = new Dictionary<string, string>()
{
{ "TableName", AWSSemanticConventions.AttributeAWSDynamoTableName },
{ "QueueUrl", AWSSemanticConventions.AttributeAWSSQSQueueUrl },
{ "ModelId", AWSSemanticConventions.AttributeGenAiModelId },
{ "AgentId", AWSSemanticConventions.AttributeAWSBedrockAgentId },
{ "DataSourceId", AWSSemanticConventions.AttributeAWSBedrockDataSourceId },
{ "GuardrailId", AWSSemanticConventions.AttributeAWSBedrockGuardrailId },
{ "KnowledgeBaseId", AWSSemanticConventions.AttributeAWSBedrockKnowledgeBaseId },
};

// for Bedrock Agent operations, we map each supported operation to one resource: Agent, DataSource, or KnowledgeBase
internal static List<string> BedrockAgentAgentOps =
internal static List<string> BedrockAgentAgentOps { get; } =
[
"CreateAgentActionGroup",
"CreateAgentAlias",
Expand All @@ -56,7 +61,7 @@ internal class AWSServiceHelper
"UpdateAgent"
];

internal static List<string> BedrockAgentKnowledgeBaseOps =
internal static List<string> BedrockAgentKnowledgeBaseOps { get; } =
[
"AssociateAgentKnowledgeBase",
"CreateDataSource",
Expand All @@ -68,13 +73,15 @@ internal class AWSServiceHelper
"UpdateAgentKnowledgeBase"
];

internal static List<string> BedrockAgentDataSourceOps =
internal static List<string> BedrockAgentDataSourceOps { get; } =
[
"DeleteDataSource",
"GetDataSource",
"UpdateDataSource"
];

internal IDictionary<string, string> ParameterAttributeMap { get; }

internal static IReadOnlyDictionary<string, string> OperationNameToResourceMap()
{
var operationClassMap = new Dictionary<string, string>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ namespace OpenTelemetry.Instrumentation.AWS.Implementation;
/// </summary>
internal class AWSTracingPipelineCustomizer : IRuntimePipelineCustomizer
{
public const string UniqueName = "AWS Tracing Registration Customization";

private readonly AWSClientInstrumentationOptions options;

public AWSTracingPipelineCustomizer(AWSClientInstrumentationOptions options)
{
this.options = options;
}

public string UniqueName => "AWS Tracing Registration Customization";
string IRuntimePipelineCustomizer.UniqueName => UniqueName;

public void Customize(Type serviceClientType, RuntimePipeline pipeline)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
using Amazon.Runtime;
using Amazon.Runtime.Internal;
using Amazon.Runtime.Telemetry;
using OpenTelemetry.AWS;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;

namespace OpenTelemetry.Instrumentation.AWS.Implementation;

Expand All @@ -19,25 +19,29 @@ namespace OpenTelemetry.Instrumentation.AWS.Implementation;
internal sealed class AWSTracingPipelineHandler : PipelineHandler
{
private readonly AWSClientInstrumentationOptions options;
private readonly AWSSemanticConventions awsSemanticConventions;
private readonly AWSServiceHelper awsServiceHelper;

public AWSTracingPipelineHandler(AWSClientInstrumentationOptions options)
{
this.options = options;
this.awsSemanticConventions = new AWSSemanticConventions(options.SemanticConventionVersion);
this.awsServiceHelper = new AWSServiceHelper(this.awsSemanticConventions);
}

public override void InvokeSync(IExecutionContext executionContext)
{
var activity = this.ProcessBeginRequest(executionContext);
base.InvokeSync(executionContext);
ProcessEndRequest(activity, executionContext);
this.ProcessEndRequest(activity, executionContext);
}

public override async Task<T> InvokeAsync<T>(IExecutionContext executionContext)
{
var activity = this.ProcessBeginRequest(executionContext);
var ret = await base.InvokeAsync<T>(executionContext).ConfigureAwait(false);

ProcessEndRequest(activity, executionContext);
this.ProcessEndRequest(activity, executionContext);

return ret;
}
Expand All @@ -48,7 +52,7 @@ public override async Task<T> InvokeAsync<T>(IExecutionContext executionContext)
"IL2075",
Justification = "The reflected properties were already used by the AWS SDK's marshallers so the properties could not have been trimmed.")]
#endif
private static void AddResponseSpecificInformation(Activity activity, IExecutionContext executionContext)
private void AddResponseSpecificInformation(Activity activity, IExecutionContext executionContext)
{
var service = executionContext.RequestContext.ServiceMetaData.ServiceId;
var responseContext = executionContext.ResponseContext;
Expand All @@ -67,14 +71,14 @@ private static void AddResponseSpecificInformation(Activity activity, IExecution
var operationName = Utils.RemoveSuffix(response.GetType().Name, "Response");
if (AWSServiceHelper.OperationNameToResourceMap()[operationName] == parameter)
{
AddBedrockAgentResponseAttribute(activity, response, parameter);
this.AddBedrockAgentResponseAttribute(activity, response, parameter);
}
}

var property = response.GetType().GetProperty(parameter);
if (property != null)
{
if (AWSServiceHelper.ParameterAttributeMap.TryGetValue(parameter, out var attribute))
if (this.awsServiceHelper.ParameterAttributeMap.TryGetValue(parameter, out var attribute))
{
activity.SetTag(attribute, property.GetValue(response));
}
Expand All @@ -95,7 +99,7 @@ private static void AddResponseSpecificInformation(Activity activity, IExecution
"IL2075",
Justification = "The reflected properties were already used by the AWS SDK's marshallers so the properties could not have been trimmed.")]
#endif
private static void AddBedrockAgentResponseAttribute(Activity activity, AmazonWebServiceResponse response, string parameter)
private void AddBedrockAgentResponseAttribute(Activity activity, AmazonWebServiceResponse response, string parameter)
{
var responseObject = response.GetType().GetProperty(Utils.RemoveSuffix(parameter, "Id"));
if (responseObject != null)
Expand All @@ -106,7 +110,7 @@ private static void AddBedrockAgentResponseAttribute(Activity activity, AmazonWe
var property = attributeObject.GetType().GetProperty(parameter);
if (property != null)
{
if (AWSServiceHelper.ParameterAttributeMap.TryGetValue(parameter, out var attribute))
if (this.awsServiceHelper.ParameterAttributeMap.TryGetValue(parameter, out var attribute))
{
activity.SetTag(attribute, property.GetValue(attributeObject));
}
Expand All @@ -121,7 +125,7 @@ private static void AddBedrockAgentResponseAttribute(Activity activity, AmazonWe
"IL2075",
Justification = "The reflected properties were already used by the AWS SDK's marshallers so the properties could not have been trimmed.")]
#endif
private static void AddRequestSpecificInformation(Activity activity, IRequestContext requestContext)
private void AddRequestSpecificInformation(Activity activity, IRequestContext requestContext)
{
var service = requestContext.ServiceMetaData.ServiceId;

Expand All @@ -145,7 +149,7 @@ private static void AddRequestSpecificInformation(Activity activity, IRequestCon
var property = request.GetType().GetProperty(parameter);
if (property != null)
{
if (AWSServiceHelper.ParameterAttributeMap.TryGetValue(parameter, out var attribute))
if (this.awsServiceHelper.ParameterAttributeMap.TryGetValue(parameter, out var attribute))
{
activity.SetTag(attribute, property.GetValue(request));
}
Expand All @@ -161,7 +165,7 @@ private static void AddRequestSpecificInformation(Activity activity, IRequestCon

if (AWSServiceType.IsDynamoDbService(service))
{
activity.SetTag(SemanticConventions.AttributeDbSystem, AWSSemanticConventions.AttributeValueDynamoDb);
this.awsSemanticConventions.TagBuilder.SetTagAttributeDbSystemToDynamoDb(activity);
}
else if (AWSServiceType.IsSqsService(service))
{
Expand All @@ -175,18 +179,18 @@ private static void AddRequestSpecificInformation(Activity activity, IRequestCon
}
else if (AWSServiceType.IsBedrockRuntimeService(service))
{
activity.SetTag(AWSSemanticConventions.AttributeGenAiSystem, AWSSemanticConventions.AttributeAWSBedrock);
this.awsSemanticConventions.TagBuilder.SetTagAttributeGenAiSystemToBedrock(activity);
}
}

private static void ProcessEndRequest(Activity? activity, IExecutionContext executionContext)
private void ProcessEndRequest(Activity? activity, IExecutionContext executionContext)
{
if (activity == null || !activity.IsAllDataRequested)
{
return;
}

AddResponseSpecificInformation(activity, executionContext);
this.AddResponseSpecificInformation(activity, executionContext);
}

private Activity? ProcessBeginRequest(IExecutionContext executionContext)
Expand All @@ -205,7 +209,7 @@ private static void ProcessEndRequest(Activity? activity, IExecutionContext exec
return null;
}

AddRequestSpecificInformation(currentActivity, executionContext.RequestContext);
this.AddRequestSpecificInformation(currentActivity, executionContext.RequestContext);
return currentActivity;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<TargetFrameworks Condition="$(OS) == 'Windows_NT'">$(TargetFrameworks);$(NetFrameworkMinimumSupportedVersion)</TargetFrameworks>
<Description>AWS client instrumentation for OpenTelemetry .NET.</Description>
<MinVerTagPrefix>Instrumentation.AWS-</MinVerTagPrefix>
<DefineConstants>INSTRUMENTATION_AWS;$(DefineConstants)</DefineConstants>
</PropertyGroup>

<!-- Do not run Package Baseline Validation as this package has never released a stable version.
Expand All @@ -25,9 +26,9 @@
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)\src\Shared\AWS\*.cs" Link="Includes\AWS\%(Filename).cs" />
<Compile Include="$(RepoRoot)\src\Shared\AssemblyVersionExtensions.cs" Link="Includes\AssemblyVersionExtensions.cs" />
<Compile Include="$(RepoRoot)\src\Shared\Guard.cs" Link="Includes\Guard.cs" />
<Compile Include="$(RepoRoot)\src\Shared\SemanticConventions.cs" Link="Includes\SemanticConventions.cs" />
</ItemGroup>

</Project>
45 changes: 45 additions & 0 deletions src/OpenTelemetry.Instrumentation.AWS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,48 @@ public void ConfigureServices(IServiceCollection services)
.AddOtlpExporter());
}
```

## Semantic Conventions

_For an overview on Semantic Conventions, see
[OpenTelemetery - Semantic Conventions](https://opentelemetry.io/docs/concepts/semantic-conventions/)_.

While this library is intended for production use, it relies on several
Semantic Conventions that are still considered Experimental, meaning
they may undergo additional changes before becoming Stable. This can impact
the aggregation and analysis of telemetry signals in environments with
multiple applications or microservices.

For example, a microservice using an older version of the Semantic Conventions
for Http Attributes may emit `"http.method"` with a value of GET, while a
different microservice, using a new version of Semantic Convention may instead
emit the GET as `"http.request.method"`.

Future versions the OpenTelemetry.*.AWS libraries will include updates to the
Semantic Convention, which may break compatibility with a previous version.

The default will remain as `V1_28_0` until the next major version bump.

To opt in to automatic upgrades, you can use `SemanticConventionVersion.Latest`
or you can specify a specific version:

```csharp
using OpenTelemetry;
using OpenTelemetry.AWS;
using OpenTelemetry.Contrib.Extensions.AWSXRay.Trace;
using OpenTelemetry.Trace;

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddOpenTelemetryTracing((builder) => builder
.AddAWSInstrumentation(opt => {
// pin to a specific Semantic Convention version
opt.SemanticConventionVersion = SemanticConventionVersion.V1_29_0;
});
}
```

**NOTE:** Once a Semantic Convention becomes Stable, OpenTelemetry.*.AWS
libraries will remain on that version until the
next major version bump.
Loading

0 comments on commit c455a85

Please sign in to comment.