ASP.NET Core使用NLog記錄日志
一、前言
在所有的應(yīng)用程序中,日志功能是不可或缺的模塊,我們可以根據(jù)日志信息進(jìn)行調(diào)試、查看產(chǎn)生的錯(cuò)誤信息,在ASP.NET Core中我們可以使用log4net或者NLog日志組件來實(shí)現(xiàn)記錄日志的功能,這里講解如何在ASP.NET Core中使用NLog。
這里采用的是.NET Core 3.1創(chuàng)建應(yīng)用程序。
那么什么是NLog呢?
NLog是一個(gè)基于.NET平臺(tái)編寫的類庫(kù),我們可以使用NLog在應(yīng)用程序中添加即為完善的跟蹤調(diào)試代碼。
NLog是一個(gè)簡(jiǎn)單靈活的.NET日志記錄類庫(kù)。通過使用NLog,我們可以在任何一種.NET語言中輸出帶有上下文的調(diào)試診斷信息,根據(jù)個(gè)人的愛好配置其輸出的樣式,然后發(fā)送到一個(gè)或多個(gè)輸出目標(biāo)(target)中。
NLog的API非常類似于log4net,且配置方式非常簡(jiǎn)單。NLog使用路由表進(jìn)行配置,這樣就讓NLog的配置文件非常容易閱讀,并便于今后維護(hù)。
NLog遵循BSD license,即允許商業(yè)應(yīng)用且完全開放源代碼。任何人都可以免費(fèi)使用并對(duì)其進(jìn)行測(cè)試,然后通過郵件列表反饋問題以及建議。
NLog支持.NET、C/C++以及COM組件,因此我們的程序、組件、包括用C++/COM編寫的遺留模塊都可以通過同一個(gè)路由引擎將信息發(fā)送至NLog中。
簡(jiǎn)單來說,NLog就是用來記錄項(xiàng)目日志的組件。
二、使用NLog
首先我們創(chuàng)建一個(gè)WebAPI的項(xiàng)目:

1、引入NLog
直接在NuGet里面搜索NLog.Web.AspNetCore,然后進(jìn)行安裝即可,如下圖所示:

安裝完成以后在依賴項(xiàng)里面就可以看到了:

修改Program類,在里面配置使用NLog,代碼如下所示:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using NLog.Web;
namespace NLogDemo
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
// 配置使用NLog
.UseNLog();
}
}2、添加配置文件
右鍵添加新建項(xiàng),然后選擇Web配置文件,命名為nlog.config如下圖所示:

nlog.config文件結(jié)構(gòu)如下:
<?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"
throwConfigExceptions="true"
internalLogLevel="info"
internalLogFile="E:\log\internal-nlog.txt">
<!--autoReload:修改后自動(dòng)加載,可能會(huì)有延遲-->
<!--throwConfigExceptions:NLog日志系統(tǒng)拋出異常-->
<!--internalLogLevel:內(nèi)部日志的級(jí)別-->
<!--internalLogFile:內(nèi)部日志保存路徑,日志的內(nèi)容大概就是NLog的版本信息,配置文件的地址等等-->
<!--輸出日志的配置,用于rules讀取-->
<targets>
<!--write logs to file-->
<!--將日志寫入文件中,fileName可以指定日志生成的路徑-->
<target xsi:type="File" name="allfile" fileName="D:\Log\nlog-all-${shortdate}.log"
layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />
<!--同樣是將文件寫入日志中,寫入的內(nèi)容有所差別,差別在layout屬性中體現(xiàn)。寫入日志的數(shù)量有差別,差別在路由邏輯中體現(xiàn)-->
<target xsi:type="File" name="ownFile-web" fileName="D:\Log\nlog-my-${shortdate}.log"
layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />
<target xsi:type="Null" name="blackhole" />
</targets>
<rules>
<!--路由順序會(huì)對(duì)日志打印產(chǎn)生影響。路由匹配邏輯為順序匹配。-->
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />
<!--Skip Microsoft logs and so log only own logs-->
<!--以Microsoft打頭的日志將進(jìn)入此路由,由于此路由沒有writeTo屬性,所有會(huì)被忽略-->
<!--且此路由設(shè)置了final,所以當(dāng)此路由被匹配到時(shí)。不會(huì)再匹配此路由下面的路由。未匹配到此路由時(shí)才會(huì)繼續(xù)匹配下一個(gè)路由-->
<logger name="Microsoft.*" minlevel="Trace" final="true" />
<!--上方已經(jīng)過濾了所有Microsoft.*的日志,所以此處的日志只會(huì)打印除Microsoft.*外的日志-->
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>添加完配置文件以后,我們還需要修改配置文件的屬性,設(shè)置為始終復(fù)制,如下圖所示:

