Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased
### Added
- [Automatic configuration binding from "ApplicationInsights" section in appsettings.json for both AspNetCore and WorkerService packages with configuration precedence: environment variables > explicit configuration > appsettings.json](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3064/changes)
- [Added support for Entra ID (Azure Active Directory) authentication using Azure.Core.TokenCredential](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3054)

## Version 3.0.0-beta1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ public static IServiceCollection AddApplicationInsightsTelemetry(this IServiceCo
{
if (!IsApplicationInsightsAdded(services))
{
// Register the default configuration options to automatically read from appsettings.json
services.AddOptions<ApplicationInsightsServiceOptions>()
.Configure<IConfiguration>((options, config) =>
{
AddTelemetryConfiguration(config, options);
});

services.AddOpenTelemetry()
.WithApplicationInsights()
.UseApplicationInsightsTelemetry();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ public static IServiceCollection AddApplicationInsightsTelemetryWorkerService(th
{
if (!IsApplicationInsightsAdded(services))
{
// Register the default configuration options to automatically read from appsettings.json
services.AddOptions<ApplicationInsightsServiceOptions>()
.Configure<IConfiguration>((options, config) =>
{
AddTelemetryConfiguration(config, options);
});

services.AddOpenTelemetry()
.WithApplicationInsights()
.UseApplicationInsightsTelemetry();
Expand Down
19 changes: 0 additions & 19 deletions NETCORE/src/Shared/Extensions/ApplicationInsightsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,10 @@ public static partial class ApplicationInsightsExtensions
{
private const string VersionKeyFromConfig = "version";
private const string ConnectionStringFromConfig = "ApplicationInsights:ConnectionString";
private const string DeveloperModeFromConfig = "ApplicationInsights:TelemetryChannel:DeveloperMode";
private const string EndpointAddressFromConfig = "ApplicationInsights:TelemetryChannel:EndpointAddress";

private const string ConnectionStringEnvironmentVariable = "APPLICATIONINSIGHTS_CONNECTION_STRING";
private const string DeveloperModeForWebSites = "APPINSIGHTS_DEVELOPER_MODE";
private const string EndpointAddressForWebSites = "APPINSIGHTS_ENDPOINTADDRESS";

private const string ApplicationInsightsSectionFromConfig = "ApplicationInsights";
private const string TelemetryChannelSectionFromConfig = "ApplicationInsights:TelemetryChannel";

/// <summary>
/// Read configuration from appSettings.json, appsettings.{env.EnvironmentName}.json,
Expand All @@ -54,26 +49,12 @@ internal static void AddTelemetryConfiguration(
try
{
config.GetSection(ApplicationInsightsSectionFromConfig).Bind(serviceOptions);
config.GetSection(TelemetryChannelSectionFromConfig).Bind(serviceOptions);

if (config.TryGetValue(primaryKey: ConnectionStringEnvironmentVariable, backupKey: ConnectionStringFromConfig, value: out string connectionStringValue))
{
serviceOptions.ConnectionString = connectionStringValue;
}

if (config.TryGetValue(primaryKey: DeveloperModeForWebSites, backupKey: DeveloperModeFromConfig, value: out string developerModeValue))
{
if (bool.TryParse(developerModeValue, out bool developerMode))
{
serviceOptions.DeveloperMode = developerMode;
}
}

if (config.TryGetValue(primaryKey: EndpointAddressForWebSites, backupKey: EndpointAddressFromConfig, value: out string endpointAddress))
{
serviceOptions.EndpointAddress = endpointAddress;
}

if (config.TryGetValue(primaryKey: VersionKeyFromConfig, value: out string version))
{
serviceOptions.ApplicationVersion = version;
Expand Down
10 changes: 0 additions & 10 deletions NETCORE/src/Shared/Extensions/ApplicationInsightsServiceOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ public class ApplicationInsightsServiceOptions
/// </summary>
public string ApplicationVersion { get; set; } = Assembly.GetEntryAssembly()?.GetName().Version.ToString();

/// <summary>
/// Gets or sets a value indicating whether telemetry channel should be set to developer mode.
/// </summary>
public bool? DeveloperMode { get; set; }

/// <summary>
/// Gets or sets the endpoint address of the channel.
/// </summary>
Expand Down Expand Up @@ -117,11 +112,6 @@ public class ApplicationInsightsServiceOptions
/// <param name="target">Target instance to copy properties to.</param>
internal void CopyPropertiesTo(ApplicationInsightsServiceOptions target)
{
if (this.DeveloperMode != null)
{
target.DeveloperMode = this.DeveloperMode;
}

if (!string.IsNullOrEmpty(this.EndpointAddress))
{
target.EndpointAddress = this.EndpointAddress;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
namespace Microsoft.ApplicationInsights.WorkerService
#if AI_ASPNETCORE_WEB
namespace Microsoft.AspNetCore.Hosting
#else
namespace Microsoft.ApplicationInsights.WorkerService
#endif
{
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
#if AI_ASPNETCORE_WEB
using Microsoft.ApplicationInsights.AspNetCore.Extensions;
#endif
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

/// <summary>
/// <see cref="IConfigureOptions&lt;ApplicationInsightsServiceOptions&gt;"/> implementation that reads options from provided IConfiguration.
/// <see cref="IConfigureOptions{ApplicationInsightsServiceOptions}"/> implementation that reads options from provided IConfiguration.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "This class is instantiated by Dependency Injection.")]
internal class DefaultApplicationInsightsServiceConfigureOptions : IConfigureOptions<ApplicationInsightsServiceOptions>
{
private readonly IConfiguration configuration;
Expand All @@ -30,11 +35,6 @@ public void Configure(ApplicationInsightsServiceOptions options)
{
ApplicationInsightsExtensions.AddTelemetryConfiguration(this.configuration, options);
}

if (Debugger.IsAttached)
{
options.DeveloperMode = true;
}
}
}
}
208 changes: 208 additions & 0 deletions NETCORE/test/IntegrationTests.Tests/AspNetCoreConfigurationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
using System;
using System.IO;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.ApplicationInsights.AspNetCore.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
using Xunit.Abstractions;

namespace IntegrationTests.Tests
{
public class AspNetCoreConfigurationTests
{
private readonly ITestOutputHelper output;
private const string TestConnectionString = "InstrumentationKey=11111111-2222-3333-4444-555555555555;IngestionEndpoint=http://127.0.0.1";

public AspNetCoreConfigurationTests(ITestOutputHelper output)
{
this.output = output;
}

[Fact]
public void ReadsConnectionStringFromApplicationInsightsSectionInConfig()
{
// ARRANGE
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-connection-string.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT
services.AddApplicationInsightsTelemetry();

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.Equal(TestConnectionString, options.ConnectionString);
}

[Fact]
public void ReadsEnableAdaptiveSamplingFromApplicationInsightsSectionInConfig()
{
// ARRANGE
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-all-settings-false.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT
services.AddApplicationInsightsTelemetry();

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.False(options.EnableAdaptiveSampling);
}

[Fact]
public void ReadsEnableQuickPulseMetricStreamFromApplicationInsightsSectionInConfig()
{
// ARRANGE
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-all-settings-false.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT
services.AddApplicationInsightsTelemetry();

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.False(options.EnableQuickPulseMetricStream);
}

[Fact]
public void ReadsApplicationVersionFromApplicationInsightsSectionInConfig()
{
// ARRANGE
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-all-settings-true.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT
services.AddApplicationInsightsTelemetry();

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.Equal("1.0.0", options.ApplicationVersion);
}

[Fact]
public void ReadsRequestCollectionOptionsFromApplicationInsightsSectionInConfig()
{
// ARRANGE
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-all-settings-false.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT
services.AddApplicationInsightsTelemetry();

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.False(options.RequestCollectionOptions.InjectResponseHeaders);
Assert.False(options.RequestCollectionOptions.TrackExceptions);
}

[Fact]
public void ConfigurationFlowsFromApplicationInsightsSectionToAzureMonitorExporter()
{
// ARRANGE
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-all-settings-false.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT
services.AddApplicationInsightsTelemetry();

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();

// Verify ApplicationInsightsServiceOptions
var aiOptions = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.Equal("InstrumentationKey=22222222-2222-3333-4444-555555555555;IngestionEndpoint=http://testendpoint", aiOptions.ConnectionString);
Assert.False(aiOptions.EnableAdaptiveSampling);
Assert.False(aiOptions.EnableQuickPulseMetricStream);

// Verify AzureMonitorExporterOptions gets the values
var exporterOptions = serviceProvider.GetRequiredService<IOptions<AzureMonitorExporterOptions>>().Value;
Assert.Equal("InstrumentationKey=22222222-2222-3333-4444-555555555555;IngestionEndpoint=http://testendpoint", exporterOptions.ConnectionString);
Assert.Equal(1.0F, exporterOptions.SamplingRatio); // No sampling when EnableAdaptiveSampling is false
Assert.False(exporterOptions.EnableLiveMetrics);
}

[Fact]
public void EnvironmentVariablesTakePrecedenceOverAppSettings()
{
// ARRANGE
const string envConnectionString = "InstrumentationKey=AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE;IngestionEndpoint=http://env-endpoint";
Environment.SetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING", envConnectionString);

try
{
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-connection-string.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).AddEnvironmentVariables().Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT
services.AddApplicationInsightsTelemetry();

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.Equal(envConnectionString, options.ConnectionString);
}
finally
{
Environment.SetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING", null);
}
}

[Fact]
public void ExplicitConfigurationTakesPrecedenceOverDefaultConfiguration()
{
// ARRANGE
const string explicitConnectionString = "InstrumentationKey=CCCCCCCC-DDDD-EEEE-FFFF-111111111111;IngestionEndpoint=http://explicit-endpoint";
var jsonFullPath = Path.Combine(Directory.GetCurrentDirectory(), "content", "config-connection-string.json");
this.output.WriteLine("json:" + jsonFullPath);
var config = new ConfigurationBuilder().AddJsonFile(jsonFullPath).Build();

var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);

// ACT - Pass explicit options
services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = explicitConnectionString;
});

// VALIDATE
IServiceProvider serviceProvider = services.BuildServiceProvider();
var options = serviceProvider.GetRequiredService<IOptions<ApplicationInsightsServiceOptions>>().Value;
Assert.Equal(explicitConnectionString, options.ConnectionString);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="content\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=11111111-2222-3333-4444-555555555555;IngestionEndpoint=http://testendpoint",
"EnableAdaptiveSampling": false,
"EnableQuickPulseMetricStream": true,
"ApplicationVersion": "Version",
"RequestCollectionOptions": {
"InjectResponseHeaders": true,
"TrackExceptions": false
},
"DependencyCollectionOptions": {
"EnableLegacyCorrelationHeadersInjection": false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=22222222-2222-3333-4444-555555555555;IngestionEndpoint=http://testendpoint",
"EnableAdaptiveSampling": false,
"EnableQuickPulseMetricStream": false,
"RequestCollectionOptions": {
"InjectResponseHeaders": false,
"TrackExceptions": false
},
"DependencyCollectionOptions": {
"EnableLegacyCorrelationHeadersInjection": false
}
}
}
Loading
Loading