日志系统实战(一)—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实战) 日志系统
前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块.相信你还记得,一条查询语句的执行过程一般是经过连接器.分析器.优化器.执行器等功能模块,最后到达存储引擎. 那么,一条更新语 ...
随机推荐
- 阿里云服务器上开启linux远程桌面连接
一.说明: 本文的目的是实现在windows机器上利用远程桌面连接来访问远程的linux桌面. 这里使用的是阿里云服务器,操作系统为Centos6.5. 二.基本步骤: 1.首先保证服务器已经安装完毕 ...
- POJ 1979 Red and Black【DFS】
标准DFS,统计遍历过程中遇到的黑点个数 #include<cstdio> #include<vector> #include<queue> #include< ...
- Ubuntu菜鸟入门(八)—— QQ安装
一.下载 http://www.ubuntukylin.com/application/show.php?lang=cn&id=279 二.安装流程 1.解压,到解压文件夹中运行 sudo d ...
- jsonp使用,spring4.x对jsonp的支持
1.Java中接口 @RequestMapping("/token/{token}") @ResponseBody public Object getUserByToken(@Pa ...
- 如何关闭eslint
在vue-cli搭建webpack中,使用eslint进行代码规范化检查. 进行关闭,在根目录下有个.eslintignore直接将不想要检查的文件丢进去就可以了 也可以在重构的时候把它关闭掉
- OSG 3D场景渲染编程概述
OSG是Open Scene Graphic的缩写,是基于C++平台的使用OpenGL技术的开源3D场景开发. vs环境安装或者是在Ubuntu中环境的安装网上教程很多,都是大同小异的,认真操作容易成 ...
- TopCoder SRM 639 Div.2 500 AliceGameEasy
题意: 一个游戏有n轮,有A和B比赛,谁在第 i 轮得胜,就获得 i 分,给出x,y,问A得x分,B得y分有没有可能,如果有,输出A最少赢的盘数 解题思路: 首先判断n(n+1)/2 = (x+y)是 ...
- SVG文件:从Illustrator导文件到Web
可缩放矢量图形(SVG)是早在1998年就已经有的一种矢量图像格式.它总是和Web一起发展,但是直到现在才开始赶上Web发展的步伐.如今我们已经不能否认SVG和Web的相关性,所以让我们来学习一下从I ...
- Android应用-听听
听听是一款记歌词音乐播放器android应用. 功能特点:1.搜索网络歌词.2.本地音乐分类播放.3.离线保存网络歌词.4.页面整洁干净,风格清新. 下载APP 屏幕截图:
- nginx android app 慢网络请求超时
最近遇到了android 在慢网络下面请求服务器报 java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by ...