2017-11-02 5 views
5

Ich erstelle meine eigene SeriLog-Senke, die ILogEventSink unter Verwendung des Building a Simple Sink Beispiels mit der Absicht des Eintragens einiger Informationen von den Benutzeransprüchen einführt. Um Zugriff auf HttpContext in Core zu bekommen, würde ich normalerweise eine Instanz von IHttpContextAccessor injizieren, aber das Beispiel zeigt das Erstellen einer Instanz der Senke in einer Erweiterungsmethode, z.Wie kann ich den aktuellen HttpContext in einem SeriLog Sink bekommen?

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)); 
    } 
} 

... dann das Waschbecken zu benutzen ...

var log = new LoggerConfiguration() 
    .MinimumLevel.Information() 
    .WriteTo.MySink() 
    .CreateLogger(); 

Wie kann ich Zugriff auf den aktuellen Httpcontext in der Emit-Methode des Waschbeckens bekommen? Oder ist es möglich, dass die Senke zum Beispiel vom DI-Framework erstellt wird ?!

Ich habe eine MVC-Site, die Asp.Net Core 2 Framework gegen .Net 4.6.2 Runtime mit Serilog.AspNetCore v2.1.0 läuft.

Update - Umgehung

Nach dem Zeiger von @tsimbalar I erstellt Middleware ähnlich den Code unten. In meiner StartUp.Configure Methode füge ich es mit app.UseMiddleware<ClaimsMiddleware>();nach die App Authentifizierung Schritt ist passiert (sonst wird keine Ansprüche geladen werden).

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; 
    } 
} 

Antwort

2

UPDATE Ich glaube, Sie können zu diesem Artikel aussehen wollen: http://mylifeforthecode.com/enriching-serilog-output-with-httpcontext-information-in-asp-net-core/

Die Idee ist, eine eigene Middleware zu registrieren, die alle kontextuellen Informationen zum aktuellen LogContext während der hinzufügen wird anfordern.

Denn es Ihnen zu arbeiten, muss der Logger konfigurieren mit

Log.Logger = new LoggerConfiguration() 
     // snip ....MinimumLevel.Debug() 
     .Enrich.FromLogContext()     
     // snip ... 
.CreateLogger(); 

Dieser Artikel von Nicholas Blumhardt kann auch helfen: https://blog.getseq.net/smart-logging-middleware-for-asp-net-core/


WARNUNG - Lösung unten in diesem Fall nicht funktioniert

Die folgende Lösung funktioniert nicht, wenn der Logger registriert ist früh (in Program.Main())

Vor allem, wenn Sie zusätzliche Informationen an das geloggte Ereignis hinzugefügt werden möchten, glaube ich, was Sie wollen, ist ein Enricher.

Sie könnten dann:

  • Register IHttpContextAccessor in Ihre ServiceCollection (zum Beispiel unter Verwendung von AddHttpContextAccessor()): services.AddHttpContextAccessor();
  • eine Implementierung von ILogEventEnricher erstellen, die IHttpContextAccessor in seinem Konstruktor
  • akzeptiert Wenn Ihre Logger Konfiguration, inject IHttpContextAccessor (durch Hinzufügen eines Arguments vom Typ IHttpContextAccessor zu Startup.Configure()
  • Fügen Sie diese en reicher Logger

Die enricher etwas wie https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/ClaimValueEnricher.cs aussehen könnte.

und Sie würden configure Logger wie folgt aus:

var logger = new LoggerConfiguration() 
       .EnrichWith(new MyEnricher(contextAccessor)) 
       .WriteTo.Whatever() 
       .CreateLogger(); 
+0

Dank @tsimbalar. Im Schritt ** configure ** übergeben Sie den ContextAccessor in eine Instanz eines MyEnricher-Objekts. Dies verwendet keine Abhängigkeitsinjektion, also wie würde ich den Kontext bekommen, wenn ich die Protokollierung in der Programmhauptmethode konfiguriere, bevor der Startup (und anschließend 'ConfigureServices') aufgerufen wurde? –

+0

oh ja, wenn Sie in Programm Main sind, kann das nicht funktionieren .... mmmm nicht sicher, wie das funktionieren würde – tsimbalar

+0

@gavin Ich habe meine Antwort – tsimbalar

Verwandte Themen