diff --git a/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/DefaultTelemetryClientFactory.cs b/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/DefaultTelemetryClientFactory.cs index 905986414..561407cf8 100644 --- a/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/DefaultTelemetryClientFactory.cs +++ b/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/DefaultTelemetryClientFactory.cs @@ -9,7 +9,6 @@ using Microsoft.ApplicationInsights.Extensibility.Implementation; using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector; using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse; -using Microsoft.ApplicationInsights.WindowsServer; using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation; using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel; using Microsoft.Extensions.Logging; @@ -142,7 +141,7 @@ protected virtual QuickPulseTelemetryModule CreateQuickPulseTelemetryModule() internal static void AddInitializers(TelemetryConfiguration config) { // This picks up the RoleName from the server - config.TelemetryInitializers.Add(new AzureWebAppRoleEnvironmentTelemetryInitializer()); + config.TelemetryInitializers.Add(new WebJobsRoleEnvironmentTelemetryInitializer()); // This applies our special scope properties and gets RoleInstance name config.TelemetryInitializers.Add(new WebJobsTelemetryInitializer()); diff --git a/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/WebJobs.Logging.ApplicationInsights.csproj b/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/WebJobs.Logging.ApplicationInsights.csproj index 00df1a262..48d26f242 100644 --- a/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/WebJobs.Logging.ApplicationInsights.csproj +++ b/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/WebJobs.Logging.ApplicationInsights.csproj @@ -147,6 +147,7 @@ + diff --git a/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/WebJobsRoleEnvironmentTelmetryInitializer.cs b/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/WebJobsRoleEnvironmentTelmetryInitializer.cs new file mode 100644 index 000000000..224c2383c --- /dev/null +++ b/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/WebJobsRoleEnvironmentTelmetryInitializer.cs @@ -0,0 +1,82 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.ApplicationInsights.Extensibility.Implementation; + +namespace Microsoft.Azure.WebJobs.Logging.ApplicationInsights +{ + // This class was taken largely from https://raw.githubusercontent.com/Microsoft/ApplicationInsights-dotnet-server/91016d62f3181e10d4cf589ef8fd64dadb6b54a2/Src/WindowsServer/WindowsServer.Shared/AzureWebAppRoleEnvironmentTelemetryInitializer.cs, + // but refactored so that it did not use WEBSITE_HOSTNAME, which is determined to be unreliable for functions during slot swaps. + + /// + /// A telemetry initializer that will gather Azure Web App Role Environment context information. + /// + internal class WebJobsRoleEnvironmentTelemetryInitializer : ITelemetryInitializer + { + internal const string AzureWebsiteName = "WEBSITE_SITE_NAME"; + internal const string AzureWebsiteSlotName = "WEBSITE_SLOT_NAME"; + private const string DefaultProductionSlotName = "production"; + private const string WebAppSuffix = ".azurewebsites.net"; + + /// + /// Initializes device context. + /// + /// The telemetry to initialize. + public void Initialize(ITelemetry telemetry) + { + if (telemetry == null) + { + return; + } + + // We cannot cache these values as the environment variables can change on the fly. + if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName)) + { + telemetry.Context.Cloud.RoleName = GetRoleName(); + } + + if (string.IsNullOrEmpty(telemetry.Context.GetInternalContext().NodeName)) + { + telemetry.Context.GetInternalContext().NodeName = GetNodeName(); + } + } + + private static string GetRoleName() + { + return GetAzureWebsiteUniqueSlotName(); + } + + private static string GetNodeName() + { + string name = GetAzureWebsiteUniqueSlotName(); + + if (!string.IsNullOrEmpty(name)) + { + // maintain previous behavior of node having the full url + name = name + WebAppSuffix; + } + + return name; + } + + /// + /// Gets a value that uniquely identifies the site and slot. + /// + private static string GetAzureWebsiteUniqueSlotName() + { + string name = Environment.GetEnvironmentVariable(AzureWebsiteName); + string slotName = Environment.GetEnvironmentVariable(AzureWebsiteSlotName); + + if (!string.IsNullOrEmpty(slotName) && + !string.Equals(slotName, DefaultProductionSlotName, StringComparison.OrdinalIgnoreCase)) + { + name += $"-{slotName}"; + } + + return name?.ToLowerInvariant(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Azure.WebJobs.Host.UnitTests/Loggers/DefaultTelemetryClientFactoryTests.cs b/test/Microsoft.Azure.WebJobs.Host.UnitTests/Loggers/DefaultTelemetryClientFactoryTests.cs index d55b426c2..95f10a7ba 100644 --- a/test/Microsoft.Azure.WebJobs.Host.UnitTests/Loggers/DefaultTelemetryClientFactoryTests.cs +++ b/test/Microsoft.Azure.WebJobs.Host.UnitTests/Loggers/DefaultTelemetryClientFactoryTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.Linq; -using Microsoft.ApplicationInsights.WindowsServer; using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel; using Microsoft.Azure.WebJobs.Logging.ApplicationInsights; using Xunit; @@ -20,7 +19,7 @@ public void InitializeConfiguguration_Configures() // Verify Initializers Assert.Equal(2, config.TelemetryInitializers.Count); // These will throw if there are not exactly one - config.TelemetryInitializers.OfType().Single(); + config.TelemetryInitializers.OfType().Single(); config.TelemetryInitializers.OfType().Single(); // Verify Channel diff --git a/test/Microsoft.Azure.WebJobs.Host.UnitTests/Loggers/WebJobsRoleEnvironmentTelemetryInitializerTests.cs b/test/Microsoft.Azure.WebJobs.Host.UnitTests/Loggers/WebJobsRoleEnvironmentTelemetryInitializerTests.cs new file mode 100644 index 000000000..176cd948e --- /dev/null +++ b/test/Microsoft.Azure.WebJobs.Host.UnitTests/Loggers/WebJobsRoleEnvironmentTelemetryInitializerTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using Microsoft.ApplicationInsights.DataContracts; +using Microsoft.ApplicationInsights.Extensibility.Implementation; +using Microsoft.Azure.WebJobs.Logging.ApplicationInsights; +using Xunit; + +namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers +{ + public class WebJobsRoleEnvironmentTelemetryInitializerTests : IDisposable + { + public WebJobsRoleEnvironmentTelemetryInitializerTests() + { + // make sure these are clear before each test + SetEnvironmentVariables(null, null); + } + + [Fact] + public void Initialize_DoesNotThrow_WhenNoEnvironmentVariables() + { + var initializer = new WebJobsRoleEnvironmentTelemetryInitializer(); + + var telemetry = new TraceTelemetry(); + initializer.Initialize(telemetry); + + Assert.Null(telemetry.Context.Cloud.RoleName); + Assert.Null(telemetry.Context.GetInternalContext().NodeName); + } + + [Fact] + public void Initialize_WithSlot() + { + SetEnvironmentVariables("mytestsite", "Staging"); + + var initializer = new WebJobsRoleEnvironmentTelemetryInitializer(); + + var telemetry = new TraceTelemetry(); + initializer.Initialize(telemetry); + + Assert.Equal("mytestsite-staging", telemetry.Context.Cloud.RoleName); + Assert.Equal("mytestsite-staging.azurewebsites.net", telemetry.Context.GetInternalContext().NodeName); + } + + [Fact] + public void Initialize_WithProductionSlot() + { + SetEnvironmentVariables("mytestsite", "Production"); + + var initializer = new WebJobsRoleEnvironmentTelemetryInitializer(); + + var telemetry = new TraceTelemetry(); + initializer.Initialize(telemetry); + + Assert.Equal("mytestsite", telemetry.Context.Cloud.RoleName); + Assert.Equal("mytestsite.azurewebsites.net", telemetry.Context.GetInternalContext().NodeName); + } + + private static void SetEnvironmentVariables(string websiteName, string slotName) + { + Environment.SetEnvironmentVariable(WebJobsRoleEnvironmentTelemetryInitializer.AzureWebsiteName, websiteName); + Environment.SetEnvironmentVariable(WebJobsRoleEnvironmentTelemetryInitializer.AzureWebsiteSlotName, slotName); + } + + public void Dispose() + { + SetEnvironmentVariables(null, null); + } + } +} diff --git a/test/Microsoft.Azure.WebJobs.Host.UnitTests/WebJobs.Host.UnitTests.csproj b/test/Microsoft.Azure.WebJobs.Host.UnitTests/WebJobs.Host.UnitTests.csproj index 1c7254f99..e48e8b355 100644 --- a/test/Microsoft.Azure.WebJobs.Host.UnitTests/WebJobs.Host.UnitTests.csproj +++ b/test/Microsoft.Azure.WebJobs.Host.UnitTests/WebJobs.Host.UnitTests.csproj @@ -316,6 +316,7 @@ +