返回ABP系列

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="" />
<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);
}
}

基于DDD的.NET开发框架 - ABP日志Logger集成的更多相关文章

  1. 线上分享-- 基于DDD的.NET开发框架-ABP介绍

    前言 为了能够帮助.Net开发者开拓视野,更好的把最新的技术应用到工作中,我在3月底受邀到如鹏网.net训练营直播间为各位学弟学妹们进行ABP框架的直播分享.同时为了让更多的.NET开发者了解ABP框 ...

  2. 基于DDD的.NET开发框架 - ABP模块设计

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  3. 基于DDD的.NET开发框架 - ABP领域服务

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  4. 基于DDD的.NET开发框架 - ABP初探

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  5. 基于DDD的.NET开发框架 - ABP分层设计

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  6. 基于DDD的.NET开发框架 - ABP的Entity设计思想

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  7. 基于DDD的.NET开发框架 - ABP Session实现

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  8. 基于DDD的.NET开发框架 - ABP依赖注入

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  9. 基于DDD的.NET开发框架ABP实例,多租户 (Sass)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍

    介绍 基于ABPZERO的多租户 (Sass)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...

随机推荐

  1. 利用 druid 解析器解析SQL

    最近参与一个开源项目,一个功能的实现,用到了 druid 解析器来解析SQL,记录下如果使用 druid 来解析SQL,实现对SQL的拦截改写. 1. 对 insert 语句进行解析: private ...

  2. vue相关的 helloword示例

    <!DOCTYPE html><html> <head> <title></title> <script src="http ...

  3. 解决SQL死循环问题

    解决SQL死循环问题 当我们在执行在使用for循环的时候,往往会遇到死循环的问题,我们可以通过SSH来连接数据库,并结束其进程:代码如下: Su - db2inst1 Db2 connect to s ...

  4. C# 计算文件MD5

    因工作需要对文件进行是否被修改判断,整理的一段生成文件MD5码的代码: 1: public class FileHelper 2: { 3: /// <summary> 4: /// 对文 ...

  5. 《Remus: High Availability via Asychronous Virtual Machine Replication》翻译

    Abstract 想要让应用能够躲过硬件故障是一项非常昂贵的任务,因为这通常意味着对软件进行重构,使它包含复杂的恢复逻辑的同时需要部署专用的硬件,而这些对于提升大型的或者遗留的应用的可靠性是巨大的障碍 ...

  6. 我的Github之旅(一)

    第一站:本地环境中的Github配置 1.参考链接 作为初学者,需要了解的有[本地环境中的github配置(基于mac)][1],以及git知识,这里推荐一个网站[猴子都能懂的Git入门][2],最后 ...

  7. docker-image container 基本操作 -常用命令

    基本概念: container 容器.可以把每个 container 看做是一个独立的主机. container 的创建通常有一个 image 作为其模板.类比成虚拟机的话可以理解为 image 就是 ...

  8. VS 代码Diff 之Beyone Compare

    前提条件 机器已安装 beyone compared软件和 visual svn for vs 插件. 在VS中集成SVN,我推荐使用 visual svn扩展. visual svn 官网:http ...

  9. nginx 入门

    1.nginx 如何处理一个请求 IP,域名的处理 server { listen  80  default_server;     //添加 default_server就是一个默认的server ...

  10. hdu 1166

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...