基於DDD的.NET開發框架 - ABP日誌Logger集成

返回ABP系列javascript

ABP是「ASP.NET Boilerplate Project (ASP.NET樣板項目)」的簡稱。html

ASP.NET Boilerplate是一個用最佳實踐和流行技術開發現代WEB應用程序的新起點,它旨在成爲一個通用的WEB應用程序框架和項目模板。java

ABP的官方網站:http://www.aspnetboilerplate.comgit

ABP官方文檔:http://www.aspnetboilerplate.com/Pages/Documentsgithub

Github上的開源項目:https://github.com/aspnetboilerplateweb

1、基本概念

ABP使用Castle日誌記錄工具,而且可使用不一樣的日誌類庫,好比:Log4Net, NLog, Serilog... 等。對於全部的日誌類庫,Castle提供了一個通用的接口來實現,咱們能夠很方便的處理各類特殊的日誌庫,並且當業務須要的時候,很容易替換日誌組件。數據庫

Castle是什麼:Castle是針對.NET平臺的一個開源項目,從數據訪問框架ORM到IOC容器,再到WEB層的MVC框架、AOP,基本包括了整個開發過程當中的全部東西。ASP.NET Boilerplate的ioc容器就是經過Castle實現的。apache

Log4Net是.Net中最流行的日誌類庫之一。ABP模板中自帶了通過合適配置的Log4Net。可是,只存在一行log4net的依賴(看下面),所以,你能夠將它改成你最喜歡的類庫。瀏覽器

獲取Logger:app

不管你選擇了什麼日誌類庫,最終要記錄的日誌代碼都是相同的

一開始,咱們要處理一下記錄日誌的Logger對象。由於ABP強烈推薦使用依賴注入,因此咱們可使用屬性注入模式輕鬆地注入一個Logger對象。以下所示:

using Castle.Core.Logging; //一、導入 Logging 命名空間
public class TaskAppService : ITaskAppService
    {
        //二、使用屬性注入得到 logger
        public ILogger Logger { get; set; }
        public TaskAppService()
        {
            //三、若是沒有提供Logger,就不能記錄日誌
            Logger = NullLogger.Instance;
        }

        public void CreateTask(CreateTaskInput input)
        {
            //四、記錄日誌
            Logger.Info("Creating a new task with description: " + input.Description);

            //TODO: 保存到數據庫...
        }
    }

一、導入Castle的ILogger接口的命名空間。

二、定義一個公有的叫作Logger的ILogger對象。這是記錄日誌的對象。建立TaskAppService對象以後,依賴注入系統會自動注入這個屬性。這就是所謂的屬性注入模式。

三、將Logger設置爲NullLogger.Instance。即便沒有這行代碼,系統也會工做地很好。可是這是屬性注入模式的最佳實踐。若是沒給Logger設置任何值,那麼當咱們使用它的時候會由於它是null而拋出「空指針」異常。這個保證了它不爲null。所以,若是沒有給Logger設置值,那麼它是NullLogger。這就是所謂的null對象模式。NullLogger實際上什麼都沒作,也沒有記錄任何日誌。所以,咱們的類要不要一個實際的logger都能工做。

四、最後,咱們記錄了一個info等級的日誌文本。存在多種不一樣的等級。

經過積累使用Logger:

ASP.NET Boilerplate框架提供了MVC Controllers、Web API Controllers和Application service classes的基類。例如,Web層對應的基類是XXXControllerBase,這些基類中都聲明瞭Logger屬性。能夠直接使用Logger來記錄日誌,無需注入。以下:

        public class HomeController : SimpleTaskSystemControllerBase
        {
            public ActionResult Index()
            {
                Logger.Debug("A sample log message...");
                return View();
            }
        }

MVC Controllers:繼承XXAbpController基類

Web API Controllers:繼承XXAbpApiController基類

Application service classes:繼承XXAppServiceBase基類

配置:

若是你在官網上經過ASP.NET Boilerplate templates 來生成了你的工程,Log4Net的全部配置都自動生成了。

