日志系统实战(一)—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实战) 日志系统
前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块.相信你还记得,一条查询语句的执行过程一般是经过连接器.分析器.优化器.执行器等功能模块,最后到达存储引擎. 那么,一条更新语 ...
随机推荐
- 再见,OI
你好,NOIP 2015年9月1日 正式成为了福建省莆田一中的一名高一成员 后来学校搞了选修 大家都很激动 因为自己的兴趣和特长能够得到发挥了(或者说能逃课或者看好多电影) 发现选修提供的选项中有好几 ...
- linux内核(kernel)版本号的意义
转自:http://www.cnblogs.com/jsjliuxing/archive/2011/12/01/2271182.html 在linux下有一个目录,即/usr/src/kernels/ ...
- 远程debug调试java代码
远程debug调试java代码 日常环境和预发环境遇到问题时,可以用远程调试的方法本地打断点,在本地调试.生产环境由于网络隔离和系统稳定性考虑,不能进行远程代码调试. 整体过程是通过修改远程服务JAV ...
- SVN基本指令
Svn使用: Versions: Cornerstone: 如果命令行不出来,可以设置commonsline Tools 命令行:svn heip 获取服务器端数据 svn checkout 远端ur ...
- C++ 代码优化
1.类中所有的属性全部设置为private 然后在需要在外部调用的属性,每个都自己写Get方法返回,或者用Set方法设置 2.类成员变量采用m_前缀,代表类成员 3.采用单例模式 //设置类名为CCo ...
- React Native MAC上环境搭建笔记
今天花了一点时间搭建了一下react native环境,在这个过程中遇到了一些问题,处理并总结一下,年纪大了记性不好,只能多写写...真是岁月不饶人啊! 第一步:安装最新版本的Xcode工具 第二步: ...
- js判断图片是否加载完成
var img = new Image(); //新建一个图片对象:img.src = ...; //图片地址是你准备要加载的地址:if(img.complete){ //表示图片已经加载完成}
- underscorejs 源码走读笔记
Underscore 简介 Underscore 是一个JavaScript实用库,提供了类似Prototype.js的一些功能,但是没有继承任何JavaScript内置对象.它弥补了部分jQuery ...
- Day 1:开始重新学习
离开很久,前几天翻出以前做过的程序居然还能正常运行.有一点后悔,为什么当初没有坚持做下去.Delphi园地前一阵也曾经宣布要关站,但仍然坚持过来了,在此向站长致敬!我也要重新开始! 附图:Delphi ...
- Docker Volume 之权限管理(转)
Volume数据卷是Docker的一个重要概念.数据卷是可供一个或多个容器使用的特殊目录,可以为容器应用存储提供有价值的特性: 持久化数据与容器的生命周期解耦:在容器删除之后数据卷中的内容可以保持.D ...