Rather than coupling your logging code with ASP.NET a more robust solution could be just pushing any "additional properties" into some global context, like Serilog does with enrichers. Docs
This code's a bit rough & ready but hopefully puts the point across around how you might achieve this:
Logger logger = new();
string url = "..."; // Pulled from HttpContext
// With properties:
using (LogContext.PushProperty("Url", url))
{
logger.LogException(new Exception("Failed (First)!"));
}
// Without properties:
logger.LogException(new Exception("Failed (First)!"));
public sealed class Logger
{
public void LogException(Exception e)
{
var properties = new Dictionary<string, object>
{
{"Type", e.GetType().ToString()},
{"Message", e.Message},
{"StackTrace", e.StackTrace}
};
foreach (var property in LogContext.GetProperties())
{
properties.Add(property.Key, property.Value);
}
// Write somewhere, .e.g.
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(properties));
}
}
public static class LogContext
{
private static readonly List<Property> _properties = new();
public static Property PushProperty(string key, object value)
{
var property = new Property { Key = key, Value = value };
_properties.Add(property);
return property;
}
public static IEnumerable<Property> GetProperties()
{
foreach (var property in _properties)
{
yield return property;
}
}
public class Property : IDisposable
{
public required string Key { get; init; }
public required object Value { get; init; }
public void Dispose()
{
_properties.Remove(this);
}
}
}
If you're familiar with ASP.NET middleware you could create one that pushes all the relevant ASP.NET context properties into LogContext
by wrapping the await next.Invoke();
in a using
scope like I'm doing above:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-9.0