opentelemetry-dotnet-contrib
opentelemetry-dotnet-contrib copied to clipboard
Add ECS Task Metadata
Issue with OpenTelemetry.Contrib.Extensions.AWSXRay
List of all OpenTelemetry NuGet
packages and version that you are
using (e.g. OpenTelemetry 1.0.2
):
- OpenTelemetry.Contrib.Extensions.AWSXRay Latest
Runtime version (e.g. net462
, net48
, netcoreapp3.1
, net6.0
etc. You can
find this information from the *.csproj
file):
- net6.0
Is this a feature request or a bug?
- [x] Feature Request
- [ ] Bug
What is the expected behavior?
All relevant Container and Cloud runtime information included in Resource information
What is the actual behavior?
Only container.id
, cloud.platform
, and cloud.provider
set.
Additional Context
I believe this is because getting the rest of the information requires pinging the Metadata endpoint for a given task, and this is normally an async operation. However, as of .NET 5
, there's a sync request method that could be used to retrieve this information. I propose adding in the rest of the missing cloud
and container
information conditionally.
My very PoC implementation of the above:
private class ECSExtendedDetector : OpenTelemetry.Resources.IResourceDetector
{
private readonly AWSECSResourceDetector _awsDetecter = new();
public Resource Detect()
{
var ecsBase = _awsDetecter.Detect();
if (ecsBase is null) return Resource.Empty;
var ecsMetaUrl = Environment.GetEnvironmentVariable("ECS_CONTAINER_METADATA_URI_V4");
if (ecsMetaUrl is null) {
return Resource.Empty;
}
var ecsResourceBase = new Resource(ecsBase);
var containerId = ecsBase.First(pair => pair.Key == "container.id").Value as string;
using var client = new HttpClient();
var containerMetaRequest = new HttpRequestMessage(HttpMethod.Get, ecsMetaUrl);
var containerMetaResult = client.Send(containerMetaRequest);
if (!containerMetaResult.IsSuccessStatusCode) {
return ecsResourceBase;
}
var taskMetaRequest = new HttpRequestMessage(HttpMethod.Get, $"{ecsMetaUrl}/task");
var taskMetaResult = client.Send(taskMetaRequest);
if (!taskMetaResult.IsSuccessStatusCode) {
return ecsResourceBase;
}
var containerMeta = JsonSerializer.Deserialize<JsonDocument>(containerMetaResult.Content.ReadAsStream())!.RootElement!;
var taskMeta = JsonSerializer.Deserialize<JsonDocument>(taskMetaResult.Content.ReadAsStream())!.RootElement!;
var taskArn = Amazon.Arn.Parse(taskMeta!.GetProperty("TaskARN").GetString());
var image = containerMeta!.GetProperty("Image").ToString()!;
var imageParts = image.Split(":", 2);
return ecsResourceBase.Merge(new Resource(new Dictionary<string, object> {
{"cloud.account.id", taskArn.AccountId},
{"cloud.region", taskArn.Region},
{"cloud.availability_zone", taskMeta.GetProperty("AvailabilityZone").GetString()!},
{"container.name", containerMeta.GetProperty("Name").GetString()!},
{"container.image.name", imageParts[0]},
{"container.image.tag", imageParts[1]},
{"aws.ecs.container.arn", containerMeta.GetProperty("ContainerARN").GetString()!},
{"aws.ecs.cluster.arn", $"arn:aws:ecs:${taskArn.Region}:${taskArn.AccountId}:cluster/{taskMeta.GetProperty("Cluster").GetString()!}"},
{"aws.ecs.launchtype", taskMeta.GetProperty("LaunchType").GetString()!.ToLowerInvariant()},
{"aws.ecs.task.arn", taskArn.ToString()},
{"aws.ecs.task.family", taskMeta.GetProperty("Family").GetString()!},
{"aws.ecs.task.revision", int.Parse(taskMeta.GetProperty("Revision").GetString()!)},
}));
}
}