65.9K
CodeProject 正在变化。 阅读更多。
Home

Log4net:如何首次设置和使用

2018年10月20日

CPOL

6分钟阅读

viewsIcon

30356

downloadIcon

446

本文将介绍如何在应用程序中首次设置Log4net。

关于Log4Net的使用

Log4net是一个日志框架,在.NET编程中用于将错误/信息/警告记录到各种输出目标,以便开发人员能够轻松理解生产环境中特定应用程序的代码流程。

为什么我们需要在应用程序中使用日志框架

我刚开始职业生涯时,我学会了调试,并通过在开发环境中重现问题来解决许多bug/缺陷。我曾通过阅读/调试代码来理解应用程序中的漏洞。

有一天,我被分配了一个bug,由于某些配置限制,我无法重现该问题。后来,我得知了日志文件的存在,因为我的团队领导让我去查看。我从生产环境中获取了那个日志文件,花了几分钟时间来理解和找到确切的错误,然后找到了应用程序中生成该错误日志的代码。之后,我便体会到了日志文件的作用。

据我理解,日志文件对于找出导致特定应用程序流程问题的应用程序中的条件/源代码非常有用。当您由于环境限制/源代码不可用或其他任何原因而无法调试源代码时,它将非常有用。

如果代码中进行了适当的日志记录,那么这些日志记录就可以像编写在文件/数据库等中的良好调试信息一样发挥作用。

获取Log4Net

网上有两种获取log4net的来源:

  1. Apache log4net
  2. GitHub上的logging-log4net

我更倾向于 GitHub上的logging-log4net 而非 Apache log4net。原因是在 log4net IssueTracker Jira 上,针对 Apache log4net 版本仍有几个未解决的问题,并且其中大部分问题已在 GitHub上的logging-log4net 版本中得到内部解决。最初我也是使用Apache版本,但遇到了一个问题:“RollingLogFileAppender不使用MaxSizeRollBackups设置”,该问题自2013年12月以来仍处于未解决状态。

下载完整分支后,您需要打开位于“logging-log4net-master\src\”路径下的log4net.sln文件。只需使用“Release”配置构建log4net项目,然后在“logging-log4net-master\build\bin\net\4.5\release\”路径下获取log4net.dll。在本次教程的后续步骤中,将此DLL添加为引用。

如何在应用程序中配置log4net

我们可以通过以下简单步骤在应用程序中配置log4net:

  1. log4net.dll作为项目引用添加。
  2. 如果您希望应用程序在不与特定日志框架紧密耦合的情况下使用日志记录,那么创建一个interface ILogger,其中包含InfoError等方法,并使用依赖注入原则。
  3. 创建一个类,例如“SampleLog4NetLogger”,实现ILogger接口。
  4. 在解决方案中添加一个控制台应用程序项目。
  5. 在应用程序的配置文件(app.config/web.config)中为log4net添加配置节。
  6. 根据您的存储类型需求,在配置文件中添加任何appender节点。
