日志系统实战(一)—AOP静态注入
背景
近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息,这时就想到用Aop注入的方式。
AOP分动态注入和静态注入两种注入的方式。
动态注入方式
- 利用Remoting的ContextBoundObject或MarshalByRefObject。
- 动态代理(反射),很多AOP框架都用这种方式。
- 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静态注入的更多相关文章
- 日志系统实战(二)-AOP动态获取运行时数据
介绍 这篇距上一篇已经拖3个月之久了,批评自己下. 通过上篇介绍了解如何利用mono反射代码,可以拿出编译好的静态数据.例如方法参数信息之类的. 但实际情况是往往需要的是运行时的数据,就是用户输入等外 ...
- 日志系统实战(三)-分布式跟踪的Net实现
介绍 在大型系统开发调试中,跨系统之间联调开始变得不好使了.莫名其妙一个错误爆出来了,日志虽然有记录,但到底是哪里出问题了呢? 是Ios端参数传的不对?还是A系统或B系统提供的接口导致?相信有不少人遇 ...
- 日志系统实战 AOP静态注入
http://www.cnblogs.com/mushroom/p/3932698.html http://www.cnblogs.com/mushroom/p/4124878.html http:/ ...
- Linux系统实战项目——sudo日志审计
Linux系统实战项目——sudo日志审计 由于企业内部权限管理启用了sudo权限管理,但是还是有一定的风险因素,毕竟运维.开发等各个人员技术水平.操作习惯都不相同,也会因一时失误造成误操作,从而 ...
- 实战3--项目开始--准备:::资源分类, 日志系统, 写BaseDao
项目资源分类: 1. package: base, dao, dao.impl, domain, service, service.impl, util, view.action 2. co ...
- Spring Boot 揭秘与实战(三) 日志框架篇 - 如何快速集成日志系统
文章目录 1. 默认的日志框架 logback2. 常用的日志框架 log4j 1.1. 日志级别 1.2. 日志文件 3. 源代码 Java 有很多日志系统,例如,Java Util Logging ...
- 记一次基于springboot+aop实现日志记录实战
1. 为什么要记录日志 好处: a. 可以对一些重要功能进行记录,方便以后跟踪是谁操作此功能的. b. 在操作某些功能时可能会发生异常,但每次出现异常我们想定位日志都要去服务器查看我们的日志.有了日志 ...
- Ansible实战:部署分布式日志系统
本节内容: 背景 分布式日志系统架构图 创建和使用roles JDK 7 role JDK 8 role Zookeeper role Kafka role Elasticsearch role My ...
- 2 (mysql实战) 日志系统
前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块.相信你还记得,一条查询语句的执行过程一般是经过连接器.分析器.优化器.执行器等功能模块,最后到达存储引擎. 那么,一条更新语 ...
随机推荐
- TDD学习笔记【四】--- 如何隔离相依性 - 基本的可测试性
前言 相信许多读者都听过「可测试性」,甚至被它搞的要死要活的,还觉得根本是莫名其妙,徒劳无功.今天这篇文章,主要要讲的是对象的相依性,以及对象之间直接相依,会带来什么问题.为了避免发生因相依性而导致设 ...
- xpath定位实战(1)
1.执行scrapy shell "https://book.douban.com/subject/2256039/"
- 解决安装mysql的”A Windows service with the name MySQL already exists.“问题
如果以前安装过mysql,卸载重装,很可能会碰到"A Windows service with the name MySQL already exists."这样的提示.即服务已经 ...
- [spring源码学习]六、IOC源码-BeanFactory和factory-bean
一.代码实例 在我们分析spring的IOC源码的时候,发现除了配置标准的bean,并且通过getBean(beanName)的方法获取到一个bean的实例外,似乎还有这不少其他获取的方法,例如在第四 ...
- 【转载】给那些想多学习,多进步的Domino初学者
在这个社区里面,包括QQ技术群里面混了很久了.遇到了很多Domino初学者,也认识了很多致力于Domino这个技术领域的朋友,很开心.很久没有写长篇大论给大家了.我要把一些想法写出来,共大家参考.讨论 ...
- Java RMI之HelloWorld篇
Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...
- js jquery中判断checkbox是否被选中的方法
在js中: document.getElementById("checkboxID").checked 返回true或者false jQuery中: $("input ...
- 使用mongodump将mongodb数据备份带JOSN文件
备份: 首先,mongodb要连接到指定数据库 在指定数据库目录下,shift+右键--在此次打开命令行--输入以下命令: mongodump -d databaseName -o backup 备份 ...
- 面试中常问的有关随机选取k个数的总结
1.在半径为1的圆中随机选取一点. 2.给定一个未知长度的整数流,如何随机选取一个数 3.给定一个数据流,其中包含无穷尽的搜索关键字(比如,人们在谷歌搜索时不断输入的关键字).如何才能从这个无穷尽的流 ...
- Ajax跨域问题的两种解决方法
浏览器不允许Ajax跨站请求,所以存在Ajax跨域问题,目前主要有两种办法解决. 1.在请求页面上使用Access-Control-Allow-Origin标头. 使用如下标头可以接受全部网站请求: ...