Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend the use of OpenTelemetry to Core API entry points #2054

Merged
merged 5 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/dotnet/Agent/Agent.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 4 additions & 1 deletion src/dotnet/AzureOpenAI/AzureOpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0-beta.1" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 11 additions & 1 deletion src/dotnet/Common/Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@
<PackageReference Include="Microsoft.DeepDev.TokenizerLib" Version="1.3.3" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.2" />
<PackageReference Include="Microsoft.Graph" Version="5.48.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.17.4" />
<PackageReference Include="Microsoft.Identity.Web" Version="3.5.0" />
<PackageReference Include="Mime-Detective" Version="24.7.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Asp.Versioning.Http" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.4.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
Expand All @@ -78,6 +79,10 @@
<LastGenOutput>AuthorizableActionNames.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="Templates\TelemetryActivityNames.tt">
<LastGenOutput>TelemetryActivityNames.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="Templates\AuthorizationKeyVaultSecretNames.tt">
<LastGenOutput>AuthorizationKeyVaultSecretNames.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
Expand Down Expand Up @@ -165,6 +170,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>AuthorizableActionNames.tt</DependentUpon>
</Compile>
<Compile Update="Templates\TelemetryActivityNames.cs">
<DependentUpon>TelemetryActivityNames.tt</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
</Compile>
<Compile Update="Templates\AuthorizationKeyVaultSecretNames.cs">
<DependentUpon>AuthorizationKeyVaultSecretNames.tt</DependentUpon>
<DesignTime>True</DesignTime>
Expand Down
13 changes: 13 additions & 0 deletions src/dotnet/Common/Constants/Data/TelemetryActivities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"telemetry_activity_source": "CoreAPI",
"telemetry_activities": [
{
"name": "AsyncCompletions_StartCompletionOperation"
},
{
"name": "Completions_GetCompletion"
}
]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace FoundationaLLM.Common.Constants.Telemetry
{
/// <summary>
/// Provides the names of the tags used in telemetry activities.
/// </summary>
public static class TelemetryActivityTagNames
{
/// <summary>
/// The FoundationaLLM instance identifier tag.
/// </summary>
public const string InstanceId = "FoundationaLLM-InstanceId";

/// <summary>
/// The FoundationaLLM conversation identifier tag.
/// </summary>
public const string ConversationId = "FoundationaLLM-ConversationId";

/// <summary>
/// The FoundationaLLM operation identifier tag.
/// </summary>
public const string OperationId = "FoundationaLLM-OperationId";

/// <summary>
/// The FoundationaLLM user principal name tag.
/// </summary>
public const string UPN = "FoundationaLLM-UPN";

/// <summary>
/// The FoundationaLLM user identifier tag.
/// </summary>
public const string UserId = "FoundationaLLM-UserId";
}
}
70 changes: 50 additions & 20 deletions src/dotnet/Common/Services/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Azure.Monitor.OpenTelemetry.AspNetCore;
using Azure.Monitor.OpenTelemetry.Exporter;
using FoundationaLLM.Common.Authentication;
using FoundationaLLM.Common.Constants;
using FoundationaLLM.Common.Constants.Authorization;
using FoundationaLLM.Common.Constants.Configuration;
using FoundationaLLM.Common.Interfaces;
using FoundationaLLM.Common.Models.Configuration.CosmosDB;
Expand All @@ -9,21 +11,18 @@
using FoundationaLLM.Common.Services.Azure;
using FoundationaLLM.Common.Services.Security;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Cosmos;
using Microsoft.AspNetCore.Identity;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Identity.Web;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Authentication;
using FoundationaLLM.Common.Constants.Authorization;
using Microsoft.Graph.Models;
using System.Diagnostics;

