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 />有以下属性:
- name - 日志源/记录者的名字 (允许使用通配符*)
- minlevel - 该规则所匹配日志范围的最低级别
- maxlevel - 该规则所匹配日志范围的最高级别
- level - 该规则所匹配的单一日志级别
- levels - 该规则所匹配的一系列日志级别,由逗号分隔。
- writeTo - 规则匹配时日志应该被写入的一系列目标,由逗号分隔。
- 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 应用为例进行详细讲解。
- 创建示例项目:控制台执行
dotnet new mvc -n NLog.Demo
创建示例应用。 - 安装NLog 日志组件:进入项目内部,控制台执行
dotnet add package NLog.Web.AspNetCore
添加NLog.Web.AspNetCore
NuGet 包。 - 添加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" | |
} | |
] | |
} | |
} | |
- 配置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(); | |
// 省略其他代码 |
- 打印日志:修改
HomeController
中的Index
Action,添加日志: -
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(); | |
} | |
} |
- 运行项目:日志将按照上方配置输出到控制台和指定目录。
如果此时想按环境控制日志输出等级,仅需修改对应环境的配置文件即可,比如修改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日志组件的强大之处,允许开发者在仅修改配置文件的方式来丰富日志输出字段、格式,可以有效地帮助开发者记录和分析应用程序的运行情况。