背景

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

AOP分动态注入和静态注入两种注入的方式。

动态注入方式

  1. 利用Remoting的ContextBoundObject或MarshalByRefObject。
  2. 动态代理(反射),很多AOP框架都用这种方式。
  3. MVC的filter,也是反射。

第一种性能太差,必须继承基类等,所以不考虑。

第二种为了记日志,大量动态生成代理类,性能损耗不小,不建议生产环节推荐。

第三种MVC只能进行UI层的拦截,其他层需要实现自行实现动态拦截,跟第二种实现方式一样。

静态注入方式

基于Net的IL语言层级进行注入,性能损耗可以忽略不计,Net使用最多的Aop框架PostSharp采用的即是这种方式。

技术实现

声明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();
}
}

标记需要注入的方法

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

IL注入关键的地方,这里使用Mono.Cecil进行IL分析和写入。

public static void AutoWeave()
{
var assemblies = BuildManager.GetReferencedAssemblies();
var result =
assemblies.Cast<Assembly>().Where(assembly =>!assembly.FullName.StartsWith("System.", StringComparison.OrdinalIgnoreCase)); Weave(result);
} 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);
}
}
}

为了演示简便,没有在MsBuild期间进行注入,而是单写了个测试页面直接触发进行代码注入。

  protected void Page_Load(object sender, EventArgs e)
{
CodeWeaver.AutoWeave();
}

运行成功后,反编译注入的DLL看到的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";
}

Mono.Cecil地址 http://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cecil/

日志系统实战(一)—AOP静态注入的更多相关文章

  1. 日志系统实战(二)-AOP动态获取运行时数据

    介绍 这篇距上一篇已经拖3个月之久了,批评自己下. 通过上篇介绍了解如何利用mono反射代码,可以拿出编译好的静态数据.例如方法参数信息之类的. 但实际情况是往往需要的是运行时的数据,就是用户输入等外 ...

  2. 日志系统实战(三)-分布式跟踪的Net实现

    介绍 在大型系统开发调试中,跨系统之间联调开始变得不好使了.莫名其妙一个错误爆出来了,日志虽然有记录,但到底是哪里出问题了呢? 是Ios端参数传的不对?还是A系统或B系统提供的接口导致?相信有不少人遇 ...

  3. 日志系统实战 AOP静态注入

    http://www.cnblogs.com/mushroom/p/3932698.html http://www.cnblogs.com/mushroom/p/4124878.html http:/ ...

  4. Linux系统实战项目——sudo日志审计

    Linux系统实战项目——sudo日志审计   由于企业内部权限管理启用了sudo权限管理,但是还是有一定的风险因素,毕竟运维.开发等各个人员技术水平.操作习惯都不相同,也会因一时失误造成误操作,从而 ...

  5. 实战3--项目开始--准备:::资源分类, 日志系统, 写BaseDao

     项目资源分类: 1.   package: base, dao, dao.impl, domain, service, service.impl, util, view.action 2.   co ...

  6. Spring Boot 揭秘与实战(三) 日志框架篇 - 如何快速集成日志系统

    文章目录 1. 默认的日志框架 logback2. 常用的日志框架 log4j 1.1. 日志级别 1.2. 日志文件 3. 源代码 Java 有很多日志系统,例如,Java Util Logging ...

  7. 记一次基于springboot+aop实现日志记录实战

    1. 为什么要记录日志 好处: a. 可以对一些重要功能进行记录,方便以后跟踪是谁操作此功能的. b. 在操作某些功能时可能会发生异常,但每次出现异常我们想定位日志都要去服务器查看我们的日志.有了日志 ...

  8. Ansible实战:部署分布式日志系统

    本节内容: 背景 分布式日志系统架构图 创建和使用roles JDK 7 role JDK 8 role Zookeeper role Kafka role Elasticsearch role My ...

  9. 2 (mysql实战) 日志系统

    前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块.相信你还记得,一条查询语句的执行过程一般是经过连接器.分析器.优化器.执行器等功能模块,最后到达存储引擎. 那么,一条更新语 ...

随机推荐

  1. 从ord()中对Unicode编码的理解

    刚开始学习编程的时候,老对字符串编码的理解模模糊糊.也一直看这方便的资料,今天在看Dive in python时,突然有了新的理解(不知道是否正确). Python有个built-in函数ord(), ...

  2. 解决报错 ora-00704 ora-00604 ora-00942 启动不了数据库问题

    早上海南的同事打电话说他们的审计库连不上了启动也报错,问了下最近做了些什么操作,答复是之前添加了一次磁盘. 猜测是添加磁盘启动后/dev/sdx顺序出错,或者没有正常的关闭数据库导致数据库无法正常启动 ...

  3. DeepLearning——CNN

    工具箱下载 https://github.com/rasmusbergpalm/DeepLearnToolbox CNN_demo代码解析 http://blog.csdn.net/zouxy09/a ...

  4. c语言快速入门3

    如果你想快速入门计算机,可以参考我的上一篇帖子,先了解一些必备的软知识,然后再来进行语言的快速入门 计算机入门基础知识 c语言快速入门1 c语言快速入门2 3.4.1 字符和字符串 字符:'' 单个  ...

  5. 【CentOS】LNMP

    本文为博主JerryChan所有,如需转载,请联系博主747618706@qq.com,并附上博客链接/////////////////目录//////////////////一.LNMP的安装 1. ...

  6. linux 常用命令

    //创建目录mkdir//创建中间没有路径的文件夹mkdir -p //删除文件rm//强制删除文件rm -f//删除目录rmdir//删除多个目录rmdir -p //输出当前环境根路径echo $ ...

  7. Gym 100646 F Tanks a Lot RMQ

    Problem F: Tanks a Lot Imagine you have a car with a very large gas tank - large enough to hold what ...

  8. 谷歌浏览器如何查看或获取Cookie字符串

    注:此博客仅供非web开发人员查看,以下内容都基于谷歌浏览器. 在网页空白处点击鼠标右键,在弹出菜单中选择[审查元素],可以看到网页下方出现审查元素相关界面. 在审查元素相关界面,点击[Network ...

  9. delay(和setTimeout()的区别

    近来几日在写游戏代码时,频繁会用到定时器,偶尔想到有个.delay()方法,用了几次发现两者效果相差很大,遂就仔细考究了一下两者的区别! 1. setTimeout函数是从页面开始的时候计算time的 ...

  10. Ubuntu install JDK适合像我的小白

    1.#下载JDK,记住保存的目录 2. sudo mkdir /usr/java 3. sudo tar zxvf jdk-7u75-linux-x64.tar.gz -C /usr/java 4. ...