NLog日志框架使用探究-2

目录

  • 前言
  • 自定义参数
  • 日志输出方式
    • 文件
    • 网络传输
    • 数据库
  • 科学使用
  • 参考文档


前言

在一年前,我写过一篇关于NLog入门文章《NLog日志框架使用探究-1》,文章简单的介绍了Nlog的基本使用以及如何使用Log4View2工具配合统一收集日志查看。本篇文章会记录一些NLog常用的用法。

自定义参数

有时候我们需要根据我们的业务特征自定义一些参数。比如有个唯一的Id。这时候我们可以自定义参数,将Id提取出来,而不是放到日志内容中,这样可以方便检索。
EventProperties Layout Renderer文档中,支持自定义EventPropertie动态的渲染到Layout中。
Nlog已经有一些自定义的参数了,如${counter}${longdate}${message:format=message}等。
自定义的参数使用格式为${event-properties:item=String:culture=String:format=String}

官方示例如下,代码中定义了四种Property

...
Logger logger = LogManager.GetCurrentClassLogger();
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Pass my custom value");
theEvent.Properties["MyValue"] = "My custom string";
theEvent.Properties["MyDateTimeValue"] = new DateTime(2015, 08, 30, 11, 26, 50);
theEvent.Properties["MyDateTimeValueWithCulture"] = new DateTime(2015, 08, 30, 11, 26, 50);
theEvent.Properties["MyDateTimeValueWithCultureAndFormat"] = new DateTime(2015, 08, 30, 11, 26, 50);
logger.Log(theEvent);
...

在配置文件中可以通过${event-properties:item=String:culture=String:format=String}获取到

${event-properties:item=MyValue} -- renders "My custom string"
${event-properties:MyDateTimeValue:format=yyyy-M-dd}"; -- renders "2015-8-30"
${event-properties:MyDateTimeValueWithCulture:culture=en-US} -- renders "8/30/2015 11:26:50 AM"
${event-properties:MyDateTimeValueWithCultureAndFormat:format=yyyy-M-dd HH:mm:ss:culture=en-US} -- renders "2015-8-30 11:26:50"

从上可以看出,若我们需要自定义参数,我们需要创建LogEventInfo对象,并通过Log()方法记录LogEventInfo对象

调用 Info()等方法内部实际也是创建了LogEventInfo对象,最终还是调用Log()方法。

下面我们可以自己的代码测试一下。

修改上面的Json输出的配置,由于Memo参含有中文,因此需要将encode设置为false,防止被编码为Unicode。以下为Nlog相关源码

protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength)
{
    Inner.RenderAppendBuilder(logEvent, builder);
    if (JsonEncode && builder.Length > orgLength)
    {
        if (RequiresJsonEncode(builder, orgLength))
        {
            var str = builder.ToString(orgLength, builder.Length - orgLength);
            builder.Length = orgLength;
            Targets.DefaultJsonSerializer.AppendStringEscape(builder, str, EscapeUnicode);
        }
    }
}
private bool RequiresJsonEncode(StringBuilder target, int startPos = 0)
{
    for (int i = startPos; i < target.Length; ++i)
    {
        if (Targets.DefaultJsonSerializer.RequiresJsonEscape(target[i], EscapeUnicode))
        {
            return true;
        }
    }
    return false;
}

在上一篇文章中有同学提问,当encode设置为false时,输出的内容不会有双引号。查阅了下一源码,确实如此。具体为什么这样设计不是很理解,有知道的同学可以说明一下。

public class JsonAttribute
{
    ...
    public bool Encode
    {
        get => LayoutWrapper.JsonEncode;
        set => LayoutWrapper.JsonEncode = value;
    }
    ...
}
private bool RenderAppendJsonPropertyValue(JsonAttribute attrib, LogEventInfo logEvent, StringBuilder sb, bool beginJsonMessage)
{
    BeginJsonProperty(sb, attrib.Name, beginJsonMessage);
    if (attrib.Encode)
    {
        // "\"{0}\":{1}\"{2}\""
        sb.Append('"');
    }
    ...
    if (attrib.Encode)
    {
        sb.Append('"');
    }
    return true;
}

接下来在nlog.Config的target配置中添加以下配置

<layout xsi:type="JsonLayout" >
    <attribute name="counter" layout="${counter}" />
    <attribute name="time" layout="${longdate}" />
    <attribute name="level" layout="${level:upperCase=true}"/>
    <attribute name="message" layout="${message:format=message}"  encode="false" />
    <attribute name="Id" layout="${event-properties:item=Id}" />
    <attribute name="No" layout="${event-properties:item=No}" />
    <attribute name="Memo" layout="${event-properties:item=Memo}"  encode="false" />
