构建自己的监测器【4】-java -D参数使用(转)

java中可以传入的一个参数:-D ,它的说明 是这样的:    

-D<name>=<value>  set a system property   设置一个系统参数和值

 

可以通过下面的代码获取到 java -D 的系统属性参数列表,代码如下:

 

[java]  view plain copy
 
  1. public static void main(String[] args) {          
  2. printProperties();  
  3. }  
  4.    
  5.     public static void printProperties(){  
  6.         Properties prop = System.getProperties();  
  7.         prop.list(System.out);  
  8. }  

 

输出结果如下:

 

[java]  view plain copy
 
  1. -- listing properties --  
  2. java.runtime.name=Java(TM) SE Runtime Environment  
  3. sun.boot.library.path=D:\java\jdk1.6.0_13\jre\bin  
  4. java.vm.version=11.3-b02  
  5. java.vm.vendor=Sun Microsystems Inc.  
  6. java.vendor.url=http://java.sun.com/  
  7. path.separator=;  
  8. java.vm.name=Java HotSpot(TM) Client VM  
  9. file.encoding.pkg=sun.io  
  10. user.country=CN  
  11. sun.java.launcher=SUN_STANDARD  
  12. sun.os.patch.level=Service Pack 3  
  13. java.vm.specification.name=Java Virtual Machine Specification  
  14. user.dir=D:\myspace\monitor  
  15. java.runtime.version=1.6.0_13-b03  
  16. java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment  
  17. java.endorsed.dirs=D:\java\jdk1.6.0_13\jre\lib\endorsed  
  18. os.arch=x86  
  19. java.io.tmpdir=C:\DOCUME~1\YONGKA~1.QIY\LOCALS~1\Temp\  
  20. line.separator=  
  21.    
  22. java.vm.specification.vendor=Sun Microsystems Inc.  
  23. user.variant=  
  24. os.name=Windows XP  
  25. sun.jnu.encoding=GBK  
  26. java.library.path=D:\java\jdk1.6.0_13\bin;.;C:\WINDOWS\...  
  27. java.specification.name=Java Platform API Specification  
  28. java.class.version=50.0  
  29. sun.management.compiler=HotSpot Client Compiler  
  30. os.version=5.1  
  31. user.home=C:\Documents and Settings\yongkang.qiyk  
  32. user.timezone=  
  33. java.awt.printerjob=sun.awt.windows.WPrinterJob  
  34. file.encoding=GBK  
  35. java.specification.version=1.6  
  36. user.name=yongkang.qiyk  
  37. java.class.path=D:\myspace\monitor\bin;D:\myspace\mon...  
  38. java.vm.specification.version=1.0  
  39. sun.arch.data.model=32  
  40. java.home=D:\java\jdk1.6.0_13\jre  
  41. java.specification.vendor=Sun Microsystems Inc.  
  42. user.language=zh  
  43. awt.toolkit=sun.awt.windows.WToolkit  
  44. java.vm.info=mixed mode  
  45. monitor.file=profile.txt  
  46. java.version=1.6.0_13  
  47. java.ext.dirs=D:\java\jdk1.6.0_13\jre\lib\ext;C:\WI...  
  48. sun.boot.class.path=D:\java\jdk1.6.0_13\jre\lib\resources...  
  49. java.vendor=Sun Microsystems Inc.  
  50. file.separator=\  
  51. java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...  
  52. sun.cpu.endian=little  
  53. sun.io.unicode.encoding=UnicodeLittle  
  54. sun.desktop=windows  
  55. sun.cpu.isalist=pentium_pro+mmx pentium_pro pentium+m...  

 

其中红色部分的参数是我自己在运行时JVM参数中设置进去的。

这样就可以将一个配置文件的值通过-D这个参数传入系统。

下面是一个具体的例子,这个例子和第三节说的例子基本一样,不一样的地方就在于要监测的方法不用写死在java代码中,而是放在了外部的一个配置文件中。

1.首先定义了一个读取-D参数和解析监测配置的接口:

 

