Skip to content

Commit 82bde7b

Browse files
Merge pull request #595 from microsoft/psl-us-37748
feat: Enhance Application Insights logging configuration
2 parents 08e7f8f + 008b00a commit 82bde7b

10 files changed

Lines changed: 873 additions & 42 deletions

File tree

App/backend-api/Microsoft.GS.DPS.Host/API/ChatHost/Chat.cs

Lines changed: 342 additions & 29 deletions
Large diffs are not rendered by default.

App/backend-api/Microsoft.GS.DPS.Host/API/KernelMemory/KernelMemory.cs

Lines changed: 284 additions & 10 deletions
Large diffs are not rendered by default.

App/backend-api/Microsoft.GS.DPS.Host/DependencyConfiguration/ServiceDependencies.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public static void Inject(IHostApplicationBuilder builder)
2222
{
2323
builder.Services
2424
.AddValidatorsFromAssemblyContaining<PagingRequestValidator>()
25+
.AddSingleton<TelemetryHelper>()
2526
.AddSingleton<Microsoft.GS.DPS.API.KernelMemory>()
2627
.AddSingleton<Microsoft.GS.DPS.API.ChatHost>()
2728
.AddSingleton<Microsoft.GS.DPS.API.UserInterface.Documents>()
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
using Microsoft.ApplicationInsights;
2+
using Microsoft.ApplicationInsights.DataContracts;
3+
using System.Diagnostics;
4+
5+
namespace Microsoft.GS.DPSHost.Helpers
6+
{
7+
/// <summary>
8+
/// Helper class for Application Insights telemetry tracking
9+
/// </summary>
10+
public class TelemetryHelper
11+
{
12+
private readonly TelemetryClient? _telemetryClient;
13+
private readonly ILogger<TelemetryHelper> _logger;
14+
private readonly bool _isConfigured;
15+
16+
public TelemetryHelper(TelemetryClient? telemetryClient, ILogger<TelemetryHelper> logger)
17+
{
18+
_telemetryClient = telemetryClient;
19+
_logger = logger;
20+
21+
// Check if Application Insights is properly configured
22+
// TelemetryConfiguration.ConnectionString is the modern way (replaces deprecated InstrumentationKey)
23+
_isConfigured = _telemetryClient != null &&
24+
!string.IsNullOrEmpty(_telemetryClient.TelemetryConfiguration?.ConnectionString);
25+
26+
if (!_isConfigured)
27+
{
28+
_logger.LogWarning("Application Insights is not configured. Telemetry tracking will be disabled.");
29+
}
30+
else
31+
{
32+
_logger.LogInformation("Application Insights is configured successfully. Telemetry tracking is enabled.");
33+
}
34+
}
35+
36+
/// <summary>
37+
/// Track a custom event in Application Insights
38+
/// </summary>
39+
/// <param name="eventName">Name of the event</param>
40+
/// <param name="properties">Custom properties to track</param>
41+
/// <param name="metrics">Custom metrics to track</param>
42+
public void TrackEvent(string eventName, Dictionary<string, string>? properties = null, Dictionary<string, double>? metrics = null)
43+
{
44+
if (!_isConfigured || _telemetryClient == null)
45+
{
46+
return;
47+
}
48+
49+
try
50+
{
51+
_telemetryClient.TrackEvent(eventName, properties, metrics);
52+
}
53+
catch (Exception ex)
54+
{
55+
_logger.LogError(ex, "Failed to track event: {EventName}", eventName);
56+
}
57+
}
58+
59+
/// <summary>
60+
/// Track an exception in Application Insights
61+
/// </summary>
62+
/// <param name="exception">The exception to track</param>
63+
/// <param name="properties">Custom properties to track</param>
64+
/// <param name="metrics">Custom metrics to track</param>
65+
public void TrackException(Exception exception, Dictionary<string, string>? properties = null, Dictionary<string, double>? metrics = null)
66+
{
67+
if (!_isConfigured || _telemetryClient == null)
68+
{
69+
return;
70+
}
71+
72+
try
73+
{
74+
_telemetryClient.TrackException(exception, properties, metrics);
75+
}
76+
catch (Exception ex)
77+
{
78+
_logger.LogError(ex, "Failed to track exception");
79+
}
80+
}
81+
82+
/// <summary>
83+
/// Track a dependency call in Application Insights
84+
/// </summary>
85+
/// <param name="dependencyName">Name of the dependency</param>
86+
/// <param name="commandName">Command or operation name</param>
87+
/// <param name="startTime">Start time of the operation</param>
88+
/// <param name="duration">Duration of the operation</param>
89+
/// <param name="success">Whether the operation was successful</param>
90+
public void TrackDependency(string dependencyName, string commandName, DateTimeOffset startTime, TimeSpan duration, bool success)
91+
{
92+
if (!_isConfigured || _telemetryClient == null)
93+
{
94+
return;
95+
}
96+
97+
try
98+
{
99+
_telemetryClient.TrackDependency(dependencyName, commandName, startTime, duration, success);
100+
}
101+
catch (Exception ex)
102+
{
103+
_logger.LogError(ex, "Failed to track dependency: {DependencyName}", dependencyName);
104+
}
105+
}
106+
107+
/// <summary>
108+
/// Track a metric in Application Insights
109+
/// </summary>
110+
/// <param name="metricName">Name of the metric</param>
111+
/// <param name="value">Metric value</param>
112+
/// <param name="properties">Custom properties to track</param>
113+
public void TrackMetric(string metricName, double value, Dictionary<string, string>? properties = null)
114+
{
115+
if (!_isConfigured || _telemetryClient == null)
116+
{
117+
return;
118+
}
119+
120+
try
121+
{
122+
_telemetryClient.TrackMetric(metricName, value, properties);
123+
}
124+
catch (Exception ex)
125+
{
126+
_logger.LogError(ex, "Failed to track metric: {MetricName}", metricName);
127+
}
128+
}
129+
130+
/// <summary>
131+
/// Sets a custom property on the current activity for correlation
132+
/// </summary>
133+
/// <param name="key">Property key</param>
134+
/// <param name="value">Property value</param>
135+
public void SetActivityTag(string key, string value)
136+
{
137+
if (!_isConfigured)
138+
{
139+
return;
140+
}
141+
142+
try
143+
{
144+
Activity.Current?.SetTag(key, value);
145+
}
146+
catch (Exception ex)
147+
{
148+
_logger.LogError(ex, "Failed to set activity tag: {Key}", key);
149+
}
150+
}
151+
152+
/// <summary>
153+
/// Flush the telemetry client to ensure all telemetry is sent
154+
/// </summary>
155+
public void Flush()
156+
{
157+
if (!_isConfigured || _telemetryClient == null)
158+
{
159+
return;
160+
}
161+
162+
try
163+
{
164+
_telemetryClient.Flush();
165+
}
166+
catch (Exception ex)
167+
{
168+
_logger.LogError(ex, "Failed to flush telemetry client");
169+
}
170+
}
171+
}
172+
}

App/backend-api/Microsoft.GS.DPS.Host/Microsoft.GS.DPS.Host.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
<PackageReference Include="AutoMapper" Version="14.0.0" />
1515
<PackageReference Include="Azure.Data.AppConfiguration" Version="1.6.1" />
1616
<PackageReference Include="Azure.Identity" Version="1.14.1" />
17+
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
18+
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
1719
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.12.0" />
1820
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.6" />
1921
<PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="8.2.0" />
2022
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.6" />
23+
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.22.0" />
2124
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.6" />
2225
<PackageReference Include="Microsoft.KernelMemory.WebClient" Version="0.79.241014.2" />
2326
<PackageReference Include="Microsoft.SemanticKernel" Version="1.32.0" />

App/backend-api/Microsoft.GS.DPS.Host/Program.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.GS.DPS.Storage.Document;
77
using NSwag.AspNetCore;
88
using Microsoft.AspNetCore.Http.Features;
9+
using Microsoft.ApplicationInsights.AspNetCore.Extensions;
910

1011
var builder = WebApplication.CreateBuilder(args);
1112

@@ -17,6 +18,27 @@
1718
//Load Inject Settings and Load AppConfiguration Objects
1819
AppConfiguration.Config(builder);
1920

21+
// Configure Application Insights - Always register to ensure TelemetryClient is available for DI
22+
var connectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
23+
builder.Services.AddApplicationInsightsTelemetry(options =>
24+
{
25+
if (!string.IsNullOrEmpty(connectionString))
26+
{
27+
options.ConnectionString = connectionString;
28+
options.EnableAdaptiveSampling = builder.Configuration.GetValue<bool>("ApplicationInsights:EnableAdaptiveSampling", true);
29+
options.EnablePerformanceCounterCollectionModule = builder.Configuration.GetValue<bool>("ApplicationInsights:EnablePerformanceCounterCollectionModule", true);
30+
options.EnableQuickPulseMetricStream = builder.Configuration.GetValue<bool>("ApplicationInsights:EnableQuickPulseMetricStream", true);
31+
}
32+
});
33+
34+
// Configure logging
35+
if (!string.IsNullOrEmpty(connectionString))
36+
{
37+
builder.Logging.AddApplicationInsights();
38+
}
39+
builder.Logging.AddConsole();
40+
builder.Logging.AddDebug();
41+
2042
//Bson Register Class Maps
2143
//MongoDbConfig.RegisterClassMaps();
2244

App/backend-api/Microsoft.GS.DPS.Host/appsettings.json

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
{
22
"Logging": {
33
"LogLevel": {
4-
"Default": "Trace",
5-
"Microsoft.AspNetCore": "Trace"
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning",
6+
"Azure.Core": "Warning",
7+
"Azure.Identity": "Warning"
8+
},
9+
"ApplicationInsights": {
10+
"LogLevel": {
11+
"Default": "Information",
12+
"Microsoft.AspNetCore": "Warning"
13+
}
614
}
715
},
816
"AllowedHosts": "*",
917
"ConnectionStrings": {
1018
"AppConfig": ""
1119
},
20+
"ApplicationInsights": {
21+
"ConnectionString": "",
22+
"EnableAdaptiveSampling": true,
23+
"EnablePerformanceCounterCollectionModule": true,
24+
"EnableQuickPulseMetricStream": true
25+
},
1226
"Application": {
1327
"AIServices": {
1428
"GPT-4o": {

Deployment/appconfig/aiservice/appconfig.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"Application:Services:PersistentStorage:CosmosMongo:ConnectionString": "{cosmosmongo-connection-string}",
1919
"Application:Services:AzureAISearch:APIKey" : "{azureaisearch-apikey}",
2020
"Application:Services:AzureAISearch:Endpoint" : "{azureaisearch-endpoint}",
21+
"ApplicationInsights:ConnectionString": "{applicationinsights-connectionstring}",
2122
"KernelMemory:Services:AzureAIDocIntel:APIKey": "{azureaidocintel-apikey}",
2223
"KernelMemory:Services:AzureAIDocIntel:Endpoint": "{azureaidocintel-endpoint}",
2324
"KernelMemory:Services:AzureAISearch:APIKey": "{azureaisearch-apikey}",

Deployment/resourcedeployment.ps1

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ class DeploymentResult {
279279
[string]$AzCosmosDBConnectionString
280280
[string]$AzAppConfigEndpoint
281281
[string]$AzAppConfigName
282+
[string]$ApplicationInsightsConnectionString
283+
[string]$ApplicationInsightsInstrumentationKey
284+
[string]$ApplicationInsightsName
282285

283286
DeploymentResult() {
284287
# Resource Group
@@ -315,6 +318,10 @@ class DeploymentResult {
315318
$this.AzAppConfigEndpoint = ""
316319
# App Config Name
317320
$this.AzAppConfigName = ""
321+
# Application Insights
322+
$this.ApplicationInsightsConnectionString = ""
323+
$this.ApplicationInsightsInstrumentationKey = ""
324+
$this.ApplicationInsightsName = ""
318325

319326
}
320327

@@ -362,6 +369,11 @@ class DeploymentResult {
362369
# Azure App Configuration
363370
$this.AzAppConfigEndpoint = Get-AzdEnvValueOrDefault -KeyName "AZURE_APP_CONFIG_ENDPOINT"
364371
$this.AzAppConfigName = Get-AzdEnvValueOrDefault -KeyName "AZURE_APP_CONFIG_NAME"
372+
373+
# Application Insights
374+
$this.ApplicationInsightsConnectionString = Get-AzdEnvValueOrDefault -KeyName "APPLICATIONINSIGHTS_CONNECTION_STRING"
375+
$this.ApplicationInsightsInstrumentationKey = Get-AzdEnvValueOrDefault -KeyName "APPLICATIONINSIGHTS_INSTRUMENTATION_KEY"
376+
$this.ApplicationInsightsName = Get-AzdEnvValueOrDefault -KeyName "APPLICATIONINSIGHTS_NAME"
365377
}
366378

367379
[void]MapResultAz([string]$resourceGroupName) {
@@ -450,6 +462,11 @@ class DeploymentResult {
450462
# App Configuration
451463
$this.AzAppConfigEndpoint = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "azurE_APP_CONFIG_ENDPOINT" -fallbackKey "azureAppConfigEndpoint"
452464
$this.AzAppConfigName = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "azurE_APP_CONFIG_NAME" -fallbackKey "azureAppConfigName"
465+
466+
# Application Insights
467+
$this.ApplicationInsightsConnectionString = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "applicationinsightS_CONNECTION_STRING" -fallbackKey "applicationInsightsConnectionString"
468+
$this.ApplicationInsightsInstrumentationKey = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "applicationinsightS_INSTRUMENTATION_KEY" -fallbackKey "applicationInsightsInstrumentationKey"
469+
$this.ApplicationInsightsName = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "applicationinsightS_NAME" -fallbackKey "applicationInsightsName"
453470
}
454471
}
455472

@@ -559,7 +576,8 @@ try {
559576
'{azurequeues-account}' = $deploymentResult.StorageAccountName
560577
'{gpt-4o-modelname}' = $deploymentResult.AzGPT4oModelName
561578
'{azureopenaiembedding-deployment}' = $deploymentResult.AzGPTEmbeddingModelName
562-
'{kernelmemory-endpoint}' = "http://kernelmemory-service"
579+
'{kernelmemory-endpoint}' = "http://kernelmemory-service"
580+
'{applicationinsights-connectionstring}' = $deploymentResult.ApplicationInsightsConnectionString
563581
}
564582

565583
## Load and update the AI service configuration template

infra/main.bicep

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,10 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6
531531
]
532532

533533
keyValues: [
534+
{
535+
name: 'ApplicationInsights:ConnectionString'
536+
value: enableMonitoring ? applicationInsights!.outputs.connectionString : ''
537+
}
534538
{
535539
name: 'Application:AIServices:GPT-4o-mini:Endpoint'
536540
value: avmOpenAi.outputs.endpoint
@@ -1113,3 +1117,12 @@ output AZ_GPT_EMBEDDING_MODEL_NAME string = embeddingModelDeployment.modelName
11131117

11141118
@description('Contains Azure OpenAI Embedding Model Deployment Name.')
11151119
output AZ_GPT_EMBEDDING_MODEL_ID string = embeddingModelDeployment.deploymentName
1120+
1121+
@description('Contains Application Insights Connection String.')
1122+
output APPLICATIONINSIGHTS_CONNECTION_STRING string = enableMonitoring ? applicationInsights!.outputs.connectionString : ''
1123+
1124+
@description('Contains Application Insights Instrumentation Key.')
1125+
output APPLICATIONINSIGHTS_INSTRUMENTATION_KEY string = enableMonitoring ? applicationInsights!.outputs.instrumentationKey : ''
1126+
1127+
@description('Contains Application Insights Name.')
1128+
output APPLICATIONINSIGHTS_NAME string = enableMonitoring ? applicationInsights!.outputs.name : ''

0 commit comments

Comments
 (0)