One possible solution is to create your own ILoggerProvider
and ILogger
implementations that act as a shim for the underlying provider and then have your ILogger.Log()
method make the actual decision to log based on the logger's category name and then modify the log level.
In my case, I assumed that all ASP.NET Core applications have logger category names that start with "Microsoft.AspNetCore" and then I change the log level from Information to Debug for these events, something like:
public class MyLogger : ILogger
{
private string categoryName;
private ILogger baseLogger;
public MyLogger(string categoryName, ILogger baseLogger)
{
this.categoryName = categoryName;
this.baseLogger = baseLogger;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (logLevel == LogLevel.Information
&& categoryName.StartsWith("Microsoft.AspNetCore"))
{
logLevel = LogLevel.Debug;
}
baseLogger.Log(logLevel, eventId, state, exception, formatter);
}
}
This is a bit of a hack but it effectively converts ASP.NET events logged as INFORMATION to DEBUG events, so they won't fill up your logs when you don't need them, but you then you can always set your log level to DEBUG to show them.
UPDATE:
The Startup.cs
file below demonstrates how to wire this up:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Logging.Debug;
namespace WebApplication1
{
public class LoggerProviderShim : ILoggerProvider
{
private ILoggerProvider baseLoggerProvider;
public LoggerProviderShim(ILoggerProvider baseLoggerProvider)
{
this.baseLoggerProvider = baseLoggerProvider;
}
public ILogger CreateLogger(string categoryName)
{
return new LoggerShim(baseLoggerProvider.CreateLogger(categoryName), categoryName);
}
public void Dispose()
{
baseLoggerProvider.Dispose();
}
}
public class LoggerShim : ILogger
{
private ILogger baseLogger;
private bool treatInfoAsDebug;
public LoggerShim(ILogger baseLogger, string categoryName)
{
this.baseLogger = baseLogger;
this.treatInfoAsDebug = categoryName != null && categoryName.StartsWith("Microsoft.AspNetCore.");
}
public IDisposable BeginScope<TState>(TState state)
{
return baseLogger.BeginScope(state);
}
public bool IsEnabled(LogLevel logLevel)
{
return baseLogger.IsEnabled(logLevel);
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (treatInfoAsDebug && logLevel == LogLevel.Information)
{
logLevel = LogLevel.Debug;
}
baseLogger.Log(logLevel, eventId, state, exception, formatter);
}
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var debugLogProvider = new DebugLoggerProvider();
loggerFactory.AddProvider(debugLogProvider);
//loggerFactory.AddProvider(new LoggerProviderShim(debugLogProvider));
app.UseMvc();
}
}
}
I've added the LoggerProviderShim
and LoggerShim
classes at the top of the file. LoggerProviderShim
is intended to wrap a specific logger provider implementation. For this example I'm wrapping a DebugLoggerProvider
. LoggerShim
is used the wrap loggers returned by the wrapped (aka real) logger provider.
The idea here is that instead of adding real ILoggerProvider
to the logger factory in Startup.Configure()
, you'll wrap the real provider in a LoggerProviderShim
and then add the shim to the logger factory.
LoggerProviderShim
works by obtaining a real ILogger
from the real logger provider and then wrapping that in a LoggerShim
and then returning the shim. The LoggerShim
constructor examines the created logger category name to decide whether it should log INFORMATION events as DEBUG. In this case, we're doing this for events that look like they're coming from an ASP.NET CORE component by looking for logger category names that come from ASP (e.g. that are prefixed by "Microsoft.AspNetCore."
The logger shim's Log()
method then changes INFORMATION loglevel to DEBUG for these loggers before calling the underlying real logger.
You can see this in action by including this code in a simple WebAPI project:
- Make sure the log levels in
appsettings.json
are set to Debug.
- Run the application and hit the controller endpoint a few times.
- In the Visual Studio DEBUG output panel, note the ASP.NET events logged as INFORMATION.
- Stop the app and in the
Startup.Configure()
method, comment out the first AddProvider()
call and uncomment the second one and test the application again.
- Now note that the ASP.NET events are logged as DEBUG. The shims transformed the event level.
You'll need to specifically wrap any of the logging providers you care about. In this example, I wrapped the DebugLogProvider
. You'd need to do the same for the Application Insights or any others separately (did I already mention that this is a bit of a hack :)