我将在下面详细解释每个步骤。
  • 在您的解决方案中创建一个类库项目,例如“Common”,并从“logging-log4net-master\build\bin\net\4.5\release\”文件夹中添加log4net.dll引用。

  • 如果您希望应用程序在不与特定日志框架紧密耦合的情况下使用日志记录,那么创建一个interface ILogger,其中包含InfoError等方法,并使用依赖注入原则。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Common.Contract
    {
        public interface ILogger
        {
            #region Debug
            void Debug(object message);
            void Debug(object message, Exception exceptionData);
            #endregion
            #region Info
            void Info(object message);
            void Info(object message, Exception exceptionData);
            #endregion
            #region Warn
            void Warn(object message);
            void Warn(object message, Exception exceptionData);
            #endregion
            #region Error
            void Error(object message);
            void Error(object message, Exception exceptionData);
            #endregion
            #region Fatal
            void Fatal(object message);
            void Fatal(object message, Exception exceptionData);
            #endregion
        }
    }
  • 创建一个类,例如“SampleLog4NetLogger”,实现ILogger接口。
    1. 除了实现ILogger之外,还要添加“log4net”的using指令。
    2. 添加一个类型为ILog的只读字段,并通过调用LogManager.GetLogger的结果来设置它。
    3. 使用这个“ILog”类型的字段在每个接口方法中调用适当的日志记录方法。
    4. 您也可以将此实现设为单例。
    using Common.Contract;
    using log4net;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Common.Implementation
    {
        public class SampleLog4NetLogger : ILogger
        {
            #region Fields
    
            private readonly ILog log4NetReal = LogManager.GetLogger(typeof(SampleLog4NetLogger));
            private static SampleLog4NetLogger thisLogger;
            #endregion
    
            #region Properties
    
            public static SampleLog4NetLogger Current
            {
                get
                {
                    if (thisLogger == null)
                    {
                        thisLogger = new SampleLog4NetLogger();
                    }
    
                    return thisLogger;
                }
            }
    
            #endregion
            
            #region Debug
            public void Debug(object message)
            {
                log4NetReal.Debug(message);
            }
    
            public void Debug(object message, Exception exceptionData)
            {
                log4NetReal.Debug(message, exceptionData);
            }
            #endregion
    
            #region Error
            public void Error(object message)
            {
                log4NetReal.Error(message);
            }
    
            public void Error(object message, Exception exceptionData)
            {
                log4NetReal.Error(message, exceptionData);
            }
            #endregion
    
            #region Fatal
            public void Fatal(object message)
            {
                log4NetReal.Fatal(message);
            }
    
            public void Fatal(object message, Exception exceptionData)
            {
                log4NetReal.Fatal(message, exceptionData);
            }
            #endregion
    
            #region Info
            public void Info(object message)
            {
                log4NetReal.Info(message);
            }
    
            public void Info(object message, Exception exceptionData)
            {
                log4NetReal.Info(message, exceptionData);
            }
            #endregion
    
            #region Warn
            public void Warn(object message)
            {
                log4NetReal.Warn(message);
            }
    
            public void Warn(object message, Exception exceptionData)
            {
                log4NetReal.Warn(message, exceptionData);
            } 
            #endregion
        }
    }
  • 在解决方案中添加一个控制台应用程序项目,并在其中添加“Common”项目引用和“log4net.dll”引用。
  • 在控制台应用程序的配置文件(app.config/web.config)中为log4net添加config section
    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
    </configSections>
  • 根据您的存储类型需求,在配置文件中添加任何appender节点。
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
      </configSections>
    
      <log4net threshold="ALL">
        <appender name="SampleRollingAppenderTest" type="log4net.Appender.RollingFileAppender">
          <param name="CountDirection" value="1"/>
          <param name="threshold" value="DEBUG"/>
          <datePattern>_dd_MMMM_yyyy_dddd_HH_mm</datePattern>
          <dateTimeStrategy type="log4net.Appender.RollingFileAppender+LocalDateTime"/>
          <maximumFileSize>100KB</maximumFileSize>
          <preserveLogFileNameExtension>true</preserveLogFileNameExtension>
          <rollingStyle>date</rollingStyle>
          <staticLogFileName>false</staticLogFileName>
          <appendToFile>true</appendToFile>
          <encoding>UTF-8</encoding>
          <file type="log4net.Util.PatternString" value="..\..\Logs\RahulLog.txt"/>
          <immediateFlush>true</immediateFlush>
          <layout type="log4net.Layout.DynamicPatternLayout">
            <header value="%newline**** Trace Opened Local: 
            %date{yyyy-MM-dd HH:mm:ss.fff} UTC: %utcdate{yyyy-MM-dd HH:mm:ss.fff} ****%newline"/>
            <footer value="**** Trace Closed %date{yyyy-MM-dd HH:mm:ss.fff} ****%newline"/>
            <conversionPattern value="%newline Logged Date : %date,%newline Exception : 
            %exception,%newline File : %file,%newline Location : %location,%newline Level : 
            %level,%newline Logged Message : %message,%newline Method Detail : 
            %method %newline ********************** %newline "/>
          </layout>
          <lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
        </appender>
        <root>
          <level value="DEBUG"/>
          <appender-ref ref="SampleRollingAppenderTest"/>
        </root>
      </log4net>
    
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
      </startup>
    </configuration>

