NLog组件

接触.net项目的同志们都清楚,最初在项目中记录日志常用的是log4net日志组件,随着.net框架的不对优化升级,最近新流行的日志框架nlog,下面我就对nlog组件说说自己的认知:

下载

通过Nuget安装NLog

 

 配置

在项目根目录下新建一个NLog.config(在Nuget包中也可以下载NLog.config包,下载默认的位置是C盘,可能和你的工程不在同一个文件夹,不建议使用),基本目录结构:targets下面配置日志输出目标及相关参数,rules下面配置目标输出规则:

复制代码
 1 <?xml version="1.0" ?>
 2 <nlog>
 3     <targets>
 4         <target></target>
 5         <target></target>
 6     </targets>
 7     <rules>
 8         <logger></logger>
 9         <logger></logger>
10     </rules>
11 </nlog>
复制代码

记得在NLog.config的属性中设置 Copy to Output Directory: Copy always,作用是每次重新生成解决方案的时候都会将改配置文件复制的本地目录,否则本地找不到配置文件无法将日志记录到文件中:

完整的配置文件如下,日志配置文件尽量单独创建一个文件:

复制代码
 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 3   <targets>
 4     <!--写入文件-->
 5     <target   xsi:type="File" name="DebugFile"   fileName="Logs\Debug\${shortdate}.log"
 6      layout="日志时间:${longdate}${newline}日志来源:${callsite}${newline}日志级别:${uppercase:${level}}${newline}消息内容:${message}${newline}异常信息:${exception}${newline}==============================================================${newline}" >
 7     </target>
 8     <target   xsi:type="File" name="InfoFile"    fileName="Logs\Info\${shortdate}.log"
 9       layout="日志时间:${longdate}${newline}日志来源:${callsite}${newline}日志级别:${uppercase:${level}}${newline}消息内容:${message}${newline}异常信息:${exception}${newline}==============================================================${newline}" >
10     </target>
11     <target  xsi:type="File"  name="ErrorFile"   fileName="Logs\Error\${shortdate}.log"
12       layout="日志时间:${longdate}${newline}日志来源:${callsite}${newline}日志级别:${uppercase:${level}}${newline}消息内容:${message}${newline}异常信息:${exception}${newline}==============================================================${newline}" >
13     </target>
14 
15     <target xsi:type="Database" name="NewDatabase" >
16       <dbProvider>System.Data.SqlClient</dbProvider>
17       <connectionString>
18         Data Source=127.0.0.1;Initial Catalog=FlightAnalysis;Persist Security Info=true;User ID=sa;Password=111111;       
19       </connectionString>
20       <commandText>
21         insert into OperatorLog(Id,AppName,ModuleName,ProcName,OperationType,Logger,LogMessage,IP,UserName,LogLevel)
22         <!--values(@Id,'','','',0,'','','','','',getdate())-->
23         values(@Id,@AppName,@ModuleName,@ProcName,@OperationType,@Logger,@LogMessage,@IP,@UserName,@LogLevel)
24       </commandText>
25       <parameter name="@Id" layout="${event-context:item=Id}" />
26       <parameter name="@AppName" layout="${event-context:item=AppName}" />
27       <parameter name="@ModuleName" layout="${event-context:item=ModuleName}" />
28       <parameter name="@ProcName" layout="${event-context:item=ProcName}" />
29       <parameter name="@OperationType" layout="${event-context:item=OperationType}" />
30       <parameter name="@Logger" layout="${event-context:item=Logger}" />
31       <parameter name="@LogMessage" layout="${event-context:item=LogMessage}" />
32       <parameter name="@IP" layout="${event-context:item=IP}" />
33       <parameter name="@Longdate" layout="${event-context:item=Longdate}" />
34       <parameter name="@UserName" layout="${event-context:item=UserName}" />
35       <parameter name="@Createdate" layout="${longdate}" />
36       <parameter name="@LogLevel" layout="${level}" />
37     </target>
38   </targets>
39 
40   <rules>
41     <!--根据日志级别分别写文件,也可以放一个文件中-->
42     <!--<logger name="DbLogger" levels="Debug,Info,Error" writeTo="MyFile" />-->
43     <logger name="MyLogger" level="Debug" writeTo="DebugFile" />
44     <logger name="MyLogger" level="Info" writeTo="InfoFile" />
45     <logger name="MyLogger" level="Error" writeTo="ErrorFile" />
46     <!--写数据库-->
47     <logger name="MyLogger" levels="Trace,Debug,Info,Error"  writeTo="NewDatabase"/>
48   </rules>
49 </nlog>
复制代码
  • 如在根节点(nlog)配置 internalLogLevel, internalLogFile,可以查看NLog输出日志时的内部信息,比如你配置文件有错误,很有帮助,不过项目发布后还是关闭比较好,以免影响效率;
  • 在target外面罩了一个 <target>并且xsi:type为 AsyncWrapper,即表示这条 target 将异步输出,这里我将文件和数据库日志异步输出;
  • db target内指定了数据库连接字符串 connectionString,SQL语句,SQL参数,还可以指定数据库/表创建和删除的脚本(推荐看NLog源码示例,这里不介绍),同时我们自定义了2个参数 action和amount;
  • target参数里有些是NLog内置参数,比如message,level,date,longdate,exception,stacktrace等,NLog在输出时会自动赋值;
  • layout设置了每条日志的格式;
  • 在rules节点,我们分别指定了三个target输出日志的级别,NLog 用于输出日志的级别包括:Trace,Debug,Info,Warn,Error,Fatal,可以设置 minlevel设置最小级别,也可以用 levels定义你所有需要的级别(多个用逗号分隔)。
  • event-context代表自定义的参数。

 数据库连接示例: https://www.cnblogs.com/weiweictgu/p/5848805.html

 

 