</layout>

代码对应的Properites

LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "自定义动态参数");
theEvent.Properties["Id"] = Guid.NewGuid();
theEvent.Properties["No"] = "1";
theEvent.Properties["Memo"] = "备注";
logger.Log(theEvent);

输出如下图

需要注意若我们自定义参数,使用json格式写入到文件,则代码中的Properties的Key必须和配置文件的${event-properties:item=key}中的key大小写一致。

日志输出方式

文件

当我们日志需要以文件存放时,通常情况需要根据服务名、模块名等区分日志目录,同样可以通过自定义参数输出。

假设我们需要将日志按服务分目录,同时日志文件名含有我们指定的内容前缀,在每一条日志中需要记录一个唯一的编号。日志配置如下

...
<targets>
<target xsi:type="File"
        name="InfoFile"
        fileName="${basedir}/logs/${event-properties:item=ServiceName}/${logger:shortName=true}/${shortdate}/${event-properties:item=LogPrefix}_log.txt"
        encoding="utf-8">
    <layout xsi:type="JsonLayout" >
        <attribute name="counter" layout="${counter}" />
        <attribute name="time" layout="${longdate}" />
        <attribute name="level" layout="${level:upperCase=true}"/>
        <attribute name="UniqueNo" layout="${event-properties:item=UniqueNo}" />
        <attribute name="Message" layout="${message:format=message}"  encode="false" />
    </layout>
</target>
</targets>
<rules>
    <logger name="*" maxlevel="Info" writeTo="InfoFile" />
</rules>

先看下记录下来的日志,圈出来的都是我们自定义生成的值。

  1. ${basedir}是程序的运行目录
  2. ${logger:shortName=true}是代码中指定的程序名名称,在代码中可以通过NLog.LogManager.GetLogger("test")指定日志名或NLog.LogManager.GetCurrentClassLogger()指向当前类的全名(包括命名空间)。
  3. ${shortdate}是短日期格式,Nlog也内置了${date}获取完整的时间,前面我们说过了可以通过${event-properties:item=String:culture=String:format=String}自定义格式,这里也可以通过${date:format=String}自定义日期格式,比如${date:format=yyyyMMdd}输出的就是如20191201的日期格式。

网络传输

上一章我们提到,日志通过网络发送到Log4View2等工具统一汇总。

<targets async="true">
    <target xsi:type="Network" address="udp://127.0.0.1:878" name="network" newLine="false" maxMessageSize="65000" encoding="gbk" layout="${log4jxmlevent:includeCallSite=true:includeNLogData=true}"/>
</targets>

<rules>
    <logger name="*" minlevel="Info" writeTo="network" />
    ...
</rules>

Nlog支持tcp或udp协议进行网络传输。

  • 通过xsi:type="Network"指定网络传输
  • 通过address="协议://ip:端口"指定协议和地址。
  • 通过layout="${log4jxmlevent:includeCallSite=true:includeNLogData=true}序列化为XML传输,当然我们也可以传输自定义的格式或Json格式。只要在目标端使用对应的格式解析即可。

下面还是通过Xml序列化传输到Log4View2为例。当我们代码中自定义的字段序列化成Xml发送到对端。我们可以在Log4View2界面上的列右键选择Show Column Chooser项选择哪些字段显示,非常方便。

还支持筛选列,方便我们查找。

数据库

一般情况下日志也不需要实时查看,通常都是排查问题的时候需要看,因此有时候我们可以希望先将日志统一汇总后在做日志分析等工作。Nlog支持将数据插入数据库,比如我将日志入库到Oracle数据库中,使用Oracle.ManagedDataAccess。我们可以通过nuget安装库包Install-Package Oracle.ManagedDataAccess