3、在控制器中使用
通過上面的步驟,我們已經(jīng)完成NLog的配置,接下來我們就可以在控制器中使用了,通過構(gòu)造函數(shù)注入的方式實(shí)現(xiàn)注入??刂破鞔a如下:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace NLogDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class NLogTestController : ControllerBase
{
private readonly ILogger<NLogTestController> _logger;
public NLogTestController(ILogger<NLogTestController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
_logger.LogError("這是錯(cuò)誤信息");
_logger.LogDebug("這是調(diào)試信息");
_logger.LogInformation("這是提示信息");
return Ok();
}
}
}運(yùn)行程序,訪問nlogtest控制器,然后查看是否有日志生成:

我們?cè)趎log.config里面配置的文件路徑是D:\Log,從上面的截圖中看到,有日志生成了 。這里生成了兩個(gè)日志文件,這是因?yàn)槲覀冊(cè)趎log.config里面配置的日志級(jí)別不同。日志內(nèi)容如下:

可以看到,啟動(dòng)過程中的Microsoft日志也輸出了,如果不想輸出Microsoft日志,修改nlog.config里rules節(jié)點(diǎn)下面的路徑規(guī)則順序即可。
4、讀取指定位置的配置文件
上面的例子中,我們是直接在項(xiàng)目的根目錄下面添加的nlog.config文件,有時(shí)候我們想把程序里面的配置文件都放到單獨(dú)的文件夾里面,這樣方便管理,那么該如何設(shè)置讓程序讀取指定位置的nlog.config文件呢?看下面的例子。
新建一個(gè)文件夾,命名為XmlConfig,然后把nlog.config文件移到到XmlConfig文件夾下面,移到后的結(jié)構(gòu)如下圖所示:

