From a90b985bcb5d6af6e402573b755bbd6821a5543e Mon Sep 17 00:00:00 2001 From: plockwood Date: Wed, 4 Oct 2023 17:41:44 +0100 Subject: [PATCH] add serilog and api user enricher --- TramsDataApi/Program.cs | 58 ++++++++++++------- .../SerilogCustomEnrichers/ApiUserEnricher.cs | 57 ++++++++++++++++++ TramsDataApi/Startup.cs | 5 ++ TramsDataApi/TramsDataApi.csproj | 2 + TramsDataApi/appsettings.json | 18 +++++- 5 files changed, 117 insertions(+), 23 deletions(-) create mode 100644 TramsDataApi/SerilogCustomEnrichers/ApiUserEnricher.cs diff --git a/TramsDataApi/Program.cs b/TramsDataApi/Program.cs index ff5fdce29..c3980562f 100644 --- a/TramsDataApi/Program.cs +++ b/TramsDataApi/Program.cs @@ -1,25 +1,39 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; +using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Serilog; +using System; +using TramsDataApi; +using TramsDataApi.SerilogCustomEnrichers; -namespace TramsDataApi +var builder = WebApplication.CreateBuilder(args); + + +var startup = new Startup(builder.Configuration); + +startup.ConfigureServices(builder.Services); + +builder.Host.UseSerilog((context, services, loggerConfiguration) => { - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) - { - var builder = Host.CreateDefaultBuilder(args); - return builder.ConfigureLogging(c => - { - c.ClearProviders(); - c.AddConsole(); - }) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); - } - } -} \ No newline at end of file + var enricher = services.GetRequiredService(); + + loggerConfiguration + .WriteTo.ApplicationInsights(services.GetRequiredService(), TelemetryConverter.Traces) + .Enrich.FromLogContext() + .Enrich.With(enricher) + .WriteTo.Console(); + }); + +var app = builder.Build(); + +var provider = app.Services.GetRequiredService(); + +startup.Configure(app, app.Environment, provider); + +ILogger logger = app.Services.GetRequiredService>(); + +logger.LogInformation("Logger is working..."); + +app.Run(); diff --git a/TramsDataApi/SerilogCustomEnrichers/ApiUserEnricher.cs b/TramsDataApi/SerilogCustomEnrichers/ApiUserEnricher.cs new file mode 100644 index 000000000..683585ebc --- /dev/null +++ b/TramsDataApi/SerilogCustomEnrichers/ApiUserEnricher.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Serilog.Core; +using Serilog.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using TramsDataApi.ResponseModels; +using TramsDataApi.UseCases; + +namespace TramsDataApi.SerilogCustomEnrichers +{ + public class ApiUserEnricher : ILogEventEnricher + { + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUseCase _apiKeyService; + + public ApiUserEnricher(IHttpContextAccessor httpContextAccessor, IUseCase apiKeyService) + { + _httpContextAccessor = httpContextAccessor; + _apiKeyService = apiKeyService; + } + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + var httpContext = _httpContextAccessor.HttpContext; + + if (httpContext is null) + { + return; + } + + ApiUser user = null; + + if (httpContext.Request.Headers.TryGetValue("ApiKey", out var apiKey)) + { + user = _apiKeyService.Execute(apiKey); + } + + var httpContextModel = new HttpContextModel + { + Method = httpContext.Request.Method, + User = user?.UserName ?? "Unknow or not applicable" + + }; + + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ApiUser", httpContextModel.User, true)); + } + } + + public class HttpContextModel + { + public string Method { get; init; } + + public string User { get; init; } + } +} diff --git a/TramsDataApi/Startup.cs b/TramsDataApi/Startup.cs index 5c4ed6db1..0ca076828 100644 --- a/TramsDataApi/Startup.cs +++ b/TramsDataApi/Startup.cs @@ -15,6 +15,8 @@ namespace TramsDataApi using Middleware; using Swagger; using UseCases; + using TramsDataApi.SerilogCustomEnrichers; + using TramsDataApi.ResponseModels; public class Startup { @@ -93,6 +95,9 @@ public void ConfigureServices(IServiceCollection services) opt.ConnectionString = appInsightsCnnStr; }); } + + services.AddSingleton, ApiKeyService>(); + services.AddSingleton(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/TramsDataApi/TramsDataApi.csproj b/TramsDataApi/TramsDataApi.csproj index e3abc3726..d15c3f9a3 100644 --- a/TramsDataApi/TramsDataApi.csproj +++ b/TramsDataApi/TramsDataApi.csproj @@ -19,6 +19,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/TramsDataApi/appsettings.json b/TramsDataApi/appsettings.json index 71bf017c4..8d5b1d1d7 100644 --- a/TramsDataApi/appsettings.json +++ b/TramsDataApi/appsettings.json @@ -21,5 +21,21 @@ "ConnectionStrings": { "DefaultConnection": "Server=localhost,1433;Database=sip;User Id=sa;TrustServerCertificate=True;Password=Your_password123" }, - "SyncAcademyConversionProjectsSchedule": "0 0/15 * * * *" + "SyncAcademyConversionProjectsSchedule": "0 0/15 * * * *", + "Serilog": { + "Using": [ + "Serilog.Sinks.ApplicationInsights" + ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "Enrich": [ "FromLogContext" ], + "Properties": { + "Application": "Dfe.Academies.Api" + } + } }