默認的配置格式以下:

  • Log level: 日誌記錄等級,有DEBUG, INFO, WARN, ERROR or FATAL5個。
  • Date and time: 日誌記錄時間。
  • Thread number: 每行日誌寫時候的線程號。
  • Logger name: 日誌記錄器的名字,一般狀況就是類名稱。
  • Log text: 你寫入的日誌內容。

配置文件:log4net.config 通常都在項目的web目錄下面。

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" >
    <file value="Logs/Logs.txt" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="10000KB" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%-5level %date [%-5.5thread] %-40.40logger - %message%newline" />
    </layout>
  </appender>
  <root>
    <appender-ref ref="RollingFileAppender" />
    <level value="DEBUG" />
  </root>
  <logger name="NHibernate">
    <level value="WARN" />
  </logger>
</log4net>

Log4Net是一個很是強大和易用的日誌庫組件,你能夠寫各類日誌,好比寫到txt文件,寫入到數據庫等等。你能設置最小的日誌等級,就像上面這個針對NHibernate的配置。不一樣的記錄器寫不一樣的日誌。

最後,在工程的Global.asax 文件中,來定義Log4Net的配置文件:

public class MvcApplication : AbpWebApplication
{
    protected override void Application_Start(object sender, EventArgs e)
    {
        IocManager.Instance.IocContainer.AddFacility<LoggingFacility>(f => f.UseLog4Net().WithConfig("log4net.config"));
        base.Application_Start(sender, e);
    }
}

客戶端:

ABP爲客戶端定義了一個javascript日誌記錄API。默認會將日誌記錄到瀏覽器的控制檯。記錄日誌的javascript代碼樣例以下:

abp.log.warn('a sample log message...');

 

2、ABP底層代碼分析

LogSeverity:枚舉類型,定義了5個日誌級別:Info,Debug,Warn,Error, Fatal.

namespace Abp.Logging
{
    /// <summary>
    /// Indicates severity for log.
    /// </summary>
    public enum LogSeverity
    {
        /// <summary>
        /// Debug.
        /// </summary>
        Debug,

        /// <summary>
        /// Info.
        /// </summary>
        Info,

        /// <summary>
        /// Warn.
        /// </summary>
        Warn,

        /// <summary>
        /// Error.
        /// </summary>
        Error,

        /// <summary>
        /// Fatal.
        /// </summary>
        Fatal
    }
}
View Code

IHasLogSeverity:封裝了LogSeverity。UserFriendlyException,AbpValidationException實現了這個接口。Loghelper在對exeption作log的時候能夠方便的經過實現了IHasLogSeverity的exeption的實例獲取到logSeverity。而後根據logSeverity的級別log.

namespace Abp.Logging
{
    /// <summary>
    /// Interface to define a <see cref="Severity"/> property (see <see cref="LogSeverity"/>).
    /// </summary>
    public interface IHasLogSeverity
    {
        /// <summary>
        /// Log severity.
        /// </summary>
        LogSeverity Severity { get; set; }
    }
}
View Code

Loghelper靜態類。調用logger實例(實現CastleIlogger接口)完成log操做

using System;
using System.Linq;
using Abp.Collections.Extensions;
using Abp.Dependency;
using Abp.Runtime.Validation;
using Castle.Core.Logging;

namespace Abp.Logging
{
    /// <summary>
    /// This class can be used to write logs from somewhere where it's a hard to get a reference to the <see cref="ILogger"/>.
    /// Normally, use <see cref="ILogger"/> with property injection wherever it's possible.
    /// </summary>
    public static class LogHelper
    {
        /// <summary>
        /// A reference to the logger.
        /// </summary>
        public static ILogger Logger { get; private set; }

        static LogHelper()
        {
            Logger = IocManager.Instance.IsRegistered(typeof(ILoggerFactory))
                ? IocManager.Instance.Resolve<ILoggerFactory>().Create(typeof(LogHelper))
                : NullLogger.Instance;
        }

        public static void LogException(Exception ex)
        {
            LogException(Logger, ex);
        }

