Skip to content

Commit 3ba680c

Browse files
Application insights updates (#9835)
* Application insights updates
1 parent 49732b0 commit 3ba680c

File tree

12 files changed

+199
-87
lines changed

12 files changed

+199
-87
lines changed

release_notes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@
1313
- Customers can opt-out of this behavior by setting `SendCanceledInvocationsToWorker` to `false` in host.json
1414
- If a worker does not support CancellationTokens, cancelled invocations will not be sent to the worker
1515
- Warn when `FUNCTIONS_WORKER_RUNTIME` is not set (#9799)
16+
- Added support for ManagedIdentity authentication in ApplicationInsights pipeline. (#9758)
17+
- Added support for AutocollectedMetricsExtractor, MetricsCustomDimensionOptimization and QueryStringTracing. (#9758)
1618
- Add an app setting to allow CORS configuration (#9846)

src/WebJobs.Script.Abstractions/WebJobs.Script.Abstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
</ItemGroup>
3232

3333
<ItemGroup>
34-
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.21.0" />
34+
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
3535
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
3636
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.21.0" />
3737
<PackageReference Include="Microsoft.ApplicationInsights.WindowsServer" Version="2.21.0" />

src/WebJobs.Script.Grpc/WebJobs.Script.Grpc.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
<ItemGroup>
2525
<PackageReference Include="Grpc.AspNetCore" Version="2.55.0" />
26-
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.21.0" />
26+
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
2727
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
2828
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.21.0" />
2929
<PackageReference Include="Microsoft.ApplicationInsights.WindowsServer" Version="2.21.0" />

src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
<ItemGroup>
6565
<PackageReference Include="Azure.Identity" Version="1.10.2" />
6666
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.2.0" />
67-
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.21.0" />
67+
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
6868
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
6969
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.21.0" />
7070
<PackageReference Include="Microsoft.ApplicationInsights.WindowsServer" Version="2.21.0" />

src/WebJobs.Script/Config/TransmissionStatusHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ internal static void Handler(object sender, TransmissionStatusEventArgs args)
3737
internal static string FormattedLog(object sender, TransmissionStatusEventArgs args)
3838
{
3939
var transmission = sender as Transmission;
40-
var items = transmission?.TelemetryItems.GroupBy(n => n.GetEnvelopeName())
40+
var items = transmission?.TelemetryItems?.GroupBy(n => n.GetEnvelopeName())
4141
.Select(n => new TelemetryItem
4242
{
4343
Type = n.Key,
4444
Count = n.Count()
4545
});
4646

4747
// Log all the unique ikeys in the transmission, avoid storing the complete key.
48-
IEnumerable<string> iKeys = transmission?.TelemetryItems.GroupBy(n => n.Context.InstrumentationKey)
48+
IEnumerable<string> iKeys = transmission?.TelemetryItems?.GroupBy(n => n.Context.InstrumentationKey)
4949
.Select(n => n.Key.Length > 24 ? $"{n.Key[..24]}************" : n.Key);
5050

5151
IngestionServiceResponse response = null;

src/WebJobs.Script/Environment/EnvironmentSettingNames.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public static class EnvironmentSettingNames
2626
public const string AzureWebJobsSecretStorage = "AzureWebJobsStorage";
2727
public const string AppInsightsInstrumentationKey = "APPINSIGHTS_INSTRUMENTATIONKEY";
2828
public const string AppInsightsConnectionString = "APPLICATIONINSIGHTS_CONNECTION_STRING";
29+
public const string AppInsightsAuthenticationString = "APPLICATIONINSIGHTS_AUTHENTICATION_STRING";
30+
public const string AppInsightsEventListenerLogLevel = "APPLICATIONINSIGHTS_EVENT_LISTENER_LOGLEVEL";
2931
public const string AppInsightsQuickPulseAuthApiKey = "APPINSIGHTS_QUICKPULSEAUTHAPIKEY";
3032
public const string AppInsightsAgent = "APPLICATIONINSIGHTS_ENABLE_AGENT";
3133
public const string FunctionsExtensionVersion = "FUNCTIONS_EXTENSION_VERSION";

src/WebJobs.Script/ScriptHostBuilderExtensions.cs

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.ComponentModel;
7+
using System.Diagnostics.Tracing;
68
using System.Runtime.InteropServices;
9+
using System.Threading;
10+
using Azure.Identity;
711
using Microsoft.ApplicationInsights;
812
using Microsoft.ApplicationInsights.Channel;
913
using Microsoft.ApplicationInsights.DataContracts;
@@ -12,7 +16,6 @@
1216
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel;
1317
using Microsoft.Azure.WebJobs.Host;
1418
using Microsoft.Azure.WebJobs.Host.Executors;
15-
using Microsoft.Azure.WebJobs.Host.Scale;
1619
using Microsoft.Azure.WebJobs.Hosting;
1720
using Microsoft.Azure.WebJobs.Logging;
1821
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
@@ -42,6 +45,7 @@
4245
using Microsoft.Extensions.Logging.Abstractions;
4346
using Microsoft.Extensions.Logging.Configuration;
4447
using Microsoft.Extensions.Options;
48+
using TokenCredentialOptions = Microsoft.Azure.WebJobs.Logging.ApplicationInsights.TokenCredentialOptions;
4549

4650
namespace Microsoft.Azure.WebJobs.Script
4751
{
@@ -232,8 +236,9 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
232236
}
233237
catch (Exception ex)
234238
{
235-
string appInsightsConnStr = context.Configuration[EnvironmentSettingNames.AppInsightsConnectionString];
236-
RecordAndThrowExternalStartupException("Error configuring services in an external startup class.", ex, loggerFactory, appInsightsConnStr);
239+
string appInsightsConnStr = GetConfigurationValue(EnvironmentSettingNames.AppInsightsConnectionString, context.Configuration);
240+
string appInsightsAuthStr = GetConfigurationValue(EnvironmentSettingNames.AppInsightsAuthenticationString, context.Configuration);
241+
RecordAndThrowExternalStartupException("Error configuring services in an external startup class.", ex, loggerFactory, appInsightsConnStr, appInsightsAuthStr);
237242
}
238243
}
239244

@@ -260,8 +265,9 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
260265
catch (Exception ex)
261266
{
262267
// Go directly to the environment; We have no valid configuration from the customer at this point.
263-
string appInsightsConnStr = Environment.GetEnvironmentVariable(EnvironmentSettingNames.AppInsightsConnectionString);
264-
RecordAndThrowExternalStartupException("Error building configuration in an external startup class.", ex, loggerFactory, appInsightsConnStr);
268+
string appInsightsConnStr = GetConfigurationValue(EnvironmentSettingNames.AppInsightsConnectionString);
269+
string appInsightsAuthStr = GetConfigurationValue(EnvironmentSettingNames.AppInsightsAuthenticationString);
270+
RecordAndThrowExternalStartupException("Error building configuration in an external startup class.", ex, loggerFactory, appInsightsConnStr, appInsightsAuthStr);
265271
}
266272
});
267273
}
@@ -402,16 +408,37 @@ public static IHostBuilder SetAzureFunctionsConfigurationRoot(this IHostBuilder
402408

403409
internal static void ConfigureApplicationInsights(HostBuilderContext context, ILoggingBuilder builder)
404410
{
405-
string appInsightsInstrumentationKey = context.Configuration[EnvironmentSettingNames.AppInsightsInstrumentationKey];
406-
string appInsightsConnectionString = context.Configuration[EnvironmentSettingNames.AppInsightsConnectionString];
411+
string appInsightsInstrumentationKey = GetConfigurationValue(EnvironmentSettingNames.AppInsightsInstrumentationKey, context.Configuration);
412+
string appInsightsConnectionString = GetConfigurationValue(EnvironmentSettingNames.AppInsightsConnectionString, context.Configuration);
407413

408414
// Initializing AppInsights services during placeholder mode as well to avoid the cost of JITting these objects during specialization
409415
if (!string.IsNullOrEmpty(appInsightsInstrumentationKey) || !string.IsNullOrEmpty(appInsightsConnectionString) || SystemEnvironment.Instance.IsPlaceholderModeEnabled())
410416
{
417+
string eventLogLevel = GetConfigurationValue(EnvironmentSettingNames.AppInsightsEventListenerLogLevel, context.Configuration);
418+
string authString = GetConfigurationValue(EnvironmentSettingNames.AppInsightsAuthenticationString, context.Configuration);
419+
// Initialize ScriptTelemetryInitializer before any other telemetry initializers.
420+
// This will allow HostInstanceId to be removed as part of MetricsCustomDimensionOptimization.
421+
builder.Services.AddSingleton<ITelemetryInitializer, ScriptTelemetryInitializer>();
411422
builder.AddApplicationInsightsWebJobs(o =>
412423
{
413424
o.InstrumentationKey = appInsightsInstrumentationKey;
414425
o.ConnectionString = appInsightsConnectionString;
426+
427+
if (!string.IsNullOrEmpty(eventLogLevel))
428+
{
429+
if (Enum.TryParse(eventLogLevel, ignoreCase: true, out EventLevel level))
430+
{
431+
o.DiagnosticsEventListenerLogLevel = level;
432+
}
433+
else
434+
{
435+
throw new InvalidEnumArgumentException($"Invalid `{EnvironmentSettingNames.AppInsightsEventListenerLogLevel}`.");
436+
}
437+
}
438+
if (!string.IsNullOrEmpty(authString))
439+
{
440+
o.TokenCredentialOptions = TokenCredentialOptions.ParseAuthenticationString(authString);
441+
}
415442
}, t =>
416443
{
417444
if (t.TelemetryChannel is ServerTelemetryChannel channel)
@@ -425,7 +452,6 @@ internal static void ConfigureApplicationInsights(HostBuilderContext context, IL
425452

426453
builder.Services.ConfigureOptions<ApplicationInsightsLoggerOptionsSetup>();
427454
builder.Services.AddSingleton<ISdkVersionProvider, FunctionsSdkVersionProvider>();
428-
builder.Services.AddSingleton<ITelemetryInitializer, ScriptTelemetryInitializer>();
429455

430456
if (SystemEnvironment.Instance.IsPlaceholderModeEnabled())
431457
{
@@ -502,7 +528,7 @@ private static T GetAndRemove<T>(this IDictionary<object, object> dictionary, st
502528
}
503529
}
504530

505-
private static void RecordAndThrowExternalStartupException(string message, Exception ex, ILoggerFactory loggerFactory, string appInsightsConnStr)
531+
private static void RecordAndThrowExternalStartupException(string message, Exception ex, ILoggerFactory loggerFactory, string appInsightsConnStr, string appInsightsAuthString)
506532
{
507533
var startupEx = new ExternalStartupException(message, ex);
508534

@@ -511,22 +537,46 @@ private static void RecordAndThrowExternalStartupException(string message, Excep
511537

512538
// Send the error to App Insights if possible. This is happening during ScriptHost construction so we
513539
// have no existing TelemetryClient to use. Create a one-off client and flush it ASAP.
514-
if (appInsightsConnStr is not null)
540+
if (!string.IsNullOrEmpty(appInsightsConnStr))
515541
{
516542
using TelemetryConfiguration telemetryConfiguration = new()
517543
{
518544
ConnectionString = appInsightsConnStr,
519545
TelemetryChannel = new InMemoryChannel()
520546
};
521547

548+
if (!string.IsNullOrEmpty(appInsightsAuthString))
549+
{
550+
TokenCredentialOptions credentialOptions = TokenCredentialOptions.ParseAuthenticationString(appInsightsAuthString);
551+
// Default is connection string based ingestion
552+
telemetryConfiguration.SetAzureTokenCredential(new ManagedIdentityCredential(credentialOptions.ClientId));
553+
}
554+
522555
TelemetryClient telemetryClient = new(telemetryConfiguration);
523556
telemetryClient.Context.GetInternalContext().SdkVersion = new ApplicationInsightsSdkVersionProvider().GetSdkVersion();
524557
telemetryClient.TrackTrace(startupEx.ToString(), SeverityLevel.Error);
525558
telemetryClient.TrackException(startupEx);
526559
telemetryClient.Flush();
560+
// Flush is async, sleep for 1 sec to give it time to flush
561+
Thread.Sleep(1000);
527562
}
528-
529563
throw startupEx;
530564
}
565+
566+
private static string GetConfigurationValue(string key, IConfiguration configuration = null)
567+
{
568+
if (configuration != null && configuration[key] is string configValue)
569+
{
570+
return configValue;
571+
}
572+
else if (Environment.GetEnvironmentVariable(key) is string envValue)
573+
{
574+
return envValue;
575+
}
576+
else
577+
{
578+
return null;
579+
}
580+
}
531581
}
532582
}

src/WebJobs.Script/WebJobs.Script.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<PackageReference Include="Azure.Core" Version="1.35.0" />
4343
<PackageReference Include="Azure.Identity" Version="1.10.2" />
4444
<PackageReference Include="Azure.Storage.Blobs" Version="12.13.0" />
45-
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.21.0" />
45+
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
4646
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
4747
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.21.0" />
4848
<PackageReference Include="Microsoft.ApplicationInsights.WindowsServer" Version="2.21.0" />
@@ -66,7 +66,7 @@
6666
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.2.0" />
6767
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Timers.Storage" Version="1.0.0-beta.1" />
6868
<PackageReference Include="Microsoft.Azure.WebJobs.Script.Abstractions" Version="1.0.4-preview" />
69-
<PackageReference Include="Microsoft.Azure.WebJobs.Logging.ApplicationInsights" Version="3.0.38" />
69+
<PackageReference Include="Microsoft.Azure.WebJobs.Logging.ApplicationInsights" Version="3.0.41-11321" />
7070
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.3.1" />
7171
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
7272
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />

test/WebJobs.Script.Tests.Integration/WebJobs.Script.Tests.Integration.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
<ItemGroup>
3232
<PackageReference Include="appinsights.testlogger" Version="1.0.0" />
33-
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.21.0" />
33+
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
3434
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
3535
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.21.0" />
3636
<PackageReference Include="Microsoft.ApplicationInsights.WindowsServer" Version="2.21.0" />

test/WebJobs.Script.Tests/Configuration/ApplicationInsightsLoggerOptionsSetupTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,63 @@ public void Configure_EnableLiveMetricsFilters(string value, bool expectedValue)
286286
Assert.Equal(expectedValue, options.EnableLiveMetricsFilters);
287287
}
288288

289+
[Theory]
290+
[InlineData(null, false)] // default value
291+
[InlineData("true", true)]
292+
[InlineData("false", false)]
293+
public void Configure_EnableMetricsCustomDimensionOptimization(string value, bool expectedValue)
294+
{
295+
IConfiguration config = new ConfigurationBuilder()
296+
.AddInMemoryCollection(new Dictionary<string, string>
297+
{
298+
{ "EnableMetricsCustomDimensionOptimization", value },
299+
})
300+
.Build();
301+
302+
ApplicationInsightsLoggerOptionsSetup setup = new ApplicationInsightsLoggerOptionsSetup(new MockLoggerConfiguration(config), _environment);
303+
ApplicationInsightsLoggerOptions options = new ApplicationInsightsLoggerOptions();
304+
setup.Configure(options);
305+
Assert.Equal(expectedValue, options.EnableMetricsCustomDimensionOptimization);
306+
}
307+
308+
[Theory]
309+
[InlineData(null, false)] // default value
310+
[InlineData("true", true)]
311+
[InlineData("false", false)]
312+
public void Configure_EnableQueryStringTracing(string value, bool expectedValue)
313+
{
314+
IConfiguration config = new ConfigurationBuilder()
315+
.AddInMemoryCollection(new Dictionary<string, string>
316+
{
317+
{ "EnableQueryStringTracing", value },
318+
})
319+
.Build();
320+
321+
ApplicationInsightsLoggerOptionsSetup setup = new ApplicationInsightsLoggerOptionsSetup(new MockLoggerConfiguration(config), _environment);
322+
ApplicationInsightsLoggerOptions options = new ApplicationInsightsLoggerOptions();
323+
setup.Configure(options);
324+
Assert.Equal(expectedValue, options.EnableQueryStringTracing);
325+
}
326+
327+
[Theory]
328+
[InlineData(null, false)] // default value
329+
[InlineData("true", true)]
330+
[InlineData("false", false)]
331+
public void Configure_EnableAutocollectedMetricsExtractor(string value, bool expectedValue)
332+
{
333+
IConfiguration config = new ConfigurationBuilder()
334+
.AddInMemoryCollection(new Dictionary<string, string>
335+
{
336+
{ "EnableAutocollectedMetricsExtractor", value },
337+
})
338+
.Build();
339+
340+
ApplicationInsightsLoggerOptionsSetup setup = new ApplicationInsightsLoggerOptionsSetup(new MockLoggerConfiguration(config), _environment);
341+
ApplicationInsightsLoggerOptions options = new ApplicationInsightsLoggerOptions();
342+
setup.Configure(options);
343+
Assert.Equal(expectedValue, options.EnableAutocollectedMetricsExtractor);
344+
}
345+
289346
private class MockLoggerConfiguration : ILoggerProviderConfiguration<ApplicationInsightsLoggerProvider>
290347
{
291348
public MockLoggerConfiguration(IConfiguration configuration)

0 commit comments

Comments
 (0)