然后修改Program文件,在程序里面設(shè)置讀取XmlConfig文件夾下面的nlog.config文件,代碼如下:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using NLog.Web;
namespace NLogDemo
{
public class Program
{
public static void Main(string[] args)
{
// 設(shè)置讀取指定位置的nlog.config文件
NLogBuilder.ConfigureNLog("XmlConfig/nlog.config");
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
// 配置使用NLog
.UseNLog();
}
}這樣就可以讀取XmlConfig文件夾下面的配置文件了。
5、封裝
上面的例子中,最后輸出的日志格式是根據(jù)nlog.config配置文件里面的layout樣式輸出的,有時(shí)候輸出的內(nèi)容可能不滿足我們的需求,我們可以對(duì)程序中的日志功能模塊進(jìn)行封裝,輸出我們自己定義的日志格式。
在解決方案中添加一個(gè)類庫(kù),命名為Nlog.Framework,然后在類庫(kù)中添加一個(gè)Log文件夾,把所有Log相關(guān)的文件都放到該文件夾下,添加后的項(xiàng)目結(jié)構(gòu)如下圖所示:

添加LogMessage類,里面是要記錄的一些信息屬性字段:
using System;
namespace Nlog.Framework.Log
{
/// <summary>
/// 日志消息
/// </summary>
public class LogMessage
{
/// <summary>
/// IP
/// </summary>
public string IpAddress { get; set; }
/// <summary>
/// 操作人
/// </summary>
public string OperationName { get; set; }
/// <summary>
/// 操作時(shí)間
/// </summary>
public DateTime OperationTime { get; set; }
/// <summary>
/// 日志信息
/// </summary>
public string LogInfo { get; set; }
/// <summary>
/// 跟蹤信息
/// </summary>
public string StackTrace { get; set; }
}
}添加一個(gè)格式化類,用來格式化日志輸出內(nèi)容:
using System.Text;
namespace Nlog.Framework.Log
{
/// <summary>
/// 格式化輸出樣式
/// </summary>
public class LogFormat
{
public static string ErrorFormat(LogMessage logMessage)
{
StringBuilder strInfo = new StringBuilder();
strInfo.Append("1. 操作時(shí)間: " + logMessage.OperationTime +" \r\n");
strInfo.Append("2. 操作人: " + logMessage.OperationName + " \r\n");
strInfo.Append("3. Ip : " + logMessage.IpAddress +"\r\n");
strInfo.Append("4. 錯(cuò)誤內(nèi)容: " + logMessage.LogInfo + "\r\n");
strInfo.Append("5. 跟蹤: " + logMessage.StackTrace + "\r\n");
strInfo.Append("-----------------------------------------------------------------------------------------------------------------------------\r\n");
return strInfo.ToString();
}
}
}這里使用依賴注入的方式,所以我們首先定義一個(gè)接口,代碼如下:
using System;
namespace Nlog.Framework.Log
{
public interface INLogHelper
{
void LogError(Exception ex);
}
}然后定義接口的實(shí)現(xiàn)類,代碼如下:
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
namespace Nlog.Framework.Log
{
public class NLogHelper : INLogHelper
{
//public static Logger logger { get; private set; }
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<NLogHelper> _logger;
public NLogHelper(IHttpContextAccessor httpContextAccessor, ILogger<NLogHelper> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public void LogError(Exception ex)
{
LogMessage logMessage = new LogMessage();
logMessage.IpAddress = _httpContextAccessor.HttpContext.Request.Host.Host;
if (ex.InnerException != null)
logMessage.LogInfo = ex.InnerException.Message;
else
logMessage.LogInfo = ex.Message;
logMessage.StackTrace = ex.StackTrace;
logMessage.OperationTime = DateTime.Now;
logMessage.OperationName = "admin";
_logger.LogError(LogFormat.ErrorFormat(logMessage));
}
}
}為了演示效果,我們添加一個(gè)全局異常過濾器,代碼如下:
using Microsoft.AspNetCore.Mvc.Filters;
using Nlog.Framework.Log;
using System.Threading.Tasks;
namespace NLogDemo.Filter
{
/// <summary>
/// 異步版本自定義全局異常過濾器
/// </summary>
public class CustomerGlobalExceptionFilterAsync : IAsyncExceptionFilter
{
private readonly INLogHelper _logHelper;
public CustomerGlobalExceptionFilterAsync(INLogHelper logHelper)
{
_logHelper = logHelper;
}
/// <summary>
/// 重新OnExceptionAsync方法
/// </summary>
/// <param name="context">異常信息</param>
/// <returns></returns>
public Task OnExceptionAsync(ExceptionContext context)
{
// 如果異常沒有被處理,則進(jìn)行處理
if (context.ExceptionHandled == false)
{
// 記錄錯(cuò)誤信息
_logHelper.LogError(context.Exception);
// 設(shè)置為true,表示異常已經(jīng)被處理了,其它捕獲異常的地方就不會(huì)再處理了
context.ExceptionHandled = true;
}
return Task.CompletedTask;
}
}
}接著添加一個(gè)控制器,在控制器里面模擬發(fā)生錯(cuò)誤的操作,代碼如下:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace NLogDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
/// <summary>
/// 日志
/// </summary>
private readonly ILogger<ValuesController> _logger;
public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Test()
{
_logger.LogError("測(cè)試封裝日志");
int i = 0;
int result = 10 / i;
return Ok();
}
}
}修改Program類:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using NLog;
using NLog.Web;
using System;
namespace NLogDemo
{
public class Program
{
public static void Main(string[] args)
{
// 讀取指定位置的配置文件
var logger = NLogBuilder.ConfigureNLog("XmlConfig/nlog.config").GetCurrentClassLogger();
try
{
logger.Info("Init Main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
logger.Error(ex, "Stopped program because of exception");
}
finally
{
LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
// 配置使用NLog
.UseNLog();
}
}最后在Startup類里面注入:
public void ConfigureServices(IServiceCollection services)
{
#region 添加異常處理過濾器
services.AddControllers(options => options.Filters.Add(typeof(CustomerGlobalExceptionFilterAsync)));
#endregion
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<INLogHelper, NLogHelper>();
// NLogHelper.LoadLogger();
services.AddControllers();
}這樣就完成了一個(gè)簡(jiǎn)單的封裝,運(yùn)行程序,訪問value控制器測(cè)試:

上面的例子中,只是封裝了Error,如果是其他級(jí)別的日志,可以自己封裝。
GitHub地址:https://github.com/jxl1024/NLog
到此這篇關(guān)于ASP.NET Core使用NLog記錄日志的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解
ABP是為新的現(xiàn)代Web應(yīng)用程序使用最佳實(shí)踐和使用最流行工具的一個(gè)起點(diǎn)。可作為一般用途的應(yīng)用程序的基礎(chǔ)框架或項(xiàng)目模板。接下來通過本文給大家詳細(xì)介紹ABP入門教程,感興趣的朋友一起看看吧2017-10-10
asp.net 簡(jiǎn)便無刷新文件上傳系統(tǒng)
之前寫過一個(gè)仿163網(wǎng)盤無刷新多文件上傳系統(tǒng),已經(jīng)對(duì)無刷新上傳文件的原理做了詳細(xì)的分析而這次的系統(tǒng)主要是針對(duì)單個(gè)file控件的,便攜版,使用更簡(jiǎn)單,還有更深入的分析2012-05-05
asp.net 自制的單選、多選列表實(shí)現(xiàn)代碼
在ASP.NET的頁面上,ListBox最終是渲染成select元素,而CheckListBox最終被渲染成div或者是table,使得二者的樣式無法統(tǒng)一,或者說要統(tǒng)一很麻煩。2009-08-08
ASP.NET 運(yùn)行時(shí)錯(cuò)誤: 沒有為擴(kuò)展名“.asax”注冊(cè)的生成提供程序修正版
ASP.NET 運(yùn)行時(shí)錯(cuò)誤: 沒有為擴(kuò)展名“.asax”注冊(cè)的生成提供程序??梢栽?machine.config 或 web.config 中的 <compilation><buildProviders> 節(jié)注冊(cè)一個(gè)。2009-01-01
讓aspx頁面自主控制調(diào)用記錄的數(shù)量,類型,隨時(shí)更改,不用重新編譯的實(shí)現(xiàn)方法
我們經(jīng)常會(huì)做一些企業(yè)站點(diǎn),為了數(shù)據(jù)調(diào)用,我們傷透腦筋,我們想方設(shè)法讓數(shù)據(jù)顯示變得簡(jiǎn)單,又易于維護(hù),這使得我們創(chuàng)造諸于模板之類的東東去搞企業(yè)站,門戶等,2011-08-08

