概述
在开发项目的时候,我们不免要使用Log记录日志,使用最多的是Log4Net和EntLib Log,在需要记录日志的代码处加入log.Write(日志信息),假设我需要跟踪业务方法,记录方法的传递参数,执行时间,返回数据等;或者我需要查看方法的调用关系,希望进入方法的时候自动记录参数信息,出方法时记录结果和执行时间信息。这时就是一个典型的AOP运用,Java在AOP方面是很容易实现的,因为java有类加载器。但是.Net在AOP方面就不怎么容易,严格意义上.Net没有真正的AOP。这话并不代表.Net不能实现AOP,比如:PostSharp和Enterprise library就能实现。.
缺点:编译器需要PostSharp组件,维护代码困难,因为IL代码不好识别;
优点:使用方便(PostSharp2是收费版,破解也比较方便,在此不介绍破解)
这里我重点介绍如何使用Enterprise Library实现自动化Log。
实现
1.首先我们需要下载Enterprise Library,最新为5.0版本;
2.新建一个控制台项目,并添加以下程序集
Microsoft.Practices.EnterpriseLibrary.Common
Microsoft.Practices.EnterpriseLibrary.Logging
Microsoft.Practices.EnterpriseLibrary.PolicyInjection
Microsoft.Practices.ServiceLocation
Microsoft.Practices.Unity
Microsoft.Practices.Unity.Interception
3.添加AutoLogCallHandler类,实现ICallHandler接口
这个类是执行调用目标方法,在调用目标方法前获取方法的参数信息,并用EntLib Log记录日志;
方法结束后,再次记录日志,并统计执行时间和异常处理
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Practices.Unity.InterceptionExtension;using Microsoft.Practices.EnterpriseLibrary.Logging;using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;using System.Diagnostics;using System.Reflection;namespace AutoLog {public class AutoLogCallHandler:ICallHandler {private LogWriter logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();public AutoLogCallHandler() { }public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) {StringBuilder sb = null;ParameterInfo pi = null;string methodName = input.MethodBase.Name;logWriter.Write(string.Format("Enter method " + methodName));if (input.Arguments != null && input.Arguments.Count > 0) {sb = new StringBuilder();for (int i = 0; i < input.Arguments.Count; i++) {pi = input.Arguments.GetParameterInfo(i);sb.Append(pi.Name).Append(" : ").Append(input.Arguments[i]).AppendLine();}logWriter.Write(sb.ToString());}Stopwatch sw = new Stopwatch();sw.Start();IMethodReturn result = getNext()(input, getNext);//如果发生异常则,result.Exception != nullif (result.Exception != null) {logWriter.Write("Exception:" + result.Exception.Message);//必须将异常处理掉,否则无法继续执行result.Exception = null;}sw.Stop();logWriter.Write(string.Format("Exit method {0}, use {1}.",methodName, sw.Elapsed));return result;}public int Order { get; set; }}}
4.要自动化日志就需要创建一个标记属性,指定方法能自动进行日志
这里就创建AutoLogCallHandlerAttribute标记属性
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Practices.Unity.InterceptionExtension;using Microsoft.Practices.EnterpriseLibrary.Logging;using System.Diagnostics;using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;namespace AutoLog {public class AutoLogCallHandlerAttribute:HandlerAttribute {public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container) {return new AutoLogCallHandler() { Order = this.Order };}}}
5.创建实体类
注意:我在Work和ToString方法上方加上了AutoLogCallHandler属性,它是AutoLogCallHandlerAttribute的简写形式。用以指示这两个方法用AutoLogCallHandler的Invoke来处理。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Practices.Unity;namespace AutoLog {public class Employee : MarshalByRefObject{public Employee() {}public string Name { get; set; }[AutoLogCallHandler()]public void Work() {Console.WriteLine("Now is {0},{1} is working hard!",DateTime.Now.ToShortTimeString(),Name);throw new Exception("Customer Exception");}[AutoLogCallHandler()]public override string ToString() {return string.Format("I'm {0}.",Name);}}}
6.测试代码
注意:必须使用PolicyInjection.Create<Employee>()来创建对象,不然无法实现。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Practices.EnterpriseLibrary.PolicyInjection;using Microsoft.Practices.Unity;using Microsoft.Practices.EnterpriseLibrary.Logging;using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;namespace AutoLog {class Program {private static LogWriter logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();static void Main(string[] args) {Employee emp = PolicyInjection.Create<Employee>();emp.Name = "Lele";emp.Work();Console.WriteLine(emp);}}}
7.还需要用EntLib的配置工具完成Log配置,将Log信息写入Trace.log文件中
<?xml version="1.0"?><configuration><configSections><section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /></configSections><loggingConfiguration name="" tracingEnabled="true" defaultCategory="General"><listeners><add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"fileName="trace.log" formatter="Text Formatter" /></listeners><formatters><add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"template="Timestamp: {timestamp}{newline}Message: {message}{newline}Category: {category}{newline}Priority: {priority}{newline}EventId: {eventid}{newline}Severity: {severity}{newline}Title:{title}{newline}Machine: {localMachine}{newline}App Domain: {localAppDomain}{newline}ProcessId: {localProcessId}{newline}Process Name: {localProcessName}{newline}Thread Name: {threadName}{newline}Win32 ThreadId:{win32ThreadId}{newline}Extended Properties: {dictionary({key} - {value}{newline})}"name="Text Formatter" /></formatters><categorySources><add switchValue="All" name="General"><listeners><add name="Flat File Trace Listener" /></listeners></add></categorySources><specialSources><allEvents switchValue="All" name="All Events" /><notProcessed switchValue="All" name="Unprocessed Category" /><errors switchValue="All" name="Logging Errors & Warnings"><listeners><add name="Flat File Trace Listener" /></listeners></errors></specialSources></loggingConfiguration><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
好了,测试一下,控制台输入:
Now is 14:03,Lele is working hard!
I'm Lele.
再看看Trace.log文件内容:
----------------------------------------Timestamp: 2012/3/19 6:03:00Message: Enter method WorkCategory: GeneralPriority: -1EventId: 1Severity: InformationTitle:Machine: PC4App Domain: AutoLog.exeProcessId: 4200Process Name: D:\Codes\Enterprise Library\Enterprise Library Demos\PIAB\Demo\AutoLog\bin\Debug\AutoLog.exeThread Name:Win32 ThreadId:4000Extended Properties:--------------------------------------------------------------------------------Timestamp: 2012/3/19 6:03:00Message: Exception:Customer ExceptionCategory: GeneralPriority: -1EventId: 1Severity: InformationTitle:Machine: PC4App Domain: AutoLog.exeProcessId: 4200Process Name: D:\Codes\Enterprise Library\Enterprise Library Demos\PIAB\Demo\AutoLog\bin\Debug\AutoLog.exeThread Name:Win32 ThreadId:4000Extended Properties:--------------------------------------------------------------------------------Timestamp: 2012/3/19 6:03:00Message: Exit method Work, use 00:00:00.0024272.Category: GeneralPriority: -1EventId: 1Severity: InformationTitle:Machine: PC4App Domain: AutoLog.exeProcessId: 4200Process Name: D:\Codes\Enterprise Library\Enterprise Library Demos\PIAB\Demo\AutoLog\bin\Debug\AutoLog.exeThread Name:Win32 ThreadId:4000Extended Properties:--------------------------------------------------------------------------------Timestamp: 2012/3/19 6:03:00Message: Enter method ToStringCategory: GeneralPriority: -1EventId: 1Severity: InformationTitle:Machine: PC4App Domain: AutoLog.exeProcessId: 4200Process Name: D:\Codes\Enterprise Library\Enterprise Library Demos\PIAB\Demo\AutoLog\bin\Debug\AutoLog.exeThread Name:Win32 ThreadId:4000Extended Properties:--------------------------------------------------------------------------------Timestamp: 2012/3/19 6:03:00Message: Exit method ToString, use 00:00:00.0001410.Category: GeneralPriority: -1EventId: 1Severity: InformationTitle:Machine: PC4App Domain: AutoLog.exeProcessId: 4200Process Name: D:\Codes\Enterprise Library\Enterprise Library Demos\PIAB\Demo\AutoLog\bin\Debug\AutoLog.exeThread Name:Win32 ThreadId:4000Extended Properties:----------------------------------------
实现了自动化Log后,回过头来再看第5步,Employee继承了MarshalByRefObject,一般我们的业务类或数据访问类都有基类,那么我们就需要使用接口
这里我添加一个IEmployee接口,里面就Work方法(ToString是重写Object的)。
using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Practices.Unity;namespace AutoLog {public interface IEmployee {void Work();}public class Employee : IEmployee{public Employee() {//this.Name = "Lele";}public string Name { get; set; }[AutoLogCallHandler()]public void Work() {Console.WriteLine("Now is {0},{1} is working hard!",DateTime.Now.ToShortTimeString(),Name);throw new Exception("Customer Exception");}[AutoLogCallHandler()]public override string ToString() {return string.Format("I'm {0}.",Name);}}}
然后在测试类改动一下
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Practices.EnterpriseLibrary.PolicyInjection;using Microsoft.Practices.Unity;using Microsoft.Practices.EnterpriseLibrary.Logging;using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;namespace AutoLog {class Program {private static LogWriter logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();static void Main(string[] args) {IEmployee emp = PolicyInjection.Create<Employee, IEmployee>();emp.Work();Console.WriteLine(emp);}}}