路由规则(Rules)

   <rules />区域定义了日志的路由规则。每一个路由表项就是一个<logger />元素。<logger />有以下属性:

  1. name - 日志源/记录者的名字 (允许使用通配符*)
  2. minlevel - 该规则所匹配日志范围的最低级别
  3. maxlevel - 该规则所匹配日志范围的最高级别
  4. level - 该规则所匹配的单一日志级别
  5. levels - 该规则所匹配的一系列日志级别,由逗号分隔。
  6. writeTo - 规则匹配时日志应该被写入的一系列目标,由逗号分隔。
  7. final - 标记当前规则为最后一个规则。其后的规则即时匹配也不会被运行

封装

对NLog.config的Logger进行简单封装:

复制代码
  1  /// <summary>
  2     /// 日志类,只提供接口
  3     /// 2018-11-6 15:32:01
  4     /// </summary>
  5     public class Logger
  6     {
  7         #region 初始化
  8         //获取指定的名称为logger。
  9         //private static NLog.Logger _dblogger = NLog.LogManager.GetLogger("MyLogger");
 10         /// <summary>
 11         /// 日事件间类
 12         /// </summary>
 13         private LogEventInfo lei = new LogEventInfo();
 14         /// <summary>
 15         /// 数据错误无法获取用户时使用
 16         /// </summary>
 17         public static string DefaultUser = "system";
 18         /// <summary>
 19         /// 默认IP地址
 20         /// </summary>
 21         public static string DefaultIP = "127.0.0.1";
 22         /// <summary>
 23         /// 提供日志接口和实用程序功能
 24         /// </summary>
 25         private  NLog.Logger _logger = null;
 26         /// <summary>
 27         /// 自定义日志对象供外部使用
 28         /// </summary>
 29         public static Logger Default { get; private set; }
 30          
 31         private Logger(NLog.Logger logger)
 32         {
 33             _logger = logger;
 34         }
 35         public Logger(string name) : this(LogManager.GetLogger(name))
 36         { }
 37        
 38         static Logger()
 39         {
 40             //获取具有当前类名称的日志程序。
 41             Default = new Logger("MyLogger");
 42         }
 43         #endregion 
 44 
 45         #region Debug
 46         public void Debug(string msg, params object[] args)
 47         {
 48             _logger.Debug(msg, args);
 49         }
 50 
 51         public void Debug(string msg, Exception err)
 52         {
 53             _logger.Debug(err, msg);
 54         }
 55         #endregion
 56 
 57         #region Info
 58         public void Info(string msg, params object[] args)
 59         {
 60             _logger.Info(msg, args);
 61         }
 62 
 63         public void Info(string msg, Exception err)
 64         {
 65             _logger.Info(err, msg);
 66         }
 67         #endregion
 68 
 69         #region Warn
 70         /// <summary>
 71         ///警告
 72         /// </summary>
 73         /// <param name="msg">警告信息</param>
 74         /// <param name="args">动态参数</param>
 75         public void Warn(string msg, params object[] args)
 76         {
 77             _logger.Warn(msg, args);
 78         }
 79         /// <summary>
 80         ///警告
 81         /// </summary>
 82         /// <param name="msg">警告信息</param>
 83         /// <param name="err">异常信息</param>
 84         public void Warn(string msg, Exception err)
 85         {
 86             _logger.Warn(err, msg);
 87         }
 88         #endregion
 89 
 90         #region Trace
 91         /// <summary>
 92         /// 使用指定的参数在跟踪级别写入诊断消息
 93         /// </summary>
 94         /// <param name="msg">跟踪信息</param>
 95         /// <param name="args">动态参数</param>
 96         public void Trace(string msg, params object[] args)
 97         {
 98             _logger.Trace(msg, args);
 99         }
100         /// <summary>
101         /// 使用指定的参数在跟踪级别写入诊断消息
102         /// </summary>
103         /// <param name="msg">跟踪信息</param>
104         /// <param name="args">异常信息</param>
105         public void Trace(string msg, Exception err)
106         {
107             _logger.Trace(err, msg);
108         }
109         #endregion
110 
111         #region Error
112         /// <summary>
113         /// 使用指定的参数在错误级别写入诊断消息。
114         /// </summary>
115         /// <param name="msg">错误信息</param>
116         /// <param name="args">动态参数</param>
117         public void Error(string msg, params object[] args)
118         {
119             _logger.Error(msg, args);
120         }
121         /// <summary>
122         /// 使用指定的参数在错误级别写入诊断消息。
123         /// </summary>
124         /// <param name="msg">错误信息</param>
125         /// <param name="args">异常信息</param>
126         public void Error(string msg, Exception err)
127         {
128             _logger.Error(err, msg);
129         }
130         #endregion
131 
132         #region Fatal
133         /// <summary>
134         /// 使用指定的参数在致命级别写入诊断消息。
135         /// </summary>
136         /// <param name="msg">致命错误</param>
137         /// <param name="args">动态参数</param>
138         public void Fatal(string msg, params object[] args)
139         {
140             _logger.Fatal(msg, args);
141         }
142         /// <summary>
143         /// 使用指定的参数在致命级别写入诊断消息。
144         /// </summary>
145         /// <param name="msg">致命错误</param>
146         /// <param name="args">异常信息</param>
147         public void Fatal(string msg, Exception err)
148         {
149             _logger.Fatal(err, msg);
150         }
151         /// <summary>
152         /// 刷新所有挂起的日志消息(在异步目标的情况下)。
153         /// </summary>
154         /// <param name="timeoutMilliseconds">最大的时间允许冲洗。此后的任何消息都将被丢弃。</param>
155         public void Flush(int? timeoutMilliseconds = null)
156         {
157             if (timeoutMilliseconds != null)
158                 NLog.LogManager.Flush(timeoutMilliseconds.Value);
159 
160             NLog.LogManager.Flush();
161         }
162         #endregion
163 
164 
165         #region Operator日志写入
166         /// <summary>
167         /// 写入日志信息
168         /// </summary>
169         /// <param name="operatorLogModel">操作信息</param>
170         public void InsOperatorLog(OperatorLogModel operatorLogModel)
171         {
172             var level = LogLevel.Info;
173             if (operatorLogModel.LogLevel == NLog.LogLevel.Trace)
174                 level = LogLevel.Trace;
175             else if (operatorLogModel.LogLevel == NLog.LogLevel.Debug)
176                 level = LogLevel.Debug;
177             else if (operatorLogModel.LogLevel == NLog.LogLevel.Info)
178                 level = LogLevel.Info;
179             else if (operatorLogModel.LogLevel == NLog.LogLevel.Warn)
180                 level = LogLevel.Warn;
181             else if (operatorLogModel.LogLevel == NLog.LogLevel.Error)
182                 level = LogLevel.Error;
183             else if (operatorLogModel.LogLevel == NLog.LogLevel.Fatal)
184                 level = LogLevel.Fatal;
185 
186             if (operatorLogModel.LogMessage.Length > 3000)
187             {
188                 operatorLogModel.LogMessage = operatorLogModel.LogMessage.Substring(0, 3000);
189             }
190             lei.Properties["Id"] = Guid.NewGuid().ToString("D");
191             lei.Properties["AppName"] = operatorLogModel.AppName;
192             lei.Properties["ModuleName"] = operatorLogModel.ModuleName;
193             lei.Properties["ProcName"] = operatorLogModel.ProcName;
194             lei.Properties["OperationType"] = operatorLogModel.OperationType;
195             lei.Properties["Logger"] = operatorLogModel.Logger;
196             lei.Properties["LogMessage"] = operatorLogModel.LogMessage;
197             lei.Properties["IP"] = operatorLogModel.IP ?? DefaultIP;
198             lei.Properties["Longdate"] = operatorLogModel.Longdate;
199             lei.Properties["UserName"] = operatorLogModel.UserName ?? DefaultUser;
200             lei.Properties["Createdate"] = operatorLogModel.Createdate;
201             lei.Level = operatorLogModel.LogLevel;
202             _logger.Log(level, lei);
203         }
204         #endregion 
205     }
复制代码