配置初始化

手动方法调用初始化

现在,这种方法始终有效。您必须在主入口方法(例如program类的main方法)中调用XmlConfigurator.ConfigureAndWatch方法来初始化log4net设置,以符合您的config设置。方法“ConfigureAndWatch”使用指定的配置文件来配置log4net,监视文件更改,并在检测到更改时重新加载配置。

XmlConfigurator.ConfigureAndWatch(new FileInfo(@"..\..\app.config"));

基于属性的初始化

这种方法仅在日志记录器实现“SampleLog4NetLogger”位于主可执行项目时才有效,它实际调用的是LogManager.GetLogger方法。在这种情况下,请在控制台应用程序项目的AssemblyInfo.cs文件中最后添加XmlConfigurator属性到程序集log4net。我在解决方案中添加了另一个名为“SingleSetupExample”的控制台应用程序项目来展示其用法。

[assembly: log4net.Config.XmlConfigurator(ConfigFile = @"..\..\app.config", Watch = true)]

在这两种方法中,如果您为日志记录器特定的配置创建了另一个配置文件,那么您可以提供其相对路径(并附加文件名)。

请记住,配置文件路径应相对于控制台应用程序的EXE文件。

有关log4net基于属性初始化的使用限制,请参阅 此链接,了解有关AssemblyInfo特定配置的bug。

