最近公司使用到了NLog来进行一个日志记录功能,了解了一下,觉得很实用,特此做个分享。
小时候看迪迦,里面一段话特别印象深刻,“努力活完短暂的一生,将成果留给后代继承,人类就是如此反复,真的很了不起!”这些开源软件就是最好的证明,前人帮我们造好了一个又一个轮子,我们需要好好的使用,为往圣继绝学。
1.在需要使用的项目点击鼠标右键
2.点击浏览->输入名字->选择如图所示的程序包(这恐怖的下载量)->点击安装(教程写的日期是2022/12/01,NLog最新版就是5.1.0)
3.点击确定
4.在项目的引用中可以看到NLog这个dll就算安装成功了。
NLog将自动在某些默认位置中搜索其配置文件。当NLog和标准的exe文件配合使用时,将自动按照顺序搜索下列路径,以得到配置文件:
1.应用程序的标准配置文件(通常为applicationname.exe.config)
2.应用程序所在目录中的applicationname.exe.nlog文件
3.应用程序所在目录中的NLog.config文件
4.NLog.dll所在目录中的NLog.dll.nlog文件
5.环境变量NLOG_GLOBAL_CONFIG_FILE所指向的文件
1.可以写在项目中的App.config文件中。使用标准化的configSection机制
2.可以单独写一个配置文件负责日志记录(取名NLog)
推荐还是单独写一个配置文件,毕竟健壮性更好。
<?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" >
<targets>
<!--输出目标:name名称f,xsi:type输出类型文件, fileName输出到程序根目录logs文件夹中, 以日期作为生成log文件名称, layout生成内容的格式-->
<target name="f"
xsi:type="File"
fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
</targets>
<rules>
<!--日志路由规则:最低级别Debug,输出到target目标f-->
<logger name="*" minlevel="Debug" writeTo="f" />
</rules>
</nlog>
using NLog;
using NLog.Config;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
//创建日志记录对象
private Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public Form1()
{
InitializeComponent();
//初始化配置日志
LogManager.Configuration = new XmlLoggingConfiguration(string.Format("{0}/NLog.config", AppDomain.CurrentDomain.BaseDirectory.ToString()));
//打出日志
Logger.Debug("日志成功打出!--lili");
}
private void button1_Click(object sender, EventArgs e)
{
Logger.Debug("点击按钮1!--lili");
}
private void button2_Click(object sender, EventArgs e)
{
Logger.Debug("点击按钮2!--lili");
}
}
}
NLog如果只是可以做到上文那样的日志管理,也就名不副实了。上文只是它强大功能的冰山一角,我抛砖引玉,介绍一下我所了解到的其他功能。
配置文件大致分为 全局配置、根元素、targets定义日志的输出目标、rules定义路由规则
我把常用的属性功能都进行了翻译,也深知大家都喜欢拿来主义,代码附上
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwExceptions="false"
throwConfigExceptions="true"
>
其中最重要也是最关键的属性就是layout
layout中属性真的很全面,基本上所有你想的到可能需要记录的信息,都有对应的属性,这里我只选择了一些我需要或者我觉得以后可能需要的属性进行了备注。(其他详细属性信息可以去官网查看)
layout="${longdate} ${uppercase:${level}} ${message} ${onexception:inner=${exception:format=message} ${newline} ${stacktrace}}"
本文中layout语句的含义是:正常情况下只记录日志的时间 等级和消息,一旦在记录消息时候传入了Exception参数,就额外记录报错信息和当时的程序调用栈信息。
效果如下图
代码附上
<target name="f"
xsi:type="File"
fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message} ${onexception:inner=${exception:format=message} ${newline} ${stacktrace}}"
maxArchiveFiles="999"
archiveFileName="${basedir}/archives/${logger}-${level}-${shortdate}-{#####}.txt"
createDirs="true"
archiveAboveSize="102400"
archiveEvery="Day"
keepFileOpen="true"
openFileCacheTimeout="30"
autoFlush="false"
openFileFlushTimeout="10"
concurrentWrites="true"
encoding="UTF-8">
</target>
本文中使用到的路由规则比较简单,按照个人需求是可以设置的比较复杂的。
例如:
<logger name="Name.Space.Class1" writeTo="target2" />
<logger name="Name.Space.*" writeTo="target1" />
NameSpace.Class1 记录的信息 往target2 + target1 都写。
其他的logger记录的信息 只往target1 里写。
这时候如果在第一条rule里加上 final=true,那么NameSpace.Class1 记录的信息就只往target2里写了。
其他的logger记录的信息 依旧往target1 里写。
<logger name="Name.Space.Class2" maxlevel="Warn" final="true" />
<logger name="Name.Space.*" writeTo="target1" />
所有的日志信息都会往target1 里写,但是Class2的只会记录 <=Warn等级的消息
<logger name="Name.Space.Class3" maxlevel="Off" final="true" />
<logger name="Name.Space.*" writeTo="target1" />
Class3啥都写不出去
其他的logger 写到target1里
如果以上例子还满足不了个人的需求的话,NLog还提供了日志过滤器 filters
例如
<logger name="*" minlevel="Debug" maxlevel="Error" writeTo="f">
<filters>
//忽略消息长度>100的日志
<when condition="length('${message}') > 100" action="Ignore" />
//过滤在10:00和12:59之间记录的日志
<hourRange fromHour="10" toHour="12" action="Ignore"/>
</filters>
</logger>
如果需要使用日志管理系统,为了尽可能的不浪费资源,除了在配置文件的属性中做好对应的设置,推荐一下几种做法。
大多数应该都能够想到,新建 NLog.Logger 对象肯定消耗一定的开销,所以用单例模式,只创建一个对象就可以了。
将NLog的方法进行简单的封装,避免直接调用。以及别忘记资源的释放。本人写的简单封装如下,抛砖引玉
using NLog;
using NLog.Config;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WindowsFormsApp1
{
public sealed class LogHelper
{
private static Logger logger = LogManager.GetCurrentClassLogger();
//单例模式
private static readonly LogHelper instance = new LogHelper();
private LogHelper()
{
//初始化配置日志
LogManager.Configuration = new XmlLoggingConfiguration(string.Format("{0}/NLog.config", AppDomain.CurrentDomain.BaseDirectory.ToString()));
}
///
/// 日志等级
///
public enum LogLevel
{
///
/// 最常见的记录信息,一般用于普通输出
///
Trace = 0,
///
/// 记录信息,,一般用来调试程序
///
Debug,
///
/// 信息类型的消息
///
Info,
///
/// 警告信息
///
Warn,
///
/// 错误信息
///
Error,
///
/// 致命异常信息
///
Fatal
}
///
/// 添加日志
///
/// 日志信息
/// 日志等级
public static void AddLogger(string message, LogLevel logLevel = LogLevel.Debug)
{
switch (logLevel)
{
case LogLevel.Trace:
logger.Trace(message);
break;
case LogLevel.Debug:
logger.Debug(message);
break;
case LogLevel.Info:
logger.Info(message);
break;
case LogLevel.Warn:
logger.Warn(message);
break;
case LogLevel.Error:
logger.Error(message);
break;
case LogLevel.Fatal:
logger.Fatal(message);
break;
default:
logger.Debug(message);
break;
}
}
///
/// 添加日志(catch到的Exception建议使用此方法)
///
/// 错误信息
/// 日志等级
public static void AddLogger(Exception exception, LogLevel logLevel = LogLevel.Error)
{
switch (logLevel)
{
case LogLevel.Trace:
logger.Trace(exception);
break;
case LogLevel.Debug:
logger.Debug(exception);
break;
case LogLevel.Info:
logger.Info(exception);
break;
case LogLevel.Warn:
logger.Warn(exception);
break;
case LogLevel.Error:
logger.Error(exception);
break;
case LogLevel.Fatal:
logger.Fatal(exception);
break;
default:
logger.Error(exception);
break;
}
}
///
/// 添加日志(catch到的Exception建议使用此方法)
///
/// 错误信息
/// 错误描述
/// 日志等级
public static void AddLogger(Exception exception, string message, LogLevel logLevel = LogLevel.Error)
{
switch (logLevel)
{
case LogLevel.Trace:
logger.Trace(exception, message);
break;
case LogLevel.Debug:
logger.Debug(exception, message);
break;
case LogLevel.Info:
logger.Info(exception, message);
break;
case LogLevel.Warn:
logger.Warn(exception, message);
break;
case LogLevel.Error:
logger.Error(exception, message);
break;
case LogLevel.Fatal:
logger.Fatal(exception, message);
break;
default:
logger.Error(exception, message);
break;
}
}
///
/// 释放日志资源
///
public static void Dispose()
{
NLog.LogManager.Shutdown();
}
}
}
可以将异常对象Exception ex直接传递给 NLog.Logger,这和传ex.Message+ex.StackTrace是不同的效果,区别如下图,各有优劣。(但是大型软件的ex.StackTrace真的是太长了)
因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激。