对操作类型进行简单封装,也可以自定义:

复制代码
 1 /// <summary>
 2     /// 操作类型枚举
 3     /// </summary>
 4     public enum OperationType
 5     {
 6         /// <summary>
 7         /// 保存或添加
 8         /// </summary>
 9         [System.ComponentModel.Description("添加")]
10         ADD,
11         /// <summary>
12         /// 更新
13         /// </summary>
14         [System.ComponentModel.Description("更新")]
15         UPDATE,
16         /// <summary>
17         /// 核销
18         /// </summary>
19         [System.ComponentModel.Description("核销")]
20         AUDIT,
21         /// <summary>
22         /// 查看
23         /// </summary>
24         [System.ComponentModel.Description("指派")]
25         ASSIGN,
26         /// <summary>
27         /// 删除
28         /// </summary>
29         [System.ComponentModel.Description("删除")]
30         DELETE,
31         /// <summary>
32         /// 读取/查询
33         /// </summary>
34         [System.ComponentModel.Description("查询")]
35         RETRIEVE,
36         /// <summary>
37         /// 登录
38         /// </summary>
39         [System.ComponentModel.Description("登录")]
40         LOGIN,
41         /// <summary>
42         /// 查看
43         /// </summary>
44         [System.ComponentModel.Description("查看")]
45         LOOK
46     }
复制代码