[java]  view plain copy
 
  1. package monitor.agent;  
  2.    
  3. /** 
  4.  * 读取系统设置的参数,已经从参数中解析监测配置 
  5.  * @author yongkang.qiyk 
  6.  * 
  7.  */  
  8. public interface MonitorConfig {  
  9.       
  10.     public String getStringValue(String key,String defaultValue);  
  11.       
  12.     public boolean getBooleanValue(String key,boolean defautValue);  
  13.       
  14.     public Integer getIntegerValue(String key,Integer defaultValue);  
  15. }  

 

实现类如下:

 

[java]  view plain copy
 
  1. package monitor.agent;  
  2.    
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.util.Properties;  
  8.    
  9. /** 
  10.  * TODO Comment of MonitorConfigImpl 
  11.  * @author yongkang.qiyk 
  12.  * 
  13.  */  
  14. public class MonitorConfigImpl implements MonitorConfig {  
  15.       
  16.     private String MONITOR_CONF = "monitor.conf";  
  17.     private Properties prop   = null;  
  18.       
  19.     public MonitorConfigImpl() throws IOException{  
  20.         prop = System.getProperties();  
  21.         String monitorConf = prop.getProperty(MONITOR_CONF);  
  22.         File monitorFile = new File(monitorConf);  
  23.         InputStream inputStream = new FileInputStream(monitorFile);  
  24.         prop.load(inputStream);  
  25.     }  
  26.    
  27.     /* (non-Javadoc) 
  28.      * @see monitor.agent.MonitorConfig#getStringValue(java.lang.String, java.lang.String) 
  29.      */  
  30.     @Override  
  31.     public String getStringValue(String key, String defaultValue) {  
  32.         return prop.getProperty(key, defaultValue);  
  33.     }  
  34.    
  35.     /* (non-Javadoc) 
  36.      * @see monitor.agent.MonitorConfig#getBooleanValue(java.lang.String, boolean) 
  37.      */  
  38.     @Override  
  39.     public boolean getBooleanValue(String key, boolean defaultValue) {  
  40.         String value = prop.getProperty(key, null);  
  41.           
  42.         return (null!=value) ? Boolean.valueOf(value) : defaultValue;  
  43.     }  
  44.    
  45.     /* (non-Javadoc) 
  46.      * @see monitor.agent.MonitorConfig#getIntegerValue(java.lang.String, java.lang.Integer) 
  47.      */  
  48.     @Override  
  49.     public Integer getIntegerValue(String key, Integer defaultValue) {  
  50.         String value = prop.getProperty(key, null);  
  51.         return (null!=value) ? Integer.valueOf(value) : defaultValue;  
  52.     }  
  53.    
  54. }  

 

2.读取配置文件的接口实现完毕后,接着就是修改我们的修改字节码的类MonitorTransformer了。

修改如下:

 

