Как я могу получить текущий HttpContext в раковине SeriLog?
Я создаю свой собственный приемник SeriLog, реализующий ILogEventSink
используя пример " Построение простого приемника" с целью регистрации некоторой информации из заявлений пользователей. Чтобы получить доступ к HttpContext в Core, я обычно вставляю в экземпляр IHttpContextAccessor
но пример показывает создание экземпляра приемника в методе расширения, например
public class MySink : ILogEventSink
{
private readonly IFormatProvider _formatProvider;
public MySink(IFormatProvider formatProvider)
{
_formatProvider = formatProvider;
}
public void Emit(LogEvent logEvent)
{
// How to get HttpContext here?
}
}
public static class MySinkExtensions
{
public static LoggerConfiguration MySink(
this LoggerSinkConfiguration loggerConfiguration,
IFormatProvider formatProvider = null)
{
return loggerConfiguration.Sink(new MySink(formatProvider));
}
}
... затем использовать раковину...
var log = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.MySink()
.CreateLogger();
Как я могу получить доступ к текущему HttpContext в методе Emit приемника? Или, например, можно создать приемник в DI-фреймворке?!
У меня есть сайт MVC, на котором работает среда Asp.Net Core 2 против среды выполнения.Net 4.6.2 с использованием Serilog.AspNetCore v2.1.0.
Обновление - Обходной путь
После указателя от @tsimbalar я создал промежуточное программное обеспечение, похожее на код ниже. В моем StartUp.Configure
метод, который я добавляю с помощью app.UseMiddleware<ClaimsMiddleware>();
после выполнения шага аутентификации приложения (в противном случае заявки не будут загружены).
public class ClaimsMiddleware
{
private static readonly ILogger Log = Serilog.Log.ForContext<ClaimsMiddleware>();
private readonly RequestDelegate next;
public ClaimsMiddleware(RequestDelegate next)
{
this.next = next ?? throw new ArgumentNullException(nameof(next));
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
// Get values from claims here
var myVal = httpContext
.User
.Claims
.Where(x => x.Type == "MyVal")
.Select(x => x.Value)
.DefaultIfEmpty(string.Empty)
.SingleOrDefault();
using (LogContext.PushProperty("MyVal", myVal))
{
try
{
await next(httpContext);
}
// Never caught, because `LogException()` returns false.
catch (Exception ex) when (LogException(httpContext, ex)) { }
}
}
private static bool LogException(HttpContext httpContext, Exception ex)
{
var logForContext = Log.ForContext("StackTrace", ex.StackTrace);
logForContext.Error(ex, ex.Message);
return false;
}
}
2 ответа
ОБНОВЛЕНИЕ Я думаю, что вы можете посмотреть эту статью: http://mylifeforthecode.com/enriching-serilog-output-with-httpcontext-information-in-asp-net-core/
Идея состоит в том, чтобы зарегистрировать пользовательское промежуточное ПО, которое добавит всю контекстную информацию к текущей LogContext
во время запроса.
Чтобы он работал, вы должны настроить свой регистратор с
Log.Logger = new LoggerConfiguration()
// snip ....MinimumLevel.Debug()
.Enrich.FromLogContext()
// snip ...
.CreateLogger();
Эта статья Николаса Блумхардта также может помочь: https://blog.getseq.net/smart-logging-middleware-for-asp-net-core/
ВНИМАНИЕ - Решение ниже не работает в этом случае
Приведенное ниже решение не может работать, если регистратор зарегистрирован заранее (в Program.Main())
Прежде всего, если вы хотите добавить дополнительную информацию, прикрепленную к зарегистрированному событию, я считаю, что вам нужен Enricher.
Вы могли бы тогда:
- Зарегистрируйте IHttpContextAccessor в вашей коллекции ServiceCollection (например, используя
AddHttpContextAccessor()
):services.AddHttpContextAccessor();
- Создать реализацию
ILogEventEnricher
что принимаетIHttpContextAccessor
в своем конструкторе - При настройке вашего регистратора введите
IHttpContextAccessor
(добавив аргумент типаIHttpContextAccessor
вStartup.Configure()
- Добавьте это обогащение в ваш логгер
Расширение может выглядеть примерно так: https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/ClaimValueEnricher.cs.
И вы бы настроили свой логгер так:
var logger = new LoggerConfiguration()
.EnrichWith(new MyEnricher(contextAccessor))
.WriteTo.Whatever()
.CreateLogger();
Я изо всех сил пытался сделать то же самое, и я наконец нашел правильное решение.
Не добавляйте обогащение при создании Logger. Вам нужно будет добавить обогащение в промежуточное программное обеспечение, где вы можете получить доступ к IServiceProvider
, Ключ в том, что LogContext
есть метод, Push
, что может добавить обогащение:
public async Task Invoke(HttpContext httpContext)
{
IServiceProvider serviceProvider = httpContext.RequestServices;
using (LogContext.Push(new LogEnricher(serviceProvider))) {
await _next(httpContext);
}
}
в ConfigureServices
Я добавляю services.AddScoped<HttpContextToLog>()
вызов.
Затем я заполняю HttpContextToLog
объект в нескольких местах, доступ к нему так:
HttpContextToLog contextToLog = _serviceProvider.GetService<HttpContextToLog>();
в Enrich
метод, в IActionFilter
в IPageFilter
, так далее.