自定义类,主要用于绑定数据:

复制代码
 1 /// <summary>
 2     /// 操作日志类
 3     /// </summary>
 4     public class OperatorLogModel
 5     {
 6         /// <summary>
 7         /// 自增主键ID
 8         /// </summary>
 9         public string Id { get; set; }
10         /// <summary>
11         /// 一级菜单
12         /// </summary>
13         public string AppName { get; set; }
14         /// <summary>
15         /// 二级菜单
16         /// </summary>
17         public string ModuleName { get; set; }
18         /// <summary>
19         /// 本级菜单
20         /// </summary>
21         public string ProcName { get; set; }
22         /// <summary>
23         /// 操作类型
24         /// </summary>
25         public int OperationType { get; set; }
26         /// <summary>
27         /// 日志文件
28         /// </summary>
29         public string Logger { get; set; }
30         /// <summary>
31         /// 日志信息
32         /// </summary>
33         public string LogMessage { get; set; }
34         /// <summary>
35         /// IP地址
36         /// </summary>
37         public string IP { get; set; }
38         /// <summary>
39         /// 记录时间
40         /// </summary>
41         public string Longdate { get; set; }
42         /// <summary>
43         /// 用户名称
44         /// </summary>
45         public string UserName { get; set; }
46         /// <summary>
47         /// 日志级别
48         /// </summary>
49         public NLog.LogLevel LogLevel { get; set; }
50         /// <summary>
51         /// 创建时间
52         /// </summary>
53         public DateTime Createdate { get; set; }
54     }
复制代码

