Adding Azure Attributes to OpenTelemetry Spans

When you’re working with Azure Functions, or AppService (including ContainerApps) there are a number of Environment Variables that can be really interesting to use for debugging your production apps. These are pretty easy to add to OpenTelemetry.

These include things like the AppService Name, but also Resource Group, runtime versions and Instance Ids. The full list is here

So I wrote an extension method for the ResourceBuilder that will automatically add them if they exist in the current context.

    public static class ResourceBuilderExtensions
    {
        /// <summary>
        /// Adds any values it finds from the AppService environment variables
        /// <see href="https://docs.microsoft.com/en-us/azure/app-service/reference-app-settings">AppService Environment Variables</see>
        /// </summary>
        /// <param name="resourceBuilder"></param>
        /// <returns></returns>
        public static ResourceBuilder AddAzureAttributes(this ResourceBuilder resourceBuilder)
        {
            var envVars = Environment.GetEnvironmentVariables();
            var attributesToAdd = new List<KeyValuePair<string, object>>();

            var envVarsToAdd = new List<Tuple<string, string>> {
                new("azure.appservice.site_name", "WEBSITE_SITE_NAME"),
                new("azure.resource_group", "WEBSITE_RESOURCE_GROUP"),
                new("azure.subscription_id", "WEBSITE_OWNER_NAME"),
                new("azure.region", "REGION_NAME"),
                new("azure.appservice.platform_version", "WEBSITE_PLATFORM_VERSION"),
                new("azure.appservice.sku", "WEBSITE_SKU"),
                new("azure.appservice.bitness", "SITE_BITNESS"), // x86 vs AMD64
                new("azure.appservice.hostname", "WEBSITE_HOSTNAME"),
                new("azure.appservice.role_instance_id", "WEBSITE_ROLE_INSTANCE_ID"),
                new("azure.appservice.slot_name", "WEBSITE_SLOT_NAME"),
                new("azure.appservice.instance_id", "WEBSITE_INSTANCE_ID"),
                new("azure.appservice.website_logging_enabled", "WEBSITE_HTTPLOGGING_ENABLED"),
                new("azure.appservice.internal_ip", "WEBSITE_PRIVATE_IP"),
                new("azure.appservice.functions_extensions_version", "FUNCTIONS_EXTENSION_VERSION"),
                new("azure.appservice.functions.worker_runtime", "FUNCTIONS_WORKER_RUNTIME"),
                new("azure.appservice.function_placeholder_mode", "WEBSITE_PLACEHOLDER_MODE"),
            };

            resourceBuilder.AddAttributes(
                envVarsToAdd
                    .Where(attr => envVars.Contains(attr.Item2) && 
                           !string.IsNullOrEmpty(envVars[attr.Item2].ToString()))
                    .Select(attr =>
                    {
                        var (name, key) = attr;
                        return new KeyValuePair<string, object>(name, envVars[key].ToString());
                    })
            );
            
            resourceBuilder.AddAttributes(attributesToAdd);
            return resourceBuilder;
        }
    }

With this added, you should now be able to call it as part of your OpenTelemetry setup.

appBuilder.Services.AddOpenTelemetryTracing(builder => {
    builder.SetResourceBuilder(
        ResourceBuilder
            .CreateDefault()
            .AddAzureAttributes()
    );
});

That’s it, you’ll now get all that information in every span for that service, allowing you to do some cool filtering of the data by Azure attributes.

What sort of questions can you answer with it?

  • Which Azure AppService Instance have the longest duration?
  • What Resource Groups have the most Azure AppService Instances producing telemetry
  • Which Resource Groups cause the most CosmosDb usage (by adding the CosmosDb Request Charge to each outbound span).
  • Which Function App Instances are the ones receiving traffic?

Conclusion

Resource Attributes are really easy to add, so why aren’t you doing it?

Leave a comment

Website Built with WordPress.com.

Up ↑