类加载器隔离朴实案例(三)logback实战加密

背景:

公司框架日志包经常冲突太乱了,经常打不出来,拟搞一套私有的日志

框架 tomcat appclassloader

经过实践,本方法不适用log4j2 高版本slf4j 高版本logback

基于

slf4j-api-1.7.25.jar

logback-classic-1.2.3.jar

logback-core-1.2.3.jar

 

0:

类加载器加载内部资源

字节码加密解密(应对扫描)

适配tomcat容器

代码仓库:
/Users/joyce/work/MyTest/MyMock/src/main/java/com/test/privatelogclass/PrivateLogFactory.java

 

实践

1 没有-D,也没有你config

start to find resource file logback.groovy
start to find resource file logback.xml (这在log4j2和高版本logback中没有)

日志打到log

 

可以看到虽然重定向了logback.xml但是仍然getresource了logback.xml

而且日志打到重定向的logP

 

2 -Dlogback.configurationFile=file:///Users/joyce/work/MyTest/MyMock/src/main/resources/privatelog/privatelogD.xml

没有findresouce logback.xml

而且日志打到-D的logD

 

3 config

没有findresouce logback.xml

而且日志打到-D的logP

 

package com.test.privatelogclass;

import java.io.*;
import java.lang.reflect.Method;

/**
 * Created by joyce on 2020/3/28.
 */
public class PrivateLogFactory {