如何在应用程序中使用已配置的log4net

  1. 在控制台应用程序中创建一个名为“SimpleCalculator.cs”的类,其构造函数具有“ILogger”类型的参数。在这里,存储此日志记录器值并在其实现中恰当地使用它。
    using Common.Contract;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonExampleApp
    {
        public class SimpleCalculator
        {
            private ILogger logger;
            private const string EntryFormat = "Starting Method {0} 
            with Left operand : {1} and Right Operand : {2}";
            private const string SuccessfulExitFormat = "Successfully Finishing Method {0} 
            with Left operand : {1}, Right Operand : {2}, Output : {3}";
            private const string FailedExitFormat = "Finishing with Failure in Method : {0} 
            with Left operand : {1} and Right Operand : {2}";
            private const string ParseFormat = "In Method {0} with Left operand : {1} 
            and Right Operand : {2}, Successfully Parsed {3} number.";
            private const string SuccessfulOutcomeFormat = "In Method {0} with Left operand : {1} 
            and Right Operand : {2}, Operation Successful with Output:  {3}.";
            private const string Left = "left";
            private const string Right = "right";
    
            public SimpleCalculator(ILogger _logger)
            {
                logger = _logger;
            }
    
            public double Add(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format
                (EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number + right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
    
            public double Subtract(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format(EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number - right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
    
            public double Multiply(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format
                (EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number * right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
    
            public double Divide(string left, string right)
            {
                double result = 0;
                StackTrace trace = new StackTrace();
    
                logger.Info(string.Format
                (EntryFormat, trace.GetFrame(1).GetMethod().Name, left, right));
    
                try
                {
                    double left_Number = double.Parse(left);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Left));
    
                    double right_Number = double.Parse(right);
                    logger.Debug(string.Format
                    (ParseFormat, trace.GetFrame(1).GetMethod().Name, left, right, Right));
    
                    result = left_Number / right_Number;
                    logger.Debug(string.Format
                    (SuccessfulOutcomeFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format
                    (FailedExitFormat, trace.GetFrame(1).GetMethod().Name, left, right), ex);
                }
    
                logger.Info(string.Format
                (SuccessfulExitFormat, trace.GetFrame(1).GetMethod().Name, left, right, result));
    
                return result;
            }
        }
    }
  2. program.csmain方法中,通过传递SampleLog4NetLogger的实例来创建SimpleCalculator的实例,并调用其方法。
    using Common.Implementation;
    using log4net.Config;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CommonExampleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                XmlConfigurator.ConfigureAndWatch(new FileInfo(@"..\..\app.config"));
                SimpleCalculator calculator = 
                    new CommonExampleApp.SimpleCalculator(SampleLog4NetLogger.Current);
                double result = 0;
    
                SampleLog4NetLogger.Current.Debug("Adding two correct numbers....");
                result = calculator.Add("23", "56");
    
                SampleLog4NetLogger.Current.Debug("Adding two numbers with incorrect format....");
                result = calculator.Add("23", "Test");
    
                SampleLog4NetLogger.Current.Debug("Subtracting two correct numbers....");
                result = calculator.Subtract("1343", "56");
    
                SampleLog4NetLogger.Current.Debug("Subtracting two numbers with incorrect format....");
                result = calculator.Subtract("1343", "Robin");
    
                SampleLog4NetLogger.Current.Debug("Multiplying two correct numbers....");
                result = calculator.Multiply("43", "90");
    
                SampleLog4NetLogger.Current.Debug("Multiplying two numbers with incorrect format....");
                result = calculator.Multiply("43", "Cream");
    
                SampleLog4NetLogger.Current.Debug("Dividing two correct numbers....");
                result = calculator.Divide("225", "25");
    
                SampleLog4NetLogger.Current.Debug("Dividing two numbers with incorrect format....");
                result = calculator.Divide("225", "Number");
    
                SampleLog4NetLogger.Current.Debug("Dividing number by zero....");
                result = calculator.Divide("225", "0");
            }
        }
    }

日志记录的输出

您可以在Logs文件夹中看到如下所示的日志文件和日志:

Logged Date : 2018-08-11 18:52:45,620,
 Exception : ,
 File : C:\Users\USER1\OneDrive - Test Gaming\Attachments\Rahul Backup\
 General\Published\Example\Log4NetExamples\Common\Implementation\SampleLog4NetLogger.cs,
 Location : Common.Implementation.SampleLog4NetLogger.Debug
 (C:\Users\USER1\OneDrive - Test Gaming\Attachments\Rahul Backup\
 General\Published\Example\Log4NetExamples\Common\Implementation\SampleLog4NetLogger.cs:39),
 Level : DEBUG,
 Logged Message : Adding two correct numbers....,
 Method Detail : Debug

这是日志记录的输出。

Log4net支持将日志记录到平面文件、数据库表、TCP客户端、控制台等。对于每种不同的存储,都有不同的appender。其中大多数继承了一些通用属性,可用于指定日志消息格式、编码、阈值等,我将在每个appender特定文章中进行描述。

Log4net的各种用途

Log4net用于将消息记录到各种类型的存储中。以下是广泛用于日志记录的主要appenders列表:

用法 Appender
当您需要将日志消息存储在平面文件或文本文件中时 RollingFileAppender
当您需要将日志消息存储在数据库(表)中时 AdoNetAppender
当您需要将日志消息输出到具有为每个日志级别指定颜色的控制台时 ColoredConsoleAppender, AnsiColorTerminalAppender, ManagedColoredConsoleAppender
当您需要将日志消息输出到控制台时 ConsoleAppender
当您需要将日志消息存储到任何跟踪监听器(您可以在VS输出窗口中看到日志,或将其存储为Excel/Access等其他文件格式)时 DebugAppender, TraceAppender
当您需要将日志消息发送到电子邮件时 SmtpAppender
当您需要将日志消息传输到telnet客户端时 TelnetAppender
当您需要将日志消息传输到udp服务器时 UdpAppender
当您需要将日志消息传输到远程服务器时 RemotingAppender
当您需要将日志消息存储到系统事件日志中以供事件查看器监视时 EventLogAppender
当您需要将相同的日志消息缓冲转发到各种存储时 BufferingForwardingAppender
当您需要将相同的日志消息转发到各种存储时 ForwardingAppender

重要提示

  1. 在C#代码中,我们准备所有要添加到最终日志存储的日志消息。为此,我们创建一个logger类来调用log4net方法。这是日志记录器类型实现特定的功能。
  2. 在配置文件中,我们过滤那些(由C#代码发送的)日志消息,并为每个最终日志消息存储(如文本文件、数据库等)定义类型和路径。这是appender特定的功能。

关注点

Log4net是一个很好的日志记录框架,用于跟踪和排查在生产环境中遇到的代码问题。我使用它已经很长时间了,我非常喜欢它。

© . All rights reserved.