ASP.NET Core Logging
2024-07-01
筆記 ASP.NET Core Logging 的基本要點。
Logging
We can use primary constructor to inject ILogger
[Route("api/[controller]")]
public class LogController(ILogger<LogController> _logger) : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
var message = "Get Method Invoked";
_logger.LogInformation(message);
return Ok(message);
}
}
Kestrel Log will be console window.
info is Level, Controllers.LogController is Category, Get Method Invoked is Message, [0] is EventId.
IIS Express will log will be output window.
Debug Model will log will be Debug output window.
Log Level
Log Level | Description |
---|---|
5 | Critical |
4 | Error |
3 | Warning |
2 | Information |
1 | Debug |
0 | Trace |
The default log level is Information, if we want to change it, we can do it in appsettings.json
(in development environment, we should cahnge appsettings.Development.json
).
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft.AspNetCore": "Warning"
}
}
}
[HttpGet("level")]
public IActionResult Level()
{
var message = "GetLogLevel Method Invoked";
_logger.LogCritical(message);
_logger.LogError(message);
_logger.LogWarning(message);
_logger.LogInformation(message);
_logger.LogDebug(message);
_logger.LogTrace(message);
return Ok(message);
}
We can custom message with multiple parameters.
var message = "GetLogLevel Method Invoked {}";
_logger.LogCritical(message, Request.Path);
Log Category
Instead of ILogger
public class LogController(ILoggerFactory factory) : ControllerBase
{
readonly ILogger _logger = factory.CreateLogger("MyLog");
...
}
With LogFactory to create logger, then we can use log filter to filter log messages in program.cs
builder.Logging.AddFilter((category, level) =>
{
return category == "MyLog" && level >= LogLevel.Warning;
});
💡 However the best practice is to set log level in appsettings.json
or appsettings.Development.json
, by the logger name.
We disable Default log level, and set MyLog log level to Warning.
{
"Logging": {
"LogLevel": {
"MyLog": "Warning",
"Default": "None",
"Microsoft.AspNetCore": "Warning"
}
}
}
We even can use namespace as category to filter log messages 😉
readonly ILogger _logger = factory.CreateLogger("MyNamespace.MyClass");
{
"Logging": {
"LogLevel": {
"MyNamespace.MyClass": "Warning",
"Default": "None",
}
}
}
Log to File
We can not log to file directly, but we can log to file by using NLog
library.
Getting started with ASP.NET Core 6
<ItemGroup>
<PackageReference Include="NLog.Web.AspNetCore" Version="5.*" />
<PackageReference Include="NLog" Version="5.*" />
</ItemGroup>
And then add a nlog.config
file to the root of the project.
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Info"
internalLogFile="c:\temp\internal-nlog-AspNetCore.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- File Target for all log messages with basic details -->
<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />
<!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />
<!--Output hosting lifetime messages to console target for faster startup detection -->
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />
<!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) -->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<logger name="System.Net.Http.*" maxlevel="Info" final="true" />
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
The default log path will be "c:\temp".
program.cs
builder.Host.UseNLog(new NLogAspNetCoreOptions() { RemoveLoggerFactoryFilter = false });
Factory Pattern (Multiple Logger)
We can use factory pattern to create multiple loggers.
public class LoggerFactory
{
private readonly IServiceProvider _serviceProvider;
public LoggerFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ICustomLogger CreateLogger(string loggerType)
{
switch (loggerType.ToLower())
{
case "console":
return _serviceProvider.GetRequiredService<ConsoleCustomLogger>();
case "debug":
return _serviceProvider.GetRequiredService<DebugCustomLogger>();
default:
throw new ArgumentException("Invalid logger type", nameof(loggerType));
}
}
}
program.cs
builder.Services.AddTransient<ConsoleCustomLogger>();
builder.Services.AddTransient<DebugCustomLogger>();
builder.Services.AddTransient<LoggerFactory>();