<target xsi:type="Database"
              name="DB45"
              dbProvider="Oracle.ManagedDataAccess.Client.OracleConnection, Oracle.ManagedDataAccess"
              keepConnection="true" optimizeBufferReuse="true"
              connectionString="Data Source=10.60.45.239/devdb;user id=fgmain10001;password=test1"
              commandText="insert into NETWORKLAYERLOG (TIME, LOGLEVEL, APPID, LOGGER,IDENTITY, REQUESTID, MESSAGE,EXCEPTION)
              values (to_timestamp(:TIME,'YYYY-MM-DD HH24:MI:SS.FF'), :LOGLEVEL, :APPID, :LOGGER,:IDENTITY, :REQUESTID, :MESSAGE,:EXCEPTION)">
        <parameter name=":TIME" layout="${longdate}" />
        <parameter name=":LOGLEVEL" layout="${level}" />
        <parameter name=":APPID" layout="${event-properties:item=AppId}" />
        <parameter name=":LOGGER" layout="${logger}" />
        <parameter name=":IDENTITYID" layout="${event-properties:item=IdentityId}" />
        <parameter name=":REQUESTID" layout="${event-properties:item=RequestId}" />
        <parameter name=":MESSAGE" layout="${message}" />
        <parameter name=":EXCEPTION" layout="${exception:format=toString,Data}" />
      </target>
  • dbProvider:首先配置dbProvider。
  • keepConnection:表示是否需要保持连接。
  • optimizeBufferReuse:表示是否是否使用连接池。
  • connectionString:入库的语句。
  • connectionString:连接字符串。
  • commandText:sql语句,sql语句支持参数化。
  • <parameter name="ColumnName" layout="Value" />:参数,name为参数名,layout为参数值。

在Log4View2可以将数据源指向日志所在的表。选择数据库接收器。

填写相关配置后,选择表。

选择表的时候需要选择一个Key,但并不是所有Key都是可选的。

从Log4View2源码看,Key需要满足是id结尾时类型是数值或时间类型或者列设置了自增。

this.IsKey = ((this.Name.ToLowerInvariant().EndsWith("id") && DbMessageKey.IsValidKeyType(this.Type)) || column.AutoIncrement);
public static bool IsValidKeyType(Type type)
{
    return type == typeof(int) || type == typeof(uint) || type == typeof(long) || type == typeof(ulong) || type == typeof(decimal) || type == typeof(DateTime);
}

这个key是用于时间筛选的。在日志配置中也可以设置获取指定的日志等级,或者,我们可以指定一个列作为时间筛选条件。

看下Log4View2的源码,在DBReceiver初始化的时候会把我们选择的Key传入赋值给_dbMessageKey


public DbReceiver(IReceiverFactory factory, ReceiverConfig recRow) : base(factory, recRow)
{
    ...
    this._columns = new DbColumns(dbReceiverConfig.DbColumns);
    this._dbMessageKey = this._columns.Key;
    ...
}

在初始化查询sql的时候就会用到该值

private DbCommand CreateReadQuery()
{
    ...
    DbCommand dbCommand = this._database.CreateCommand();
    dbCommand.CommandTimeout = this._commandTimeout;
    string parameterName = this._database.GetParameterName(0);
    string arg = this._dbMessageKey.IsUnique ? ">" : ">=";
    string text = this._database.QuoteName(this._dbMessageKey.Name);
    string tableName = this._database.QuoteName(this._tableName);
    DbParameter dbParameter = dbCommand.CreateParameter();
    dbParameter.ParameterName = parameterName;
    dbParameter.DbType = this._dbMessageKey.DbType;
    dbParameter.Value = this._dbMessageKey.ParameterValue;
    dbCommand.Parameters.Add(dbParameter);
    string text2 = string.Format("WHERE ({0} {2} {1})", text, parameterName, arg);
    ...
    return dbCommand;
}

科学使用

Log4View2工具首次安装使用有30天的试用期,试用期过了一些功能就会被限制。下一章我会讲解如何是使用反编译工具科(po)学(jie)使用Log4View2。


参考文档

  1. Event Context Layout Renderer
  2. EventProperties Layout Renderer
  3. Log4ViewHelp
  4. DatabaseTarget.DBProvider Property
  5. Database target


微信扫一扫二维码关注订阅号杰哥技术分享
出处:本文地址:https://www.cnblogs.com/Jack-Blog/p/11972400.html
作者:杰哥很忙
本文使用「CC BY 4.0」创作共享协议。欢迎转载,请在明显位置给出出处及链接。

原文地址:https://www.cnblogs.com/Jack-Blog/p/11972400.html

时间: 2024-10-09 10:49:06

NLog日志框架使用探究-2的相关文章

NLog日志框架使用探究

