首页 > 其他分享 >NetCore 之 log4net 实战

NetCore 之 log4net 实战

时间:2023-09-13 14:11:27浏览次数:73  
标签:log4net 实战 NetCore LoggerProperty public logger loggingEvent 日志

上一篇主要详细介绍log4net相关的一些配置项,本章意在从实战角度详解log4net在NetCore中使用。

1、创建Netcore consol application 通过Nuget package安装log4net(Microsoft.Extensions.Logging.Log4Net.AspNetCore), Hosting(Microsoft.Extensions.Hosting)及DI(Microsoft.Extensions.DependencyInjection),除了log4net 安装DI及Hosting旨在通过DI方式使用ILog。

2、创建log4net.config 文件,设置Copy to Output Directory:Copy Always

<log4net debug="false" update="Merge" threshold="ALL">
    <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
        <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
        <file value="log4netdemo.log"/>
        <appendToFile value = "true"/>
        <rollingStyle value="Size"/>
        <maximumFileSize value = "10KB" />
        <maxSizeRollbackups value = "2"/>
        <staticLogFileName value = "true"/>
        <!--<layout type ="Log4netDemo.ELKWebLayout, Log4netDemo"/>-->
        <layout type="Log4netDemo.DemoPatterLayout,Log4netDemo">
            <param name="ConversionPattern" value="%d [%t] %5level %clientIP %userName %logger.%method [%line] -MESSAGE: %message %newline %exception"/>
        </layout>
    </appender>
    <root>
        <level value="All" />
        <appender-ref ref="RollingFile"/>
    </root>
</log4net>
log4net basic
  • 配置详解:file 指定日志输出文件地址;appendToFile指定以追加的方式写入日志;rollingStyle指定为size;maximumFileSize最大文件大小10kb;maxSizeRollbackups保留日志文件个数2个;staticLogFileName日志文件为静态命名,rollback的文件会添加后缀.1,.2
  • ConverionPattern value中formart即日志输出到文件中的格式,其中  %clientIP %userName 为自定义PatternConvert.用于将自定义信息以patternLayout的方式输出到日志,具体设置稍后介绍。

创建一个LogService 用于测试Logger

public class LogService
    {
        private static ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private static ILog _loggerInfo = LogManager.GetLogger(Assembly.GetEntryAssembly(), "SpecifyLogger");

        //private readonly ILogger _logger;
        //public LogService(ILogger<LogService> logger)
        //{
        //    _logger = logger;
        //}   

        public void Run()
        {
            _logger.Info($"The application start: {DateTime.Now}.");
           
        }

        public void RunV2()
        {
            for (int i = 0; i < 200; i++)
            {
                _loggerInfo.Info($"The application start: {DateTime.Now}.");
                _logger.Error($"The application start: {DateTime.Now}.");
            }
        }

        public void RunV3()
        {
            LogicalThreadContext.Properties[LoggerProperty.Ip] = "127.0.0.1";
            LogicalThreadContext.Properties[LoggerProperty.UserName] = "panda";
            LogicalThreadContext.Properties[LoggerProperty.Company] = "zoo";
            LogicalThreadContext.Properties[LoggerProperty.Department] = "suckler";
            _logger.Info($"Test for the customize elk layout.");
        }

        public void RunV4()
        {
            for (int i = 0; i < 200; i++)
            {
                _logger.Info($"This is root logger - {i}.");
            }
            _logger.Fatal($"This is FATAL level error.");
            _loggerInfo.Info($"This is sub logger");
        }
    }
LogService
  • 可以通过构造函数的方式依赖注入ILogger
  • 另一种方式是通过LogManager同样构建ILog
  • 如果配置文件中配置了多个Logger,可以通过指定logger的方式获取到指定Logger,比如LogManager.GetLogger(Assembly.GetEntryAssembly(), "SpecifyLogger");获取SpecifyLogger对象。

将LogService注入到容器中及配置log4net

using Log4netDemo;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var builder = new HostBuilder()
    .ConfigureServices((hostContext, services) =>
    {
        services.AddTransient<LogService>();
    })
    .ConfigureLogging(logBuilder =>
    {
        logBuilder.AddLog4Net("log4net.config", true);
    }).UseConsoleLifetime();