namespace FoundationaLLM
{
Expand Down Expand Up @@ -60,23 +59,54 @@
string connectionStringConfigurationKey,
string serviceName)
{
// Add the OpenTelemetry telemetry service and send telemetry data to Azure Monitor.
builder.Services.AddOpenTelemetry().UseAzureMonitor(options =>
var resourceBuilder = ResourceBuilder
.CreateDefault()
.AddAttributes(new Dictionary<string, object>
{
{ "service.name", serviceName },
{ "service.namespace", "FoundationaLLM" },
{ "service.version", builder.Configuration[EnvironmentVariables.FoundationaLLM_Version]! },
{ "service.instance.id", ValidatedEnvironment.MachineName }
});


// Add the OpenTelemetry logging provider and send logs to Azure Monitor.
builder.Logging.AddOpenTelemetry(openTelemetryLoggerOptions =>
{
options.ConnectionString = builder.Configuration[connectionStringConfigurationKey];
openTelemetryLoggerOptions.SetResourceBuilder(resourceBuilder);
openTelemetryLoggerOptions.IncludeFormattedMessage = true;
openTelemetryLoggerOptions.IncludeScopes = true;
openTelemetryLoggerOptions.AddAzureMonitorLogExporter(azureMonitorOptions =>
{
azureMonitorOptions.ConnectionString = builder.Configuration[connectionStringConfigurationKey];
});
});

// Create a dictionary of resource attributes.
var resourceAttributes = new Dictionary<string, object> {
{ "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 =>
resourceBuilder.AddAttributes(resourceAttributes)));
// Add the OpenTelemetry telemetry service and send telemetry data to Azure Monitor.
builder.Services.AddOpenTelemetry()
.WithTracing(traceProviderBuilder => traceProviderBuilder
.SetResourceBuilder(resourceBuilder)
.AddSource("Azure.*")
.AddSource(serviceName)
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation(httpOptions => httpOptions.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(azureMonitorOptions =>
{
azureMonitorOptions.ConnectionString = builder.Configuration[connectionStringConfigurationKey];
}));
}

/// <summary>
Expand Down Expand Up @@ -121,7 +151,7 @@
policyBuilder.RequireAuthenticatedUser();
if (requireScopes)
{
var requiredScopes = builder.Configuration[entraScopesConfigurationKey]?.Split(' ');

Check warning on line 154 in src/dotnet/Common/Services/DependencyInjection.cs

View workflow job for this annotation

GitHub Actions / build (OrchestrationAPI)

Possible null reference argument for parameter 'key' in 'string? IConfiguration.this[string key]'.

Check warning on line 154 in src/dotnet/Common/Services/DependencyInjection.cs

View workflow job for this annotation

GitHub Actions / build (CoreWorker)

Possible null reference argument for parameter 'key' in 'string? IConfiguration.this[string key]'.

Check warning on line 154 in src/dotnet/Common/Services/DependencyInjection.cs

View workflow job for this annotation

GitHub Actions / build (CoreAPI)

Possible null reference argument for parameter 'key' in 'string? IConfiguration.this[string key]'.

Check warning on line 154 in src/dotnet/Common/Services/DependencyInjection.cs

View workflow job for this annotation

GitHub Actions / build (GatekeeperAPI)

Possible null reference argument for parameter 'key' in 'string? IConfiguration.this[string key]'.

Check warning on line 154 in src/dotnet/Common/Services/DependencyInjection.cs

View workflow job for this annotation

GitHub Actions / build (SemanticKernelAPI)

Possible null reference argument for parameter 'key' in 'string? IConfiguration.this[string key]'.
if (requiredScopes != null && requiredScopes.Length > 0)
{
policyBuilder.RequireClaim(ClaimConstants.Scope, requiredScopes);
Expand Down
16 changes: 16 additions & 0 deletions src/dotnet/Common/Telemetry/TelemetryActivitySources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using FoundationaLLM.Common.Constants;
using System.Diagnostics;

namespace FoundationaLLM.Common.Telemetry
{
/// <summary>
/// Provides predefined telemetry activity sources for the components of the platform.
/// </summary>
public class TelemetryActivitySources
{
/// <summary>
/// The activity source for the Core API.
/// </summary>
public static readonly ActivitySource CoreAPIActivitySource = new (ServiceNames.CoreAPI);
}
}
19 changes: 19 additions & 0 deletions src/dotnet/Common/Templates/TelemetryActivityNames.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace FoundationaLLM.Common.Constants.Telemetry
{
/// <summary>
/// Defines constants for all telemetry activity names.
/// </summary>
public static class TelemetryActivityNames
{
/// <summary>
/// The telemetry activity name for the CoreAPI AsyncCompletions_StartCompletionOperation action.
/// </summary>
public const string CoreAPI_AsyncCompletions_StartCompletionOperation = "AsyncCompletions_StartCompletionOperation";

/// <summary>
/// The telemetry activity name for the CoreAPI Completions_GetCompletion action.
/// </summary>
public const string CoreAPI_Completions_GetCompletion = "Completions_GetCompletion";

}
}
36 changes: 36 additions & 0 deletions src/dotnet/Common/Templates/TelemetryActivityNames.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.IO" #>
<#@ assembly name="System.Text.Json" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ assembly name="System.Memory" #>
<#@ import namespace="System.Text.Json" #>
<#@ import namespace="System.Text.Json.Nodes" #>
<#@ output extension=".cs" #>
<#
string inputFileName = this.Host.ResolvePath($"../Constants/Data/TelemetryActivities.json");
string inputContent = File.ReadAllText(inputFileName);
var jsonObjects = JsonNode.Parse(inputContent)!;
#>
namespace FoundationaLLM.Common.Constants.Telemetry
{
/// <summary>
/// Defines constants for all telemetry activity names.
/// </summary>
public static class TelemetryActivityNames
{
<#
foreach (var jsonObject in jsonObjects.AsArray())
{
foreach (var telemetryActivity in jsonObject["telemetry_activities"].AsArray())
{#>
/// <summary>
/// The telemetry activity name for the <#= jsonObject["telemetry_activity_source"] #> <#= telemetryActivity["name"] #> action.
/// </summary>
public const string <#= jsonObject["telemetry_activity_source"] #>_<#= telemetryActivity["name"] #> = "<#= telemetryActivity["name"] #>";

<#}
}
#>
}
}
1 change: 0 additions & 1 deletion src/dotnet/Configuration/Configuration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Azure.Data.AppConfiguration" Version="1.4.0" />
</ItemGroup>

Expand Down
7 changes: 5 additions & 2 deletions src/dotnet/Core/Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0-beta.1" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" />
<PackageReference Include="Azure.Search.Documents" Version="11.5.1" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.39.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="Polly.Core" Version="8.3.1" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
35 changes: 33 additions & 2 deletions src/dotnet/CoreAPI/Controllers/CompletionsController.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
using FoundationaLLM.Common.Authentication;
using FoundationaLLM.Common.Constants.Authorization;
using FoundationaLLM.Common.Constants.ResourceProviders;
using FoundationaLLM.Common.Constants.Telemetry;
using FoundationaLLM.Common.Exceptions;
using FoundationaLLM.Common.Interfaces;
using FoundationaLLM.Common.Models.Orchestration;
using FoundationaLLM.Common.Models.Orchestration.Request;
using FoundationaLLM.Common.Models.ResourceProviders;
using FoundationaLLM.Common.Models.ResourceProviders.Agent;
using FoundationaLLM.Common.Telemetry;
using FoundationaLLM.Core.Interfaces;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;

namespace FoundationaLLM.Core.API.Controllers
{
Expand Down Expand Up @@ -68,10 +71,25 @@ public CompletionsController(ICoreService coreService,
/// <param name="instanceId">The instance ID of the current request.</param>
/// <param name="completionRequest">The user prompt for which to generate a completion.</param>
[HttpPost("completions", Name = "GetCompletion")]
public async Task<IActionResult> GetCompletion(string instanceId, [FromBody] CompletionRequest completionRequest) =>
!string.IsNullOrWhiteSpace(completionRequest.SessionId)
public async Task<IActionResult> GetCompletion(string instanceId, [FromBody] CompletionRequest completionRequest)
{
using var telemetryActivity = TelemetryActivitySources.CoreAPIActivitySource.StartActivity(
TelemetryActivityNames.CoreAPI_Completions_GetCompletion,
ActivityKind.Consumer,
parentContext: default,
tags: new Dictionary<string, object?>
{
{ TelemetryActivityTagNames.InstanceId, instanceId },
{ TelemetryActivityTagNames.ConversationId, completionRequest.SessionId ?? "N/A" },
{ TelemetryActivityTagNames.OperationId, completionRequest.OperationId ?? "N/A" },
{ TelemetryActivityTagNames.UPN, _callContext.CurrentUserIdentity?.UPN ?? "N/A" },
{ TelemetryActivityTagNames.UserId, _callContext.CurrentUserIdentity?.UserId ?? "N/A" }
});

return !string.IsNullOrWhiteSpace(completionRequest.SessionId)
? Ok(await _coreService.GetChatCompletionAsync(instanceId, completionRequest))
: Ok(await _coreService.GetCompletionAsync(instanceId, completionRequest));
}

/// <summary>
/// Begins a completion operation.
Expand All @@ -82,6 +100,19 @@ public async Task<IActionResult> GetCompletion(string instanceId, [FromBody] Com
[HttpPost("async-completions")]
public async Task<ActionResult<LongRunningOperation>> StartCompletionOperation(string instanceId, CompletionRequest completionRequest)
{
using var telemetryActivity = TelemetryActivitySources.CoreAPIActivitySource.StartActivity(
TelemetryActivityNames.CoreAPI_AsyncCompletions_StartCompletionOperation,
ActivityKind.Consumer,
parentContext: default,
tags: new Dictionary<string, object?>
{
{ TelemetryActivityTagNames.InstanceId, instanceId },
{ TelemetryActivityTagNames.ConversationId, completionRequest.SessionId ?? "N/A" },
{ TelemetryActivityTagNames.OperationId, completionRequest.OperationId ?? "N/A" },
{ TelemetryActivityTagNames.UPN, _callContext.CurrentUserIdentity?.UPN ?? "N/A" },
{ TelemetryActivityTagNames.UserId, _callContext.CurrentUserIdentity?.UserId ?? "N/A" }
});

var state = await _coreService.StartCompletionOperation(instanceId, completionRequest);
return Accepted(state);
}
Expand Down
Loading
Loading