[java]  view plain copy
 
  1. /** 
  2.  * TODO Comment of MonitorTransformer 
  3.  * @author yongkang.qiyk 
  4.  * 
  5.  */  
  6. public class MonitorTransformer implements ClassFileTransformer {  
  7.       
  8.     final static String prefix = "\nlong startTime = System.currentTimeMillis();\n";  
  9.     final static String postfix = "\nlong endTime = System.currentTimeMillis();\n";  
  10.     final static char point_regex = ';';  
  11.     final static List<String> methodList = new ArrayList<String>();  
  12. //    static{  
  13. //        methodList.add("monitor.agent.MyTest.sayHello");  
  14. //        methodList.add("monitor.agent.MyTest.sayHello2");  
  15. //    }  
  16.     public MonitorTransformer(){  
  17.         MonitorConfig config;  
  18.         try {  
  19.             //读取配置文件  
  20.             config = new MonitorConfigImpl();  
  21.             String methodStr = config.getStringValue("methodList"null);  
  22.             Iterable<String> it = Splitter.on(point_regex).split(methodStr);  
  23.             //将读取的配置文件加入要检测的方法列表  
  24.             if(null!=it){  
  25.                 Iterator<String> itor = it.iterator();  
  26.                 while (itor.hasNext()) {  
  27.                     methodList.add(itor.next());   
  28.                 }  
  29.             }  
  30.         } catch (IOException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.     }  
  34.    
  35.     /* (non-Javadoc) 
  36.      * @see java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[]) 
  37.      */  
  38.     @Override  
  39.     public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,  
  40.                             ProtectionDomain protectionDomain, byte[] classfileBuffer)  
  41.             throws IllegalClassFormatException {  
  42.         //先判断下现在加载的class的包路径是不是需要监控的类,通过instrumentation进来的class路径用‘/’分割  
  43.         if(className.startsWith("monitor/agent")){  
  44.             //将‘/’替换为‘.’m比如monitor/agent/Mytest替换为monitor.agent.Mytest  
  45.             className = className.replace("/"".");  
  46.             CtClass ctclass = null;  
  47.             try {  
  48.                 // 用于取得字节码类,必须在当前的classpath中,使用全称 ,这部分是关于javassist的知识  
  49.                 ctclass = ClassPool.getDefault().get(className);  
  50.             //循环一下,看看哪些方法需要加时间监测  
  51.             for(String method : methodList){  
  52.                 if (method.startsWith(className)){  
  53.                          //获取方法名  
  54.                         String methodName = method.substring(method.lastIndexOf('.')+1, method.length());  
  55.                         String outputStr = "\nSystem.out.println(\"this method "+methodName+" cost:\" +(endTime - startTime) +\"ms.\");";  
  56.                         //得到这方法实例  
  57.                         CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);  
  58.                         // 新定义一个方法叫做比如sayHello$impl    
  59.                         String newMethodName = methodName + "$impl";   
  60.                      // 原来的方法改个名字    
  61.                         ctmethod.setName(newMethodName);  
  62.                           
  63.                       //创建新的方法,复制原来的方法  ,名字为原来的名字  
  64.                         CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);  
  65.                         //构建新的方法体  
  66.                         StringBuilder bodyStr = new StringBuilder();   
  67.                         bodyStr.append("{");   
  68.                         bodyStr.append(prefix);    
  69.                         // 调用原有代码,类似于method();($$)表示所有的参数    
  70.                         bodyStr.append(newMethodName + "($$);\n");    
  71.                     
  72.                         bodyStr.append(postfix);   
  73.                         bodyStr.append(outputStr);   
  74.                     
  75.                         bodyStr.append("}");    
  76.                         // 替换新方法    
  77.                         newMethod.setBody(bodyStr.toString());   
  78.                         // 增加新方法    
  79.                         ctclass.addMethod(newMethod);    
  80.                 }  
  81.             }       
  82.                 return ctclass.toBytecode();  
  83.             } catch (IOException e) {  
  84.                 // TODO Auto-generated catch block  
  85.                 e.printStackTrace();  
  86.             } catch (CannotCompileException e) {  
  87.                 // TODO Auto-generated catch block  
  88.                 e.printStackTrace();  
  89.             } catch (NotFoundException e) {  
  90.                 // TODO Auto-generated catch block  
  91.                 e.printStackTrace();  
  92.             }  
  93.         }  
  94.         return null;  
  95.     }  
  96.    
  97. }  

 

可以看到最大的改进在哪里:原来写死的methodList被注释掉了,改为了在构造函数中通过刚才的配置文件读取类来读取要监测的方法,然后加入methodList中。如图:

 

3、MyAgent类没有任何修改,

 

[java]  view plain copy
 
  1. public class MyAgent {  
  2.       
  3.     public static void premain(String agentArgs, Instrumentation inst){  
  4.         inst.addTransformer(new MonitorTransformer());  
  5.     }  
  6. }  

 

 

4、MyTest也没有任何变化,只是运行时的JVM参数不一样了:

剪切板(2012-07-25 15_16_28).png

 

-Dmonitor.conf=D:/tools/java/profile.txt 参数指定了配置文件的路径。profile.txt的文件内容如下:

剪切板(2012-07-25 15_21_00).png

我将main方法也加入被监测的方法列表了,执行结果:

 

剪切板(2012-07-25 15_23_46).png

 

这样,我们下次要在监测另外一些方法的耗时时,再也不用修改任何代码,只要在profile.txt文件中配置要监测的方法就够了。。good..

猜你喜欢

转载自summer85.iteye.com/blog/2206427