前言 为什么是NLog? 目的 配置 基本配置 日志等级 输出例子 目标 文件输出 Json格式保存 多目标 参数 规则 日志分发 日志收集 结语 参考文档 前言 日志是每个程序的基本模块.本文是为了探究如何通过NLog方便及记录日志并通过Log4View工具收集日志统一查看. 为什么是NLog? 下载量NLog和Log4Net差不多,这两个日志模块是.Net平台使用最多的两大日志模块. Log4Net上次更新已经是17年3月 NLog更新的比较频繁,开发者比较活跃,有问题的话修复更及时. NL

Nlog 日志框架简单教程

安装 Nuget获取 配置寻找 会自动寻找在应用程序目录下的NLog.config(大小写敏感) 如何配置config <?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

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

一.回顾系统进度以及本章概要 目前博客系统已经数据库创建.以及依赖注入Autofac集成,接下来就是日志和缓存集成,这里日志用的是Nlog,其实还有其他的日志框架如log4,这些博客园都有很多介绍,这里就不说了,缓存机制用的是微软自带的MemoryCache和比较流行Redis,这里我也只是了解使用,没有做更升入的研究,以后好好学一下Redis,然后就是实现一个BaseController父类用来重写JsonResult方法为的是返回时间格式问题,默认json返回的时间格式是Date(84923

一个简单好用的日志框架NLog

之前我介绍过如何使用log4net来记录日志,但最近喜欢上了另一个简单好用的日志框架NLog. 关于NLog和log4net的比较这里就不多讨论了,感兴趣的朋友可以参看.NET日志工具介绍和log4net vs. Nlog这两篇文章.本文主要介绍一下如何在项目中使用NLog. 在Nuget中安装NLog NLog可以直接使用Nuget安装: PM > Install-Package Nlog 使用NLog NLog的使用方式基本上和其它的Log库差不多,分为Trace.Debug.Info.Er

C/C++/C#/Python日志框架

俗话说,打得一手好log才是一个优秀的程序员. **打log的目的是为了迅速排错或在有争议时拿出证据证明自己.基于这个目的,log不在多,只要抓住一切对自己有利的信息,就可以了.** 日志框架列表 C/C++      spdlog  只需要引用头文件就可以了 C#      NLog  第三方库,性能比log4net好,支持跨平台 python       logging  自带模块

解读ASP.NET 5 &amp; MVC6系列(9):日志框架

原文:解读ASP.NET 5 & MVC6系列(9):日志框架 框架介绍 在之前的.NET中,微软还没有提供过像样的日志框架,目前能用的一些框架比如Log4Net.NLog.CommonLogging使用起来多多少少都有些费劲,和java的SLF4J根本无法相比.但在新版的ASP.NET5中,可谓是牛气冲天,微软提供的Microsoft.Framework.Logging框架集可谓就是.NET版的SLF4J,提供相应的接口,其它第三方组件可以根据接口实现自己的实现. ILoggerFactory

.NET开源分布式日志框架ExceptionLess实战演练(公开版)

一.课程介绍 在以前,我们做日志收集大多使用 Log4net,Nlog 等框架,在应用程序变得复杂并且集群的时候,可能传统的方式已经不是很好的适用了,因为收集各个日志并且分析他们将变得麻烦而且浪费时间.相信大家的项目中日志功能已经做为基础设施里必不可少的一部分了,日志记录不仅可以更好的记录用户行为,还可以记录系统运行日志,从而看到判断系统运行的健壮性.了解决实时日志监控问题,ELK提供的一套的解决方案就应运而生了 ,作为NET技术的我们,开源的轻量级分布式ExceptionLess 日志框架或许

开源日志框架Exceptionless使用教程

原文:开源日志框架Exceptionless使用教程 Exceptionless是一款日志记录框架,它开源.免费.提供管理界面.易于安装和使用.ExceptionLess底层采用ElasticSearch作为日志存储,提供了快速.丰富的查询API,方便我们进行系统集成.本文将介绍ExceptionLess的常见用法. 安装ExceptionLess 在ExceptionLess官网提供了基于Docker的私有化部署方式,我们可以按照官网的方式进行测试环境的安装. 在官网github中下载最新的r

Java 日志框架终极教程

概述 对于现代的 Java 应用程序来说,只要被部署到真实的生产环境,其日志的重要性就是不言而喻的,很难想象没有任何日志记录功能的应用程序被运行于生产环境中.日志 API 所能提供的功能是多种多样的,包括记录程序运行时产生的错误信息.状态信息.调试信息和执行时间信息等.在生产环境中,日志是查找问题来源的重要依据,应用程序运行时的产生的各种重要信息,都应该通过日志 API 来进行记录. 很多Java开发人员习惯于使用 System.out.println.System.err.println 以及