From a68ee63a84b1744b00665f2b68dd43bdf9d80371 Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Mon, 12 Aug 2024 08:58:56 -0400 Subject: [PATCH 1/9] Update logging settings/configs --- src/dotnet/AuthorizationAPI/Program.cs | 2 + src/dotnet/AuthorizationAPI/appsettings.json | 20 +++-- src/dotnet/Common/Common.csproj | 1 + .../Common/Services/DependencyInjection.cs | 83 +++++++++++++++++-- src/dotnet/CoreAPI/Program.cs | 2 + src/dotnet/CoreAPI/appsettings.json | 48 +++++++---- src/dotnet/CoreWorker/appsettings.json | 18 ++-- src/dotnet/GatekeeperAPI/Program.cs | 2 + src/dotnet/GatekeeperAPI/appsettings.json | 30 ++++--- src/dotnet/GatewayAPI/Program.cs | 2 + src/dotnet/GatewayAPI/appsettings.json | 20 +++-- src/dotnet/GatewayAdapterAPI/appsettings.json | 31 +++++-- src/dotnet/ManagementAPI/Program.cs | 2 + src/dotnet/ManagementAPI/appsettings.json | 20 +++-- src/dotnet/OrchestrationAPI/Program.cs | 2 + src/dotnet/OrchestrationAPI/appsettings.json | 32 ++++--- src/dotnet/SemanticKernelAPI/Program.cs | 2 + src/dotnet/SemanticKernelAPI/appsettings.json | 34 ++++---- src/dotnet/StateAPI/Program.cs | 2 + src/dotnet/StateAPI/appsettings.json | 10 ++- src/dotnet/VectorizationAPI/appsettings.json | 8 +- src/python/AgentHubAPI/app/main.py | 6 +- src/python/DataSourceHubAPI/app/main.py | 6 +- src/python/LangChainAPI/app/main.py | 6 +- src/python/PromptHubAPI/app/main.py | 6 +- .../foundationallm/telemetry/telemetry.py | 49 ++++++++++- 26 files changed, 329 insertions(+), 115 deletions(-) diff --git a/src/dotnet/AuthorizationAPI/Program.cs b/src/dotnet/AuthorizationAPI/Program.cs index f5e5ba0454..9af234497a 100644 --- a/src/dotnet/AuthorizationAPI/Program.cs +++ b/src/dotnet/AuthorizationAPI/Program.cs @@ -44,6 +44,8 @@ requireScopes: false, allowACLAuthorization: true); +builder.AddLogging(); + // Add OpenTelemetry. builder.AddOpenTelemetry( AuthorizationKeyVaultSecretNames.FoundationaLLM_APIEndpoints_AuthorizationAPI_AppInsightsConnectionString, diff --git a/src/dotnet/AuthorizationAPI/appsettings.json b/src/dotnet/AuthorizationAPI/appsettings.json index 10f68b8c8b..e9dfa821db 100644 --- a/src/dotnet/AuthorizationAPI/appsettings.json +++ b/src/dotnet/AuthorizationAPI/appsettings.json @@ -1,9 +1,15 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + } + }, + "AllowedHosts": "*" } diff --git a/src/dotnet/Common/Common.csproj b/src/dotnet/Common/Common.csproj index c875a3fb74..ba6aa21821 100644 --- a/src/dotnet/Common/Common.csproj +++ b/src/dotnet/Common/Common.csproj @@ -68,6 +68,7 @@ + diff --git a/src/dotnet/Common/Services/DependencyInjection.cs b/src/dotnet/Common/Services/DependencyInjection.cs index c0d9a4bc38..8c7f9b64e5 100644 --- a/src/dotnet/Common/Services/DependencyInjection.cs +++ b/src/dotnet/Common/Services/DependencyInjection.cs @@ -1,4 +1,5 @@ using Azure.Monitor.OpenTelemetry.AspNetCore; +using Azure.Monitor.OpenTelemetry.Exporter; using FoundationaLLM.Common.Authentication; using FoundationaLLM.Common.Constants; using FoundationaLLM.Common.Constants.Configuration; @@ -14,12 +15,18 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Azure; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Hosting; using Microsoft.Identity.Web; +using OpenTelemetry; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.ResourceDetectors.Azure; using OpenTelemetry.Resources; using OpenTelemetry.Trace; +using System.Diagnostics; using Microsoft.Extensions.Configuration; namespace FoundationaLLM @@ -31,6 +38,27 @@ public static partial class DependencyInjection { /// /// Adds CORS policies the the dependency injection container. + /// Configures logging defaults. + /// + /// The host application builder. + public static void AddLogging(this IHostApplicationBuilder builder) => + builder.Services.AddLogging(config => + { + // clear out default configuration + config.ClearProviders(); + + config.AddConfiguration(builder.Configuration.GetSection("Logging")); + config.AddDebug(); + config.AddEventSourceLogger(); + + if (builder.Configuration["ASPNETCORE_ENVIRONMENT"] == EnvironmentName.Development) + { + config.AddConsole(); + } + }); + + /// + /// Add CORS policies the the dependency injection container. /// /// The application builder managing the dependency injection container. public static void AddCorsPolicies(this IHostApplicationBuilder builder) => @@ -57,18 +85,57 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, string connectionStringConfigurationKey, string serviceName) { - // Add the OpenTelemetry telemetry service and send telemetry data to Azure Monitor. - builder.Services.AddOpenTelemetry().UseAzureMonitor(options => + AzureMonitorOptions options = new AzureMonitorOptions { ConnectionString = connectionStringConfigurationKey }; + + builder.Services.AddOpenTelemetry() + .WithTracing(b => + { + b + .AddSource("Azure.*") + //.AddConsoleExporter() + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(o => o.FilterHttpRequestMessage = (_) => + { + // Azure SDKs create their own client span before calling the service using HttpClient + // In this case, we would see two spans corresponding to the same operation + // 1) created by Azure SDK 2) created by HttpClient + // To prevent this duplication we are filtering the span from HttpClient + // as span from Azure SDK contains all relevant information needed. + var parentActivity = Activity.Current?.Parent; + if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http")) + { + return false; + } + return true; + }) + .AddAzureMonitorTraceExporter(options => { options.ConnectionString = builder.Configuration[connectionStringConfigurationKey]; }); + }); + + Action configureResource = (r) => r + .AddAttributes(new[] { new KeyValuePair("telemetry.distro.name", "Azure.Monitor.OpenTelemetry.AspNetCore") }); + + builder.Services.AddLogging(logging => { - options.ConnectionString = builder.Configuration[connectionStringConfigurationKey]; + logging.AddOpenTelemetry(builderOptions => + { + var resourceBuilder = ResourceBuilder.CreateDefault(); + configureResource(resourceBuilder); + builderOptions.SetResourceBuilder(resourceBuilder); + + builderOptions.IncludeFormattedMessage = true; + builderOptions.IncludeScopes = false; + builderOptions.AddAzureMonitorLogExporter(options => { options.ConnectionString = builder.Configuration[connectionStringConfigurationKey]; }); + }) + .AddConfiguration(builder.Configuration); + }); // Create a dictionary of resource attributes. var resourceAttributes = new Dictionary { - { "service.name", serviceName }, - { "service.namespace", "FoundationaLLM" }, - { "service.instance.id", ValidatedEnvironment.MachineName } - }; + { "service.name", serviceName }, + { "service.namespace", "FoundationaLLM" }, + { "service.instance.id", ValidatedEnvironment.MachineName } + }; // Configure the OpenTelemetry tracer provider to add the resource attributes to all traces. builder.Services.ConfigureOpenTelemetryTracerProvider((sp, builder) => diff --git a/src/dotnet/CoreAPI/Program.cs b/src/dotnet/CoreAPI/Program.cs index 40c6f519b6..aaf2fd2a8b 100644 --- a/src/dotnet/CoreAPI/Program.cs +++ b/src/dotnet/CoreAPI/Program.cs @@ -132,6 +132,8 @@ public static void Main(string[] args) allowACLAuthorization: isE2ETestEnvironment ); + builder.AddLogging(); + // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_CoreAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/CoreAPI/appsettings.json b/src/dotnet/CoreAPI/appsettings.json index 3effe65ba3..ac19ddf821 100644 --- a/src/dotnet/CoreAPI/appsettings.json +++ b/src/dotnet/CoreAPI/appsettings.json @@ -1,20 +1,32 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } - }, - "AllowedHosts": "*", - "FoundationaLLM": { - "AppConfig": { - "ConnectionString": "" - } - } + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + }, + "Debug": { // Debug provider. + "LogLevel": { + "Default": "Warning", // Overrides preceding LogLevel:Default setting. + "Microsoft.Hosting": "Warning" // Debug:Microsoft.Hosting category. + } + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + }, + "ApplicationInsights": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } + } + }, + "AllowedHosts": "*", + "FoundationaLLM": { + "AppConfig": { + "ConnectionString": "" + } + } } diff --git a/src/dotnet/CoreWorker/appsettings.json b/src/dotnet/CoreWorker/appsettings.json index eee48e4609..529823951c 100644 --- a/src/dotnet/CoreWorker/appsettings.json +++ b/src/dotnet/CoreWorker/appsettings.json @@ -1,10 +1,16 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.Hosting.Lifetime": "Information" - } - }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.Hosting.Lifetime": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + } + }, "FoundationaLLM": { "AppConfig": { "ConnectionString": "" diff --git a/src/dotnet/GatekeeperAPI/Program.cs b/src/dotnet/GatekeeperAPI/Program.cs index 53e05eabb7..cd27f51f6c 100644 --- a/src/dotnet/GatekeeperAPI/Program.cs +++ b/src/dotnet/GatekeeperAPI/Program.cs @@ -66,6 +66,8 @@ public static void Main(string[] args) builder.AddGroupMembership(); builder.AddAuthorizationService(); + builder.AddLogging(); + // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_GatekeeperAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/GatekeeperAPI/appsettings.json b/src/dotnet/GatekeeperAPI/appsettings.json index 3effe65ba3..21a6ed41e4 100644 --- a/src/dotnet/GatekeeperAPI/appsettings.json +++ b/src/dotnet/GatekeeperAPI/appsettings.json @@ -1,16 +1,22 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } - }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + }, + "ApplicationInsights": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } + } + }, "AllowedHosts": "*", "FoundationaLLM": { "AppConfig": { diff --git a/src/dotnet/GatewayAPI/Program.cs b/src/dotnet/GatewayAPI/Program.cs index f4974235c0..aec3f3289a 100644 --- a/src/dotnet/GatewayAPI/Program.cs +++ b/src/dotnet/GatewayAPI/Program.cs @@ -43,6 +43,8 @@ if (builder.Environment.IsDevelopment()) builder.Configuration.AddJsonFile("appsettings.development.json", true, true); +builder.AddLogging(); + builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_GatewayAPI_Essentials_AppInsightsConnectionString, ServiceNames.GatewayAPI); diff --git a/src/dotnet/GatewayAPI/appsettings.json b/src/dotnet/GatewayAPI/appsettings.json index 10f68b8c8b..e9dfa821db 100644 --- a/src/dotnet/GatewayAPI/appsettings.json +++ b/src/dotnet/GatewayAPI/appsettings.json @@ -1,9 +1,15 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + } + }, + "AllowedHosts": "*" } diff --git a/src/dotnet/GatewayAdapterAPI/appsettings.json b/src/dotnet/GatewayAdapterAPI/appsettings.json index 10f68b8c8b..a70614408c 100644 --- a/src/dotnet/GatewayAdapterAPI/appsettings.json +++ b/src/dotnet/GatewayAdapterAPI/appsettings.json @@ -1,9 +1,26 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + }, + "ApplicationInsights": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } + } + }, + "AllowedHosts": "*", + "FoundationaLLM": { + "AppConfig": { + "ConnectionString": "" + } + } } diff --git a/src/dotnet/ManagementAPI/Program.cs b/src/dotnet/ManagementAPI/Program.cs index 34394d606f..b2207d24d8 100644 --- a/src/dotnet/ManagementAPI/Program.cs +++ b/src/dotnet/ManagementAPI/Program.cs @@ -118,6 +118,8 @@ public static void Main(string[] args) allowACLAuthorization: isE2ETestEnvironment ); + builder.AddLogging(); + // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_ManagementAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/ManagementAPI/appsettings.json b/src/dotnet/ManagementAPI/appsettings.json index 10f68b8c8b..e9dfa821db 100644 --- a/src/dotnet/ManagementAPI/appsettings.json +++ b/src/dotnet/ManagementAPI/appsettings.json @@ -1,9 +1,15 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + } + }, + "AllowedHosts": "*" } diff --git a/src/dotnet/OrchestrationAPI/Program.cs b/src/dotnet/OrchestrationAPI/Program.cs index cd71d1cf81..13bab000b6 100644 --- a/src/dotnet/OrchestrationAPI/Program.cs +++ b/src/dotnet/OrchestrationAPI/Program.cs @@ -70,6 +70,8 @@ public static void Main(string[] args) // Add services to the container. + builder.AddLogging(); + // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_OrchestrationAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/OrchestrationAPI/appsettings.json b/src/dotnet/OrchestrationAPI/appsettings.json index 5dd3d1702e..21a6ed41e4 100644 --- a/src/dotnet/OrchestrationAPI/appsettings.json +++ b/src/dotnet/OrchestrationAPI/appsettings.json @@ -1,20 +1,26 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - } - }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + }, + "ApplicationInsights": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } + } + }, "AllowedHosts": "*", "FoundationaLLM": { "AppConfig": { "ConnectionString": "" } } -} \ No newline at end of file +} diff --git a/src/dotnet/SemanticKernelAPI/Program.cs b/src/dotnet/SemanticKernelAPI/Program.cs index c859156a6e..6190f9c5f0 100644 --- a/src/dotnet/SemanticKernelAPI/Program.cs +++ b/src/dotnet/SemanticKernelAPI/Program.cs @@ -55,6 +55,8 @@ public static void Main(string[] args) if (builder.Environment.IsDevelopment()) builder.Configuration.AddJsonFile("appsettings.development.json", true, true); + builder.AddLogging(); + // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_SemanticKernelAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/SemanticKernelAPI/appsettings.json b/src/dotnet/SemanticKernelAPI/appsettings.json index a9dcdf4bfa..afc096fc4e 100644 --- a/src/dotnet/SemanticKernelAPI/appsettings.json +++ b/src/dotnet/SemanticKernelAPI/appsettings.json @@ -1,18 +1,24 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "Microsoft.SemanticKernel": "Error" - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "Microsoft.SemanticKernel": "Error" - } - } - }, + "Logging": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning", + "Microsoft.SemanticKernel": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + }, + "ApplicationInsights": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning", + "Microsoft.SemanticKernel": "Warning" + } + } + }, "AllowedHosts": "*", "FoundationaLLM": { "AppConfig": { diff --git a/src/dotnet/StateAPI/Program.cs b/src/dotnet/StateAPI/Program.cs index bc869692d2..41a544c7dc 100644 --- a/src/dotnet/StateAPI/Program.cs +++ b/src/dotnet/StateAPI/Program.cs @@ -44,6 +44,8 @@ if (builder.Environment.IsDevelopment()) builder.Configuration.AddJsonFile("appsettings.development.json", true, true); +builder.AddLogging(); + builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_StateAPI_Essentials_AppInsightsConnectionString, ServiceNames.StateAPI); diff --git a/src/dotnet/StateAPI/appsettings.json b/src/dotnet/StateAPI/appsettings.json index cdd1ae462c..a70614408c 100644 --- a/src/dotnet/StateAPI/appsettings.json +++ b/src/dotnet/StateAPI/appsettings.json @@ -1,14 +1,20 @@ { "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Warning", "Microsoft.AspNetCore": "Warning" }, - "ApplicationInsights": { + "OpenTelemetry": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Information" } + }, + "ApplicationInsights": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } }, "AllowedHosts": "*", diff --git a/src/dotnet/VectorizationAPI/appsettings.json b/src/dotnet/VectorizationAPI/appsettings.json index 9c3a5de988..e9dfa821db 100644 --- a/src/dotnet/VectorizationAPI/appsettings.json +++ b/src/dotnet/VectorizationAPI/appsettings.json @@ -1,8 +1,14 @@ { "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Warning", "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } } }, "AllowedHosts": "*" diff --git a/src/python/AgentHubAPI/app/main.py b/src/python/AgentHubAPI/app/main.py index 32a377178b..6c1bc08c3c 100644 --- a/src/python/AgentHubAPI/app/main.py +++ b/src/python/AgentHubAPI/app/main.py @@ -13,7 +13,7 @@ # Open a connection to the app configuration config = get_config() # Start collecting telemetry -# Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString') +# Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString', API_NAME) app = FastAPI( title=f'FoundationaLLM {API_NAME}', @@ -24,7 +24,7 @@ contact={ 'name':'Solliance, Inc.', 'email':'contact@solliance.net', - 'url':'https://solliance.net/' + 'url':'https://solliance.net/' }, openapi_url='/swagger/v1/swagger.json', docs_url='/swagger', @@ -43,7 +43,7 @@ async def root(): """ Root path of the API. - + Returns ------- str diff --git a/src/python/DataSourceHubAPI/app/main.py b/src/python/DataSourceHubAPI/app/main.py index 0f6a367e42..5449318863 100644 --- a/src/python/DataSourceHubAPI/app/main.py +++ b/src/python/DataSourceHubAPI/app/main.py @@ -13,7 +13,7 @@ # Open a connection to the app configuration config = get_config() # Start collecting telemetry -# Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString') +# Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString', API_NAME) app = FastAPI( title=f'FoundationaLLM {API_NAME}', @@ -24,7 +24,7 @@ contact={ 'name':'Solliance, Inc.', 'email':'contact@solliance.net', - 'url':'https://solliance.net/' + 'url':'https://solliance.net/' }, openapi_url='/swagger/v1/swagger.json', docs_url='/swagger', @@ -43,7 +43,7 @@ async def root(): """ Root path of the API. - + Returns ------- str diff --git a/src/python/LangChainAPI/app/main.py b/src/python/LangChainAPI/app/main.py index 000f157f37..fe6c798ef9 100644 --- a/src/python/LangChainAPI/app/main.py +++ b/src/python/LangChainAPI/app/main.py @@ -14,7 +14,7 @@ # Open a connection to the app configuration config = get_config() # Start collecting telemetry -Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString') +Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString', API_NAME) app = FastAPI( title=f'FoundationaLLM {API_NAME}', @@ -25,7 +25,7 @@ contact={ 'name':'Solliance, Inc.', 'email':'contact@solliance.net', - 'url':'https://solliance.net/' + 'url':'https://solliance.net/' }, openapi_url='/swagger/v1/swagger.json', docs_url='/swagger', @@ -45,7 +45,7 @@ async def root(): """ Root path of the API. - + Returns ------- str diff --git a/src/python/PromptHubAPI/app/main.py b/src/python/PromptHubAPI/app/main.py index e310a67257..e516c04ac4 100644 --- a/src/python/PromptHubAPI/app/main.py +++ b/src/python/PromptHubAPI/app/main.py @@ -13,7 +13,7 @@ # Open a connection to the app configuration config = get_config() # Start collecting telemetry -# Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString') +# Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString', API_NAME) app = FastAPI( title=f'FoundationaLLM {API_NAME}', @@ -24,7 +24,7 @@ contact={ 'name':'Solliance, Inc.', 'email':'contact@solliance.net', - 'url':'https://solliance.net/' + 'url':'https://solliance.net/' }, openapi_url='/swagger/v1/swagger.json', docs_url='/swagger', @@ -43,7 +43,7 @@ async def root(): """ Root path of the API. - + Returns ------- str diff --git a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py index 347962bf43..9564f88aa7 100644 --- a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py +++ b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py @@ -1,16 +1,34 @@ import logging from azure.monitor.opentelemetry import configure_azure_monitor +from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter from opentelemetry import trace from opentelemetry.trace import Span, Status, StatusCode, Tracer +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.resources import SERVICE_NAME, Resource +from opentelemetry.sdk.trace.export import ( + BatchSpanProcessor, + ConsoleSpanExporter, +) + from foundationallm.config import Configuration +from opentelemetry._logs import ( + get_logger_provider, + set_logger_provider, +) + +from opentelemetry.sdk._logs import ( + LoggerProvider, + LoggingHandler, +) + class Telemetry: """ Manages logging and the recording of application telemetry. """ @staticmethod - def configure_monitoring(config: Configuration, telemetry_connection_string: str): + def configure_monitoring(config: Configuration, telemetry_connection_string: str, api_name : str): """ Configures monitoring and sends logs, metrics, and events to Azure Monitor. @@ -22,10 +40,39 @@ def configure_monitoring(config: Configuration, telemetry_connection_string: str telemetry_connection_string : str The connection string used to connect to Azure Application Insights. """ + + """ configure_azure_monitor( connection_string=config.get_value(telemetry_connection_string), disable_offline_storage=True ) + """ + + trace.set_tracer_provider( + TracerProvider( + resource=Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{api_name}"}) + ) + ) + + azure_exporter = AzureMonitorTraceExporter( + connection_string=config.get_value(telemetry_connection_string) + ) + + # Create a BatchSpanProcessor and add the exporter to it + azure_span_processor = BatchSpanProcessor(azure_exporter) + + # add to the tracer + trace.get_tracer_provider().add_span_processor(azure_span_processor) + + logger_provider = LoggerProvider( + resource=Resource.create( + { + SERVICE_NAME: api_name + } + ) + ) + + set_logger_provider(logger_provider) @staticmethod def get_logger(name: str, level: int = logging.INFO) -> logging.Logger: From 01952e1659e991781684fcdf54bb7e06e73617bc Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Tue, 20 Aug 2024 21:02:21 -0400 Subject: [PATCH 2/9] Add logging filters; remove appsettings; Python changes --- src/dotnet/AuthorizationAPI/Program.cs | 2 - src/dotnet/AuthorizationAPI/appsettings.json | 6 +- src/dotnet/Common/Common.csproj | 2 +- .../Services/Azure/AzureCosmosDBService.cs | 2 +- .../Common/Services/DependencyInjection.cs | 30 ++- .../Templates/AppConfigurationKeyFilters.cs | 8 +- src/dotnet/CoreAPI/CoreAPI.csproj | 2 +- src/dotnet/CoreAPI/Program.cs | 3 +- src/dotnet/CoreAPI/appsettings.json | 18 +- src/dotnet/CoreWorker/Program.cs | 6 + src/dotnet/CoreWorker/appsettings.json | 6 +- src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj | 2 +- src/dotnet/GatekeeperAPI/Program.cs | 3 +- src/dotnet/GatekeeperAPI/appsettings.json | 12 +- src/dotnet/GatewayAPI/Program.cs | 3 +- src/dotnet/GatewayAPI/appsettings.json | 6 +- src/dotnet/ManagementAPI/ManagementAPI.csproj | 2 +- src/dotnet/ManagementAPI/Program.cs | 2 - src/dotnet/ManagementAPI/appsettings.json | 6 +- .../KnowledgeManagementOrchestration.cs | 2 +- .../OrchestrationAPI/OrchestrationAPI.csproj | 2 +- src/dotnet/OrchestrationAPI/Program.cs | 3 +- src/dotnet/OrchestrationAPI/appsettings.json | 12 +- src/dotnet/SemanticKernelAPI/Program.cs | 3 +- .../SemanticKernelAPI.csproj | 2 +- src/dotnet/SemanticKernelAPI/appsettings.json | 14 +- src/dotnet/StateAPI/Program.cs | 3 +- src/dotnet/StateAPI/appsettings.json | 12 +- src/dotnet/VectorizationAPI/Program.cs | 1 + .../VectorizationAPI/VectorizationAPI.csproj | 2 +- src/dotnet/VectorizationAPI/appsettings.json | 4 +- src/dotnet/VectorizationWorker/Program.cs | 1 + .../VectorizationWorker.csproj | 2 +- .../VectorizationWorker/appsettings.json | 8 +- src/python/AgentHubAPI/app/dependencies.py | 15 +- .../DataSourceHubAPI/app/dependencies.py | 15 +- .../app/dependencies.py | 15 +- src/python/LangChainAPI/app/dependencies.py | 24 ++- .../LangChainAPI/app/routers/completions.py | 3 +- src/python/PromptHubAPI/app/dependencies.py | 15 +- .../foundationallm/config/configuration.py | 12 +- .../langchain/agents/langchain_agent_base.py | 41 ++-- .../langchain_knowledge_management_agent.py | 21 +- .../search_service_filter_retriever.py | 10 +- .../operations/operations_manager.py | 61 ++++-- .../services/image_analysis_service.py | 8 +- .../foundationallm/telemetry/__init__.py | 1 + .../foundationallm/telemetry/telemetry.py | 197 +++++++++++++++--- 48 files changed, 421 insertions(+), 209 deletions(-) diff --git a/src/dotnet/AuthorizationAPI/Program.cs b/src/dotnet/AuthorizationAPI/Program.cs index 9af234497a..f5e5ba0454 100644 --- a/src/dotnet/AuthorizationAPI/Program.cs +++ b/src/dotnet/AuthorizationAPI/Program.cs @@ -44,8 +44,6 @@ requireScopes: false, allowACLAuthorization: true); -builder.AddLogging(); - // Add OpenTelemetry. builder.AddOpenTelemetry( AuthorizationKeyVaultSecretNames.FoundationaLLM_APIEndpoints_AuthorizationAPI_AppInsightsConnectionString, diff --git a/src/dotnet/AuthorizationAPI/appsettings.json b/src/dotnet/AuthorizationAPI/appsettings.json index e9dfa821db..36c0678c20 100644 --- a/src/dotnet/AuthorizationAPI/appsettings.json +++ b/src/dotnet/AuthorizationAPI/appsettings.json @@ -1,13 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" + "Default": "Warning" } } }, diff --git a/src/dotnet/Common/Common.csproj b/src/dotnet/Common/Common.csproj index ba6aa21821..538ca51d6e 100644 --- a/src/dotnet/Common/Common.csproj +++ b/src/dotnet/Common/Common.csproj @@ -50,7 +50,7 @@ - + diff --git a/src/dotnet/Common/Services/Azure/AzureCosmosDBService.cs b/src/dotnet/Common/Services/Azure/AzureCosmosDBService.cs index f842618697..4a27821336 100644 --- a/src/dotnet/Common/Services/Azure/AzureCosmosDBService.cs +++ b/src/dotnet/Common/Services/Azure/AzureCosmosDBService.cs @@ -279,7 +279,7 @@ public async Task DeleteSessionAndMessagesAsync(string sessionId, CancellationTo var response = _sessions.GetItemQueryIterator(query); - Console.WriteLine($"Deleting {sessionId} session and related messages."); + _logger.LogInformation($"Deleting {sessionId} session and related messages."); var batch = _sessions.CreateTransactionalBatch(partitionKey); var count = 0; diff --git a/src/dotnet/Common/Services/DependencyInjection.cs b/src/dotnet/Common/Services/DependencyInjection.cs index 8c7f9b64e5..c0bf27420c 100644 --- a/src/dotnet/Common/Services/DependencyInjection.cs +++ b/src/dotnet/Common/Services/DependencyInjection.cs @@ -51,9 +51,16 @@ public static void AddLogging(this IHostApplicationBuilder builder) => config.AddDebug(); config.AddEventSourceLogger(); - if (builder.Configuration["ASPNETCORE_ENVIRONMENT"] == EnvironmentName.Development) + //get the log level + string logLevel = builder.Configuration["Logging:LogLevel:Default"]; + + //enable console for debug or trace + switch(logLevel) { - config.AddConsole(); + case "Trace": + case "Debug": + config.AddConsole(); + break; } }); @@ -116,6 +123,25 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, builder.Services.AddLogging(logging => { + // clear out default configuration + logging.ClearProviders(); + + logging.AddConfiguration(builder.Configuration.GetSection("Logging")); + logging.AddDebug(); + logging.AddEventSourceLogger(); + + //get the log level + string logLevel = builder.Configuration["Logging:LogLevel:Default"]; + + //enable console for debug or trace + switch (logLevel) + { + case "Trace": + case "Debug": + logging.AddConsole(); + break; + } + logging.AddOpenTelemetry(builderOptions => { var resourceBuilder = ResourceBuilder.CreateDefault(); diff --git a/src/dotnet/Common/Templates/AppConfigurationKeyFilters.cs b/src/dotnet/Common/Templates/AppConfigurationKeyFilters.cs index 6169443edb..4cb1ef46ad 100644 --- a/src/dotnet/Common/Templates/AppConfigurationKeyFilters.cs +++ b/src/dotnet/Common/Templates/AppConfigurationKeyFilters.cs @@ -17,7 +17,13 @@ public static partial class AppConfigurationKeyFilters /// public const string FoundationaLLM_Instance = "FoundationaLLM:Instance:*"; - + + /// + /// Filter for the configuration section used to identify the settings related to the FoundationaLLM instance. + /// + public const string FoundationaLLM_Logging = + "Logging:*"; + /// /// Filter for the configuration section used to identify the Azure Key Vault settings related to the FoundationaLLM instance. /// diff --git a/src/dotnet/CoreAPI/CoreAPI.csproj b/src/dotnet/CoreAPI/CoreAPI.csproj index d76dd36c29..bccd6933eb 100644 --- a/src/dotnet/CoreAPI/CoreAPI.csproj +++ b/src/dotnet/CoreAPI/CoreAPI.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/dotnet/CoreAPI/Program.cs b/src/dotnet/CoreAPI/Program.cs index aaf2fd2a8b..a143205f02 100644 --- a/src/dotnet/CoreAPI/Program.cs +++ b/src/dotnet/CoreAPI/Program.cs @@ -50,6 +50,7 @@ public static void Main(string[] args) options.SetCredential(DefaultAuthentication.AzureCredential); }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Branding); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_CoreAPI_Configuration_CosmosDB); @@ -132,8 +133,6 @@ public static void Main(string[] args) allowACLAuthorization: isE2ETestEnvironment ); - builder.AddLogging(); - // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_CoreAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/CoreAPI/appsettings.json b/src/dotnet/CoreAPI/appsettings.json index ac19ddf821..7f4b3b96c2 100644 --- a/src/dotnet/CoreAPI/appsettings.json +++ b/src/dotnet/CoreAPI/appsettings.json @@ -1,25 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" - }, - "Debug": { // Debug provider. - "LogLevel": { - "Default": "Warning", // Overrides preceding LogLevel:Default setting. - "Microsoft.Hosting": "Warning" // Debug:Microsoft.Hosting category. - } + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" } } }, diff --git a/src/dotnet/CoreWorker/Program.cs b/src/dotnet/CoreWorker/Program.cs index 6c25ab38f4..dfca39443f 100644 --- a/src/dotnet/CoreWorker/Program.cs +++ b/src/dotnet/CoreWorker/Program.cs @@ -1,3 +1,4 @@ +using FoundationaLLM; using FoundationaLLM.Common.Authentication; using FoundationaLLM.Common.Constants; using FoundationaLLM.Common.Constants.Configuration; @@ -34,6 +35,11 @@ if (builder.Environment.IsDevelopment()) builder.Configuration.AddJsonFile("appsettings.development.json", true, true); +// Add OpenTelemetry. +builder.AddOpenTelemetry( + AppConfigurationKeys.FoundationaLLM_APIEndpoints_CoreAPI_Essentials_AppInsightsConnectionString, + ServiceNames.CoreAPI); + builder.Services.AddOptions() .Bind(builder.Configuration.GetSection(AppConfigurationKeySections.FoundationaLLM_APIEndpoints_CoreAPI_Configuration_CosmosDB)); diff --git a/src/dotnet/CoreWorker/appsettings.json b/src/dotnet/CoreWorker/appsettings.json index 529823951c..152092d429 100644 --- a/src/dotnet/CoreWorker/appsettings.json +++ b/src/dotnet/CoreWorker/appsettings.json @@ -1,13 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.Hosting.Lifetime": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" + "Default": "Warning" } } }, diff --git a/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj b/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj index e6fcae9481..b835708c53 100644 --- a/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj +++ b/src/dotnet/GatekeeperAPI/GatekeeperAPI.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/dotnet/GatekeeperAPI/Program.cs b/src/dotnet/GatekeeperAPI/Program.cs index cd27f51f6c..338a20a141 100644 --- a/src/dotnet/GatekeeperAPI/Program.cs +++ b/src/dotnet/GatekeeperAPI/Program.cs @@ -46,6 +46,7 @@ public static void Main(string[] args) options.SetCredential(DefaultAuthentication.AzureCredential); }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatekeeperAPI_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatekeeperAPI_Essentials); @@ -66,8 +67,6 @@ public static void Main(string[] args) builder.AddGroupMembership(); builder.AddAuthorizationService(); - builder.AddLogging(); - // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_GatekeeperAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/GatekeeperAPI/appsettings.json b/src/dotnet/GatekeeperAPI/appsettings.json index 21a6ed41e4..10cf3577fd 100644 --- a/src/dotnet/GatekeeperAPI/appsettings.json +++ b/src/dotnet/GatekeeperAPI/appsettings.json @@ -1,19 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" } } }, diff --git a/src/dotnet/GatewayAPI/Program.cs b/src/dotnet/GatewayAPI/Program.cs index aec3f3289a..71f71a7283 100644 --- a/src/dotnet/GatewayAPI/Program.cs +++ b/src/dotnet/GatewayAPI/Program.cs @@ -29,6 +29,7 @@ options.SetCredential(DefaultAuthentication.AzureCredential); }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatewayAPI_Essentials); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_GatewayAPI_Configuration); @@ -43,8 +44,6 @@ if (builder.Environment.IsDevelopment()) builder.Configuration.AddJsonFile("appsettings.development.json", true, true); -builder.AddLogging(); - builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_GatewayAPI_Essentials_AppInsightsConnectionString, ServiceNames.GatewayAPI); diff --git a/src/dotnet/GatewayAPI/appsettings.json b/src/dotnet/GatewayAPI/appsettings.json index e9dfa821db..36c0678c20 100644 --- a/src/dotnet/GatewayAPI/appsettings.json +++ b/src/dotnet/GatewayAPI/appsettings.json @@ -1,13 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" + "Default": "Warning" } } }, diff --git a/src/dotnet/ManagementAPI/ManagementAPI.csproj b/src/dotnet/ManagementAPI/ManagementAPI.csproj index 4496df9366..1cd4c5d5cb 100644 --- a/src/dotnet/ManagementAPI/ManagementAPI.csproj +++ b/src/dotnet/ManagementAPI/ManagementAPI.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/dotnet/ManagementAPI/Program.cs b/src/dotnet/ManagementAPI/Program.cs index b2207d24d8..34394d606f 100644 --- a/src/dotnet/ManagementAPI/Program.cs +++ b/src/dotnet/ManagementAPI/Program.cs @@ -118,8 +118,6 @@ public static void Main(string[] args) allowACLAuthorization: isE2ETestEnvironment ); - builder.AddLogging(); - // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_ManagementAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/ManagementAPI/appsettings.json b/src/dotnet/ManagementAPI/appsettings.json index e9dfa821db..36c0678c20 100644 --- a/src/dotnet/ManagementAPI/appsettings.json +++ b/src/dotnet/ManagementAPI/appsettings.json @@ -1,13 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" + "Default": "Warning" } } }, diff --git a/src/dotnet/Orchestration/Orchestration/KnowledgeManagementOrchestration.cs b/src/dotnet/Orchestration/Orchestration/KnowledgeManagementOrchestration.cs index dd43bbaa94..470a59967a 100644 --- a/src/dotnet/Orchestration/Orchestration/KnowledgeManagementOrchestration.cs +++ b/src/dotnet/Orchestration/Orchestration/KnowledgeManagementOrchestration.cs @@ -288,7 +288,7 @@ private OpenAITextMessageContentItem TransformOpenAIAssistantsTextMessage(OpenAI .ToDictionary( a => $"({a.Text!})", a => $"({a.FileUrl})"); - + var input = openAITextMessage.Value!; var regex = new Regex(@"\(sandbox:[^)]*\)"); diff --git a/src/dotnet/OrchestrationAPI/OrchestrationAPI.csproj b/src/dotnet/OrchestrationAPI/OrchestrationAPI.csproj index 5431ddd970..8bfe9406f9 100644 --- a/src/dotnet/OrchestrationAPI/OrchestrationAPI.csproj +++ b/src/dotnet/OrchestrationAPI/OrchestrationAPI.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/dotnet/OrchestrationAPI/Program.cs b/src/dotnet/OrchestrationAPI/Program.cs index 13bab000b6..4b3771c346 100644 --- a/src/dotnet/OrchestrationAPI/Program.cs +++ b/src/dotnet/OrchestrationAPI/Program.cs @@ -45,6 +45,7 @@ public static void Main(string[] args) options.SetCredential(DefaultAuthentication.AzureCredential); }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); //TODO: Replace this with a more granular approach that would only bring in the configuration namespaces that are actually needed. @@ -70,8 +71,6 @@ public static void Main(string[] args) // Add services to the container. - builder.AddLogging(); - // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_OrchestrationAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/OrchestrationAPI/appsettings.json b/src/dotnet/OrchestrationAPI/appsettings.json index 21a6ed41e4..10cf3577fd 100644 --- a/src/dotnet/OrchestrationAPI/appsettings.json +++ b/src/dotnet/OrchestrationAPI/appsettings.json @@ -1,19 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" } } }, diff --git a/src/dotnet/SemanticKernelAPI/Program.cs b/src/dotnet/SemanticKernelAPI/Program.cs index 6190f9c5f0..91ab2c4637 100644 --- a/src/dotnet/SemanticKernelAPI/Program.cs +++ b/src/dotnet/SemanticKernelAPI/Program.cs @@ -44,6 +44,7 @@ public static void Main(string[] args) options.SetCredential(DefaultAuthentication.AzureCredential); }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Events_Profiles_VectorizationAPI); @@ -55,8 +56,6 @@ public static void Main(string[] args) if (builder.Environment.IsDevelopment()) builder.Configuration.AddJsonFile("appsettings.development.json", true, true); - builder.AddLogging(); - // Add OpenTelemetry. builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_SemanticKernelAPI_Essentials_AppInsightsConnectionString, diff --git a/src/dotnet/SemanticKernelAPI/SemanticKernelAPI.csproj b/src/dotnet/SemanticKernelAPI/SemanticKernelAPI.csproj index 8374c55f79..001df40092 100644 --- a/src/dotnet/SemanticKernelAPI/SemanticKernelAPI.csproj +++ b/src/dotnet/SemanticKernelAPI/SemanticKernelAPI.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/dotnet/SemanticKernelAPI/appsettings.json b/src/dotnet/SemanticKernelAPI/appsettings.json index afc096fc4e..10cf3577fd 100644 --- a/src/dotnet/SemanticKernelAPI/appsettings.json +++ b/src/dotnet/SemanticKernelAPI/appsettings.json @@ -1,21 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning", - "Microsoft.SemanticKernel": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning", - "Microsoft.SemanticKernel": "Warning" + "Default": "Warning" } } }, diff --git a/src/dotnet/StateAPI/Program.cs b/src/dotnet/StateAPI/Program.cs index 41a544c7dc..2f31499faf 100644 --- a/src/dotnet/StateAPI/Program.cs +++ b/src/dotnet/StateAPI/Program.cs @@ -39,13 +39,12 @@ }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_StateAPI_Essentials); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_APIEndpoints_StateAPI_Configuration_CosmosDB); }); if (builder.Environment.IsDevelopment()) builder.Configuration.AddJsonFile("appsettings.development.json", true, true); -builder.AddLogging(); - builder.AddOpenTelemetry( AppConfigurationKeys.FoundationaLLM_APIEndpoints_StateAPI_Essentials_AppInsightsConnectionString, ServiceNames.StateAPI); diff --git a/src/dotnet/StateAPI/appsettings.json b/src/dotnet/StateAPI/appsettings.json index a70614408c..7f4b3b96c2 100644 --- a/src/dotnet/StateAPI/appsettings.json +++ b/src/dotnet/StateAPI/appsettings.json @@ -1,19 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" } } }, diff --git a/src/dotnet/VectorizationAPI/Program.cs b/src/dotnet/VectorizationAPI/Program.cs index c538ff6feb..30150e413f 100644 --- a/src/dotnet/VectorizationAPI/Program.cs +++ b/src/dotnet/VectorizationAPI/Program.cs @@ -44,6 +44,7 @@ options.SetCredential(DefaultAuthentication.AzureCredential); }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Vectorization_Queues); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Vectorization_Steps); diff --git a/src/dotnet/VectorizationAPI/VectorizationAPI.csproj b/src/dotnet/VectorizationAPI/VectorizationAPI.csproj index 331616cffe..8b4d03f41f 100644 --- a/src/dotnet/VectorizationAPI/VectorizationAPI.csproj +++ b/src/dotnet/VectorizationAPI/VectorizationAPI.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/dotnet/VectorizationAPI/appsettings.json b/src/dotnet/VectorizationAPI/appsettings.json index e9dfa821db..098e09476f 100644 --- a/src/dotnet/VectorizationAPI/appsettings.json +++ b/src/dotnet/VectorizationAPI/appsettings.json @@ -6,8 +6,8 @@ }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" } } }, diff --git a/src/dotnet/VectorizationWorker/Program.cs b/src/dotnet/VectorizationWorker/Program.cs index 98a8706047..30b4bf3fc9 100644 --- a/src/dotnet/VectorizationWorker/Program.cs +++ b/src/dotnet/VectorizationWorker/Program.cs @@ -42,6 +42,7 @@ options.SetCredential(DefaultAuthentication.AzureCredential); }); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Instance); + options.Select(AppConfigurationKeyFilters.FoundationaLLM_Logging); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Configuration); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Vectorization_Queues); options.Select(AppConfigurationKeyFilters.FoundationaLLM_Vectorization_Steps); diff --git a/src/dotnet/VectorizationWorker/VectorizationWorker.csproj b/src/dotnet/VectorizationWorker/VectorizationWorker.csproj index 04c3544ea7..1754745bfa 100644 --- a/src/dotnet/VectorizationWorker/VectorizationWorker.csproj +++ b/src/dotnet/VectorizationWorker/VectorizationWorker.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/dotnet/VectorizationWorker/appsettings.json b/src/dotnet/VectorizationWorker/appsettings.json index 9c3a5de988..098e09476f 100644 --- a/src/dotnet/VectorizationWorker/appsettings.json +++ b/src/dotnet/VectorizationWorker/appsettings.json @@ -1,8 +1,14 @@ { "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Warning", "Microsoft.AspNetCore": "Warning" + }, + "OpenTelemetry": { + "LogLevel": { + "Default": "Warning", + "Microsoft.AspNetCore": "Warning" + } } }, "AllowedHosts": "*" diff --git a/src/python/AgentHubAPI/app/dependencies.py b/src/python/AgentHubAPI/app/dependencies.py index a40da147bd..5702a1deab 100644 --- a/src/python/AgentHubAPI/app/dependencies.py +++ b/src/python/AgentHubAPI/app/dependencies.py @@ -7,6 +7,11 @@ from fastapi import Depends, HTTPException from fastapi.security import APIKeyHeader from foundationallm.config import Configuration +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = Telemetry.get_logger(__name__) +tracer = Telemetry.get_tracer(__name__) __config: Configuration = None API_NAME = 'AgentHubAPI' @@ -14,7 +19,7 @@ def get_config(action: str = None) -> Configuration: """ Obtains the application configuration settings. - + Returns ------- Configuration @@ -28,18 +33,18 @@ def get_config(action: str = None) -> Configuration: else: __config = __config or Configuration() end = time.time() - print(f'Time to load config: {end-start}') + logger.log(f'Time to load config: {end-start}') return __config def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Key'))): """ Validates that the X-API-Key value in the request header matches the key expected for this API. - + Parameters ---------- x_api_key : str The X-API-Key value in the request header. - + Returns bool Returns True of the X-API-Key value from the request header matches the expected value. @@ -58,7 +63,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke def handle_exception(exception: Exception, status_code: int = 500): """ Handles an exception that occurred while processing a request. - + Parameters ---------- exception : Exception diff --git a/src/python/DataSourceHubAPI/app/dependencies.py b/src/python/DataSourceHubAPI/app/dependencies.py index 0ccfd59cb8..4961c70df2 100644 --- a/src/python/DataSourceHubAPI/app/dependencies.py +++ b/src/python/DataSourceHubAPI/app/dependencies.py @@ -7,6 +7,11 @@ from fastapi import Depends, HTTPException from fastapi.security import APIKeyHeader from foundationallm.config import Configuration +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = Telemetry.get_logger(__name__) +tracer = Telemetry.get_tracer(__name__) __config: Configuration = None API_NAME = 'DataSourceHubAPI' @@ -14,7 +19,7 @@ def get_config(action: str = None) -> Configuration: """ Obtains the application configuration settings. - + Returns ------- Configuration @@ -28,18 +33,18 @@ def get_config(action: str = None) -> Configuration: else: __config = __config or Configuration() end = time.time() - print(f'Time to load config: {end-start}') + logger.info(f'Time to load config: {end-start}') return __config def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Key'))): """ Validates that the X-API-Key value in the request header matches the key expected for this API. - + Parameters ---------- x_api_key : str The X-API-Key value in the request header. - + Returns bool Returns True of the X-API-Key value from the request header matches the expected value. @@ -59,7 +64,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke def handle_exception(exception: Exception, status_code: int = 500): """ Handles an exception that occurred while processing a request. - + Parameters ---------- exception : Exception diff --git a/src/python/GatekeeperIntegrationAPI/app/dependencies.py b/src/python/GatekeeperIntegrationAPI/app/dependencies.py index 1568beb07b..e0739d5a1e 100644 --- a/src/python/GatekeeperIntegrationAPI/app/dependencies.py +++ b/src/python/GatekeeperIntegrationAPI/app/dependencies.py @@ -7,6 +7,11 @@ from fastapi import Depends, HTTPException from fastapi.security import APIKeyHeader from foundationallm.integration.config import Configuration +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = Telemetry.get_logger(__name__) +tracer = Telemetry.get_tracer(__name__) __config: Configuration = None API_NAME = 'GatekeeperIntegrationAPI' @@ -14,7 +19,7 @@ def get_config(action: str = None) -> Configuration: """ Obtains the application configuration settings. - + Returns ------- Configuration @@ -28,18 +33,18 @@ def get_config(action: str = None) -> Configuration: else: __config = __config or Configuration() end = time.time() - print(f'Time to load config: {end-start}') + logger.info(f'Time to load config: {end-start}') return __config def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Key'))): """ Validates that the X-API-Key value in the request header matches the key expected for this API. - + Parameters ---------- x_api_key : str The X-API-Key value in the request header. - + Returns bool Returns True of the X-API-Key value from the request header matches the expected value. @@ -58,7 +63,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke def handle_exception(exception: Exception, status_code: int = 500): """ Handles an exception that occurred while processing a request. - + Parameters ---------- exception : Exception diff --git a/src/python/LangChainAPI/app/dependencies.py b/src/python/LangChainAPI/app/dependencies.py index b69d754275..ae3829b4cf 100644 --- a/src/python/LangChainAPI/app/dependencies.py +++ b/src/python/LangChainAPI/app/dependencies.py @@ -6,6 +6,11 @@ from fastapi import Depends, HTTPException from fastapi.security import APIKeyHeader from foundationallm.config import Configuration +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = None +tracer = None __config: Configuration = None API_NAME = 'LangChainAPI' @@ -13,13 +18,14 @@ def get_config(action: str = None) -> Configuration: """ Obtains the application configuration settings. - + Returns ------- Configuration Returns the application configuration settings. """ global __config + global logger start = time.time() if action is not None and action=='refresh': @@ -27,18 +33,20 @@ def get_config(action: str = None) -> Configuration: else: __config = __config or Configuration() end = time.time() - print(f'Time to load config: {end-start}') + + logger = Telemetry.get_logger(__name__) + logger.info(f'Time to load config: {end-start}') return __config async def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Key'))) -> bool: """ Validates that the X-API-Key value in the request header matches the key expected for this API. - + Parameters ---------- x_api_key : str The X-API-Key value in the request header. - + Returns bool Returns True of the X-API-Key value from the request header matches the expected value. @@ -48,7 +56,8 @@ async def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X- result = x_api_key == get_config().get_value(f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:APIKey') if not result: - logging.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.') + logger = Telemetry.get_logger(__name__) + logger.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.') raise HTTPException( status_code = 401, detail = 'Invalid API key. You must provide a valid API key in the X-API-KEY header.' @@ -57,13 +66,14 @@ async def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X- def handle_exception(exception: Exception, status_code: int = 500): """ Handles an exception that occurred while processing a request. - + Parameters ---------- exception : Exception The exception that occurred. """ - logging.error(exception, stack_info=True, exc_info=True) + logger = Telemetry.get_logger(__name__) + logger.error(exception, stack_info=True, exc_info=True) raise HTTPException( status_code = status_code, detail = str(exception) diff --git a/src/python/LangChainAPI/app/routers/completions.py b/src/python/LangChainAPI/app/routers/completions.py index fbf9f9ed6f..602e46ba73 100644 --- a/src/python/LangChainAPI/app/routers/completions.py +++ b/src/python/LangChainAPI/app/routers/completions.py @@ -29,6 +29,7 @@ from foundationallm.operations import OperationsManager from foundationallm.langchain.orchestration import OrchestrationManager from foundationallm.telemetry import Telemetry + from app.dependencies import handle_exception, validate_api_key_header # Initialize telemetry logging @@ -171,7 +172,7 @@ async def create_completion_response( ) except Exception as e: # Send the completion response to the State API and mark the operation as failed. - print(f'Operation {operation_id} failed with error: {e}') + logger.info(f'Operation {operation_id} failed with error: {e}') completion = CompletionResponse( operation_id = operation_id, user_prompt=completion_request.user_prompt, diff --git a/src/python/PromptHubAPI/app/dependencies.py b/src/python/PromptHubAPI/app/dependencies.py index f030066755..3ece813cbd 100644 --- a/src/python/PromptHubAPI/app/dependencies.py +++ b/src/python/PromptHubAPI/app/dependencies.py @@ -7,6 +7,11 @@ from fastapi import Depends, HTTPException from fastapi.security import APIKeyHeader from foundationallm.config import Configuration +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = Telemetry.get_logger(__name__) +tracer = Telemetry.get_tracer(__name__) __config: Configuration = None API_NAME = 'PromptHubAPI' @@ -14,7 +19,7 @@ def get_config(action: str = None) -> Configuration: """ Obtains the application configuration settings. - + Returns ------- Configuration @@ -28,18 +33,18 @@ def get_config(action: str = None) -> Configuration: else: __config = __config or Configuration() end = time.time() - print(f'Time to load config: {end-start}') + logger.info(f'Time to load config: {end-start}') return __config def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Key'))): """ Validates that the X-API-Key value in the request header matches the key expected for this API. - + Parameters ---------- x_api_key : str The X-API-Key value in the request header. - + Returns bool Returns True of the X-API-Key value from the request header matches the expected value. @@ -59,7 +64,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke def handle_exception(exception: Exception, status_code: int = 500): """ Handles an exception that occurred while processing a request. - + Parameters ---------- exception : Exception diff --git a/src/python/PythonSDK/foundationallm/config/configuration.py b/src/python/PythonSDK/foundationallm/config/configuration.py index 8696373e1d..42253d4b99 100644 --- a/src/python/PythonSDK/foundationallm/config/configuration.py +++ b/src/python/PythonSDK/foundationallm/config/configuration.py @@ -34,7 +34,7 @@ def get_value(self, key: str) -> str: ---------- - key : str The key name of the configuration setting to retrieve. - + Returns ------- The configuration value @@ -47,10 +47,10 @@ def get_value(self, key: str) -> str: value = None # will have future usage with Azure App Configuration - # if foundationallm-configuration-allow-environment-variables exists and is True, + # if foundationallm-configuration-allow-environment-variables exists and is True, # then the environment variables will be checked first, then KV - # if foundationallm-configuration-allow-environment-variables does not exist - # OR foundationallm-configuration-allow-environment-variables is False, + # if foundationallm-configuration-allow-environment-variables does not exist + # OR foundationallm-configuration-allow-environment-variables is False, # then check App config and then KV allow_env_vars = False if "foundationallm-configuration-allow-environment-variables" in os.environ: @@ -82,11 +82,11 @@ def get_feature_flag(self, key: str) -> bool: ---------- - key : str The key name of the feature flag to retrieve. - + Returns ------- The enabled value of the feature flag - + """ if key is None: raise KeyError('The key parameter is required for Configuration.get_feature_flag().') diff --git a/src/python/PythonSDK/foundationallm/langchain/agents/langchain_agent_base.py b/src/python/PythonSDK/foundationallm/langchain/agents/langchain_agent_base.py index 1a74dfd05d..05a687009f 100644 --- a/src/python/PythonSDK/foundationallm/langchain/agents/langchain_agent_base.py +++ b/src/python/PythonSDK/foundationallm/langchain/agents/langchain_agent_base.py @@ -2,6 +2,7 @@ from typing import List from azure.identity import DefaultAzureCredential, get_bearer_token_provider +from langchain.callbacks.tracers import ConsoleCallbackHandler from langchain_core.language_models import BaseLanguageModel from langchain_openai import AzureChatOpenAI, AzureOpenAI, ChatOpenAI, OpenAI from openai import AzureOpenAI as aoi @@ -20,6 +21,7 @@ from foundationallm.models.resource_providers.attachments import Attachment from foundationallm.models.resource_providers.configuration import APIEndpointConfiguration from foundationallm.models.resource_providers.prompts import MultipartPrompt +from foundationallm.telemetry import Telemetry class LangChainAgentBase(): """ @@ -44,11 +46,18 @@ def __init__(self, instance_id: str, user_identity: UserIdentity, config: Config self.has_indexing_profiles = False self.has_retriever = False + #get log level + self.log_level = self.config.get_value('Logging:LogLevel:Default') + self.lcel_callbacks = [] + + if self.log_level == 'Debug': + self.lcel_callbacks = [ConsoleCallbackHandler()] + @abstractmethod def invoke(self, request: CompletionRequestBase) -> CompletionResponse: """ Gets the completion for the request. - + Parameters ---------- request : CompletionRequestBase @@ -65,7 +74,7 @@ def invoke(self, request: CompletionRequestBase) -> CompletionResponse: async def ainvoke(self, request: CompletionRequestBase) -> CompletionResponse: """ Gets the completion for the request using an async request. - + Parameters ---------- request : CompletionRequestBase @@ -98,12 +107,12 @@ def _get_prompt_from_object_id(self, prompt_object_id: str, objects: dict) -> Mu if prompt_object_id is None or prompt_object_id == '': raise LangChainException("Invalid prompt object id.", 400) - + try: prompt = MultipartPrompt(**objects.get(prompt_object_id)) except Exception as e: raise LangChainException(f"The prompt object provided in the request.objects dictionary is invalid. {str(e)}", 400) - + if prompt is None: raise LangChainException("The prompt object is missing in the request.objects dictionary.", 400) @@ -117,12 +126,12 @@ def _get_ai_model_from_object_id(self, ai_model_object_id: str, objects: dict) - if ai_model_object_id is None or ai_model_object_id == '': raise LangChainException("Invalid AI model object id.", 400) - + try: ai_model = AIModelBase(**objects.get(ai_model_object_id)) except Exception as e: raise LangChainException(f"The AI model object provided in the request.objects dictionary is invalid. {str(e)}", 400) - + if ai_model is None: raise LangChainException("The AI model object is missing in the request.objects dictionary.", 400) @@ -136,12 +145,12 @@ def _get_api_endpoint_from_object_id(self, api_endpoint_object_id: str, objects: if api_endpoint_object_id is None or api_endpoint_object_id == '': raise LangChainException("Invalid API endpoint object id.", 400) - + try: api_endpoint = APIEndpointConfiguration(**objects.get(api_endpoint_object_id)) except Exception as e: raise LangChainException(f"The API endpoint object provided in the request.objects dictionary is invalid. {str(e)}", 400) - + if api_endpoint is None: raise LangChainException("The API endpoint object is missing in the request.objects dictionary.", 400) @@ -155,16 +164,16 @@ def _get_attachment_from_object_id(self, attachment_object_id: str, agent_parame if attachment_object_id is None or attachment_object_id == '': return None - + try: attachment = Attachment(**agent_parameters.get(attachment_object_id)) except Exception as e: raise LangChainException(f"The attachment object provided in the agent parameters is invalid. {str(e)}", 400) - + if attachment is None: raise LangChainException("The attachment object is missing in the agent parameters.", 400) - return attachment + return attachment def _build_conversation_history(self, messages:List[MessageHistoryItem]=None, message_count:int=None) -> str: """ @@ -196,7 +205,7 @@ def _record_full_prompt(self, prompt: str) -> str: ---------- prompt : str The prompt that is populated with context. - + Returns ------- str @@ -215,7 +224,7 @@ def _get_language_model(self, override_operation_type: OperationTypes = None, is ------- BaseLanguageModel Returns an API connector for a chat completion model. - """ + """ language_model:BaseLanguageModel = None api_key = None @@ -237,7 +246,7 @@ def _get_language_model(self, override_operation_type: OperationTypes = None, is DefaultAzureCredential(exclude_environment_credential=True), scope ) - + if op_type == OperationTypes.CHAT: language_model = AzureChatOpenAI( azure_endpoint=self.api_endpoint.url, @@ -276,7 +285,7 @@ def _get_language_model(self, override_operation_type: OperationTypes = None, is if api_key is None: raise LangChainException("API key is missing from the configuration settings.", 400) - + if op_type == OperationTypes.CHAT: language_model = AzureChatOpenAI( azure_endpoint=self.api_endpoint.url, @@ -310,7 +319,7 @@ def _get_language_model(self, override_operation_type: OperationTypes = None, is if api_key is None: raise LangChainException("API key is missing from the configuration settings.", 400) - + language_model = ( ChatOpenAI(base_url=self.api_endpoint.url, api_key=api_key) if self.api_endpoint.operation_type == OperationTypes.CHAT diff --git a/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py b/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py index 3f31c60377..561f3412de 100644 --- a/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py +++ b/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py @@ -37,12 +37,19 @@ ) from foundationallm.services.gateway_text_embedding import GatewayTextEmbeddingService from openai.types import CompletionUsage +from openai import AzureOpenAI, AsyncAzureOpenAI + +from foundationallm.telemetry import Telemetry class LangChainKnowledgeManagementAgent(LangChainAgentBase): """ The LangChain Knowledge Management agent. """ + # Initialize telemetry logging + logger = Telemetry.get_logger(__name__) + tracer = Telemetry.get_tracer(__name__) + def _get_document_retriever( self, request: KnowledgeManagementCompletionRequest, @@ -258,6 +265,8 @@ def invoke(self, request: KnowledgeManagementCompletionRequest) -> CompletionRes Returns a CompletionResponse with the generated summary, the user_prompt, generated full prompt with context and token utilization and execution cost details. """ + self.logger.info("Executing synchronous completion request.") + self._validate_request(request) agent = request.agent @@ -266,6 +275,7 @@ def invoke(self, request: KnowledgeManagementCompletionRequest) -> CompletionRes image_analysis_results = None image_attachments = [attachment for attachment in request.attachments if (attachment.provider == AttachmentProviders.FOUNDATIONALLM_ATTACHMENT and attachment.content_type.startswith('image/'))] if request.attachments is not None else [] if len(image_attachments) > 0: + self.logger.info(f"Analyzing {len(image_attachments)} images.") image_analysis_client = self._get_language_model(override_operation_type=OperationTypes.IMAGE_ANALYSIS, is_async=False) image_analysis_svc = ImageAnalysisService(config=self.config, client=image_analysis_client, deployment_model=self.ai_model.deployment_name) image_analysis_results, usage = image_analysis_svc.analyze_images(image_attachments) @@ -281,6 +291,7 @@ def invoke(self, request: KnowledgeManagementCompletionRequest) -> CompletionRes # Check for Assistants API capability if "OpenAI.Assistants" in agent.capabilities: + self.logger.info("OpenAI.Assistants capability detected.") operation_type_override = OperationTypes.ASSISTANTS_API # create the service assistant_svc = OpenAIAssistantsApiService(azure_openai_client=self._get_language_model(override_operation_type=operation_type_override, is_async=False)) @@ -351,11 +362,14 @@ def invoke(self, request: KnowledgeManagementCompletionRequest) -> CompletionRes user_prompt = request.user_prompt ) + self.logger.info("No OpenAI.Assistants capability detected.") + with get_openai_callback() as cb: try: # Get the vector document retriever, if it exists. retriever = self._get_document_retriever(request, agent) if retriever is not None: + self.logger.info("Document retriever found.") self.has_retriever = True # Get the prompt template. prompt_template = self._get_prompt_template( @@ -392,7 +406,8 @@ def invoke(self, request: KnowledgeManagementCompletionRequest) -> CompletionRes | StrOutputParser() ) - completion = chain.invoke(request.user_prompt) + completion = chain.invoke(request.user_prompt, config={'callbacks' : self.lcel_callbacks}) + response_content = OpenAITextMessageContentItem( value = completion, agent_capability_category = AgentCapabilityCategories.FOUNDATIONALLM_KNOWLEDGE_MANAGEMENT @@ -569,9 +584,9 @@ async def ainvoke(self, request: KnowledgeManagementCompletionRequest) -> Comple # ainvoke isn't working if search is involved in the completion request. Need to dive deeper into how to get this working. if self.has_retriever: - completion = chain.invoke(request.user_prompt) + completion = chain.invoke(request.user_prompt, config={'callbacks' : self.lcel_callbacks}) else: - completion = await chain.ainvoke(request.user_prompt) + completion = await chain.ainvoke(request.user_prompt, config={'callbacks' : self.lcel_callbacks}) response_content = OpenAITextMessageContentItem( value = completion, diff --git a/src/python/PythonSDK/foundationallm/langchain/retrievers/search_service_filter_retriever.py b/src/python/PythonSDK/foundationallm/langchain/retrievers/search_service_filter_retriever.py index e0c4f916a4..6e2d83c741 100644 --- a/src/python/PythonSDK/foundationallm/langchain/retrievers/search_service_filter_retriever.py +++ b/src/python/PythonSDK/foundationallm/langchain/retrievers/search_service_filter_retriever.py @@ -16,6 +16,12 @@ from azure.search.documents.models import VectorizedQuery from azure.core.credentials import AzureKeyCredential +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = Telemetry.get_logger(__name__) +tracer = Telemetry.get_tracer(__name__) + class SearchServiceFilterRetriever(BaseRetriever): """ LangChain retriever for Azure AI Search. @@ -101,10 +107,10 @@ def _get_relevant_documents( page_content=result[self.text_field_name] )) except Exception as e: - print(e) + logger.error(e) except Exception as e: - print(e) + logger.error(e) if ( filter == "search.ismatch('*', 'metadata', 'simple', 'all')"): break diff --git a/src/python/PythonSDK/foundationallm/operations/operations_manager.py b/src/python/PythonSDK/foundationallm/operations/operations_manager.py index 5b286dc6fd..ea6edbce16 100644 --- a/src/python/PythonSDK/foundationallm/operations/operations_manager.py +++ b/src/python/PythonSDK/foundationallm/operations/operations_manager.py @@ -9,6 +9,11 @@ OperationStatus ) from foundationallm.models.orchestration import CompletionResponse +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = Telemetry.get_logger(__name__) +tracer = Telemetry.get_tracer(__name__) class OperationsManager(): """ @@ -20,8 +25,8 @@ def __init__(self, config: Configuration): self.state_api_url = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:Essentials:APIUrl').rstrip('/') self.state_api_key = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:Essentials:APIKey') env = os.environ.get('FOUNDATIONALLM_ENV', 'prod') - self.verify_certs = False if env == 'dev' else True - + self.verify_certs = False if env == 'dev' or 'localhost' in self.state_api_url else True + async def create_operation( self, operation_id: str, @@ -30,26 +35,26 @@ async def create_operation( Creates a background operation by settings its initial state through the State API. POST {state_api_url}/instances/{instanceId}/operations/{operationId} -> LongRunningOperation - + Parameters ---------- operation_id : str The unique identifier for the operation. instance_id : str The unique identifier for the FLLM instance. - + Returns ------- LongRunningOperation Object representing the operation. - """ + """ try: headers = { "x-api-key": self.state_api_key, "charset":"utf-8", "Content-Type":"application/json" } - + # Call the State API to create a new operation. r = requests.post( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', @@ -60,7 +65,9 @@ async def create_operation( if r.status_code != 200: raise Exception(f'An error occurred while creating the operation {operation_id}: ({r.status_code}) {r.text}') - return r.json() + jData = r.json() + logger.info(f"create_operation : {operation_id} : {jData}") + return jData except Exception as e: raise e @@ -73,7 +80,7 @@ async def update_operation(self, Updates the state of a background operation through the State API. PUT {state_api_url}/instances/{instanceId}/operations/{operationId} -> LongRunningOperation - + Parameters ---------- operation : LongRunningOperation @@ -84,7 +91,7 @@ async def update_operation(self, The new status to assign to the operation. status_message: str The message to associate with the new status. - + Returns ------- LongRunningOperation @@ -95,7 +102,7 @@ async def update_operation(self, status=status, status_message=status_message ) - + try: # Call the State API to create a new operation. headers = { @@ -117,7 +124,9 @@ async def update_operation(self, if r.status_code != 200: raise Exception(f'An error occurred while updating the status of operation {operation_id}: ({r.status_code}) {r.text}') - return r.json() + jData = r.json() + logger.info(f"update_operation : {operation_id} : {jData}") + return jData except Exception as e: raise e @@ -129,14 +138,14 @@ async def get_operation( Retrieves the state of a background operation through the State API. GET {state_api_url}/instances/{instanceId}/operations/{operationId} -> LongRunningOperation - + Parameters ---------- operation_id : str The unique identifier for the operation. instance_id : str The unique identifier for the FLLM instance. - + Returns ------- LongRunningOperation @@ -150,7 +159,7 @@ async def get_operation( "Content-Type":"application/json" } - print(f'status endpoint: {self.state_api_url}/instances/{instance_id}/operations/{operation_id}') + logger.info(f'status endpoint: {self.state_api_url}/instances/{instance_id}/operations/{operation_id}') r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', @@ -164,7 +173,9 @@ async def get_operation( if r.status_code != 200: raise Exception(f'An error occurred while retrieving the status of the operation {operation_id}: ({r.status_code}) {r.text}') - return r.json() + jData = r.json() + logger.info(f"get_operation : {operation_id} : {jData}") + return jData except Exception as e: raise e @@ -177,7 +188,7 @@ async def set_operation_result( Sets the result of a completion operation through the State API. PUT {state_api_url}/instances/{instanceId}/operations/{operationId}/result -> CompletionResponse - + Parameters ---------- operation_id : str @@ -195,6 +206,8 @@ async def set_operation_result( "Content-Type":"application/json" } + logger.info(f"set_operation_result : {operation_id}") + r = requests.post( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/result', json=completion_response.model_dump(), @@ -219,14 +232,14 @@ async def get_operation_result( Retrieves the result of an async completion operation through the State API. GET {state_api_url}/instances/{instanceId}/operations/{operationId}/result -> CompletionResponse - + Parameters ---------- operation_id : str The unique identifier for the operation. instance_id : str The unique identifier for the FLLM instance. - + Returns ------- CompletionResponse @@ -252,7 +265,9 @@ async def get_operation_result( if r.status_code != 200: raise Exception(f'An error occurred while retrieving the result of operation {operation_id}: ({r.status_code}) {r.text}') - return r.json() + jData = r.json() + logger.info(jData) + return jData except Exception as e: raise e @@ -264,14 +279,14 @@ async def get_operation_log( Retrieves a list of log entries for an async operation through the State API. GET {state_api_url}/instances/{instanceId}/operations/{operationId}/log -> List[LongRunningOperationLogEntry] - + Parameters ---------- operation_id : str The unique identifier for the operation. instance_id : str The unique identifier for the FLLM instance. - + Returns ------- List[LongRunningOperationLogEntry] @@ -297,6 +312,8 @@ async def get_operation_log( if r.status_code != 200: raise Exception(f'An error occurred while retrieving the log of steps for the operation {operation_id}: ({r.status_code}) {r.text}') - return r.json() + jData = r.json() + logger.log(jData) + return jData except Exception as e: raise e diff --git a/src/python/PythonSDK/foundationallm/services/image_analysis_service.py b/src/python/PythonSDK/foundationallm/services/image_analysis_service.py index 0e157e3f80..ea00128c9d 100644 --- a/src/python/PythonSDK/foundationallm/services/image_analysis_service.py +++ b/src/python/PythonSDK/foundationallm/services/image_analysis_service.py @@ -7,6 +7,12 @@ from openai.types import CompletionUsage from typing import List, Union +from foundationallm.telemetry import Telemetry + +# Initialize telemetry logging +logger = Telemetry.get_logger(__name__) +tracer = Telemetry.get_tracer(__name__) + class ImageAnalysisService: """ Performs image analysis via the Azure OpenAI SDK. @@ -71,7 +77,7 @@ def _get_as_base64(self, mime_type: str, storage_account_name, file_path: str) - else: raise Exception(f'The specified image {storage_account_name}/{file_path} does not exist.') except Exception as e: - print(f'Error getting image as base64: {e}') + logger.error(f'Error getting image as base64: {e}') return None def format_results(self, image_analyses: dict) -> str: diff --git a/src/python/PythonSDK/foundationallm/telemetry/__init__.py b/src/python/PythonSDK/foundationallm/telemetry/__init__.py index 6caed962a2..cdd054a1d9 100644 --- a/src/python/PythonSDK/foundationallm/telemetry/__init__.py +++ b/src/python/PythonSDK/foundationallm/telemetry/__init__.py @@ -1 +1,2 @@ from .telemetry import Telemetry +from .telemetry import ExcludeTraceLogsFilter diff --git a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py index 9564f88aa7..30a03f2d62 100644 --- a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py +++ b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py @@ -1,5 +1,8 @@ +import sys import logging from azure.monitor.opentelemetry import configure_azure_monitor +from logging import getLogger + from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter from opentelemetry import trace from opentelemetry.trace import Span, Status, StatusCode, Tracer @@ -10,6 +13,16 @@ ConsoleSpanExporter, ) +from azure.monitor.opentelemetry.exporter import ( # pylint: disable=import-error,no-name-in-module + ApplicationInsightsSampler, + AzureMonitorLogExporter, + AzureMonitorMetricExporter, + AzureMonitorTraceExporter, +) + +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor + from foundationallm.config import Configuration from opentelemetry._logs import ( @@ -22,11 +35,26 @@ LoggingHandler, ) +from langchain.globals import set_debug, set_verbose + +# Custom filter to exclude trace logs +class ExcludeTraceLogsFilter(logging.Filter): + def filter(self, record): + filter_out = 'applicationinsights' not in record.getMessage().lower() + filter_out = filter_out and 'response status' not in record.getMessage().lower() + filter_out = filter_out and 'transmission succeeded' not in record.getMessage().lower() + return filter_out + class Telemetry: """ Manages logging and the recording of application telemetry. """ + log_level : int = logging.WARNING + langchain_log_level : int = logging.NOTSET + api_name : str = None + telemetry_connection_string : str = None + @staticmethod def configure_monitoring(config: Configuration, telemetry_connection_string: str, api_name : str): """ @@ -41,41 +69,132 @@ def configure_monitoring(config: Configuration, telemetry_connection_string: str The connection string used to connect to Azure Application Insights. """ - """ + Telemetry.telemetry_connection_string = config.get_value(telemetry_connection_string) + Telemetry.api_name = api_name + resource = Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{api_name}"}) + logger_name = None + + # Configure Azure Monitor defaults configure_azure_monitor( - connection_string=config.get_value(telemetry_connection_string), - disable_offline_storage=True + connection_string=Telemetry.telemetry_connection_string, + disable_offline_storage=True, + disable_metrics=True, + disable_tracing=False, + disable_logging=False, + resource=resource ) - """ - trace.set_tracer_provider( - TracerProvider( - resource=Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{api_name}"}) - ) - ) + #Configure telemetry logging + Telemetry.configure_logging(config) - azure_exporter = AzureMonitorTraceExporter( - connection_string=config.get_value(telemetry_connection_string) - ) + @staticmethod + def configure_logging(config: Configuration): + #Get dotnet log level + str_log_level = config.get_value("Logging:LogLevel:Default") - # Create a BatchSpanProcessor and add the exporter to it - azure_span_processor = BatchSpanProcessor(azure_exporter) + #Get environment (prod vs dev) + env = config.get_value("FOUNDATIONALLM_ENV") - # add to the tracer - trace.get_tracer_provider().add_span_processor(azure_span_processor) + #Log output handlers + handlers = [] - logger_provider = LoggerProvider( - resource=Resource.create( - { - SERVICE_NAME: api_name + #map log level to python log level - console is only added for Information or higher + if str_log_level == "Debug": + Telemetry.log_level = logging.DEBUG + set_debug(True) + handlers.append(logging.StreamHandler()) + elif str_log_level == "Trace": + Telemetry.log_level = logging.DEBUG + handlers.append(logging.StreamHandler()) + set_verbose(True) + elif str_log_level == "Information": + Telemetry.log_level = logging.INFO + handlers.append(logging.StreamHandler()) + set_debug(False) + elif str_log_level == "Warning": + set_debug(False) + Telemetry.log_level = logging.WARNING + elif str_log_level == "Error": + set_debug(False) + Telemetry.log_level = logging.ERROR + elif str_log_level == "Critical": + set_debug(False) + Telemetry.log_level = logging.CRITICAL + else: + set_debug(False) + Telemetry.log_level = logging.NOTSET + + #Logging configuration + LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'default': { + 'format': '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s' + }, + 'standard': { + 'format': '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s' + }, + 'azure': { + 'format': '%(name)s: %(message)s' + }, + 'error': { + 'format': '[%(asctime)s] [%(levelname)s] %(name)s %(process)d::%(module)s|%(lineno)s:: %(message)s' } - ) - ) + }, + 'handlers': { + 'default': { + 'level': Telemetry.log_level, + 'formatter': 'standard', + 'class': 'logging.StreamHandler', + 'filters' : ['exclude_trace_logs'], + 'stream': 'ext://sys.stdout', + }, + 'console': { + 'level': Telemetry.log_level, + 'formatter': 'standard', + 'class': 'logging.StreamHandler', + 'filters' : ['exclude_trace_logs'], + 'stream': 'ext://sys.stdout' + }, + "azure": { + 'formatter': 'azure', + 'level': Telemetry.log_level, + "class": "opentelemetry.sdk._logs.LoggingHandler", + 'filters' : ['exclude_trace_logs'], + } + }, + 'filters': { + 'exclude_trace_logs': { + '()': 'foundationallm.telemetry.ExcludeTraceLogsFilter', + }, + }, + 'loggers': { + 'azure': { # Adjust the logger name accordingly + 'level': Telemetry.log_level, # Set to WARNING or higher + "class": "opentelemetry.sdk._logs.LoggingHandler" + }, + '': { + 'handlers': ['console'], + 'level': Telemetry.log_level, + 'filters': ['exclude_trace_logs'], + }, + }, + "root": { + "handlers": ["azure", "console"], + "level": Telemetry.log_level, + } + } - set_logger_provider(logger_provider) + #remove console if prod env (cut down on duplicate log data) + if env == "prod": + LOGGING['root']['handlers'] = ["azure"] + + #set the logging configuration + logging.config.dictConfig(LOGGING) @staticmethod - def get_logger(name: str, level: int = logging.INFO) -> logging.Logger: + def get_logger(name: str = None, level: int = logging.INFO) -> logging.Logger: """ Creates a logger by the specified name and logging level. @@ -92,7 +211,35 @@ def get_logger(name: str, level: int = logging.INFO) -> logging.Logger: Returns a logger object with the specified name and logging level. """ logger = logging.getLogger(name) - logger.setLevel(level) + + #if telemetry is configured, add the azure monitor exporter to the logger - by default only the root logger gets it + if Telemetry.api_name is not None and Telemetry.telemetry_connection_string is not None: + + #set the service name + resource = Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{Telemetry.api_name}", 'application': ''}) + logger_provider = LoggerProvider(resource=resource) + handler = LoggingHandler(logger_provider=logger_provider) + + exists = False + + for h in logger.handlers: + if h.__class__ == handler.__class__: + exists = True + break + + if not exists: + log_exporter = AzureMonitorLogExporter( + connection_string=Telemetry.telemetry_connection_string + ) + log_record_processor = BatchLogRecordProcessor( + log_exporter, + ) + logger_provider.add_log_record_processor(log_record_processor) + set_logger_provider(logger_provider) + handler.setLevel(Telemetry.log_level) + logger = getLogger() + logger.addHandler(handler) + return logger @staticmethod From 8ea9d6fb1debc4ca0858e4a6c19fd976a64c82ce Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Wed, 21 Aug 2024 15:31:41 -0400 Subject: [PATCH 3/9] small fix for logger_provider init --- .../PythonSDK/foundationallm/telemetry/telemetry.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py index 30a03f2d62..8846fc6fcf 100644 --- a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py +++ b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py @@ -215,9 +215,13 @@ def get_logger(name: str = None, level: int = logging.INFO) -> logging.Logger: #if telemetry is configured, add the azure monitor exporter to the logger - by default only the root logger gets it if Telemetry.api_name is not None and Telemetry.telemetry_connection_string is not None: - #set the service name - resource = Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{Telemetry.api_name}", 'application': ''}) - logger_provider = LoggerProvider(resource=resource) + logger_provider = get_logger_provider() + + if logger_provider is None: + #set the service name + resource = Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{Telemetry.api_name}", 'application': ''}) + logger_provider = LoggerProvider(resource=resource) + handler = LoggingHandler(logger_provider=logger_provider) exists = False From ac22cbf2d49a0832ffb9cc2f1b5e7d981a99b1fe Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Wed, 21 Aug 2024 16:26:13 -0400 Subject: [PATCH 4/9] fix resource attributes; add filter; --- .../Common/Services/DependencyInjection.cs | 29 ++++++++++--------- .../foundationallm/telemetry/telemetry.py | 24 +++++++++++---- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/dotnet/Common/Services/DependencyInjection.cs b/src/dotnet/Common/Services/DependencyInjection.cs index c0bf27420c..1e3217ba4d 100644 --- a/src/dotnet/Common/Services/DependencyInjection.cs +++ b/src/dotnet/Common/Services/DependencyInjection.cs @@ -28,6 +28,7 @@ using OpenTelemetry.Trace; using System.Diagnostics; using Microsoft.Extensions.Configuration; +using FoundationaLLM.Common.Constants.Configuration; namespace FoundationaLLM { @@ -94,10 +95,25 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, { AzureMonitorOptions options = new AzureMonitorOptions { ConnectionString = connectionStringConfigurationKey }; + var resourceBuilder = ResourceBuilder.CreateDefault(); + + // Create a dictionary of resource attributes. + var resourceAttributes = new Dictionary { + { "service.name", serviceName }, + { "service.namespace", "FoundationaLLM" }, + { "service.version", builder.Configuration[EnvironmentVariables.FoundationaLLM_Version] }, + { "service.instance.id", ValidatedEnvironment.MachineName } + }; + + resourceBuilder.AddAttributes(resourceAttributes); + builder.Services.AddOpenTelemetry() .WithTracing(b => { + + b + .SetResourceBuilder(resourceBuilder) .AddSource("Azure.*") //.AddConsoleExporter() .AddAspNetCoreInstrumentation() @@ -118,9 +134,6 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, .AddAzureMonitorTraceExporter(options => { options.ConnectionString = builder.Configuration[connectionStringConfigurationKey]; }); }); - Action configureResource = (r) => r - .AddAttributes(new[] { new KeyValuePair("telemetry.distro.name", "Azure.Monitor.OpenTelemetry.AspNetCore") }); - builder.Services.AddLogging(logging => { // clear out default configuration @@ -144,10 +157,7 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, logging.AddOpenTelemetry(builderOptions => { - var resourceBuilder = ResourceBuilder.CreateDefault(); - configureResource(resourceBuilder); builderOptions.SetResourceBuilder(resourceBuilder); - builderOptions.IncludeFormattedMessage = true; builderOptions.IncludeScopes = false; builderOptions.AddAzureMonitorLogExporter(options => { options.ConnectionString = builder.Configuration[connectionStringConfigurationKey]; }); @@ -156,13 +166,6 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, }); - // Create a dictionary of resource attributes. - var resourceAttributes = new Dictionary { - { "service.name", serviceName }, - { "service.namespace", "FoundationaLLM" }, - { "service.instance.id", ValidatedEnvironment.MachineName } - }; - // Configure the OpenTelemetry tracer provider to add the resource attributes to all traces. builder.Services.ConfigureOpenTelemetryTracerProvider((sp, builder) => builder.ConfigureResource(resourceBuilder => diff --git a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py index 8846fc6fcf..ef7789c1d3 100644 --- a/src/python/PythonSDK/foundationallm/telemetry/telemetry.py +++ b/src/python/PythonSDK/foundationallm/telemetry/telemetry.py @@ -1,5 +1,7 @@ import sys import logging +import platform +import foundationallm from azure.monitor.opentelemetry import configure_azure_monitor from logging import getLogger @@ -7,7 +9,7 @@ from opentelemetry import trace from opentelemetry.trace import Span, Status, StatusCode, Tracer from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.resources import SERVICE_NAME, Resource +from opentelemetry.sdk.resources import SERVICE_NAME, Resource, SERVICE_INSTANCE_ID, SERVICE_VERSION, SERVICE_NAMESPACE from opentelemetry.sdk.trace.export import ( BatchSpanProcessor, ConsoleSpanExporter, @@ -71,8 +73,13 @@ def configure_monitoring(config: Configuration, telemetry_connection_string: str Telemetry.telemetry_connection_string = config.get_value(telemetry_connection_string) Telemetry.api_name = api_name - resource = Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{api_name}"}) - logger_name = None + resource = Resource.create( + { + SERVICE_NAME: f"{Telemetry.api_name}", + SERVICE_NAMESPACE : f"FoundationaLLM", + SERVICE_VERSION: f"{foundationallm.__version__}", + SERVICE_INSTANCE_ID: f"{platform.node()}" + }) # Configure Azure Monitor defaults configure_azure_monitor( @@ -172,7 +179,8 @@ def configure_logging(config: Configuration): 'loggers': { 'azure': { # Adjust the logger name accordingly 'level': Telemetry.log_level, # Set to WARNING or higher - "class": "opentelemetry.sdk._logs.LoggingHandler" + "class": "opentelemetry.sdk._logs.LoggingHandler", + 'filters': ['exclude_trace_logs'] }, '': { 'handlers': ['console'], @@ -219,7 +227,13 @@ def get_logger(name: str = None, level: int = logging.INFO) -> logging.Logger: if logger_provider is None: #set the service name - resource = Resource.create({SERVICE_NAME: f"[FoundationaLLM]/{Telemetry.api_name}", 'application': ''}) + resource = Resource.create( + { + SERVICE_NAME: f"{Telemetry.api_name}", + SERVICE_NAMESPACE : f"FoundationaLLM", + SERVICE_VERSION: f"{foundationallm.__version__}", + SERVICE_INSTANCE_ID: f"{platform.node()}" + }) logger_provider = LoggerProvider(resource=resource) handler = LoggingHandler(logger_provider=logger_provider) From a84bc3bbd0201599c5382c39d1dbb4f7bd8d5d91 Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Thu, 22 Aug 2024 03:56:45 -0400 Subject: [PATCH 5/9] clear app settings --- src/dotnet/GatewayAdapterAPI/appsettings.json | 12 ++---------- src/dotnet/VectorizationAPI/appsettings.json | 6 ++---- src/dotnet/VectorizationWorker/appsettings.json | 6 ++---- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/dotnet/GatewayAdapterAPI/appsettings.json b/src/dotnet/GatewayAdapterAPI/appsettings.json index a70614408c..d71012d221 100644 --- a/src/dotnet/GatewayAdapterAPI/appsettings.json +++ b/src/dotnet/GatewayAdapterAPI/appsettings.json @@ -1,19 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Information" - } - }, - "ApplicationInsights": { - "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Information" } } }, diff --git a/src/dotnet/VectorizationAPI/appsettings.json b/src/dotnet/VectorizationAPI/appsettings.json index 098e09476f..36c0678c20 100644 --- a/src/dotnet/VectorizationAPI/appsettings.json +++ b/src/dotnet/VectorizationAPI/appsettings.json @@ -1,13 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" } } }, diff --git a/src/dotnet/VectorizationWorker/appsettings.json b/src/dotnet/VectorizationWorker/appsettings.json index 098e09476f..36c0678c20 100644 --- a/src/dotnet/VectorizationWorker/appsettings.json +++ b/src/dotnet/VectorizationWorker/appsettings.json @@ -1,13 +1,11 @@ { "Logging": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" }, "OpenTelemetry": { "LogLevel": { - "Default": "Warning", - "Microsoft.AspNetCore": "Warning" + "Default": "Warning" } } }, From 3e15b9ec1e300d9ec2832286d34b6be7c4527d41 Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Mon, 23 Sep 2024 15:52:47 -0400 Subject: [PATCH 6/9] Add simple high level activity; Add Source, serviceName --- src/dotnet/Common/Logging/ActivitySources.cs | 33 +++++++++++++++++++ .../Common/Services/DependencyInjection.cs | 4 ++- .../Controllers/CompletionsController.cs | 19 ++++++++--- .../Controllers/CompletionsController.cs | 18 ++++++++-- 4 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 src/dotnet/Common/Logging/ActivitySources.cs diff --git a/src/dotnet/Common/Logging/ActivitySources.cs b/src/dotnet/Common/Logging/ActivitySources.cs new file mode 100644 index 0000000000..2e36349313 --- /dev/null +++ b/src/dotnet/Common/Logging/ActivitySources.cs @@ -0,0 +1,33 @@ +using FoundationaLLM.Common.Constants; +using System.Diagnostics; + +namespace FoundationaLLM.Common.Logging +{ + public class ActivitySources + { + public static readonly ActivitySource AgentHubAPIActivitySource = new(ServiceNames.AgentHubAPI); + public static readonly ActivitySource CoreAPIActivitySource = new(ServiceNames.CoreAPI); + public static readonly ActivitySource GatekeeperAPIActivitySource = new(ServiceNames.GatekeeperAPI); + public static readonly ActivitySource GatewayAdapterAPIActivitySource = new(ServiceNames.GatewayAdapterAPI); + public static readonly ActivitySource ManagementAPIActivitySource = new(ServiceNames.ManagementAPI); + public static readonly ActivitySource OrchestrationAPIActivitySource = new(ServiceNames.OrchestrationAPI); + public static readonly ActivitySource SemanticKernelAPIActivitySource = new(ServiceNames.SemanticKernelAPI); + public static readonly ActivitySource StateAPIActivitySource = new(ServiceNames.StateAPI); + public static readonly ActivitySource VectorizationAPIActivitySource = new(ServiceNames.VectorizationAPI); + + public static Activity StartActivity(string name, ActivitySource source, ActivityKind kind = System.Diagnostics.ActivityKind.Consumer, bool addBaggage = true) + { + var activity = source.StartActivity(name, kind); + + if (addBaggage && activity != null) + { + foreach (var bag in activity?.Parent?.Baggage) + { + activity?.AddTag(bag.Key, bag.Value); + } + } + + return activity; + } + } +} diff --git a/src/dotnet/Common/Services/DependencyInjection.cs b/src/dotnet/Common/Services/DependencyInjection.cs index 1e3217ba4d..824c01f1fe 100644 --- a/src/dotnet/Common/Services/DependencyInjection.cs +++ b/src/dotnet/Common/Services/DependencyInjection.cs @@ -115,6 +115,7 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, b .SetResourceBuilder(resourceBuilder) .AddSource("Azure.*") + .AddSource(serviceName) //.AddConsoleExporter() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation(o => o.FilterHttpRequestMessage = (_) => @@ -169,7 +170,8 @@ public static void AddOpenTelemetry(this IHostApplicationBuilder builder, // Configure the OpenTelemetry tracer provider to add the resource attributes to all traces. builder.Services.ConfigureOpenTelemetryTracerProvider((sp, builder) => builder.ConfigureResource(resourceBuilder => - resourceBuilder.AddAttributes(resourceAttributes))); + resourceBuilder.AddAttributes(resourceAttributes)).AddSource(serviceName) + ); } /// diff --git a/src/dotnet/CoreAPI/Controllers/CompletionsController.cs b/src/dotnet/CoreAPI/Controllers/CompletionsController.cs index 6c1bff841b..3635fac8e7 100644 --- a/src/dotnet/CoreAPI/Controllers/CompletionsController.cs +++ b/src/dotnet/CoreAPI/Controllers/CompletionsController.cs @@ -2,6 +2,7 @@ using FoundationaLLM.Common.Exceptions; using FoundationaLLM.Common.Extensions; using FoundationaLLM.Common.Interfaces; +using FoundationaLLM.Common.Logging; using FoundationaLLM.Common.Models.Orchestration; using FoundationaLLM.Common.Models.Orchestration.Request; using FoundationaLLM.Common.Models.Orchestration.Response; @@ -10,6 +11,7 @@ using FoundationaLLM.Core.Interfaces; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using System.Diagnostics; namespace FoundationaLLM.Core.API.Controllers { @@ -63,8 +65,13 @@ public CompletionsController(ICoreService coreService, /// The user prompt for which to generate a completion. [HttpPost("completions", Name = "GetCompletion")] public async Task GetCompletion(string instanceId, [FromBody] CompletionRequest completionRequest) - => !string.IsNullOrWhiteSpace(completionRequest.SessionId) ? Ok(await _coreService.GetChatCompletionAsync(instanceId, completionRequest)) : - Ok(await _coreService.GetCompletionAsync(instanceId, completionRequest)); + { + using (var activity = ActivitySources.CoreAPIActivitySource.StartActivity("GetCompletion", ActivityKind.Consumer, parentContext: default, tags: new Dictionary { { "UPN", _callContext.CurrentUserIdentity?.UPN }, { "RequestId", completionRequest.SessionId }, { "UserId", _callContext.CurrentUserIdentity?.UserId }, { "UserPrompt", completionRequest.UserPrompt } })) + { + return !string.IsNullOrWhiteSpace(completionRequest.SessionId) ? Ok(await _coreService.GetChatCompletionAsync(instanceId, completionRequest)) : + Ok(await _coreService.GetCompletionAsync(instanceId, completionRequest)); + } + } @@ -78,8 +85,12 @@ public async Task GetCompletion(string instanceId, [FromBody] Com [HttpPost("async-completions")] public async Task> StartCompletionOperation(string instanceId, CompletionRequest completionRequest) { - var state = await _coreService.StartCompletionOperation(instanceId, completionRequest); - return Accepted(state); + using (var activity = ActivitySources.CoreAPIActivitySource.StartActivity("GetCompletion", ActivityKind.Consumer, parentContext: default, tags: new Dictionary { { "UPN", _callContext.CurrentUserIdentity?.UPN }, { "RequestId", completionRequest.SessionId }, { "UserId", _callContext.CurrentUserIdentity?.UserId }, { "UserPrompt", completionRequest.UserPrompt } })) + { + + var state = await _coreService.StartCompletionOperation(instanceId, completionRequest); + return Accepted(state); + } } /// diff --git a/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs b/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs index abdf43649c..86e1fb0548 100644 --- a/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs +++ b/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs @@ -1,9 +1,11 @@ using FoundationaLLM.Common.Authentication; +using FoundationaLLM.Common.Logging; using FoundationaLLM.Common.Models.Orchestration; using FoundationaLLM.Common.Models.Orchestration.Request; using FoundationaLLM.Common.Models.Orchestration.Response; using FoundationaLLM.Orchestration.Core.Interfaces; using Microsoft.AspNetCore.Mvc; +using System.Diagnostics; namespace FoundationaLLM.Orchestration.API.Controllers { @@ -65,8 +67,20 @@ public async Task GetCompletionOperationStatus(string inst /// The ID of the operation to retrieve. /// Returns a completion response [HttpGet("async-completions/{operationId}/result")] - public async Task GetCompletionOperationResult(string instanceId, string operationId) => - await _orchestrationService.GetCompletionOperationResult(instanceId, operationId); + public async Task GetCompletionOperationResult(string instanceId, string operationId) + { + using (var activity = ActivitySources.OrchestrationAPIActivitySource.StartActivity("GetCompletionOperationResult", ActivityKind.Consumer, parentContext: default)) + { + + var completionResponse = await _orchestrationService.GetCompletionOperationResult(instanceId, operationId); + + activity?.AddTag("PromptTokens", completionResponse.PromptTokens); + activity?.AddTag("CompletionTokens", completionResponse.CompletionTokens); + activity?.AddTag("TotalTokens", completionResponse.TotalTokens); + + return completionResponse; + } + } } } From 4bc28c0d5bb589238f8ce66b6297c4cbef0bacdf Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Mon, 23 Sep 2024 16:08:57 -0400 Subject: [PATCH 7/9] Update CompletionsController.cs --- .../Controllers/CompletionsController.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs b/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs index 86e1fb0548..c9ca63e85a 100644 --- a/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs +++ b/src/dotnet/OrchestrationAPI/Controllers/CompletionsController.cs @@ -34,8 +34,19 @@ public class CompletionsController( /// The completion request. /// The completion response. [HttpPost("completions")] - public async Task GetCompletion(string instanceId, [FromBody] CompletionRequest completionRequest) => - await _orchestrationService.GetCompletion(instanceId, completionRequest); + public async Task GetCompletion(string instanceId, [FromBody] CompletionRequest completionRequest) + { + using (var activity = ActivitySources.OrchestrationAPIActivitySource.StartActivity("GetCompletion", ActivityKind.Consumer, parentContext: default)) + { + var completionResponse = await _orchestrationService.GetCompletion(instanceId, completionRequest); + + activity?.AddTag("PromptTokens", completionResponse.PromptTokens); + activity?.AddTag("CompletionTokens", completionResponse.CompletionTokens); + activity?.AddTag("TotalTokens", completionResponse.TotalTokens); + + return completionResponse; + } + } /// /// Begins a completion operation. From fcdeffff484fff037f8d115180cec0b6fae94add Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Thu, 3 Oct 2024 07:22:13 -0400 Subject: [PATCH 8/9] Address review comments. --- .../Constants/Data/AppConfiguration.json | 9 ++ src/dotnet/Common/Logging/ActivitySources.cs | 46 +++++++ .../Common/Services/DependencyInjection.cs | 117 ++++++++++++++++++ 3 files changed, 172 insertions(+) diff --git a/src/dotnet/Common/Constants/Data/AppConfiguration.json b/src/dotnet/Common/Constants/Data/AppConfiguration.json index ca015f30ea..1de16bcf78 100644 --- a/src/dotnet/Common/Constants/Data/AppConfiguration.json +++ b/src/dotnet/Common/Constants/Data/AppConfiguration.json @@ -40,6 +40,15 @@ } ] }, + { + "namespace": "Logging", + "dependency_injection_key": null, + "configuration_section": { + "description": "Configuration section used to configure logging for the FoundationaLLM services." + }, + "configuration_keys": [ + ] + }, { "namespace": "Configuration", "dependency_injection_key": null, diff --git a/src/dotnet/Common/Logging/ActivitySources.cs b/src/dotnet/Common/Logging/ActivitySources.cs index 2e36349313..0305150915 100644 --- a/src/dotnet/Common/Logging/ActivitySources.cs +++ b/src/dotnet/Common/Logging/ActivitySources.cs @@ -3,18 +3,64 @@ namespace FoundationaLLM.Common.Logging { + /// + /// All ActivitySources for the FoundationaLLM services. This is a central place to define all ActivitySources. + /// public class ActivitySources { + /// + /// AgentHubAPI ActivitySource. + /// public static readonly ActivitySource AgentHubAPIActivitySource = new(ServiceNames.AgentHubAPI); + + /// + /// CoreAPI ActivitySource. + /// public static readonly ActivitySource CoreAPIActivitySource = new(ServiceNames.CoreAPI); + + /// + /// GatekeeperAPI ActivitySource. + /// public static readonly ActivitySource GatekeeperAPIActivitySource = new(ServiceNames.GatekeeperAPI); + + /// + /// GatewayAdapterAPI ActivitySource. + /// public static readonly ActivitySource GatewayAdapterAPIActivitySource = new(ServiceNames.GatewayAdapterAPI); + + /// + /// ManagementAPI ActivitySource. + /// public static readonly ActivitySource ManagementAPIActivitySource = new(ServiceNames.ManagementAPI); + + /// + /// OrchestrationAPI ActivitySource. + /// public static readonly ActivitySource OrchestrationAPIActivitySource = new(ServiceNames.OrchestrationAPI); + + /// + /// SemanticKernelAPI ActivitySource. + /// public static readonly ActivitySource SemanticKernelAPIActivitySource = new(ServiceNames.SemanticKernelAPI); + + /// + /// StateAPI ActivitySource. + /// public static readonly ActivitySource StateAPIActivitySource = new(ServiceNames.StateAPI); + + /// + /// VectorizationAPI ActivitySource. + /// public static readonly ActivitySource VectorizationAPIActivitySource = new(ServiceNames.VectorizationAPI); + /// + /// Start an activity with the given name and source. If addBaggage is true, the baggage from the parent activity will be added to the new activity. + /// + /// + /// + /// + /// + /// public static Activity StartActivity(string name, ActivitySource source, ActivityKind kind = System.Diagnostics.ActivityKind.Consumer, bool addBaggage = true) { var activity = source.StartActivity(name, kind); diff --git a/src/dotnet/Common/Services/DependencyInjection.cs b/src/dotnet/Common/Services/DependencyInjection.cs index 824c01f1fe..83f7753193 100644 --- a/src/dotnet/Common/Services/DependencyInjection.cs +++ b/src/dotnet/Common/Services/DependencyInjection.cs @@ -65,6 +65,34 @@ public static void AddLogging(this IHostApplicationBuilder builder) => } }); + /// + /// Configures logging defaults. + /// + /// The dependency injection container service collection. + /// The application configuration manager. + public static void AddLogging(this IServiceCollection services, IConfigurationManager configuration) => + services.AddLogging(config => + { + // clear out default configuration + config.ClearProviders(); + + config.AddConfiguration(configuration.GetSection("Logging")); + config.AddDebug(); + config.AddEventSourceLogger(); + + //get the log level + string logLevel = configuration["Logging:LogLevel:Default"]; + + //enable console for debug or trace + switch (logLevel) + { + case "Trace": + case "Debug": + config.AddConsole(); + break; + } + }); + /// /// Add CORS policies the the dependency injection container. /// @@ -293,6 +321,95 @@ public static void AddAzureResourceManager( this IHostApplicationBuilder builder) => builder.Services.AddSingleton(); + /// + /// Adds OpenTelemetry the the dependency injection container. + /// + /// The application builder managing the dependency injection container. + /// The configuration key for the OpenTelemetry connection string. + /// The name of the service. + public static void AddOpenTelemetry(this IServiceCollection services, IConfigurationManager configuration, + string connectionStringConfigurationKey, + string serviceName) + { + AzureMonitorOptions options = new AzureMonitorOptions { ConnectionString = configuration[connectionStringConfigurationKey] }; + + var resourceBuilder = ResourceBuilder.CreateDefault(); + + // Create a dictionary of resource attributes. + var resourceAttributes = new Dictionary { + { "service.name", serviceName }, + { "service.namespace", "FoundationaLLM" }, + { "service.version", configuration[EnvironmentVariables.FoundationaLLM_Version] }, + { "service.instance.id", ValidatedEnvironment.MachineName } + }; + + resourceBuilder.AddAttributes(resourceAttributes); + + services.AddOpenTelemetry() + .WithTracing(b => + { + + + b + .SetResourceBuilder(resourceBuilder) + .AddSource("Azure.*") + .AddSource(serviceName) + //.AddConsoleExporter() + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(o => o.FilterHttpRequestMessage = (_) => + { + // Azure SDKs create their own client span before calling the service using HttpClient + // In this case, we would see two spans corresponding to the same operation + // 1) created by Azure SDK 2) created by HttpClient + // To prevent this duplication we are filtering the span from HttpClient + // as span from Azure SDK contains all relevant information needed. + var parentActivity = Activity.Current?.Parent; + if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http")) + { + return false; + } + return true; + }) + .AddAzureMonitorTraceExporter(options => { options.ConnectionString = configuration[connectionStringConfigurationKey]; }); + }); + + services.AddLogging(logging => + { + // clear out default configuration + logging.ClearProviders(); + + logging.AddConfiguration(configuration.GetSection("Logging")); + logging.AddDebug(); + logging.AddEventSourceLogger(); + + //get the log level + string logLevel = configuration["Logging:LogLevel:Default"]; + + //enable console for debug or trace + switch (logLevel) + { + case "Trace": + case "Debug": + logging.AddConsole(); + break; + } + + logging.AddOpenTelemetry(builderOptions => + { + builderOptions.SetResourceBuilder(resourceBuilder); + builderOptions.IncludeFormattedMessage = true; + builderOptions.IncludeScopes = false; + builderOptions.AddAzureMonitorLogExporter(options => { options.ConnectionString = configuration[connectionStringConfigurationKey]; }); + }); + }); + + // Configure the OpenTelemetry tracer provider to add the resource attributes to all traces. + services.ConfigureOpenTelemetryTracerProvider((sp, builder) => + builder.ConfigureResource(resourceBuilder => + resourceBuilder.AddAttributes(resourceAttributes)).AddSource(serviceName) + ); + } + /// /// Registers the implementation with the dependency injection container. /// From 487b2e76c5d0abb2bcc7617115f88815a87c07e9 Mon Sep 17 00:00:00 2001 From: Chris Givens Date: Thu, 3 Oct 2024 16:00:39 -0400 Subject: [PATCH 9/9] Switch CoreWorker to OpenTelemetry --- src/dotnet/CoreWorker/ChangeFeedWorker.cs | 8 +------- src/dotnet/CoreWorker/Program.cs | 6 +----- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/dotnet/CoreWorker/ChangeFeedWorker.cs b/src/dotnet/CoreWorker/ChangeFeedWorker.cs index 4d94261789..5c9feb2562 100644 --- a/src/dotnet/CoreWorker/ChangeFeedWorker.cs +++ b/src/dotnet/CoreWorker/ChangeFeedWorker.cs @@ -10,7 +10,6 @@ namespace FoundationaLLM.Core.Worker public class ChangeFeedWorker : BackgroundService { private readonly ILogger _logger; - private readonly TelemetryClient _telemetryClient; private readonly ICosmosDbChangeFeedService _cosmosDbChangeFeedService; /// @@ -23,11 +22,9 @@ public class ChangeFeedWorker : BackgroundService /// The Cosmos DB change feed /// processor that performs the event processing tasks for the worker. public ChangeFeedWorker(ILogger logger, - TelemetryClient telemetryClient, ICosmosDbChangeFeedService cosmosDbChangeFeedService) { _logger = logger; - _telemetryClient = telemetryClient; _cosmosDbChangeFeedService = cosmosDbChangeFeedService; } @@ -39,10 +36,7 @@ public ChangeFeedWorker(ILogger logger, protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("{time}: Starting the ChangeFeedWorker", DateTimeOffset.Now); - using (_telemetryClient.StartOperation("StartChangeFeedWorker")) - { - await _cosmosDbChangeFeedService.StartChangeFeedProcessorsAsync(); - } + await _cosmosDbChangeFeedService.StartChangeFeedProcessorsAsync(); } } } diff --git a/src/dotnet/CoreWorker/Program.cs b/src/dotnet/CoreWorker/Program.cs index dfca39443f..d6fddcd9f7 100644 --- a/src/dotnet/CoreWorker/Program.cs +++ b/src/dotnet/CoreWorker/Program.cs @@ -37,7 +37,7 @@ // Add OpenTelemetry. builder.AddOpenTelemetry( - AppConfigurationKeys.FoundationaLLM_APIEndpoints_CoreAPI_Essentials_AppInsightsConnectionString, + AppConfigurationKeys.FoundationaLLM_APIEndpoints_CoreWorker_Essentials_AppInsightsConnectionString, ServiceNames.CoreAPI); builder.Services.AddOptions() @@ -58,10 +58,6 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHostedService(); -builder.Services.AddApplicationInsightsTelemetryWorkerService(options => -{ - options.ConnectionString = builder.Configuration[AppConfigurationKeys.FoundationaLLM_APIEndpoints_CoreWorker_Essentials_AppInsightsConnectionString]; -}); var host = builder.Build();