ABP是“ASP.NET Boilerplate Project (ASP.NET樣板項目)”的簡稱。
ASP.NET Boilerplate是一個用最佳實踐和流行技術開發現代WEB應用程序的新起點,它旨在成為一個通用的WEB應用程序框架和項目模板。
ABP的官方網站:http://www.aspnetboilerplate.com
ABP官方文檔:http://www.aspnetboilerplate.com/Pages/Documents
Github上的開源項目:https://github.com/aspnetboilerplate
一、基本概念
ABP使用Castle日志記錄工具,並且可以使用不同的日志類庫,比如:Log4Net, NLog, Serilog... 等。對於所有的日志類庫,Castle提供了一個通用的接口來實現,我們可以很方便的處理各種特殊的日志庫,而且當業務需要的時候,很容易替換日志組件。
Castle是什么:Castle是針對.NET平台的一個開源項目,從數據訪問框架ORM到IOC容器,再到WEB層的MVC框架、AOP,基本包括了整個開發過程中的所有東西。ASP.NET Boilerplate的ioc容器就是通過Castle實現的。
Log4Net是.Net中最流行的日志類庫之一。ABP模板中自帶了經過合適配置的Log4Net。但是,只存在一行log4net的依賴(看下面),因此,你可以將它改為你最喜歡的類庫。
獲取Logger:
無論你選擇了什么日志類庫,最終要記錄的日志代碼都是相同的
一開始,我們要處理一下記錄日志的Logger對象。因為ABP強烈推薦使用依賴注入,所以我們可以使用屬性注入模式輕松地注入一個Logger對象。如下所示:
using Castle.Core.Logging; //1、導入 Logging 命名空間 public class TaskAppService : ITaskAppService { //2、使用屬性注入獲得 logger public ILogger Logger { get; set; } public TaskAppService() { //3、如果沒有提供Logger,就不能記錄日志 Logger = NullLogger.Instance; } public void CreateTask(CreateTaskInput input) { //4、記錄日志 Logger.Info("Creating a new task with description: " + input.Description); //TODO: 保存到數據庫... } }
1、導入Castle的ILogger接口的命名空間。
2、定義一個公有的叫做Logger的ILogger對象。這是記錄日志的對象。創建TaskAppService對象之后,依賴注入系統會自動注入這個屬性。這就是所謂的屬性注入模式。
3、將Logger設置為NullLogger.Instance。即使沒有這行代碼,系統也會工作地很好。但是這是屬性注入模式的最佳實踐。如果沒給Logger設置任何值,那么當我們使用它的時候會因為它是null而拋出“空指針”異常。這個保證了它不為null。因此,如果沒有給Logger設置值,那么它是NullLogger。這就是所謂的null對象模式。NullLogger實際上什么都沒做,也沒有記錄任何日志。因此,我們的類要不要一個實際的logger都能工作。
4、最后,我們記錄了一個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...');
二、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 } }
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; } } }
Loghelper: 靜態類。調用logger實例(實現Castle的Ilogger接口)完成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); } } } }
LoggerExtensions: 擴展了Castle的Ilogger接口的方法,封裝更便捷的方法供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); } } } }
在具體的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); } }