Skip to content

Commit

Permalink
change AWSemanticConventions from a static to instances to improve te…
Browse files Browse the repository at this point in the history
…st stability
  • Loading branch information
ppittle committed Dec 10, 2024
1 parent 416415d commit 49db65a
Show file tree
Hide file tree
Showing 23 changed files with 658 additions and 478 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@ 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 @@ -17,24 +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 IDictionary<string, string> ParameterAttributeMap =
new Dictionary<string, string>()
.AddAttributeAWSDynamoTableName("TableName")
.AddAttributeAWSSQSQueueUrl("QueueUrl")
.AddAttributeGenAiModelId("ModelId")
.AddAttributeAWSBedrockAgentId("AgentId")
.AddAttributeAWSBedrockDataSourceId("DataSourceId")
.AddAttributeAWSBedrockGuardrailId("GuardrailId")
.AddAttributeAWSBedrockKnowledgeBaseId("KnowledgeBaseId");

// 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 @@ -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.SetTagAttributeDbSystemToDynamoDb();
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.SetTagAttributeGenAiSystemToBedrock();
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 @@ -3,7 +3,6 @@

using Amazon;
using Amazon.Runtime.Telemetry;
using OpenTelemetry.AWS;
using OpenTelemetry.Instrumentation.AWS;
using OpenTelemetry.Instrumentation.AWS.Implementation;
using OpenTelemetry.Instrumentation.AWS.Implementation.Tracing;
Expand Down Expand Up @@ -39,8 +38,6 @@ public static TracerProviderBuilder AddAWSInstrumentation(
var awsClientOptions = new AWSClientInstrumentationOptions();
configure?.Invoke(awsClientOptions);

AWSSemanticConventions.SemanticConventionVersion = awsClientOptions.SemanticConventionVersion;

_ = new AWSClientsInstrumentation(awsClientOptions);

AWSConfigs.TelemetryProvider.RegisterTracerProvider(new AWSTracerProvider());
Expand Down
11 changes: 7 additions & 4 deletions src/OpenTelemetry.Instrumentation.AWSLambda/AWSLambdaWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using Amazon.Lambda.Core;
using OpenTelemetry.AWS;
using OpenTelemetry.Instrumentation.AWSLambda.Implementation;
using OpenTelemetry.Internal;
using OpenTelemetry.Trace;
Expand All @@ -25,6 +26,8 @@ public static class AWSLambdaWrapper
/// </summary>
internal static bool DisableAwsXRayContextExtraction { get; set; }

internal static AWSSemanticConventions AWSSemanticConventions { get; set; } = new();

#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters

/// <summary>
Expand Down Expand Up @@ -163,9 +166,9 @@ public static Task<TResult> TraceAsync<TInput, TResult>(
}

// No parallel invocation of the same lambda handler expected.
var functionTags = AWSLambdaUtils.GetFunctionTags(input, context, isColdStart);
var functionTags = new AWSLambdaUtils(AWSSemanticConventions).GetFunctionTags(input, context, isColdStart);
isColdStart = false;
var httpTags = AWSLambdaHttpUtils.GetHttpTags(input);
var httpTags = AWSLambdaHttpUtils.GetHttpTags(AWSSemanticConventions, input);

// We assume that functionTags and httpTags have no intersection.
var activityName = AWSLambdaUtils.GetFunctionName(context) ?? "AWS Lambda Invoke";
Expand Down Expand Up @@ -210,7 +213,7 @@ private static TResult TraceInternal<TInput, TResult>(
try
{
var result = handler(input, context);
AWSLambdaHttpUtils.SetHttpTagsFromResult(activity, result);
AWSLambdaHttpUtils.SetHttpTagsFromResult(AWSSemanticConventions, activity, result);
return result;
}
catch (Exception ex)
Expand Down Expand Up @@ -238,7 +241,7 @@ private static async Task<TResult> TraceInternalAsync<TInput, TResult>(
try
{
var result = await handlerAsync(input, context).ConfigureAwait(false);
AWSLambdaHttpUtils.SetHttpTagsFromResult(activity, result);
AWSLambdaHttpUtils.SetHttpTagsFromResult(AWSSemanticConventions, activity, result);
return result;
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@ internal class AWSLambdaHttpUtils
private const string HeaderXForwardedProto = "x-forwarded-proto";
private const string HeaderHost = "host";

internal static IEnumerable<KeyValuePair<string, object>> GetHttpTags<TInput>(TInput input)
internal static IEnumerable<KeyValuePair<string, object>> GetHttpTags<TInput>(AWSSemanticConventions semanticConventions, TInput input)
{
var tags = new List<KeyValuePair<string, object>>();

string? httpScheme;
string? httpTarget;
string? urlPath;
Expand Down Expand Up @@ -61,9 +59,11 @@ internal static IEnumerable<KeyValuePair<string, object>> GetHttpTags<TInput>(TI
(hostName, hostPort) = GetHostAndPort(httpScheme, albHostHeader);
break;
default:
return tags;
return Enumerable.Empty<KeyValuePair<string, object>>();
}

var tags = semanticConventions.AttributeBuilder;

if (httpScheme != null)
{
tags.AddAttributeHttpScheme(httpScheme, addIfEmpty: true);
Expand Down Expand Up @@ -93,10 +93,10 @@ internal static IEnumerable<KeyValuePair<string, object>> GetHttpTags<TInput>(TI
tags.AddAttributeNetHostPort(hostPort);
tags.AddAttributeServerPort(hostPort);

return tags;
return tags.Build();
}

internal static void SetHttpTagsFromResult(Activity? activity, object? result)
internal static void SetHttpTagsFromResult(AWSSemanticConventions semanticConventions, Activity? activity, object? result)
{
if (activity == null || result == null)
{
Expand All @@ -106,16 +106,16 @@ internal static void SetHttpTagsFromResult(Activity? activity, object? result)
switch (result)
{
case APIGatewayProxyResponse response:
activity.SetTagAttributeHttpStatusCode(response.StatusCode);
activity.SetTagAttributeHttpResponseStatusCode(response.StatusCode);
semanticConventions.TagBuilder.SetTagAttributeHttpResponseStatusCode(activity, response.StatusCode);
semanticConventions.TagBuilder.SetTagAttributeHttpResponseStatusCode(activity, response.StatusCode);
break;
case APIGatewayHttpApiV2ProxyResponse responseV2:
activity.SetTagAttributeHttpStatusCode(responseV2.StatusCode);
activity.SetTagAttributeHttpResponseStatusCode(responseV2.StatusCode);
semanticConventions.TagBuilder.SetTagAttributeHttpStatusCode(activity, responseV2.StatusCode);
semanticConventions.TagBuilder.SetTagAttributeHttpResponseStatusCode(activity, responseV2.StatusCode);
break;
case ApplicationLoadBalancerResponse albResponse:
activity.SetTagAttributeHttpStatusCode(albResponse.StatusCode);
activity.SetTagAttributeHttpResponseStatusCode(albResponse.StatusCode);
semanticConventions.TagBuilder.SetTagAttributeHttpResponseStatusCode(activity, albResponse.StatusCode);
semanticConventions.TagBuilder.SetTagAttributeHttpResponseStatusCode(activity, albResponse.StatusCode);
break;
default:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,27 @@ namespace OpenTelemetry.Instrumentation.AWSLambda.Implementation;

internal sealed class AWSLambdaResourceDetector : IResourceDetector
{
private readonly AWSSemanticConventions semanticConventionBuilder;

public AWSLambdaResourceDetector(AWSSemanticConventions semanticConventionBuilder)
{
this.semanticConventionBuilder = semanticConventionBuilder;
}

/// <summary>
/// Detect the resource attributes for AWS Lambda.
/// </summary>
/// <returns>Detected resource.</returns>
public Resource Detect()
{
var resourceAttributes =
new List<KeyValuePair<string, object>>(4)
this.semanticConventionBuilder
.AttributeBuilder
.AddAttributeCloudProviderIsAWS()
.AddAttributeCloudRegion(AWSLambdaUtils.GetAWSRegion())
.AddAttributeFaasName(AWSLambdaUtils.GetFunctionName())
.AddAttributeFaasVersion(AWSLambdaUtils.GetFunctionVersion());
.AddAttributeFaasVersion(AWSLambdaUtils.GetFunctionVersion())
.Build();

return new Resource(resourceAttributes);
}
Expand Down
Loading

0 comments on commit 49db65a

Please sign in to comment.