2016-07-14 35 views
6

In ASP.NET Core 1.0, ho un'implementazione personalizzata delle interfacce ILoggerProvider e ILogger. Vorrei poter accedere a HttpContext dal metodo Log.Accesso all'attuale HttpContext da un ILogger

Sembra che sia necessario iniettare un IHttpContextAccessor nell'unità personalizzata , ma non riesco a trovare come farlo. L'oggetto ILoggerProvider viene creato all'avvio e il metodo CreateLogger non consente l'iniezione delle dipendenze.

Esiste un modo semplice per utilizzare l'integrazione delle dipendenze con ILogger?

risposta

6

Ecco un esempio

Startup.cs

public void ConfigureServices(IServiceCollection services) 
    { 
     services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 
    } 

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IServiceProvider serviceProvider) 
    { 
     loggerFactory.AddCustomLogger(serviceProvider.GetService<IHttpContextAccessor>()); 
     // 
    } 

personalizzato Logger:

public class CustomLogProvider : ILoggerProvider 
{ 

    private readonly Func<string, LogLevel, bool> _filter; 
    private readonly IHttpContextAccessor _accessor; 

    public CustomLogProvider(Func<string, LogLevel, bool> filter, IHttpContextAccessor accessor) 
    { 
     _filter = filter; 
     _accessor = accessor; 
    } 

    public ILogger CreateLogger(string categoryName) 
    { 
     return new CustomLogger(categoryName, _filter, _accessor); 
    } 

    public void Dispose() 
    { 
    } 
} 

public class CustomLogger : ILogger 
{ 
    private string _categoryName; 
    private Func<string, LogLevel, bool> _filter; 
    private readonly IHttpContextAccessor _accessor; 

    public CustomLogger(string categoryName, Func<string, LogLevel, bool> filter, IHttpContextAccessor accessor) 
    { 
     _categoryName = categoryName; 
     _filter = filter; 
     _accessor = accessor; 
    } 

    public IDisposable BeginScope<TState>(TState state) 
    { 
     return null; 
    } 

    public bool IsEnabled(LogLevel logLevel) 
    { 
     return (_filter == null || _filter(_categoryName, logLevel)); 
    } 

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) 
    { 
     if (!IsEnabled(logLevel)) 
     { 
      return; 
     } 

     if (formatter == null) 
     { 
      throw new ArgumentNullException(nameof(formatter)); 
     } 

     var message = formatter(state, exception); 

     if (string.IsNullOrEmpty(message)) 
     { 
      return; 
     } 

     message = $"{ logLevel }: {message}"; 

     if (exception != null) 
     { 
      message += Environment.NewLine + Environment.NewLine + exception.ToString(); 
     } 
     if(_accessor.HttpContext != null) // you should check HttpContext 
     { 
      message += Environment.NewLine + Environment.NewLine + _accessor.HttpContext.Request.Path; 
     } 
     // your implementation 

    } 

} 
public static class CustomLoggerExtensions 
{ 
    public static ILoggerFactory AddCustomLogger(this ILoggerFactory factory, IHttpContextAccessor accessor, 
              Func<string, LogLevel, bool> filter = null) 
    { 
     factory.AddProvider(new CustomLogProvider(filter, accessor)); 
     return factory; 
    } 
} 

Anche se sopra modo in cui funziona, io preferirei attuare personalizzato IRequestLogger invece di iniettare HttpContextAccessor. L'implementazione è come qui di seguito (che non è testato):

public interface IRequestLogger<T> 
{ 
    void Log(LogLevel logLevel, EventId eventId, string message); // you can change this 
} 

public class RequestLogger<T> : IRequestLogger<T> 
{ 
    private readonly IHttpContextAccessor _accessor; 
    private readonly ILogger _logger; 
    public RequestLogger(ILogger<T> logger, IHttpContextAccessor accessor) 
    { 
     _accessor = accessor; 
     _logger = logger; 
    } 

    public void Log(LogLevel logLevel, EventId eventId, string message) 
    { 
     Func<object, Exception, string> _messageFormatter = (object state, Exception error) => 
     { 
      return state.ToString(); 
     }; 
     _logger.Log(LogLevel.Critical, 0, new FormattedLogValues(message), null, _messageFormatter); 
    } 
} 

e di utilizzo semplice:

public class LogType 
{ 

} 
public void ConfigureServices(IServiceCollection services) 
{ 
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 
    services.AddSingleton(typeof(IRequestLogger<>), typeof(RequestLogger<>)); 
} 

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) 
{ 
    loggerFactory.AddConsole(true); 
    app.Run(async (context) => 
    { 
     var requestLogger = context.RequestServices.GetService<IRequestLogger<LogType>>(); 
     requestLogger.Log(LogLevel.Critical, 11, "<message>"); 
     // 
    }); 
} 
+0

Mi dispiace, ho inviato la mia risposta e poi visto che mi battuto sul tempo. Questo è più completo in ogni caso, quindi ho votato. –

2

non ho ancora testato questo codice, ma credo che l'approccio sarà qualcosa di simile alla seguente.

Nella classe Startup.cs, registrare un HttpContextAccessor aggiungendo la seguente riga al metodo ConfigureServices:

services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 

Quindi, aggiungere un ulteriore parametro per la IHttpContextAccessor httpContextAccessor al metodo di configurazione (ancora dentro Startup.cs), qualcosa di simile :

public void Configure(
         IApplicationBuilder app, 
         IHostingEnvironment env, 
         ILoggerFactory loggerFactory, 
         IHttpContextAccessor httpContextAccessor) 

All'interno di questo metodo di Configure, è possibile aggiungere ora richiamare un metodo di estensione sulla fabbrica logger per creare il vostro log personalizzato pr ovider, qualcosa come:

loggerFactory.AddMyCustomProvider(httpContextAccessor); 

dove il metodo di estensione (che è necessario creare) sarà qualcosa di simile:

public static class MyCustomLoggerExtensions 
    { 
    public static ILoggerFactory AddMyCustomProvider(
     this ILoggerFactory factory, 
     IHttpContextAccessor httpContextAccessor) 
    { 
     factory.AddProvider(new MyCustomLoggerProvider(httpContextAccessor)); 
     return factory; 
    } 
    }