var host = builder.Build();


var logService = host.Services.GetRequiredService<LogService>();
logService.RunV4();
host.Run();
Program.cs

启动程序可以看到生成了一个log4netdemo.log文件,并且日志成功写入

 以上只是log4net的基本使用,下面介绍log4net的进阶操作

1、如果log4net build-in PatternLayout format不能满足开发需求,比如想要将Ip,userName,Company,department等信息通过PatternLayout format记录到日志怎么做,这里就需要自定义PatternConvert,下面示例自定义了ClientIPPatternConvert及UserNamePatternConvert

public class ClientIPPatternConvert : PatternLayoutConverter
{
    protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
    {
        var props = loggingEvent.GetProperties();
        if (props?[LoggerProperty.Ip] != null)
            writer.Write($"IP:{props?[LoggerProperty.Ip].ToString()}");
    }
}
ClientIPPatternConvert
public class UserNamePatternConvert : PatternLayoutConverter
{
    protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
    {
        var props = loggingEvent.GetProperties();
        if (props?[LoggerProperty.UserName] != null)
            writer.Write($"user name:{props?[LoggerProperty.UserName]}");
    }
}
UserNamePatternConvert

 接下来需要将自定义的PatternConverter加到log4net的PatternLayout中,创建一个DemoPatternLayout继承自 log4net.Layout.PatternLayout

public class DemoPatterLayout: log4net.Layout.PatternLayout
{
    public DemoPatterLayout()
    {
        this.AddConverter("clientIP", typeof(ClientIPPatternConvert));
        this.AddConverter("userName", typeof(UserNamePatternConvert));
    }
}
DemoPatterLayout

修改log4net.config中layout配置,指定type="Log4netDemo.DemoPatterLayout,Log4netDemo",使用自定义的PatternConvert格式化字符串%clientIP, %userName

<layout type="Log4netDemo.DemoPatterLayout,Log4netDemo">
            <param name="ConversionPattern" value="%d [%t] %5level %clientIP %userName %logger.%method [%line] -MESSAGE: %message %newline %exception"/>
        </layout>
Customize layout conversation

在LogService中向log4net添加IP及UserName属性值

LogicalThreadContext.Properties[LoggerProperty.Ip] = "127.0.0.1";
LogicalThreadContext.Properties[LoggerProperty.UserName] = "panda";
log4net property value

运行结果

2、自定义layout,某些情况下不想用PatternConverter format或者想对接ElasticSearch,基于以上case可以定义Layout

创建AbstractLayout基类继承自LayoutSkeleton

public abstract class AbstractLayout : LayoutSkeleton
{
    protected static JsonSerializerOptions _defaultJsonOptions => new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };

    protected abstract string _role { get;}

    public AbstractLayout()
    {
        IgnoresException = false;
    }

    public override void ActivateOptions()
    {
        
    }

    public override void Format(TextWriter writer, LoggingEvent loggingEvent)
    {
        if (!(loggingEvent.Level == Level.Trace))
            DoFormat(writer, loggingEvent);
    }

    public abstract void DoFormat(TextWriter writer, LoggingEvent loggingEvent);
}
AbstractLayout

BaseLogger的实体类

public class LoggerMessage
{
    private string _defaultDateFormat => "yyyy-MM-ddTHH:mm:ss.fffZ";
    public LoggerMessage(LoggingEvent loggingEvent, string role)
    {
        Time = loggingEvent.TimeStampUtc.ToString(_defaultDateFormat);
        Level = loggingEvent.Level.Name;
        Thread = loggingEvent.ThreadName;
        Logger = loggingEvent.LoggerName;
        Message = loggingEvent.RenderedMessage;
        Role = role;
    }
    public String? Time { get; set; }
    public String? Level { get; set; }
    public String? Thread { get; set; }
    public String? Logger { get; set; }
    public String? Message { get; set; }
    public String? Role { get; set; }
}
Base Logger

定义web模块Layout(可以基于不同的模块或不同的image根据需要创建多个Layout)

