qing2005原文地址 C#实现自动化Log日志
在开发项目的时候,我们不免要使用Log记录日志,使用最多的是Log4Net和EntLib Log,在需要记录日志的代码处加入log.Write(日志信息),假设我需要跟踪业务方法,记录方法的传递参数,执行时间,返回数据等;或者我需要查 看方法的调用关系,希望进入方法的时候自动记录参数信息,出方法时记录结果和执行时间信息。这时就是一个典型的AOP运用,Java在AOP方面是很容易 实现的,因为java有类加载器。但是.Net在AOP方面就不怎么容易,严格意义上.Net没有真正的AOP。这话并不代表.Net不能实现AOP,比 如:PostSharp和Enterprise library就能实现。
先介绍一下PostSharp,我们知道.net代码将编译成MSIL(微软中间语言),然后CPU将MSIL的exe文件生成本地CPU的二进制文件格式,PostSharp就是在编译过程中加入IL代码,因而完成AOP功能。
缺点:编译器需要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记录日志;
方法结束后,再次记录日志,并统计执行时间和异常处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
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 != null
if
(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标记属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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来处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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>()来创建对象,不然无法实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
<?
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文件内容:
实现了自动化Log后,回过头来再看第5步,Employee继承了MarshalByRefObject,一般我们的业务类或数据访问类都有基类,那么我们就需要使用接口
这里我添加一个IEmployee接口,里面就Work方法(ToString是重写Object的)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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);
}
}
}
|
然后在测试类改动一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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);
}
}
}
|