创建数据库表,字段可以自定义,此处用的是SQL Server:

复制代码
 1 CREATE TABLE [dbo].[OperatorLog](
 2     [Id] [varchar](60) NOT NULL,
 3     [AppName] [varchar](20) NOT NULL,
 4     [ModuleName] [varchar](30) NOT NULL,
 5     [ProcName] [varchar](30) NOT NULL,
 6     [OperationType] [int] NOT NULL,
 7     [Logger] [varchar](500) NOT NULL,
 8     [LogMessage] [varchar](3000) NOT NULL,
 9     [IP] [varchar](32) NOT NULL,
10     [UserName] [varchar](36) NOT NULL,
11     [Createdate] [datetime] NOT NULL,
12     [LogLevel] [varchar](12) NOT NULL,
13  CONSTRAINT [PK_OperatorLog] PRIMARY KEY CLUSTERED 
14 (
15     [Id] ASC
16 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
17 ) ON [PRIMARY]
18 
19 GO
20 
21 SET ANSI_PADDING OFF
22 GO
23 
24 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_Id]  DEFAULT ('') FOR [Id]
25 GO
26 
27 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_AppName]  DEFAULT ('') FOR [AppName]
28 GO
29 
30 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_ModuleName]  DEFAULT ('') FOR [ModuleName]
31 GO
32 
33 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_ProcName]  DEFAULT ('') FOR [ProcName]
34 GO
35 
36 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_OperationType]  DEFAULT ((0)) FOR [OperationType]
37 GO
38 
39 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_Logger]  DEFAULT ('') FOR [Logger]
40 GO
41 
42 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_LogMessage]  DEFAULT ('') FOR [LogMessage]
43 GO
44 
45 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_IP]  DEFAULT ('') FOR [IP]
46 GO
47 
48 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_UserName]  DEFAULT ('') FOR [UserName]
49 GO
50 
51 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF__OperatorL__creat__02084FDA]  DEFAULT (getdate()) FOR [Createdate]
52 GO
53 
54 ALTER TABLE [dbo].[OperatorLog] ADD  CONSTRAINT [DF_OperatorLog_LogLevel]  DEFAULT ('') FOR [LogLevel]
55 GO
56 
57 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日志表主键ID' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'Id'
58 GO
59 
60 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'一级菜单' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'AppName'
61 GO
62 
63 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'二级菜单' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'ModuleName'
64 GO
65 
66 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'本级菜单' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'ProcName'
67 GO
68 
69 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'操作类型' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'OperationType'
70 GO
71 
72 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日志文件' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'Logger'
73 GO
74 
75 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日志内容' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'LogMessage'
76 GO
77 
78 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'IP地址' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'IP'
79 GO
80 
81 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'用户名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'UserName'
82 GO
83 
84 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'创建时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'Createdate'
85 GO
86 
87 EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日志级别' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'OperatorLog', @level2type=N'COLUMN',@level2name=N'LogLevel'
88 GO
复制代码

引用日志如下:

