From ec7e04990bcb22a267a3249a909b98e85414ffca Mon Sep 17 00:00:00 2001 From: Knut Haug <154342485+knuhau@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:19:49 +0200 Subject: [PATCH] chore(webapi): Add Opentelemetry tracing and metrics to webapi (#1202) ## Description Replace Application Insights tracing with Opentelemetry in the webapi ## Related Issue(s) - #1101 ## Verification - [x] **Your** code builds clean without any errors or warnings - [x] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) --- .../WebApplicationBuilderExtensions.cs | 49 +++++++++++++++++++ .../Digdir.Domain.Dialogporten.WebApi.csproj | 6 ++- .../Program.cs | 19 +++---- .../Features/V1/SwaggerSnapshotTests.cs | 2 +- 4 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/WebApplicationBuilderExtensions.cs diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/WebApplicationBuilderExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/WebApplicationBuilderExtensions.cs new file mode 100644 index 000000000..88b734912 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/WebApplicationBuilderExtensions.cs @@ -0,0 +1,49 @@ +using Azure.Monitor.OpenTelemetry.AspNetCore; +using OpenTelemetry.Trace; +using Npgsql; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; + +namespace Digdir.Domain.Dialogporten.WebApi.Common.Extensions; + +internal static class WebApplicationBuilderExtensions +{ + public static WebApplicationBuilder ConfigureTelemetry(this WebApplicationBuilder builder) + { + builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource + .AddService(serviceName: builder.Environment.ApplicationName)) + .WithTracing(tracing => + { + if (builder.Environment.IsDevelopment()) + { + tracing.SetSampler(new AlwaysOnSampler()); + } + + tracing.AddAspNetCoreInstrumentation(options => + { + options.Filter = (httpContext) => + !httpContext.Request.Path.StartsWithSegments("/health"); + }); + + tracing.AddHttpClientInstrumentation(); + tracing.AddNpgsql(); + }) + .WithMetrics(metrics => + { + metrics.AddRuntimeInstrumentation(); + }); + + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING"))) + { + builder.Services.AddOpenTelemetry().UseAzureMonitor(); + } + else + { + // Use Application Insights SDK for local development + builder.Services.AddApplicationInsightsTelemetry(); + } + + return builder; + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj b/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj index 0154714ba..31715419d 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj +++ b/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj @@ -8,13 +8,17 @@ + + + + @@ -25,4 +29,4 @@ - \ No newline at end of file + diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs index f6d179cba..f69497778 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs @@ -7,12 +7,12 @@ using Digdir.Domain.Dialogporten.Application.Common.Extensions; using Digdir.Domain.Dialogporten.Application.Common.Extensions.OptionExtensions; using Digdir.Domain.Dialogporten.Application.Externals.Presentation; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; using Digdir.Domain.Dialogporten.Infrastructure; using Digdir.Domain.Dialogporten.WebApi; using Digdir.Domain.Dialogporten.WebApi.Common; using Digdir.Domain.Dialogporten.WebApi.Common.Authentication; using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; -using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; using Digdir.Domain.Dialogporten.WebApi.Common.Json; using Digdir.Domain.Dialogporten.WebApi.Common.Swagger; using Digdir.Library.Utils.AspNet; @@ -26,18 +26,17 @@ using Microsoft.Extensions.Options; // Using two-stage initialization to catch startup errors. +var telemetryConfiguration = TelemetryConfiguration.CreateDefault(); Log.Logger = new LoggerConfiguration() .MinimumLevel.Warning() .Enrich.FromLogContext() .WriteTo.Console(formatProvider: CultureInfo.InvariantCulture) - .WriteTo.ApplicationInsights( - TelemetryConfiguration.CreateDefault(), - TelemetryConverter.Traces) + .WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces) .CreateBootstrapLogger(); try { - BuildAndRun(args); + BuildAndRun(args, telemetryConfiguration); } catch (Exception ex) when (ex is not OperationCanceledException) { @@ -49,7 +48,7 @@ Log.CloseAndFlush(); } -static void BuildAndRun(string[] args) +static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfiguration) { var builder = WebApplication.CreateBuilder(args); @@ -58,9 +57,7 @@ static void BuildAndRun(string[] args) .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() - .WriteTo.ApplicationInsights( - services.GetRequiredService(), - TelemetryConverter.Traces)); + .WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces)); builder.Configuration .AddAzureConfiguration(builder.Environment.EnvironmentName) @@ -74,6 +71,8 @@ static void BuildAndRun(string[] args) var thisAssembly = Assembly.GetExecutingAssembly(); + builder.ConfigureTelemetry(); + builder.Services // Options setup .ConfigureOptions() @@ -91,7 +90,6 @@ static void BuildAndRun(string[] args) .AddHttpContextAccessor() .AddValidatorsFromAssembly(thisAssembly, ServiceLifetime.Transient, includeInternalTypes: true) .AddAzureAppConfiguration() - .AddApplicationInsightsTelemetry() .AddEndpointsApiExplorer() .AddFastEndpoints() .SwaggerDocument(x => @@ -125,7 +123,6 @@ static void BuildAndRun(string[] args) .JwtBearerTokenSchemas? .Select(z => z.WellKnown) .ToList() ?? []) - // Auth .AddDialogportenAuthentication(builder.Configuration) .AddAuthorization(); diff --git a/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Features/V1/SwaggerSnapshotTests.cs b/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Features/V1/SwaggerSnapshotTests.cs index ed2d01235..187a1269d 100644 --- a/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Features/V1/SwaggerSnapshotTests.cs +++ b/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Features/V1/SwaggerSnapshotTests.cs @@ -35,7 +35,7 @@ public async Task FailIfSwaggerSnapshotDoesNotMatch() // The order of the properties in the swagger.json file is not cross-platform deterministic. // Running client.GetAsync("/swagger/v1/swagger.json"); on Windows and Mac will produce // different ordering of the results (although the content is the same). So we force an - // alphabetical ordering of the properties to make the test deterministic. + // alphabetical ordering of the properties to make the test deterministic. // Ref: https://github.com/digdir/dialogporten/issues/996 var orderedSwagger = SortJson(newSwagger);