From f9e698d09cf3ed4feff89d6e2525feb22df4df8b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 23 Sep 2019 22:08:50 +0800 Subject: [PATCH] fix logger command print parent ClassLoader result problem. #870 --- .../core/command/logger/AsmRenameUtil.java | 43 +++++++++++++++++++ .../core/command/logger/Log4j2Helper.java | 14 ++++-- .../core/command/logger/Log4jHelper.java | 25 +++++++---- .../core/command/logger/LogbackHelper.java | 2 +- .../core/command/logger/LoggerCommand.java | 34 +++++++++++---- .../arthas/core/util/ClassLoaderUtils.java | 16 +++++++ 6 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java diff --git a/core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java b/core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java new file mode 100644 index 000000000..9c66f66d0 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java @@ -0,0 +1,43 @@ +package com.taobao.arthas.core.command.logger; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; +import org.objectweb.asm.commons.SimpleRemapper; + +/** + * + * @author hengyunabc 2019-09-23 + * + */ +public class AsmRenameUtil { + + public static byte[] renameClass(byte[] bytes, final String oldName, final String newName) { + ClassReader reader = new ClassReader(bytes); + ClassWriter writer = new ClassWriter(reader, 0); + + final String internalOldName = oldName.replace('.', '/'); + final String internalNewName = newName.replace('.', '/'); +// ClassVisitor visitor = new ClassRemapper(writer, new Remapper() { +// +// @Override +// public String mapType(String internalName) { +// if (internalName.equals(internalOldName)) { +// return internalNewName; +// } else { +// return super.mapType(internalName); +// } +// } +// +// }); + + ClassVisitor visitor = new ClassRemapper(writer, new SimpleRemapper(internalOldName, internalNewName)); + + + reader.accept(visitor, 0); + return writer.toByteArray(); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/logger/Log4j2Helper.java b/core/src/main/java/com/taobao/arthas/core/command/logger/Log4j2Helper.java index b089daeff..31ded3713 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/logger/Log4j2Helper.java +++ b/core/src/main/java/com/taobao/arthas/core/command/logger/Log4j2Helper.java @@ -19,6 +19,8 @@ import org.apache.logging.log4j.core.appender.FileAppender; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; +import com.taobao.arthas.core.util.StringUtils; + /** * * @author hengyunabc 2019-09-20 @@ -30,12 +32,12 @@ public class Log4j2Helper { static { try { - Class loggerClass = Class.forName("org.apache.logging.log4j.Logger"); + Class loggerClass = Log4j2Helper.class.getClassLoader().loadClass("org.apache.logging.log4j.Logger"); // 这里可能会加载到其它上游ClassLoader的log4j2,因此需要判断是否当前classloader if (loggerClass.getClassLoader().equals(Log4j2Helper.class.getClassLoader())) { Log4j2 = true; } - + try { configField = LoggerConfig.class.getDeclaredField("config"); configField.setAccessible(true); @@ -93,6 +95,10 @@ public class Log4j2Helper { if (loggerConfig == null) { return loggerInfoMap; } + // 排掉非root时,获取到root的logger config + if (!name.equalsIgnoreCase(LoggerConfig.ROOT) && StringUtils.isEmpty(loggerConfig.getName())) { + return loggerInfoMap; + } loggerInfoMap.put(name, doGetLoggerInfo(loggerConfig)); } else { // 获取所有logger时,如果没有appender则忽略 @@ -116,7 +122,7 @@ public class Log4j2Helper { private static Object getConfigField(LoggerConfig loggerConfig) { try { - if(configField != null) { + if (configField != null) { return configField.get(loggerConfig); } } catch (Throwable e) { @@ -143,7 +149,7 @@ public class Log4j2Helper { if (config != null) { info.put(LoggerHelper.config, config); } - + info.put(LoggerHelper.additivity, loggerConfig.isAdditive()); Level level = loggerConfig.getLevel(); diff --git a/core/src/main/java/com/taobao/arthas/core/command/logger/Log4jHelper.java b/core/src/main/java/com/taobao/arthas/core/command/logger/Log4jHelper.java index e762edc87..f709a28af 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/logger/Log4jHelper.java +++ b/core/src/main/java/com/taobao/arthas/core/command/logger/Log4jHelper.java @@ -26,7 +26,7 @@ public class Log4jHelper { static { try { - Class loggerClass = Class.forName("org.apache.log4j.Logger"); + Class loggerClass = Log4jHelper.class.getClassLoader().loadClass("org.apache.log4j.Logger"); // 这里可能会加载到其它上游ClassLoader的log4j,因此需要判断是否当前classloader if (loggerClass.getClassLoader().equals(Log4jHelper.class.getClassLoader())) { Log4j = true; @@ -129,6 +129,10 @@ public class Log4jHelper { private static List> doGetLoggerAppenders(Enumeration appenders) { List> result = new ArrayList>(); + if (appenders == null) { + return result; + } + while (appenders.hasMoreElements()) { Map info = new HashMap(); Appender appender = appenders.nextElement(); @@ -143,15 +147,18 @@ public class Log4jHelper { info.put(LoggerHelper.target, ((ConsoleAppender) appender).getTarget()); } else if (appender instanceof AsyncAppender) { @SuppressWarnings("unchecked") - List> asyncs = doGetLoggerAppenders(((AsyncAppender) appender).getAllAppenders()); - // 标明异步appender - List appenderRef = new ArrayList(); - for (Map a : asyncs) { - appenderRef.add((String) a.get(LoggerHelper.name)); - result.add(a); + Enumeration appendersOfAsync = ((AsyncAppender) appender).getAllAppenders(); + if (appendersOfAsync != null) { + List> asyncs = doGetLoggerAppenders(appendersOfAsync); + // 标明异步appender + List appenderRef = new ArrayList(); + for (Map a : asyncs) { + appenderRef.add((String) a.get(LoggerHelper.name)); + result.add(a); + } + info.put(LoggerHelper.blocking, ((AsyncAppender) appender).getBlocking()); + info.put(LoggerHelper.appenderRef, appenderRef); } - info.put(LoggerHelper.blocking, ((AsyncAppender) appender).getBlocking()); - info.put(LoggerHelper.appenderRef, appenderRef); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/logger/LogbackHelper.java b/core/src/main/java/com/taobao/arthas/core/command/logger/LogbackHelper.java index d860a700d..66bfe4650 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/logger/LogbackHelper.java +++ b/core/src/main/java/com/taobao/arthas/core/command/logger/LogbackHelper.java @@ -34,7 +34,7 @@ public class LogbackHelper { static { try { - Class loggerClass = Class.forName("ch.qos.logback.classic.Logger"); + Class loggerClass = LogbackHelper.class.getClassLoader().loadClass("ch.qos.logback.classic.Logger"); // 这里可能会加载到应用中依赖的logback,因此需要判断classloader if (loggerClass.getClassLoader().equals(LogbackHelper.class.getClassLoader())) { ILoggerFactory loggerFactory = org.slf4j.LoggerFactory.getILoggerFactory(); diff --git a/core/src/main/java/com/taobao/arthas/core/command/logger/LoggerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/logger/LoggerCommand.java index be027af63..9fce86bc5 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/logger/LoggerCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/logger/LoggerCommand.java @@ -41,8 +41,8 @@ import com.taobao.text.util.RenderUtil; */ @Name("logger") @Summary("Print logger info, and update the logger level") -@Description("\nExamples:\n" + " logger\n" + " logger -c 327a647b\n" + " logger -c 327a647b --name ROOT --level debug\n" - + Constants.WIKI + Constants.WIKI_HOME + "logger") +@Description("\nExamples:\n" + " logger\n" + " logger -c 327a647b\n" + + " logger -c 327a647b --name ROOT --level debug\n" + Constants.WIKI + Constants.WIKI_HOME + "logger") public class LoggerCommand extends AnnotatedCommand { private static final Logger logger = LogUtil.getArthasLogger(); @@ -53,6 +53,9 @@ public class LoggerCommand extends AnnotatedCommand { private static Map, byte[]> classToBytesMap = new HashMap, byte[]>(); + private static String arthasClassLoaderHash = ClassLoaderUtils + .classLoaderHash(LoggerCommand.class.getClassLoader()); + static { LoggerHelperBytes = loadClassBytes(LoggerHelper.class); Log4jHelperBytes = loadClassBytes(Log4jHelper.class); @@ -278,7 +281,8 @@ public class LoggerCommand extends AnnotatedCommand { label("" + appenderInfo.get(LoggerHelper.target))); } if (appenderInfo.get(LoggerHelper.blocking) != null) { - appendersTable.row(label(LoggerHelper.blocking), label("" + appenderInfo.get(LoggerHelper.blocking))); + appendersTable.row(label(LoggerHelper.blocking), + label("" + appenderInfo.get(LoggerHelper.blocking))); } if (appenderInfo.get(LoggerHelper.appenderRef) != null) { appendersTable.row(label(LoggerHelper.appenderRef), @@ -294,21 +298,33 @@ public class LoggerCommand extends AnnotatedCommand { return sb.toString(); } + private static String helperClassNameWithClassLoader(ClassLoader classLoader, Class helperClass) { + String classLoaderHash = ClassLoaderUtils.classLoaderHash(classLoader); + String className = helperClass.getName(); + // if want to debug, change to return className + return className + arthasClassLoaderHash + classLoaderHash; + } + @SuppressWarnings("unchecked") private Map> loggerInfo(ClassLoader classLoader, Class helperClass) { Map> loggers = Collections.emptyMap(); + + String helperClassName = helperClassNameWithClassLoader(classLoader, helperClass); try { - classLoader.loadClass(helperClass.getName()); + classLoader.loadClass(helperClassName); } catch (ClassNotFoundException e) { try { - ReflectUtils.defineClass(helperClass.getName(), classToBytesMap.get(helperClass), classLoader); - } catch (Exception e1) { - // ignore + byte[] helperClassBytes = AsmRenameUtil.renameClass(classToBytesMap.get(helperClass), + helperClass.getName(), helperClassName); + ReflectUtils.defineClass(helperClassName, helperClassBytes, classLoader); + } catch (Throwable e1) { + logger.error("arthas", "arthas loggger command try to define helper class error: " + helperClassName, + e1); } } try { - Class clazz = classLoader.loadClass(helperClass.getName()); + Class clazz = classLoader.loadClass(helperClassName); Method getLoggersMethod = clazz.getMethod("getLoggers", new Class[] { String.class, boolean.class }); loggers = (Map>) getLoggersMethod.invoke(null, new Object[] { name, includeNoAppender }); @@ -326,7 +342,7 @@ public class LoggerCommand extends AnnotatedCommand { classLoader = ClassLoaderUtils.getClassLoader(inst, hashCode); } - Class clazz = classLoader.loadClass(helperClass.getName()); + Class clazz = classLoader.loadClass(helperClassNameWithClassLoader(classLoader, helperClass)); Method updateLevelMethod = clazz.getMethod("updateLevel", new Class[] { String.class, String.class }); return (Boolean) updateLevelMethod.invoke(null, new Object[] { this.name, this.level }); diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java index 7fd27ac37..555049bec 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ClassLoaderUtils.java @@ -38,4 +38,20 @@ public class ClassLoaderUtils { } return null; } + + public static String classLoaderHash(ClassLoader classLoader) { + int hashCode = 0; + if (classLoader == null) { + hashCode = System.identityHashCode(classLoader); + } else { + hashCode = classLoader.hashCode(); + } + if (hashCode <= 0) { + hashCode = System.identityHashCode(classLoader); + if (hashCode < 0) { + hashCode = -hashCode; + } + } + return Integer.toHexString(hashCode); + } }