复制代码
 1  public class Program
 2     {
 3         public static void Main(string[] args)
 4         {
 5             OperatorLogModel opModel = new OperatorLogModel();
 6             opModel.AppName = "系统管理";
 7             opModel.ModuleName = "权限管理";
 8             opModel.ProcName = "用户管理";
 9             opModel.OperationType = Convert.ToInt32(Enum.Parse(typeof(OperationType), OperationType.RETRIEVE.ToString()));
10             opModel.UserName = "ss";
11             //opModel.LogLevel = NLog.LogLevel.Trace;
12             opModel.Longdate = DateTime.Now.ToString();
13             opModel.Createdate = DateTime.Now;
14             opModel.Logger = "dfdfd";
15             opModel.LogMessage = "测试测试测试!!!";
16             Logger.Default.Error("dsfsfsfd");
17             //string ip = Request.HttpContext.Connection.RemoteIpAddress.ToString();
18 
19             //Logger.Default.Info("就是这么霸气"); 
20 
21             Logger.Default.InsOperatorLog(opModel);
22             CreateWebHostBuilder(args).Build().Run();
23         }
24 
25         public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
26             WebHost.CreateDefaultBuilder(args)
27                 .UseStartup<Startup>();
28     }
复制代码

 

NLog 日志组件的使用

那在实际使用中如何集成呢?接下来以ASP.NET Core 应用为例进行详细讲解。

  1. 创建示例项目:控制台执行dotnet new mvc -n NLog.Demo创建示例应用。
  2. 安装NLog 日志组件:进入项目内部,控制台执行dotnet add package NLog.Web.AspNetCore添加NLog.Web.AspNetCoreNuGet 包。
  3. 添加NLog 配置文件:官方提供两种方式用来添加配置,一种是添加nlog.config文件使用xml格式进行配置,一种是直接在appsettings.json文件中使用json格式进行配置,这里推荐使用json格式配置,以便和ASP.NET Core现有的配置体系对齐。在appsettings.json中添加NLog配置节点,如下所示,该配置将Info及以上级别的日志输出到控制台,将Debug及以上级别的日志输出到App_Data/Logs目录。
  {
  "Logging": {
  "LogLevel": {
  "Default": "Information",
  "Microsoft.AspNetCore": "Warning"
  }
  },
  "AllowedHosts": "*",
  "NLog": {
  "throwConfigExceptions": true,
  "variables": {
  "logDirectory": "${basedir}/App_Data/Logs"
  },
  "extensions": [
  {
  "assembly": "NLog.Web.AspNetCore"
  }
  ],
  "targets": {
  "async": true,
  "logfile": {
  "type": "File",
  "encoding": "utf-8",
  "fileName": "${logDirectory}/${shortdate}/${logger}.${level}.log"
  },
  "logconsole": {
  "type": "Console"
  }
  },
  "rules": [
  {
  "logger": "*",
  "minLevel": "Info",
  "writeTo": "logconsole"
  },
  {
  "logger": "*",
  "minLevel": "Debug",
  "writeTo": "logfile"
  }
  ]
  }
  }
   
  1. 配置NLog 日志组件:修改Program.cs如下:
  using NLog.Extensions.Logging;
  using NLog.Web;
   
  var builder = WebApplication.CreateBuilder(args);
  // Add services to the container.
  builder.Services.AddControllersWithViews();
   
  //配置从配置文件的`NLog` 节点读取配置
  var nlogConfig = builder.Configuration.GetSection("NLog");
  NLog.LogManager.Configuration = new NLogLoggingConfiguration(nlogConfig);
  //清空其他日志Providers
  builder.Logging.ClearProviders();
  //该配置用来指定使用ASP.NET Core 默认的日志过滤器
  var nlogOptions = new NLogAspNetCoreOptions() { RemoveLoggerFactoryFilter = false };
  builder.Host.UseNLog(nlogOptions); //启用NLog
   
  var app = builder.Build();
  // 省略其他代码
  1. 打印日志:修改HomeController中的IndexAction,添加日志:
  2. Nlog 实现了 Microsoft.Extensions.Logging.ILogger。首先Nuget下载添加引用:NLog.Extensions.Logging

  using System.Diagnostics;
  using Microsoft.AspNetCore.Mvc;
  using NLog.Demo.Models;
   
  namespace NLog.Demo.Controllers;
  public class HomeController : Controller
  {
  private readonly ILogger<HomeController> _logger;
  public HomeController(ILogger<HomeController> logger)
  {
  _logger = logger;
  }
  public IActionResult Index()
  {
  _logger.LogInformation("Hello {user}, this is NLog.", new { id = 1, name = "Shengjie" });
  return View();
  }
  }
  1. 运行项目:日志将按照上方配置输出到控制台和指定目录。

