Skip to content
This repository has been archived by the owner on Apr 22, 2022. It is now read-only.

Commit

Permalink
Azure App Service Tags (DataDog#646)
Browse files Browse the repository at this point in the history
* collect resource id for azure app services
  • Loading branch information
colin-higgins authored and MikeGoldsmith committed Mar 20, 2020
1 parent 4c67ede commit f911924
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/Datadog.Trace/Agent/Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Datadog.Trace.Containers;
using Datadog.Trace.DogStatsd;
using Datadog.Trace.Logging;
using Datadog.Trace.PlatformHelpers;
using Datadog.Trace.Vendors.StatsdClient;
using MsgPack.Serialization;
using Newtonsoft.Json;
Expand Down Expand Up @@ -65,7 +65,7 @@ public Api(Uri baseEndpoint, DelegatingHandler delegatingHandler, IStatsd statsd
_client.DefaultRequestHeaders.Add(AgentHttpHeaderNames.TracerVersion, TracerConstants.AssemblyVersion);

// report container id (only Linux containers supported for now)
var containerId = ContainerInfo.GetContainerId();
var containerId = ContainerMetadata.GetContainerId();

if (containerId != null)
{
Expand Down
130 changes: 130 additions & 0 deletions src/Datadog.Trace/PlatformHelpers/AzureAppservices.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Collections;
using Datadog.Trace.ExtensionMethods;
using Datadog.Trace.Logging;

namespace Datadog.Trace.PlatformHelpers
{
internal class AzureAppServices
{
/// <summary>
/// Configuration key which is used as a flag to tell us whether we are running in the context of Azure App Services.
/// </summary>
internal static readonly string AzureAppServicesContextKey = "DD_AZURE_APP_SERVICES";

/// <summary>
/// Example: 8c56d827-5f07-45ce-8f2b-6c5001db5c6f+apm-dotnet-EastUSwebspace
/// Format: {subscriptionId}+{planResourceGroup}-{hostedInRegion}
/// </summary>
internal static readonly string WebsiteOwnerNameKey = "WEBSITE_OWNER_NAME";

/// <summary>
/// This is the name of the resource group the site instance is assigned to.
/// </summary>
internal static readonly string ResourceGroupKey = "WEBSITE_RESOURCE_GROUP";

/// <summary>
/// This is the unique name of the website instance within azure app services.
/// </summary>
internal static readonly string SiteNameKey = "WEBSITE_DEPLOYMENT_ID";

static AzureAppServices()
{
Metadata = new AzureAppServices(Environment.GetEnvironmentVariables());
}

public AzureAppServices(IDictionary environmentVariables)
{
IsRelevant = GetVariableIfExists(AzureAppServicesContextKey, environmentVariables)?.ToBoolean() ?? false;
if (IsRelevant)
{
SubscriptionId = GetSubscriptionId(environmentVariables);
ResourceGroup = GetVariableIfExists(ResourceGroupKey, environmentVariables);
SiteName = GetVariableIfExists(SiteNameKey, environmentVariables);
ResourceId = CompileResourceId();
}
}

public static AzureAppServices Metadata { get; set; }

public bool IsRelevant { get; }

public string SubscriptionId { get; }

public string ResourceGroup { get; }

public string SiteName { get; }

public string ResourceId { get; }

private string CompileResourceId()
{
string resourceId = null;

try
{
var success = true;
if (SubscriptionId == null)
{
success = false;
DatadogLogging.RegisterStartupLog(log => log.Warning("Could not successfully retrieve the subscription ID from variable: {0}", WebsiteOwnerNameKey));
}

if (SiteName == null)
{
success = false;
DatadogLogging.RegisterStartupLog(log => log.Warning("Could not successfully retrieve the deployment ID from variable: {0}", SiteNameKey));
}

if (ResourceGroup == null)
{
success = false;
DatadogLogging.RegisterStartupLog(log => log.Warning("Could not successfully retrieve the resource group name from variable: {0}", ResourceGroupKey));
}

if (success)
{
resourceId = $"/subscriptions/{SubscriptionId}/resourcegroups/{ResourceGroup}/providers/microsoft.web/sites/{SiteName}".ToLowerInvariant();
}
}
catch (Exception ex)
{
DatadogLogging.RegisterStartupLog(log => log.Error(ex, "Could not successfully setup the resource id for azure app services."));
}

return resourceId;
}

private string GetSubscriptionId(IDictionary environmentVariables)
{
try
{
var websiteOwner = GetVariableIfExists(WebsiteOwnerNameKey, environmentVariables);
if (!string.IsNullOrWhiteSpace(websiteOwner))
{
var plusSplit = websiteOwner.Split('+');
if (plusSplit.Length > 0 && !string.IsNullOrWhiteSpace(plusSplit[0]))
{
return plusSplit[0];
}
}
}
catch (Exception ex)
{
DatadogLogging.RegisterStartupLog(log => log.Error(ex, "Could not successfully retrieve the subscription id for azure app services."));
}

return null;
}

private string GetVariableIfExists(string key, IDictionary environmentVariables)
{
if (environmentVariables.Contains(key))
{
return environmentVariables[key]?.ToString();
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using Datadog.Trace.Logging;

namespace Datadog.Trace.Containers
namespace Datadog.Trace.PlatformHelpers
{
/// <summary>
/// Utility class with methods to interact with container hosts.
/// </summary>
internal static class ContainerInfo
internal static class ContainerMetadata
{
private const string ControlGroupsFilePath = "/proc/self/cgroup";

private const string ContainerIdRegex = @"^(?:\d+):(?:[^:]*):/?(?:.+/)([0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}|[0-9a-f]{64}(?:\.scope)?)$";

private static readonly Lazy<string> ContainerId = new Lazy<string>(GetContainerIdInternal, LazyThreadSafetyMode.ExecutionAndPublication);

private static readonly Vendors.Serilog.ILogger Log = DatadogLogging.GetLogger(typeof(ContainerInfo));
private static readonly Vendors.Serilog.ILogger Log = DatadogLogging.GetLogger(typeof(ContainerMetadata));

/// <summary>
/// Gets the id of the container executing the code.
Expand Down
20 changes: 20 additions & 0 deletions src/Datadog.Trace/Tags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,25 @@ public static class Tags
/// Language tag, applied to root spans that are .NET runtime (e.g., ASP.NET)
/// </summary>
public const string Language = "language";

/// <summary>
/// The resource id of the site instance in azure app services where the traced application is running.
/// </summary>
public const string AzureAppServicesResourceId = "aas.resource.id";

/// <summary>
/// The resource group of the site instance in azure app services where the traced application is running.
/// </summary>
public const string AzureAppServicesResourceGroup = "aas.resource.group";

/// <summary>
/// The site name of the site instance in azure app services where the traced application is running.
/// </summary>
public const string AzureAppServicesSiteName = "aas.site.name";

/// <summary>
/// The subscription id of the site instance in azure app services where the traced application is running.
/// </summary>
public const string AzureAppServicesSubscriptionId = "aas.subscription.id";
}
}
13 changes: 13 additions & 0 deletions src/Datadog.Trace/TraceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using Datadog.Trace.Logging;
using Datadog.Trace.PlatformHelpers;

namespace Datadog.Trace
{
Expand Down Expand Up @@ -54,6 +55,7 @@ public void AddSpan(Span span)
{
// first span added is the root span
RootSpan = span;
DecorateRootSpan(span);

if (_samplingPriority == null)
{
Expand Down Expand Up @@ -126,5 +128,16 @@ public void LockSamplingPriority()
_samplingPriorityLocked = true;
}
}

private void DecorateRootSpan(Span span)
{
if (AzureAppServices.Metadata?.IsRelevant ?? false)
{
span.SetTag(Tags.AzureAppServicesSiteName, AzureAppServices.Metadata.SiteName);
span.SetTag(Tags.AzureAppServicesResourceGroup, AzureAppServices.Metadata.ResourceGroup);
span.SetTag(Tags.AzureAppServicesSubscriptionId, AzureAppServices.Metadata.SubscriptionId);
span.SetTag(Tags.AzureAppServicesResourceId, AzureAppServices.Metadata.ResourceId);
}
}
}
}
12 changes: 1 addition & 11 deletions src/Datadog.Trace/Tracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Datadog.Trace.DiagnosticListeners;
using Datadog.Trace.DogStatsd;
using Datadog.Trace.Logging;
using Datadog.Trace.PlatformHelpers;
using Datadog.Trace.Sampling;
using Datadog.Trace.Vendors.StatsdClient;

Expand Down Expand Up @@ -356,17 +357,6 @@ void IDatadogTracer.Write(List<Span> trace)
_agentWriter.WriteTrace(trace);
}

/// <summary>
/// Create an Uri to the Agent using host and port from
/// the specified <paramref name="settings"/>.
/// </summary>
/// <param name="settings">A <see cref="TracerSettings"/> object </param>
/// <returns>An Uri that can be used to send traces to the Agent.</returns>
internal static Uri GetAgentUri(TracerSettings settings)
{
return settings.AgentUri;
}

internal async Task FlushAsync()
{
await _agentWriter.FlushAndCloseAsync();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using Datadog.Trace.PlatformHelpers;
using Datadog.Trace.TestHelpers;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -12,7 +13,7 @@ public class FakeAzureAppServicesTests : TestHelper
public FakeAzureAppServicesTests(ITestOutputHelper output)
: base(SampleName, output)
{
SetEnvironmentVariable("DD_AZURE_APP_SERVICES", "1");
SetEnvironmentVariable(PlatformHelpers.AzureAppServices.AzureAppServicesContextKey, "1");
}

[Fact(Skip = "TODO: Traces from the sub process are not coming through")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading;
using Datadog.Trace.PlatformHelpers;
using Datadog.Trace.TestHelpers;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -10,7 +11,7 @@ public class FakeKuduTests : TestHelper
public FakeKuduTests(ITestOutputHelper output)
: base("FakeKudu", output)
{
SetEnvironmentVariable("DD_AZURE_APP_SERVICES", "1");
SetEnvironmentVariable(PlatformHelpers.AzureAppServices.AzureAppServicesContextKey, "1");
SetEnvironmentVariable("APP_POOL_ID", "~1KuduScmProcessIsFilteredByTilde");
}

Expand Down
4 changes: 2 additions & 2 deletions test/Datadog.Trace.IntegrationTests/ContainerTaggingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Threading.Tasks;
using Datadog.Core.Tools;
using Datadog.Trace.Configuration;
using Datadog.Trace.Containers;
using Datadog.Trace.PlatformHelpers;
using Datadog.Trace.TestHelpers;
using Xunit;
using Xunit.Abstractions;
Expand All @@ -21,7 +21,7 @@ public ContainerTaggingTests(ITestOutputHelper output)
[Fact]
public async Task Http_Headers_Contain_ContainerId()
{
string expectedContainedId = ContainerInfo.GetContainerId();
string expectedContainedId = ContainerMetadata.GetContainerId();
string actualContainerId = null;
var agentPort = TcpPortProvider.GetOpenPort();

Expand Down
Loading

0 comments on commit f911924

Please sign in to comment.