I have a basic CRUD web app that saves user form input to a database. The the current user id is saved along with the form input without any issues both locally and on IIS. However, when I save EnvironmentUserName in logs via Serilog after publishing the website to IIS, the EnvironmentUserName is always saved as "IIS APPPOOL\DefaultAppPool." When I run the web app locally, it captures my user id as expected.
I tried writing a custom enricher to use the same logic of capturing the user id as I do when I retrieve the user id to save with the form input in the database, however the results remain the same as EnvironmentUserName.
I've been reading up on impersonation in ASP NET Core apps but did not find an example where I can apply it to logging in general.
How do I include the current logged in user's id in my log when the web app is run from IIS without getting "IIS APPOOL\DefaultAppPool" via Serilog?
Custom enricher:
public class CurrentUserEnricher : ILogEventEnricher
{
static readonly string _currentUser;
/// <summary>
/// static constructor to load the environment variable
/// </summary>
static CurrentUserEnricher()
{
_currentUser = WindowsIdentity.GetCurrent().Name.ToUpper();
}
public void Enrich(
LogEvent logEvent,
ILogEventPropertyFactory propertyFactory)
{
var enrichProperty = propertyFactory
.CreateProperty(
"CurrentUser",
_currentUser);
logEvent.AddOrUpdateProperty(enrichProperty);
}
}
Here's my appSettings.config. Note that I'm saving "CurrentUser" as "EnvironmentUserName" after writing my custom enricher.
{
"AllowedHosts": "*",
"ConnectionStrings": {
"DuckDatabase": "data source=*****;initial catalog=*****;integrated security=True;TrustServerCertificate=True",
"GooseDatabase": "data source=*****;initial catalog=*****;integrated security=True;TrustServerCertificate=True"
},
"Serilog": {
"Using": [ "Serilog.Sinks.MSSqlServer", "CustomSerilogEnrichers" ],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft.AspNetCore": "Warning"
}
},
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "ConsultsDatabase",
"sinkOptionsSection": {
"tableName": "Logs",
"schemaName": "dbo",
"autoCreateSqlTable": false,
"batchPostingLimit": 1000,
"period": "0.00:00:30"
},
"restrictedToMinimumLevel": "Debug",
"columnOptionsSection": {
"addStandardColumns": [ "LogEvent" ],
"removeStandardColumns": [ "MessageTemplate", "Properties" ],
"additionalColumns": [
{
"ColumnName": "EnvironmentUserName",
"PropertyName": "CurrentUser",
"DataType": "varchar",
"AllowNull": true,
"DataLength": 50
},
{
"ColumnName": "MachineName",
"DataType": "varchar",
"AllowNull": true,
"DataLength": 50
},
{
"ColumnName": "Application",
"DataType": "varchar",
"AllowNull": true,
"DataLength": 50
}
]
}
}
}
],
"Enrich": [ "WithMachineName", "WithCurrentUser" ],
"Properties": {
"Application": "Medical Consults"
}
}
}
Program.cs, where I setup Serilog:
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Server.IIS;
using Serilog;
using System.Security.Principal;
using System.Text;
using System;
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try
{
Log.Information("Starting Medical Consults web application");
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
// Add services to the container.
builder.Services.AddControllersWithViews()
.AddMvcOptions(options => options.Filters.Add(new AuthorizeFilter("AllowedRoles")));
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AllowedRoles",
policy => policy.RequireRole("*****"));
});
var app = builder.Build();
app.UseSerilogRequestLogging();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
Project specs:
- Project type: ASP.Net Core Web App (Model-View-Controller)
- .Net framework: 7.0
- Target OS: Windows
Nuget packages:
- Microsoft.AspNetCore.Authentication.Negotiate v.7.0.10
- Microsoft.AspNetCore.Identity.EntityFrameworkCore v.7.0.10
- Microsoft.EntityFrameworkCore v.7.0.10
- Microsoft.EntityFrameworkCore.Design v.7.0.10
- Microsoft.EntityFrameworkCore.SqlServer v.7.0.10
- Microsoft.VisualStudio.Web.CodeGeneration.Design v.7.0.9
- Serilog.AspNetCore v.7.0.0
- Serilog.Enrichers.Environment v.2.2.0
- Serilog.Sinks.MSSqlServer v.6.3.0
Controller.Userand nothing else.