    public static void main(String []f) throws Exception {
        InputStream inputStream = PrivateLogFactory.class.getClassLoader().getResourceAsStream("privatelog/slf4j-api-1.7.25.jar");
        byte [] bytes = toByteArray(inputStream);
        File ff = new File("/Users/joyce/work/MyTest/MyMock/src/main/resources/privatelog/api");
        FileOutputStream fop = new FileOutputStream(ff);
        fop.write(encode(bytes));
        fop.close();
    }

    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024*4];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

    public static byte [] encode(byte [] source) throws Exception {
        byte[] out = new byte[source.length];
        for (int i = 0; i < source.length; ++i) {
            out[i] = source[source.length - 1 - i];
        }
        return out;
    }


    public static PrivateLogger getLogger(Class c) {
        try {
            Class cl = PrivateLogClassLoader.getPrivateLogClassLoader().loadClass("org.slf4j.LoggerFactory");
            Method method = cl.getMethod("getLogger", Class.class);
            Object log = method.invoke(null, c);
            PrivateLogger privateLogger = new PrivateLogger();
            privateLogger.setLogger(log);
            return privateLogger;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static class PrivateLogger {

        public void setLogger(Object logger) {
            this.logger = logger;
        }

        Object logger;

        public void error(String var1, Throwable var2) {
            try {
                Class logcl = PrivateLogClassLoader.getPrivateLogClassLoader().loadClass("org.slf4j.Logger");
                Method info = logcl.getMethod("error", String.class, Throwable.class);
                info.invoke(this.logger, var1, var2);
            } catch (Exception e) {
                throw new RuntimeException(e) ;
            }
        }

        public void error(Throwable var1) {
            try {
                Class logcl = PrivateLogClassLoader.getPrivateLogClassLoader().loadClass("org.slf4j.Logger");
                Method info = logcl.getMethod("error", String.class, Throwable.class);
                info.invoke(this.logger, var1.getMessage(), var1);
            } catch (Exception e) {
                throw new RuntimeException(e) ;
            }
        }

        public void info(String var1, Object... var2) {
            try {
                Class logcl = PrivateLogClassLoader.getPrivateLogClassLoader().loadClass("org.slf4j.Logger");
                Method info = logcl.getMethod("info", String.class, Object[].class);
                info.invoke(this.logger, var1, var2);
            } catch (Exception e) {
                throw new RuntimeException(e) ;
            }
        }
    }
}

 

package com.test.privatelogclass;

/**
 * Created by joyce on 2022/5/19.
 */
import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;


public class PrivateLogClassLoader extends ClassLoader {

    JarInputStream[] list = null;
    private HashMap<String, byte[]> classes = new HashMap<>();

    private static volatile PrivateLogClassLoader privateLogClassLoader;
    private static boolean config = true;

    private static final String [] JAR_URLS = {"api", "classic", "core"};

    static {
        try {
            JarInputStream [] inputStreams = new JarInputStream[JAR_URLS.length];
            for(int i=0; i<JAR_URLS.length; ++i) {
                InputStream inputStream = PrivateLogClassLoader.class.getClassLoader().getResourceAsStream("privatelog/"+JAR_URLS[i]);
                byte [] bytes = PrivateLogFactory.toByteArray(inputStream);
                inputStreams[i] = new JarInputStream(new ByteArrayInputStream(PrivateLogFactory.encode(bytes)));
            }
            privateLogClassLoader = new PrivateLogClassLoader(inputStreams);
            privateLogClassLoader.configureLogback();
        } catch (Exception e) {
            printError(e);
        }
    }

    public static PrivateLogClassLoader getPrivateLogClassLoader() {
        return privateLogClassLoader;
    }

    private PrivateLogClassLoader(JarInputStream [] jarInputStream) {
        this.list = jarInputStream;

        for(JarInputStream jar : list) {
            JarEntry entry;
            try {
                while ((entry = jar.getNextJarEntry()) != null) {
                    String name = entry.getName();
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    int len = -1;
                    byte [] tmp = new byte[1024];
                    while ((len = jar.read(tmp)) != -1) {
                        out.write(tmp, 0, len);
                    }
                    byte[] bytes = out.toByteArray();
                    classes.put(name, bytes);
                }
            } catch (Exception e) {
                printError(e);
            }
        }
        printInfo("total classes - " + classes.size());

    }

    private void configureLogback() throws Exception {

        if(!config)
            return;

        Boolean useLogbackxmlInAppClassLoader = false;
        if(useLogbackxmlInAppClassLoader)
            return;

        Class clLoggerFactory = loadClass("org.slf4j.LoggerFactory");

        printInfo(clLoggerFactory.getClassLoader());
        printInfo(clLoggerFactory.getClass().getClassLoader());
        printInfo(this.getClass());
        printInfo(this.getClass().getClassLoader());
        printInfo(this.getClass().getClass().getClassLoader());

        // 有可能在复杂环境中,pom中也有slf4j,直接由系统类加载器加载,那么就不重置环境了
        if(clLoggerFactory.getClassLoader() != this)
            return;

        Method getILoggerFactory = clLoggerFactory.getMethod("getILoggerFactory");
        Object loggerContext = getILoggerFactory.invoke(null);
        Class clLoggerContext = loadClass("ch.qos.logback.classic.LoggerContext");
        Method method = clLoggerContext.getMethod("reset");
        method.invoke(loggerContext);

        Class clJoranConfigurator = loadClass("ch.qos.logback.classic.joran.JoranConfigurator");
        Object configurator = clJoranConfigurator.newInstance();

        URL url = this.getClass().getClassLoader().getResource("privatelog/privatelogP.xml");
        method = clJoranConfigurator.getMethod("setContext", loadClass("ch.qos.logback.core.Context"));
        method.invoke(configurator, loggerContext);
        method = clJoranConfigurator.getMethod("doConfigure", URL.class);
        method.invoke(configurator, url);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        printInfo("start to find class " + name);
        try {
            InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int len = -1;
            byte[] tmp = new byte[1024];
            while ((len = in.read(tmp)) != -1) {
                out.write(tmp, 0, len);
            }
            byte[] bytes = out.toByteArray();
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            printError(e);
        }

        return super.findClass(name);
    }

    @Override
    public InputStream getResourceAsStream(String name) {
        printInfo("start to find resource stream " + name);
        if(classes.containsKey(name)) {
            byte [] res = classes.get(name);
            classes.remove(name);
            return new ByteArrayInputStream(res);
        }
        printInfo("getResourceAsStream - error - " + name);
        return super.getResourceAsStream(name);
    }

    @Override
    public URL getResource(String name) {
        printInfo("start to find resource file " + name);
        if(name.equals("logback.xml")) {
            return this.getClass().getClassLoader().getResource("privatelog/privatelog.xml");
        }
        return null;
    }

    private static void printInfo(String msg) {
        System.out.println(msg);
    }

    private static void printInfo(Object msg) {
        System.out.println(msg);
    }

    private static void printError(Exception e) {
        e.printStackTrace();
    }

//-Dlogback.configurationFile=file:///Users/joyce/work/MyTest/MyMock/src/main/resources/privatelog/privatelogD.xml
    public static void main(String []f) {
        PrivateLogFactory.PrivateLogger logger = PrivateLogFactory.getLogger(PrivateLogFactory.class);
        logger.error("eeexxx", new Exception("ex22"));
        logger.error(new Exception("ex33333"));
        logger.info("infoinfo");
        logger.info("infoinfo {}", 11);
        logger.info("infoinfo {} {}", 21, 31);
    }
}

 

posted on 2022-05-26 15:10  silyvin  阅读(252)  评论(0)    收藏  举报