public class ELKWebLayout : AbstractLayout
{
    protected override string _role { get => "demo-web"; }

    public override void DoFormat(TextWriter writer, LoggingEvent loggingEvent)
    {
        var props = loggingEvent.GetProperties();
        var webLogger = new WebLoggerMessage(loggingEvent, _role);
        webLogger.Ip = props?[LoggerProperty.Ip]?.ToString() ?? "";
        webLogger.Company = props?[LoggerProperty.Company]?.ToString() ?? "";
        webLogger.Department = props?[LoggerProperty.Department]?.ToString() ?? "";
        writer.WriteLine(JsonSerializer.Serialize(webLogger, _defaultJsonOptions));
    }
}
ELKWebLayout

WebLogger实体类

public class WebLoggerMessage : LoggerMessage
{
    public WebLoggerMessage(LoggingEvent loggingEvent, string role) 
        : base(loggingEvent, role)
    {
    }

    public String? Ip { get; set; }
    public String? Company { get; set; }
    public String? Department { get; set; }
}
Web Logger

在log4net.config中指定自定义的layout

<layout type ="Log4netDemo.ELKWebLayout, Log4netDemo"/>

给log4net自定义属性赋值

LogicalThreadContext.Properties[LoggerProperty.Ip] = "127.0.0.1";
LogicalThreadContext.Properties[LoggerProperty.UserName] = "panda";
log4net property value

运行结果

 3、某些时候想将一些重要的日志信息单独输出到一个文件,而不包含在公共的日志文件中,基于以上需求需要创建多个Appender及Logger,通过Logger中additivity属性决定是否将sub logger信息包含到root logger中,如下配置

<log4net debug="false" update="Merge" threshold="ALL">
    <appender name="RootRoolingFileAppender" type="log4net.Appender.RollingFileAppender">
        <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
        <file value="log4netdemo.log"/>
        <appendToFile value = "true"/>
        <rollingStyle value="Size"/>
        <maximumFileSize value = "10KB" />
        <maxSizeRollbackups value = "2"/>
        <staticLogFileName value = "true"/>
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%d [%t] %5level %logger.%method [%line] -MESSAGE: %message%newline %exception"/>
        </layout>
    </appender>
    <appender name="SubRoolingFileAppender" type="log4net.Appender.RollingFileAppender">
        <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
        <file value="log4netdemo.sub.log"/>
        <appendToFile value = "true"/>
        <rollingStyle value="Size"/>
        <maximumFileSize value = "10KB" />
        <maxSizeRollbackups value = "2"/>
        <staticLogFileName value = "true"/>
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%d [%t] %5level %logger.%method [%line] -MESSAGE: %message%newline %exception"/>
        </layout>
    </appender>
    <root>
        <level value="All" />
        <appender-ref ref="RootRoolingFileAppender"/>
    </root>
    <logger name="SpecifyLogger" additivity="false">
        <level value="All" />
        <appender-ref ref="SubRoolingFileAppender"/>
    </logger>
</log4net>
Multiple Logger Appender

在LogService中通过Logger name指定使用哪个Logger

private static ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static ILog _loggerV2 = LogManager.GetLogger(Assembly.GetEntryAssembly(), "SpecifyLogger");

启动程序生成两个log文件

 查看log信息发现log4netdemo.log中不会记录SpecifyLogger 记录的日志信息,因为在创建SpecifyLogger 时设置additivity="false"

 4、有时可能还有需求,不同Appender需要记录不同level 日志信息,比如FATAL level的信息需要单独一个文件记录,或者INFO-ERROR level range的日志信息需要记录到某个文件中,基于以上需求可以在Appender中添加filter实现

a.只会记录FATAL level的日志信息

<filter type="log4net.Filter.LevelMatchFilter">
  <levelToMatch value ="FATAL"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter"/>
FATAL Filter

b.记录Level范围在INFO和ERROR之间的日志信息

<filter type="log4net.Filter.LevelMatchFilter">
     <levelMax value ="ERROR"/>
     <levelMin value ="INFO"/>
</filter>
<filter type="log4net.Filter.DenyAllFilter"/>
Level Range

 Filter执行原理是顺序执行,如果满足上一个Filter则日志会被记录;如果不满足上一个Filter会继续匹配下一个Filter。

