Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
371 changes: 342 additions & 29 deletions App/backend-api/Microsoft.GS.DPS.Host/API/ChatHost/Chat.cs

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static void Inject(IHostApplicationBuilder builder)
{
builder.Services
.AddValidatorsFromAssemblyContaining<PagingRequestValidator>()
.AddSingleton<TelemetryHelper>()
.AddSingleton<Microsoft.GS.DPS.API.KernelMemory>()
.AddSingleton<Microsoft.GS.DPS.API.ChatHost>()
.AddSingleton<Microsoft.GS.DPS.API.UserInterface.Documents>()
Expand Down
172 changes: 172 additions & 0 deletions App/backend-api/Microsoft.GS.DPS.Host/Helpers/TelemetryHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using System.Diagnostics;

namespace Microsoft.GS.DPSHost.Helpers
{
/// <summary>
/// Helper class for Application Insights telemetry tracking
/// </summary>
public class TelemetryHelper
{
private readonly TelemetryClient? _telemetryClient;
private readonly ILogger<TelemetryHelper> _logger;
private readonly bool _isConfigured;

public TelemetryHelper(TelemetryClient? telemetryClient, ILogger<TelemetryHelper> logger)
{
_telemetryClient = telemetryClient;
_logger = logger;

// Check if Application Insights is properly configured
// TelemetryConfiguration.ConnectionString is the modern way (replaces deprecated InstrumentationKey)
_isConfigured = _telemetryClient != null &&
!string.IsNullOrEmpty(_telemetryClient.TelemetryConfiguration?.ConnectionString);

if (!_isConfigured)
{
_logger.LogWarning("Application Insights is not configured. Telemetry tracking will be disabled.");
}
else
{
_logger.LogInformation("Application Insights is configured successfully. Telemetry tracking is enabled.");
}
}

/// <summary>
/// Track a custom event in Application Insights
/// </summary>
/// <param name="eventName">Name of the event</param>
/// <param name="properties">Custom properties to track</param>
/// <param name="metrics">Custom metrics to track</param>
public void TrackEvent(string eventName, Dictionary<string, string>? properties = null, Dictionary<string, double>? metrics = null)
{
if (!_isConfigured || _telemetryClient == null)
{
return;
}

try
{
_telemetryClient.TrackEvent(eventName, properties, metrics);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to track event: {EventName}", eventName);
}
}

/// <summary>
/// Track an exception in Application Insights
/// </summary>
/// <param name="exception">The exception to track</param>
/// <param name="properties">Custom properties to track</param>
/// <param name="metrics">Custom metrics to track</param>
public void TrackException(Exception exception, Dictionary<string, string>? properties = null, Dictionary<string, double>? metrics = null)
{
if (!_isConfigured || _telemetryClient == null)
{
return;
}

try
{
_telemetryClient.TrackException(exception, properties, metrics);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to track exception");
}
}

/// <summary>
/// Track a dependency call in Application Insights
/// </summary>
/// <param name="dependencyName">Name of the dependency</param>
/// <param name="commandName">Command or operation name</param>
/// <param name="startTime">Start time of the operation</param>
/// <param name="duration">Duration of the operation</param>
/// <param name="success">Whether the operation was successful</param>
public void TrackDependency(string dependencyName, string commandName, DateTimeOffset startTime, TimeSpan duration, bool success)
{
if (!_isConfigured || _telemetryClient == null)
{
return;
}

try
{
_telemetryClient.TrackDependency(dependencyName, commandName, startTime, duration, success);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to track dependency: {DependencyName}", dependencyName);
}
}

/// <summary>
/// Track a metric in Application Insights
/// </summary>
/// <param name="metricName">Name of the metric</param>
/// <param name="value">Metric value</param>
/// <param name="properties">Custom properties to track</param>
public void TrackMetric(string metricName, double value, Dictionary<string, string>? properties = null)
{
if (!_isConfigured || _telemetryClient == null)
{
return;
}

try
{
_telemetryClient.TrackMetric(metricName, value, properties);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to track metric: {MetricName}", metricName);
}
}

/// <summary>
/// Sets a custom property on the current activity for correlation
/// </summary>
/// <param name="key">Property key</param>
/// <param name="value">Property value</param>
public void SetActivityTag(string key, string value)
{
if (!_isConfigured)
{
return;
}

try
{
Activity.Current?.SetTag(key, value);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to set activity tag: {Key}", key);
}
}

/// <summary>
/// Flush the telemetry client to ensure all telemetry is sent
/// </summary>
public void Flush()
{
if (!_isConfigured || _telemetryClient == null)
{
return;
}

try
{
_telemetryClient.Flush();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to flush telemetry client");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Azure.Data.AppConfiguration" Version="1.6.1" />
<PackageReference Include="Azure.Identity" Version="1.14.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.12.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="8.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.22.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.6" />
<PackageReference Include="Microsoft.KernelMemory.WebClient" Version="0.79.241014.2" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.32.0" />
Expand Down
22 changes: 22 additions & 0 deletions App/backend-api/Microsoft.GS.DPS.Host/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.GS.DPS.Storage.Document;
using NSwag.AspNetCore;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.ApplicationInsights.AspNetCore.Extensions;

var builder = WebApplication.CreateBuilder(args);

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

// Configure Application Insights - Always register to ensure TelemetryClient is available for DI
var connectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
builder.Services.AddApplicationInsightsTelemetry(options =>
{
if (!string.IsNullOrEmpty(connectionString))
{
options.ConnectionString = connectionString;
options.EnableAdaptiveSampling = builder.Configuration.GetValue<bool>("ApplicationInsights:EnableAdaptiveSampling", true);
options.EnablePerformanceCounterCollectionModule = builder.Configuration.GetValue<bool>("ApplicationInsights:EnablePerformanceCounterCollectionModule", true);
options.EnableQuickPulseMetricStream = builder.Configuration.GetValue<bool>("ApplicationInsights:EnableQuickPulseMetricStream", true);
}
});

// Configure logging
if (!string.IsNullOrEmpty(connectionString))
{
builder.Logging.AddApplicationInsights();
}
builder.Logging.AddConsole();
builder.Logging.AddDebug();

//Bson Register Class Maps
//MongoDbConfig.RegisterClassMaps();

Expand Down
18 changes: 16 additions & 2 deletions App/backend-api/Microsoft.GS.DPS.Host/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft.AspNetCore": "Trace"
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Azure.Core": "Warning",
"Azure.Identity": "Warning"
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"AppConfig": ""
},
"ApplicationInsights": {
"ConnectionString": "",
"EnableAdaptiveSampling": true,
"EnablePerformanceCounterCollectionModule": true,
"EnableQuickPulseMetricStream": true
},
"Application": {
"AIServices": {
"GPT-4o": {
Expand Down
1 change: 1 addition & 0 deletions Deployment/appconfig/aiservice/appconfig.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"Application:Services:PersistentStorage:CosmosMongo:ConnectionString": "{cosmosmongo-connection-string}",
"Application:Services:AzureAISearch:APIKey" : "{azureaisearch-apikey}",
"Application:Services:AzureAISearch:Endpoint" : "{azureaisearch-endpoint}",
"ApplicationInsights:ConnectionString": "{applicationinsights-connectionstring}",
"KernelMemory:Services:AzureAIDocIntel:APIKey": "{azureaidocintel-apikey}",
"KernelMemory:Services:AzureAIDocIntel:Endpoint": "{azureaidocintel-endpoint}",
"KernelMemory:Services:AzureAISearch:APIKey": "{azureaisearch-apikey}",
Expand Down
20 changes: 19 additions & 1 deletion Deployment/resourcedeployment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ class DeploymentResult {
[string]$AzCosmosDBConnectionString
[string]$AzAppConfigEndpoint
[string]$AzAppConfigName
[string]$ApplicationInsightsConnectionString
[string]$ApplicationInsightsInstrumentationKey
[string]$ApplicationInsightsName

DeploymentResult() {
# Resource Group
Expand Down Expand Up @@ -315,6 +318,10 @@ class DeploymentResult {
$this.AzAppConfigEndpoint = ""
# App Config Name
$this.AzAppConfigName = ""
# Application Insights
$this.ApplicationInsightsConnectionString = ""
$this.ApplicationInsightsInstrumentationKey = ""
$this.ApplicationInsightsName = ""

}

Expand Down Expand Up @@ -362,6 +369,11 @@ class DeploymentResult {
# Azure App Configuration
$this.AzAppConfigEndpoint = Get-AzdEnvValueOrDefault -KeyName "AZURE_APP_CONFIG_ENDPOINT"
$this.AzAppConfigName = Get-AzdEnvValueOrDefault -KeyName "AZURE_APP_CONFIG_NAME"

# Application Insights
$this.ApplicationInsightsConnectionString = Get-AzdEnvValueOrDefault -KeyName "APPLICATIONINSIGHTS_CONNECTION_STRING"
$this.ApplicationInsightsInstrumentationKey = Get-AzdEnvValueOrDefault -KeyName "APPLICATIONINSIGHTS_INSTRUMENTATION_KEY"
$this.ApplicationInsightsName = Get-AzdEnvValueOrDefault -KeyName "APPLICATIONINSIGHTS_NAME"
}

[void]MapResultAz([string]$resourceGroupName) {
Expand Down Expand Up @@ -450,6 +462,11 @@ class DeploymentResult {
# App Configuration
$this.AzAppConfigEndpoint = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "azurE_APP_CONFIG_ENDPOINT" -fallbackKey "azureAppConfigEndpoint"
$this.AzAppConfigName = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "azurE_APP_CONFIG_NAME" -fallbackKey "azureAppConfigName"

# Application Insights
$this.ApplicationInsightsConnectionString = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "applicationinsightS_CONNECTION_STRING" -fallbackKey "applicationInsightsConnectionString"
$this.ApplicationInsightsInstrumentationKey = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "applicationinsightS_INSTRUMENTATION_KEY" -fallbackKey "applicationInsightsInstrumentationKey"
$this.ApplicationInsightsName = Get-DeploymentOutputValue -outputs $deploymentOutputs -primaryKey "applicationinsightS_NAME" -fallbackKey "applicationInsightsName"
}
}

Expand Down Expand Up @@ -559,7 +576,8 @@ try {
'{azurequeues-account}' = $deploymentResult.StorageAccountName
'{gpt-4o-modelname}' = $deploymentResult.AzGPT4oModelName
'{azureopenaiembedding-deployment}' = $deploymentResult.AzGPTEmbeddingModelName
'{kernelmemory-endpoint}' = "http://kernelmemory-service"
'{kernelmemory-endpoint}' = "http://kernelmemory-service"
'{applicationinsights-connectionstring}' = $deploymentResult.ApplicationInsightsConnectionString
}

## Load and update the AI service configuration template
Expand Down
13 changes: 13 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6
]

keyValues: [
{
name: 'ApplicationInsights:ConnectionString'
value: enableMonitoring ? applicationInsights!.outputs.connectionString : ''
}
{
name: 'Application:AIServices:GPT-4o-mini:Endpoint'
value: avmOpenAi.outputs.endpoint
Expand Down Expand Up @@ -1113,3 +1117,12 @@ output AZ_GPT_EMBEDDING_MODEL_NAME string = embeddingModelDeployment.modelName

@description('Contains Azure OpenAI Embedding Model Deployment Name.')
output AZ_GPT_EMBEDDING_MODEL_ID string = embeddingModelDeployment.deploymentName

@description('Contains Application Insights Connection String.')
output APPLICATIONINSIGHTS_CONNECTION_STRING string = enableMonitoring ? applicationInsights!.outputs.connectionString : ''

@description('Contains Application Insights Instrumentation Key.')
output APPLICATIONINSIGHTS_INSTRUMENTATION_KEY string = enableMonitoring ? applicationInsights!.outputs.instrumentationKey : ''

@description('Contains Application Insights Name.')
output APPLICATIONINSIGHTS_NAME string = enableMonitoring ? applicationInsights!.outputs.name : ''
Loading