        public static void LogException(ILogger logger, Exception ex)
        {
            var severity = (ex is IHasLogSeverity)
                    ? (ex as IHasLogSeverity).Severity
                    : LogSeverity.Error;

            logger.Log(severity, ex.Message, ex);

            LogValidationErrors(logger, ex);
        }

        private static void LogValidationErrors(ILogger logger, Exception exception)
        {
            //Try to find inner validation exception
            if (exception is AggregateException && exception.InnerException != null)
            {
                var aggException = exception as AggregateException;
                if (aggException.InnerException is AbpValidationException)
                {
                    exception = aggException.InnerException;
                }
            }

            if (!(exception is AbpValidationException))
            {
                return;
            }

            var validationException = exception as AbpValidationException;
            if (validationException.ValidationErrors.IsNullOrEmpty())
            {
                return;
            }

            logger.Log(validationException.Severity, "There are " + validationException.ValidationErrors.Count + " validation errors:");
            foreach (var validationResult in validationException.ValidationErrors)
            {
                var memberNames = "";
                if (validationResult.MemberNames != null && validationResult.MemberNames.Any())
                {
                    memberNames = " (" + string.Join(", ", validationResult.MemberNames) + ")";
                }

                logger.Log(validationException.Severity, validationResult.ErrorMessage + memberNames);
            }
        }
    }
}
View Code

 

LoggerExtensions擴展了CastleIlogger接口的方法,封裝更便捷的方法供Loghelper調用。

using System;
using Castle.Core.Logging;

namespace Abp.Logging
{
    /// <summary>
    /// Extensions for <see cref="ILogger"/>.
    /// </summary>
    public static class LoggerExtensions
    {
        public static void Log(this ILogger logger, LogSeverity severity, string message)
        {
            switch (severity)
            {
                case LogSeverity.Fatal:
                    logger.Fatal(message);
                    break;
                case LogSeverity.Error:
                    logger.Error(message);
                    break;
                case LogSeverity.Warn:
                    logger.Warn(message);
                    break;
                case LogSeverity.Info:
                    logger.Info(message);
                    break;
                case LogSeverity.Debug:
                    logger.Debug(message);
                    break;
                default:
                    throw new AbpException("Unknown LogSeverity value: " + severity);
            }
        }

        public static void Log(this ILogger logger, LogSeverity severity, string message, Exception exception)
        {
            switch (severity)
            {
                case LogSeverity.Fatal:
                    logger.Fatal(message, exception);
                    break;
                case LogSeverity.Error:
                    logger.Error(message, exception);
                    break;
                case LogSeverity.Warn:
                    logger.Warn(message, exception);
                    break;
                case LogSeverity.Info:
                    logger.Info(message, exception);
                    break;
                case LogSeverity.Debug:
                    logger.Debug(message, exception);
                    break;
                default:
                    throw new AbpException("Unknown LogSeverity value: " + severity);
            }
        }

        public static void Log(this ILogger logger, LogSeverity severity, Func<string> messageFactory)
        {
            switch (severity)
            {
                case LogSeverity.Fatal:
                    logger.Fatal(messageFactory);
                    break;
                case LogSeverity.Error:
                    logger.Error(messageFactory);
                    break;
                case LogSeverity.Warn:
                    logger.Warn(messageFactory);
                    break;
                case LogSeverity.Info:
                    logger.Info(messageFactory);
                    break;
                case LogSeverity.Debug:
                    logger.Debug(messageFactory);
                    break;
                default:
                    throw new AbpException("Unknown LogSeverity value: " + severity);
            }
        }
    }
}
View Code

在具體的web項目的application_start方法中注入logger實例。如下是注入log4net代碼

public class MvcApplication : AbpWebApplication
{
    protected override void Application_Start(object sender, EventArgs e)
    {
        IocManager.Instance.IocContainer.AddFacility<LoggingFacility>(f => f.UseLog4Net().WithConfig("log4net.config"));
        base.Application_Start(sender, e);
    }
}
相關文章
相關標籤/搜索