如果此时想按环境控制日志输出等级,仅需修改对应环境的配置文件即可,比如修改appsettings.Development.json中的Logging节点配置如下,即可输出所有以Microsoft.AspNetCore为前缀Information以上级别的日志:

  "Logging": {
  "LogLevel": {
  "Default": "Information",
  "Microsoft.AspNetCore": "Information"
  }
  }

修改后,即可输出前缀为Microsoft.AspNetCore的日志,如下所示,从中可以看出该日志是使用|分割,使用的是默认的日志布局TextLayout,配置为:${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}

  2023-03-01 10:00:38.7022|INFO|Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker|Executed action NLog.Demo.Controllers.HomeController.Index (NLog.Demo) in 94.5297ms

这种日志的好处是开发环境查看比较直观,但是因为缺失了字段信息,收集后不便分析,那如何调整为结构化的日志结构呢?简单,使用JsonLayout即可,修改NLog:targets:logconsole节点添加layout节点配置即可,如下所示:

  "targets": {
  "async": true,
  "logconsole": {
  "type": "Console",
  "layout": {
  "type": "JsonLayout",
  "attributes": [
  {
  "name": "@timestamp",
  "layout": "${date}"
  },
  {
  "name": "app",
  "layout": "${processname}"
  },
  {
  "name": "env",
  "layout": "${environment:ASPNETCORE_ENVIRONMENT}"
  },
  {
  "name": "level",
  "layout": "${level}"
  },
  {
  "name": "logger",
  "layout": "${logger}"
  },
  {
  "name": "message",
  "layout": "${message}"
  },
  {
  "name": "exception",
  "layout": "${exception:format=toString}"
  },
  {
  "name": "aspnet-request-method",
  "layout": "${aspnet-request-method}"
  },
  {
  "name": "aspnet-request-url",
  "layout": "${aspnet-request-url}"
  },
  {
  "name": "aspnet-mvc-controller",
  "layout": "${aspnet-mvc-controller}"
  },
  {
  "name": "aspnet-mvc-action",
  "layout": "${aspnet-mvc-action}"
  }
  ]
  }
  }
  }

重新运行就可以得到Json结构化的日志结构,日志输出举例如下:

  {
  "@timestamp": "2023/03/01 13:25:11.912",
  "app": "NLog.Demo",
  "env": "Development",
  "level": "Info",
  "logger": "NLog.Demo.Controllers.HomeController",
  "message": "Hello { id = 1, name = Shengjie }, this is NLog.",
  "aspnet-request-method": "GET",
  "aspnet-request-url": "https://localhost/",
  "aspnet-mvc-controller": "Home",
  "aspnet-mvc-action": "Index"
  }

其中app字段,是通过NLog预置的${processname}字段获取,env字段是通过${environment}从指定的环境变量获取,以aspnet-为前缀的字段则是通过NLog.Web.AspNetCore中预置的字段中获取,因此,在配置NLog时,要在NLog节点下加入extensions配置。

  "NLog": {
  "throwConfigExceptions": true,
  "variables": {
  "logDirectory": "${basedir}/App_Data/Logs"
  },
  "extensions": [
  {
  "assembly": "NLog.Web.AspNetCore"
  }
  ]
  }

NLog除了以上预置的字段外,还有很多其他字段,比如从配置文件读取字段,从应用读取身份信息,提取请求数据包,读取请求头,截取QueryString中的指定字段。而正是是因为这些开箱即用的预置字段,保证开发者随时按需调整日志输出的字段、格式和目标。

总结

通过以上介绍,相信你发现了NLog日志组件的强大之处,允许开发者在仅修改配置文件的方式来丰富日志输出字段、格式,可以有效地帮助开发者记录和分析应用程序的运行情况。

 

posted on 2023-08-31 17:25  大西瓜3721  阅读(28)  评论(0编辑  收藏  举报

导航