首页 > 代码库 > 日志系统实战(一)-AOP静态注入

日志系统实战(一)-AOP静态注入

 

背景

近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息。这时候就想到用Aop的方式了。

技术分析

AOP分动态注入和静态注入。

动态注入方式

1:Remoting的ContextAttribute上下文(性能差)。

2:动态代理(反射),大多AOP框架都用这种方式。

3:MVC的filter,也是反射。

     第一种:性能太差不考虑。第二种:为了记日志,生产环境都用动态代理,性能损耗不小,不推荐。第三种:只有UI层能用。其他层和第二种一样。

静态注入方式 (本文重点)。

1:基于IL注入,损耗可以忽略不计。PostSharp采用的这种方式。

技术实现:

1:声明Attribute

public class WeaveSign:Attribute{}public class WeaveAction : Attribute{        }public class Log : WeaveAction{        public static void OnActionBefore(MethodBase mbBase)        {           //mbBase 要注入方法的所有信息            var t = mbBase.GetParameters();        LogManager.Record();       }}        

  

 

2:标记需要注入的方法

[Log]public static string GetUserName(int userId){        return "Vidar";}

 

3:IL注入(关键点),采用Mono.Cecil

 private static void Weave(IEnumerable<Assembly> assemblyList)        {            //assemblyList要注入的程序集列表。            foreach (var item in assemblyList)            {                var filepath = item.CodeBase.Substring(8, item.CodeBase.Length - 8);                //读取程序集                var assembly = AssemblyDefinition.ReadAssembly(filepath);                //获取WeaveSign类型的类型注入标记                var types = assembly.MainModule.Types.Where(n => n.CustomAttributes.Any(y => y.AttributeType.Resolve().Name == "WeaveSign"));                foreach (var type in types)                {                    foreach (var method in type.Methods)                    {                        //获取WeaveAction类型的方法注入标记                        var attrs =                            method.CustomAttributes.Where(y => y.AttributeType.Resolve().BaseType.Name == "WeaveAction");                        foreach (var attr in attrs)                        {                            //还原类型                            var resolve = attr.AttributeType.Resolve();                            //获取IL容器                            var ilProcessor = method.Body.GetILProcessor();                            var firstInstruction = ilProcessor.Body.Instructions.First();                            //找到标记类型的OnActionBefore方法。                            var onActionBefore = resolve.GetMethods().Single(n => n.Name == "OnActionBefore");                            //创建System.Reflection.MethodBase.GetCurrentMethod()方法引用                            var mfReference=assembly.MainModule.Import(typeof (System.Reflection.MethodBase).GetMethod("GetCurrentMethod"));                            //注入到IL(调用GetCurrentMethod,入栈)                            ilProcessor.InsertBefore(firstInstruction,                                ilProcessor.Create(OpCodes.Call,mfReference));                            //创建调用(call)标记类型的方法OnActionBefore                            ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, onActionBefore));                        }                    }                }                if (types.Any())                {                    //写入程序集                    assembly.Write(filepath);                }            }        }

3:编译成功后。反编译看到如下代码。

 

IL

  .method public hidebysig static string GetUserName(int32 userId) cil managed{    .custom instance void TestLibrary.Log::.ctor()    .maxstack 1    .locals init (        [0] string str)    L_0000: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetCurrentMethod()    L_0005: call void TestLibrary.Log::OnActionBefore(class [mscorlib]System.Reflection.MethodBase)    L_000a: nop     L_000b: ldstr "Vidar"    L_0010: stloc.0     L_0011: br.s L_0013    L_0013: ldloc.0     L_0014: ret } 

C#

[Log]public static string GetUserName(int userId){    Log.OnActionBefore(MethodBase.GetCurrentMethod());    return "Vidar";}

 

补充:

本文侧重的是核心实现思路。

 

日志系统实战(一)-AOP静态注入