[ASP.net Core] 加入log4net作logger

之前寫過文章利用.net 及Java 寫紀錄, 現在則會示範在.net Core 中利用log4net 進行記錄.

  1. 安裝log4net.
    在Package Management Console 中, 輸入以下指令.

    Install-Package log4net
  2. 設定log4net.
    建立XML 檔, 命名為log4net.config, 並輸入以下設定.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <log4net>
        <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
          <file value="C:\log\log4net" />
          <appendToFile value="true" />
          <rollingStyle value="Composite" />
          <datePattern value="_yyyy-MM-dd.lo\g" />
          <maximumFileSize value="5MB" />
          <maxSizeRollBackups value="15" />
          <staticLogFileName value="false" />
          <PreserveLogFileNameExtension value="true" />
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
          </layout>
        </appender>
        <root>
          <appender-ref ref="RollingFileAppender" />
        </root>
      </log4net>
    </configuration>
  3. 建立Logger
    因為Log4net 本身沒有implement ILogger, 加上雙方的log level 不同, 因此需要自行處理.
    建立class file 並命名為log4netLogger.cs, 並輸入以下代碼.

    public class Log4netLogger : ILogger
        {
            private readonly ILog _log;
    
            public Log4netLogger(string name, FileInfo fileInfo)
            {
                var repository = LogManager.CreateRepository(
                        Assembly.GetEntryAssembly(),
                        typeof(log4net.Repository.Hierarchy.Hierarchy)
                    );
                XmlConfigurator.Configure(repository, fileInfo);
                _log = LogManager.GetLogger(repository.Name, name);
            }
    
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }
    
            public bool IsEnabled(LogLevel logLevel)
            {
                switch (logLevel)
                {
                    case LogLevel.Critical: return _log.IsFatalEnabled;
                    case LogLevel.Debug:
                    case LogLevel.Trace: return _log.IsDebugEnabled;
                    case LogLevel.Error: return _log.IsErrorEnabled;
                    case LogLevel.Information: return _log.IsInfoEnabled;
                    case LogLevel.Warning: return _log.IsWarnEnabled;
                    default: 
                        throw new ArgumentOutOfRangeException(nameof(logLevel));
                }
            }
    
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
                Exception exception, Func<TState, Exception, string> formatter)
            {
                if (!IsEnabled(logLevel))
                {
                    return;
                }
                if (formatter == null)
                {
                    throw new ArgumentNullException(nameof(formatter));
                }
                string message = null;
                if (null != formatter)
                {
                    message = formatter(state, exception);
                }
                if (!string.IsNullOrEmpty(message) || exception != null)
                {
                    switch (logLevel)
                    {
                        case LogLevel.Critical: _log.Fatal(message); break;
                        case LogLevel.Debug:
                        case LogLevel.Trace: _log.Debug(message); break;
                        case LogLevel.Error: _log.Error(message); break;
                        case LogLevel.Information: _log.Info(message); break;
                        case LogLevel.Warning: _log.Warn(message); break;
                        default: 
                            _log.Warn($"Unknown log level {logLevel}.\r\n{message}"); 
                            break;
                    }
                }
            }
        }
  4. 建立Provider.
    因為在.net core 中, ILogger 是透過Factory Pattern 產生, 而其factory 是ILoggerProvider, 所以須要implement 它.
    建立class file 並命名為Log4netProvider.cs, 並輸入以下代碼.

    public class Log4netProvider : ILoggerProvider
        {
            private readonly FileInfo _fileInfo;
    
            public Log4netProvider(string log4netConfigFile)
            {
                _fileInfo = new FileInfo(log4netConfigFile);
            }
    
            public ILogger CreateLogger(string name)
            {
                return new Log4netLogger(name, _fileInfo);
            }
    
            public void Dispose()
            {
            }
        }
  5. 加入logger 到Webhost 中.
    在program.cs 中修改CreateWebHostBuilder method chain 如下.

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                .ConfigureLogging(logging=> {
                    logging.AddProvider(new Log4netProvider(@"log4net.config"));
                })
                .ConfigureAppConfiguration((builderContext, config) =>
                {
                    config.AddEnvironmentVariables();
                })
                .UseStartup<Startup>();
  6. 測試.
    按F5 執行程式, 當見到有記錄檔產生及資料寫入, 代表測試成功.

雖然log4net 可以透過database 儲存, 但現在其AdoNetAppender仍未支援.net Core, 所以並沒有用作實戰使用. 但仍將建立紀錄留記, 示範中會利用SQL server 存儲記錄.

  1. 建立Table.
    於SQL Server Management Studio 中執行以下指令.

    CREATE TABLE [dbo].[Log](
      [Id] [bigint] IDENTITY(1,1) NOT NULL,
      [Date] [datetime2] NOT NULL,
      [Thread] [varchar](255) NOT NULL,
      [Level] [varchar](50) NOT NULL,
      [Logger] [varchar](255) NOT NULL,
      [Message] [varchar](max) NOT NULL,
      [Exception] [varchar](max) NULL
    ) ON [PRIMARY]
    GO
  2. 加入AdoNetAppender.
    修改log4net.config, 並加入以下設定.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <log4net>
      <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
     <bufferSize value="1" />
          <connectionType value="System.Data.SqlClient.SqlConnection,System.Data,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" />
          <connectionString value="<<Connection String Here>>" />
          <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
          <parameter>
            <parameterName value="@log_date" />
            <dbType value="DateTime" />
            <layout type="log4net.Layout.RawTimeStampLayout" />
          </parameter>
          <parameter>
            <parameterName value="@thread" />
            <dbType value="String" />
            <size value="255" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%thread" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@log_level" />
            <dbType value="String" />
            <size value="50" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%level" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@logger" />
            <dbType value="String" />
            <size value="255" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%logger" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@message" />
            <dbType value="String" />
            <size value="4000" />
            <layout type="log4net.Layout.PatternLayout">
              <conversionPattern value="%message" />
            </layout>
          </parameter>
          <parameter>
            <parameterName value="@exception" />
            <dbType value="String" />
            <size value="2000" />
            <layout type="log4net.Layout.ExceptionLayout" />
          </parameter>
        </appender>
        <root>
          <appender-ref ref="AdoNetAppender" />
        </root>
      </log4net>
    </configuration>

     

About C.H. Ling 260 Articles
a .net / Java developer from Hong Kong and currently located in United Kingdom. Thanks for Google because it solve many technical problems so I build this blog as return. Besides coding and trying advance technology, hiking and traveling is other favorite to me, so I will write down something what I see and what I feel during it. Happy reading!!!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.