Using Javassist to log method calls and argument values, how to make a logger class visible in every instrumented class?

天涯浪子 提交于 2019-12-05 15:22:45

According to the documentation for Java agents the agent class is loaded by the system classloader. But if you want to instrument core Java classes and refer from those to a custom class of your own then that class would need to be available to the bootstrap classloader rather than the system one.

Move your Logger class into a separate JAR file, and list that file in the Boot-Class-Path attribute of the agent JAR's manifest:

Boot-Class-Path: ParaTracerLogger.jar

Now the logger class is visible on the bootstrap loader and can be seen by the instrumented java.lang classes.

I ended up having the ClassFileTransformer.tranform method as follows:

Basically, the code inserted by Javassist reopens the same log file every time there is a message to be written out and appends that message to the output file. Having a separate log file was important because redirecting standard output/error will result in contaminated log files if those streams are already used by the instrumented application (as is usually the case).

@Override
public byte[] transform(
        ClassLoader       loader,
        String            className,
        Class<?>          classBeingRedefined,
        ProtectionDomain  protectionDomain,
        byte[]            classfileBuffer)
                throws IllegalClassFormatException {

    byte[] byteCode = classfileBuffer;

    String normalizedClassName = className.replaceAll("/", ".");
    System.out.println( "\tNormalized: " + normalizedClassName );

    ClassMonitorSet classMonitorSet = monitorClass( normalizedClassName );
    if ( classMonitorSet != null ) {
        System.out.println( "\tMonitoring: " + normalizedClassName );
        try {
            ClassPool cp = ClassPool.getDefault();
            CtClass cc = cp.get( normalizedClassName );

            for( String methodName : classMonitorSet.monitorSet ) {
                CtMethod[]  methods  = cc.getDeclaredMethods( methodName );
                for( CtMethod method : methods ) {

                    StringBuilder sbs = new StringBuilder();
                    sbs.append( "long tid = Thread.currentThread().getId();" );
                    sbs.append( "StringBuilder sbArgs = new StringBuilder();" );
                    sbs.append( "sbArgs.append( System.identityHashCode( $0 ) );" );
                    CtClass[] pTypes = method.getParameterTypes();
                    for( int i=0; i < pTypes.length; ++i ) {
                        CtClass pType = pTypes[i];
                        if ( pType.isPrimitive() ) {
                            sbs.append( "sbArgs.append( \", \" + $args[" + i + "] );" );
                        } else {
                            sbs.append( "sbArgs.append( \", \" + System.identityHashCode( $args[" + i + "] ) );" );
                        }
                    }
                    sbs.append( "StringBuilder sb = new StringBuilder();" );
                    sbs.append( "sb.append( tid + \" : " + method.getLongName() + ".<START>(\" );" );
                    sbs.append( "sb.append( sbArgs.toString() );" );
                    sbs.append( "sb.append( \")\" );" );
                    sbs.append( "String fPath = \"/path/to/log.out\";" );
                    sbs.append( "try {" );
                    sbs.append( "   java.io.FileWriter  fw  = new java.io.FileWriter( fPath, true );" );
                    sbs.append( "   java.io.PrintWriter out = new java.io.PrintWriter( fw, true );" );
                    sbs.append( "   out.println( sb.toString() );" );
                    sbs.append( "   fw.close();" );
                    sbs.append( "} catch (java.io.IOException e) {" );
                    sbs.append( "   e.printStackTrace();" );
                    sbs.append( "}" );

                    method.insertBefore("{" + sbs.toString() + "}");

                    StringBuilder sbe = new StringBuilder();
                    sbe.append( "long tid = Thread.currentThread().getId();" );
                    sbe.append( "StringBuilder sb = new StringBuilder();" );
                    sbe.append( "sb.append( tid + \" : " + method.getLongName() + ".<END>(*)\" );" );
                    sbe.append( "String fPath = \"/path/to/log.out\";" );
                    sbe.append( "try {" );
                    sbe.append( "   java.io.FileWriter  fw  = new java.io.FileWriter( fPath, true );" );
                    sbe.append( "   java.io.PrintWriter out = new java.io.PrintWriter( fw, true );" );
                    sbe.append( "   out.println( sb.toString() );" );
                    sbe.append( "   fw.close();" );
                    sbe.append( "} catch (java.io.IOException e) {" );
                    sbe.append( "   e.printStackTrace();" );
                    sbe.append( "}" );

                    method.insertAfter("{" + sbe.toString() + "}");
                }
            }
            byteCode = cc.toBytecode();
            cc.detach();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    return byteCode;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!