Una delle cose fondamentali che serve per debuggare un applicazione sono i Logs. Avere un sistemi di log efficiente accorcia le tempistiche e favorisce un troubleshooting benfatto. In questo post mostro, brevemente, cosa si deve fare per utilizzare NLog a tal fine. Non voglio essere troppo noioso analizzando tutte le varie casistiche (nel caso vi consiglio questa lettura [1]) ma, voglio arrivare dritto al punto. Quello che a me serve è qualcosa che ad ogni eccezione venga correttamente loggata indipendentemente dal fatto che sia gestita e scriva in un file tutto quello che è successo.
A questo scopo installiamo i seguenti package NuGet:
Install-Package NLog.Web.AspNetCore
Install-Package NLog
Ed inseriremo nella root del progetto il seguente file config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Info"
internalLogFile="${basedir}\internal-nlog.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<variable name="basedir" value="${aspnet-appbasepath}\wwwroot\logs" />
<targets>
<target xsi:type="AsyncWrapper" name="AllAsyncWrapper" queueLimit="10000" batchSize="1000">
<target xsi:type="File"
name="allfile"
fileName="${var:basedir}\nlog-all-${shortdate}-${environment:ASPNETCORE_ENVIRONMENT}.log"
archiveFileName="${var:basedir}\archives\nlog-all-${shortdate}-${environment:ASPNETCORE_ENVIRONMENT}.archive-{#}.zip"
archiveEvery="Day"
maxArchiveDays="7"
archiveNumbering="DateAndSequence"
enableArchiveFileCompression="True"
layout="${longdate}|${aspnet-traceidentifier}|${uppercase:${level}}|${threadid}|${logger}|${message} ${exception:format=ToString}|${aspnet-request-method}|${aspnet-request-url}|${aspnet-mvc-action}|${aspnet-request-posted-body}" />
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Error" writeTo="AllAsyncWrapper" />
</rules>
</nlog>
</configuration>
Questo file fornisce le indicazioni su come comporre il file, dove metterlo come mantenerlo… Come detto non mi dilungo troppo ma vi pongo l’accento su un paio di punti:
<variable name="basedir" value="${aspnet-appbasepath}\wwwroot\logs" />
Questa riga sopra la utilizzo per definire come cartella dove salvare i files una cartella della www root, comoda se siete in una farm dove non avete controllo completo del file system. Per le Web API invece io uso questa dato che non esiste una wwwroot:
<variable name="basedir" value="${aspnet-appbasepath}\logs" />
Questa parte invece definisce tutte le proprità del file di log: da cosa deve contenere ed in che formato, alla dimensione massima, al nome, alla rotation… Insomma tutto quello che serve per meglio definire come loggare. Non dimenticate di flaggare il Copy del file nell’output.
<target xsi:type="File"
name="allfile"
fileName="${var:basedir}\nlog-all-${shortdate}-${environment:ASPNETCORE_ENVIRONMENT}.log"
archiveFileName="${var:basedir}\archives\nlog-all-${shortdate}-${environment:ASPNETCORE_ENVIRONMENT}.archive-{#}.zip"
archiveEvery="Day"
maxArchiveDays="7"
archiveNumbering="DateAndSequence"
enableArchiveFileCompression="True"
layout="${longdate}|${aspnet-traceidentifier}|${uppercase:${level}}|${threadid}|${logger}|${message} ${exception:format=ToString}|${aspnet-request-method}|${aspnet-request-url}|${aspnet-mvc-action}|${aspnet-request-posted-body}" />
Fatto questo resta un unico punto: fare in modo che NLog venga correttamente lanciato nel Program.cs
public class Program
{
public static void Main(string[] args)
{
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateWebHostBuilder(args).Build().Run();
//webhost.RunAsync();
}
catch (Exception exception)
{
//NLog: catch setup errors
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateWebHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Trace);
})
.UseNLog();
}
Un piccola nota: il codice del Main è strutturato per loggare l’errore nel caso l’inizializzazione non vada a buon fine.
A questo punto il più è fatto: lanciando l’applicazione è possibile trovare il logs nella cartella specificata:
Naturalmente è possibile utilizzare il logger in manier customizzata semplicemente iniettandolo all’interno del Controller e richiamandolo a piacere:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Home");
return View();
}
public IActionResult Privacy()
{
return View();
}
}