最后附上完整的代码结构

 如有任何问题,请留言,谢谢!!!

 

标签:log4net,实战,NetCore,LoggerProperty,public,logger,loggingEvent,日志
From: https://www.cnblogs.com/qindy/p/17699244.html

相关文章

  • 再聊Java Stream的一些实战技能与注意点
    大家好,又见面了。在此前我的文章中,曾分2篇详细探讨了下JAVA中Stream流的相关操作,2篇文章收获了累计10w+阅读、2k+点赞以及5k+收藏的记录。能够得到众多小伙伴的认可,是技术分享过程中最开心的事情。吃透JAVA的Stream流操作,多年实践总结讲透JAVAStream的collect用法与原理,远......
  • IIS上缺少 AspNetCoreModuleV2 如何解决
    实际上是少了装了.NetCoreSDK需要找到自己的程序使用的.NetCore对应版本进行下载https://dotnet.microsoft.com/en-us/download/dotnet/3.0只装Hosting就行了......
  • 高性能MySQL实战(三):性能优化 | 京东物流技术团队
    这篇主要介绍对慢SQL优化的一些手段,而在讲解具体的优化措施之前,我想先对EXPLAIN进行介绍,它是我们在分析查询时必要的操作,理解了它输出结果的内容更有利于我们优化SQL。为了方便大家的阅读,在下文中规定类似key1的表示二级索引,key_part1表示联合索引的第一部分,unique_key1......
  • web前端入门到实战:HTML5基础-新增标签+新增属性+布局案例
    html5中常用的结构标签article文章header头部nav导航section区域aside侧边栏hgroup区块的相关信息figure定义一组内容及标题figcaption定义figure元素的标题footer底部dialog对话框使用习惯:header/section/footer>aside/articl......
  • 《Flask Web开发:基于Python的Web应用开发实战》高清高质量PDF电子书+源码
    网盘下载:https://pan.quark.cn/s/cc9dc7402cdb......
  • Python爬虫实战:分析在线视频平台数据
    当涉及抓取和分析在线视频平台数据时,Python爬虫是一个强大而有用的工具。下面我将为您提供一些步骤和代码示例,来帮助您进行这样的实战操作。1.确定目标平台:首先,您需要确定要抓取和分析数据的在线视频平台。常见的在线视频平台包括YouTube、B站、优酷等。不同平台可能有不同的数据抓......
  • log4net性能小探
    初步测试了Log4性能。Appender架构如下。一般客户端,使用FileAppender,把Log记录在本地磁盘。<lockingModeltype="log4net.Appender.FileAppender+InterProcessLock"/> lockingModel影响性能。有三种模式:ExclusiveLock,MinimalLock,InterProcessLock。默认是ExclusiveLock,此......
  • 高性能MySQL实战(二):索引 | 京东物流技术团队
    我们在上篇高性能MySQL实战(一):表结构中已经建立好了表结构,这篇我们则是针对已有的表结构和搜索条件为表创建索引。1.根据搜索条件创建索引我们还是先将表结构的初始化SQL拿过来:CREATETABLE`service_log`(`id`bigintUNSIGNEDNOTNULLAUTO_INCREMENTCOMMENT'主键......
  • redis-实战篇-商户查询缓存
    基本思路添加缓存的原则:动态数据不要加缓存缓存cache:数据交换的缓冲区。一般读写性能较高。比如浏览器缓存,浏览器会将一些经常使用的数据缓存到本机,这样在多次加载时就不需要访问服务器,而浏览器未命中的缓存则会去tomcat获取。缓存的作用:降低后端负载、提高读写效率、降低响应......
  • Nacos实战(21)-Nacos一致性协议
    1Nacos⼀致性协议1.1为什么Nacos需要⼀致性协议Nacos尽可能减少用户部署以及运维成本,做到用户只需要⼀个程序包,就快速单机模式启动Nacos或集群模式启动Nacos。而Nacos是⼀个需要存储数据的组件,为实现目标,就要在Nacos内部实现数据存储。单机问题不大,内嵌关系型数据库......