add logger command. #736

pull/853/head
hengyunabc 5 years ago
parent 859777f92b
commit 89bd23b9de

@ -40,6 +40,18 @@ public class IOUtils {
}
}
/**
* @return a byte[] containing the information contained in the specified
* InputStream.
* @throws java.io.IOException
*/
public static byte[] getBytes(InputStream input) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
copy(input, result);
result.close();
return result.toByteArray();
}
public static IOException close(InputStream input) {
return close((Closeable) input);
}

@ -0,0 +1,16 @@
package com.taobao.arthas.common;
public class ReflectException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Throwable cause;
public ReflectException(Throwable cause) {
super(cause.getClass().getName() + "-->" + cause.getMessage());
this.cause = cause;
}
public Throwable getCause() {
return this.cause;
}
}

@ -0,0 +1,511 @@
package com.taobao.arthas.common;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* from spring
* @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ReflectUtils {
private ReflectUtils() {
}
private static final Map primitives = new HashMap(8);
private static final Map transforms = new HashMap(8);
private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader();
// SPRING PATCH BEGIN
private static final Method privateLookupInMethod;
private static final Method lookupDefineClassMethod;
private static final Method classLoaderDefineClassMethod;
private static final ProtectionDomain PROTECTION_DOMAIN;
private static final Throwable THROWABLE;
private static final List<Method> OBJECT_METHODS = new ArrayList<Method>();
static {
Method privateLookupIn;
Method lookupDefineClass;
Method classLoaderDefineClass;
ProtectionDomain protectionDomain;
Throwable throwable = null;
try {
privateLookupIn = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
try {
return MethodHandles.class.getMethod("privateLookupIn", Class.class,
MethodHandles.Lookup.class);
} catch (NoSuchMethodException ex) {
return null;
}
}
});
lookupDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
try {
return MethodHandles.Lookup.class.getMethod("defineClass", byte[].class);
} catch (NoSuchMethodException ex) {
return null;
}
}
});
classLoaderDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE,
Integer.TYPE, ProtectionDomain.class);
}
});
protectionDomain = getProtectionDomain(ReflectUtils.class);
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
Method[] methods = Object.class.getDeclaredMethods();
for (Method method : methods) {
if ("finalize".equals(method.getName())
|| (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
continue;
}
OBJECT_METHODS.add(method);
}
return null;
}
});
} catch (Throwable t) {
privateLookupIn = null;
lookupDefineClass = null;
classLoaderDefineClass = null;
protectionDomain = null;
throwable = t;
}
privateLookupInMethod = privateLookupIn;
lookupDefineClassMethod = lookupDefineClass;
classLoaderDefineClassMethod = classLoaderDefineClass;
PROTECTION_DOMAIN = protectionDomain;
THROWABLE = throwable;
}
// SPRING PATCH END
private static final String[] CGLIB_PACKAGES = { "java.lang", };
static {
primitives.put("byte", Byte.TYPE);
primitives.put("char", Character.TYPE);
primitives.put("double", Double.TYPE);
primitives.put("float", Float.TYPE);
primitives.put("int", Integer.TYPE);
primitives.put("long", Long.TYPE);
primitives.put("short", Short.TYPE);
primitives.put("boolean", Boolean.TYPE);
transforms.put("byte", "B");
transforms.put("char", "C");
transforms.put("double", "D");
transforms.put("float", "F");
transforms.put("int", "I");
transforms.put("long", "J");
transforms.put("short", "S");
transforms.put("boolean", "Z");
}
public static ProtectionDomain getProtectionDomain(final Class source) {
if (source == null) {
return null;
}
return (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return source.getProtectionDomain();
}
});
}
public static Constructor findConstructor(String desc) {
return findConstructor(desc, defaultLoader);
}
public static Constructor findConstructor(String desc, ClassLoader loader) {
try {
int lparen = desc.indexOf('(');
String className = desc.substring(0, lparen).trim();
return getClass(className, loader).getConstructor(parseTypes(desc, loader));
} catch (ClassNotFoundException ex) {
throw new ReflectException(ex);
} catch (NoSuchMethodException ex) {
throw new ReflectException(ex);
}
}
public static Method findMethod(String desc) {
return findMethod(desc, defaultLoader);
}
public static Method findMethod(String desc, ClassLoader loader) {
try {
int lparen = desc.indexOf('(');
int dot = desc.lastIndexOf('.', lparen);
String className = desc.substring(0, dot).trim();
String methodName = desc.substring(dot + 1, lparen).trim();
return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader));
} catch (ClassNotFoundException ex) {
throw new ReflectException(ex);
} catch (NoSuchMethodException ex) {
throw new ReflectException(ex);
}
}
private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException {
int lparen = desc.indexOf('(');
int rparen = desc.indexOf(')', lparen);
List params = new ArrayList();
int start = lparen + 1;
for (;;) {
int comma = desc.indexOf(',', start);
if (comma < 0) {
break;
}
params.add(desc.substring(start, comma).trim());
start = comma + 1;
}
if (start < rparen) {
params.add(desc.substring(start, rparen).trim());
}
Class[] types = new Class[params.size()];
for (int i = 0; i < types.length; i++) {
types[i] = getClass((String) params.get(i), loader);
}
return types;
}
private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException {
return getClass(className, loader, CGLIB_PACKAGES);
}
private static Class getClass(String className, ClassLoader loader, String[] packages)
throws ClassNotFoundException {
String save = className;
int dimensions = 0;
int index = 0;
while ((index = className.indexOf("[]", index) + 1) > 0) {
dimensions++;
}
StringBuffer brackets = new StringBuffer(className.length() - dimensions);
for (int i = 0; i < dimensions; i++) {
brackets.append('[');
}
className = className.substring(0, className.length() - 2 * dimensions);
String prefix = (dimensions > 0) ? brackets + "L" : "";
String suffix = (dimensions > 0) ? ";" : "";
try {
return Class.forName(prefix + className + suffix, false, loader);
} catch (ClassNotFoundException ignore) {
}
for (int i = 0; i < packages.length; i++) {
try {
return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader);
} catch (ClassNotFoundException ignore) {
}
}
if (dimensions == 0) {
Class c = (Class) primitives.get(className);
if (c != null) {
return c;
}
} else {
String transform = (String) transforms.get(className);
if (transform != null) {
try {
return Class.forName(brackets + transform, false, loader);
} catch (ClassNotFoundException ignore) {
}
}
}
throw new ClassNotFoundException(save);
}
public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
public static Object newInstance(Class type) {
return newInstance(type, EMPTY_CLASS_ARRAY, null);
}
public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
return newInstance(getConstructor(type, parameterTypes), args);
}
public static Object newInstance(final Constructor cstruct, final Object[] args) {
boolean flag = cstruct.isAccessible();
try {
if (!flag) {
cstruct.setAccessible(true);
}
Object result = cstruct.newInstance(args);
return result;
} catch (InstantiationException e) {
throw new ReflectException(e);
} catch (IllegalAccessException e) {
throw new ReflectException(e);
} catch (InvocationTargetException e) {
throw new ReflectException(e.getTargetException());
} finally {
if (!flag) {
cstruct.setAccessible(flag);
}
}
}
public static Constructor getConstructor(Class type, Class[] parameterTypes) {
try {
Constructor constructor = type.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
return constructor;
} catch (NoSuchMethodException e) {
throw new ReflectException(e);
}
}
public static String[] getNames(Class[] classes) {
if (classes == null)
return null;
String[] names = new String[classes.length];
for (int i = 0; i < names.length; i++) {
names[i] = classes[i].getName();
}
return names;
}
public static Class[] getClasses(Object[] objects) {
Class[] classes = new Class[objects.length];
for (int i = 0; i < objects.length; i++) {
classes[i] = objects[i].getClass();
}
return classes;
}
public static Method findNewInstance(Class iface) {
Method m = findInterfaceMethod(iface);
if (!m.getName().equals("newInstance")) {
throw new IllegalArgumentException(iface + " missing newInstance method");
}
return m;
}
public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) {
Set methods = new HashSet();
for (int i = 0; i < properties.length; i++) {
PropertyDescriptor pd = properties[i];
if (read) {
methods.add(pd.getReadMethod());
}
if (write) {
methods.add(pd.getWriteMethod());
}
}
methods.remove(null);
return (Method[]) methods.toArray(new Method[methods.size()]);
}
public static PropertyDescriptor[] getBeanProperties(Class type) {
return getPropertiesHelper(type, true, true);
}
public static PropertyDescriptor[] getBeanGetters(Class type) {
return getPropertiesHelper(type, true, false);
}
public static PropertyDescriptor[] getBeanSetters(Class type) {
return getPropertiesHelper(type, false, true);
}
private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) {
try {
BeanInfo info = Introspector.getBeanInfo(type, Object.class);
PropertyDescriptor[] all = info.getPropertyDescriptors();
if (read && write) {
return all;
}
List properties = new ArrayList(all.length);
for (int i = 0; i < all.length; i++) {
PropertyDescriptor pd = all[i];
if ((read && pd.getReadMethod() != null) || (write && pd.getWriteMethod() != null)) {
properties.add(pd);
}
}
return (PropertyDescriptor[]) properties.toArray(new PropertyDescriptor[properties.size()]);
} catch (IntrospectionException e) {
throw new ReflectException(e);
}
}
public static Method findDeclaredMethod(final Class type, final String methodName, final Class[] parameterTypes)
throws NoSuchMethodException {
Class cl = type;
while (cl != null) {
try {
return cl.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
cl = cl.getSuperclass();
}
}
throw new NoSuchMethodException(methodName);
}
public static List addAllMethods(final Class type, final List list) {
if (type == Object.class) {
list.addAll(OBJECT_METHODS);
} else
list.addAll(java.util.Arrays.asList(type.getDeclaredMethods()));
Class superclass = type.getSuperclass();
if (superclass != null) {
addAllMethods(superclass, list);
}
Class[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
addAllMethods(interfaces[i], list);
}
return list;
}
public static List addAllInterfaces(Class type, List list) {
Class superclass = type.getSuperclass();
if (superclass != null) {
list.addAll(Arrays.asList(type.getInterfaces()));
addAllInterfaces(superclass, list);
}
return list;
}
public static Method findInterfaceMethod(Class iface) {
if (!iface.isInterface()) {
throw new IllegalArgumentException(iface + " is not an interface");
}
Method[] methods = iface.getDeclaredMethods();
if (methods.length != 1) {
throw new IllegalArgumentException("expecting exactly 1 method in " + iface);
}
return methods[0];
}
// SPRING PATCH BEGIN
public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
return defineClass(className, b, loader, null, null);
}
public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain)
throws Exception {
return defineClass(className, b, loader, protectionDomain, null);
}
public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain,
Class<?> contextClass) throws Exception {
Class c = null;
// Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches
if (contextClass != null && contextClass.getClassLoader() == loader && privateLookupInMethod != null
&& lookupDefineClassMethod != null) {
try {
MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupInMethod.invoke(null, contextClass,
MethodHandles.lookup());
c = (Class) lookupDefineClassMethod.invoke(lookup, b);
} catch (InvocationTargetException ex) {
Throwable target = ex.getTargetException();
if (target.getClass() != LinkageError.class && target.getClass() != IllegalArgumentException.class) {
throw new ReflectException(target);
}
// in case of plain LinkageError (class already defined)
// or IllegalArgumentException (class in different package):
// fall through to traditional ClassLoader.defineClass below
} catch (Throwable ex) {
throw new ReflectException(ex);
}
}
// Classic option: protected ClassLoader.defineClass method
if (c == null && classLoaderDefineClassMethod != null) {
if (protectionDomain == null) {
protectionDomain = PROTECTION_DOMAIN;
}
Object[] args = new Object[] { className, b, 0, b.length, protectionDomain };
try {
if (!classLoaderDefineClassMethod.isAccessible()) {
classLoaderDefineClassMethod.setAccessible(true);
}
c = (Class) classLoaderDefineClassMethod.invoke(loader, args);
} catch (InvocationTargetException ex) {
throw new ReflectException(ex.getTargetException());
} catch (Throwable ex) {
// Fall through if setAccessible fails with InaccessibleObjectException on JDK
// 9+
// (on the module path and/or with a JVM bootstrapped with
// --illegal-access=deny)
if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) {
throw new ReflectException(ex);
}
}
}
// Fallback option: JDK 9+ Lookup.defineClass API even if ClassLoader does not
// match
if (c == null && contextClass != null && contextClass.getClassLoader() != loader
&& privateLookupInMethod != null && lookupDefineClassMethod != null) {
try {
MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupInMethod.invoke(null, contextClass,
MethodHandles.lookup());
c = (Class) lookupDefineClassMethod.invoke(lookup, b);
} catch (InvocationTargetException ex) {
throw new ReflectException(ex.getTargetException());
} catch (Throwable ex) {
throw new ReflectException(ex);
}
}
// No defineClass variant available at all?
if (c == null) {
throw new ReflectException(THROWABLE);
}
// Force static initializers to run.
Class.forName(className, true, loader);
return c;
}
// SPRING PATCH END
public static int findPackageProtected(Class[] classes) {
for (int i = 0; i < classes.length; i++) {
if (!Modifier.isPublic(classes[i].getModifiers())) {
return i;
}
}
return 0;
}
}

@ -116,6 +116,11 @@
<groupId>com.taobao.middleware</groupId>
<artifactId>logger.api</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>

@ -26,6 +26,7 @@ import com.taobao.arthas.core.command.klass100.OgnlCommand;
import com.taobao.arthas.core.command.klass100.RedefineCommand;
import com.taobao.arthas.core.command.klass100.SearchClassCommand;
import com.taobao.arthas.core.command.klass100.SearchMethodCommand;
import com.taobao.arthas.core.command.logger.LoggerCommand;
import com.taobao.arthas.core.command.monitor200.DashboardCommand;
import com.taobao.arthas.core.command.monitor200.HeapDumpCommand;
import com.taobao.arthas.core.command.monitor200.JvmCommand;
@ -93,6 +94,7 @@ public class BuiltinCommandPack implements CommandResolver {
commands.add(Command.create(SystemPropertyCommand.class));
commands.add(Command.create(SystemEnvCommand.class));
commands.add(Command.create(VMOptionCommand.class));
commands.add(Command.create(LoggerCommand.class));
commands.add(Command.create(HistoryCommand.class));
commands.add(Command.create(CatCommand.class));
commands.add(Command.create(PwdCommand.class));

@ -0,0 +1,156 @@
package com.taobao.arthas.core.command.logger;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Appender;
import org.apache.log4j.AsyncAppender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public class Log4jHelper {
private static boolean Log4j = false;
static {
try {
Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
// 这里可能会加载到应用中依赖的log4j因此需要判断classloader
if (loggerClass.getClassLoader().equals(Log4jHelper.class.getClassLoader())) {
LogManager.getLoggerRepository();
Log4j = true;
}
} catch (Throwable t) {
}
}
public static Boolean updateLevel(String name, String level) {
if (Log4j) {
Level l = Level.toLevel(level, Level.ERROR);
Logger logger = LogManager.getLoggerRepository().exists(name);
if (logger != null) {
logger.setLevel(l);
return true;
} else {
Logger root = LogManager.getLoggerRepository().getRootLogger();
if (root.getName().equals(name)) {
root.setLevel(l);
return true;
}
}
return false;
}
return null;
}
public static Map<String, Map<String, Object>> getLoggers(String name, boolean includeNoAppender) {
Map<String, Map<String, Object>> loggerInfoMap = new HashMap<String, Map<String, Object>>();
if (!Log4j) {
return loggerInfoMap;
}
if (name != null && !name.trim().isEmpty()) {
Logger logger = LogManager.getLoggerRepository().exists(name);
if (logger != null) {
loggerInfoMap.put(name, doGetLoggerInfo(logger));
}
} else {
// 获取所有logger时如果没有appender则忽略
@SuppressWarnings("unchecked")
Enumeration<Logger> loggers = LogManager.getLoggerRepository().getCurrentLoggers();
if (loggers != null) {
while (loggers.hasMoreElements()) {
Logger logger = loggers.nextElement();
Map<String, Object> info = doGetLoggerInfo(logger);
if (!includeNoAppender) {
List<?> appenders = (List<?>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
loggerInfoMap.put(logger.getName(), info);
}
} else {
loggerInfoMap.put(logger.getName(), info);
}
}
}
Logger root = LogManager.getLoggerRepository().getRootLogger();
if (root != null) {
Map<String, Object> info = doGetLoggerInfo(root);
if (!includeNoAppender) {
List<?> appenders = (List<?>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
loggerInfoMap.put(root.getName(), info);
}
} else {
loggerInfoMap.put(root.getName(), info);
}
}
}
return loggerInfoMap;
}
private static Map<String, Object> doGetLoggerInfo(Logger logger) {
Map<String, Object> info = new HashMap<String, Object>();
info.put(LoggerHelper.name, logger.getName());
info.put(LoggerHelper.clazz, logger.getClass());
info.put(LoggerHelper.additivity, logger.getAdditivity());
Level level = logger.getLevel(), effectiveLevel = logger.getEffectiveLevel();
if (level != null) {
info.put(LoggerHelper.level, level.toString());
}
if (effectiveLevel != null) {
info.put(LoggerHelper.effectiveLevel, effectiveLevel.toString());
}
@SuppressWarnings("unchecked")
List<Map<String, Object>> result = doGetLoggerAppenders(logger.getAllAppenders());
info.put(LoggerHelper.appenders, result);
return info;
}
private static List<Map<String, Object>> doGetLoggerAppenders(Enumeration<Appender> appenders) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
while (appenders.hasMoreElements()) {
Map<String, Object> info = new HashMap<String, Object>();
Appender appender = appenders.nextElement();
info.put(LoggerHelper.name, appender.getName());
info.put(LoggerHelper.clazz, appender.getClass());
result.add(info);
if (appender instanceof FileAppender) {
info.put(LoggerHelper.file, ((FileAppender) appender).getFile());
} else if (appender instanceof ConsoleAppender) {
info.put(LoggerHelper.target, ((ConsoleAppender) appender).getTarget());
} else if (appender instanceof AsyncAppender) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> asyncs = doGetLoggerAppenders(((AsyncAppender) appender).getAllAppenders());
// 标明异步appender
List<String> appenderRef = new ArrayList<String>();
for (Map<String, Object> a : asyncs) {
appenderRef.add((String) a.get(LoggerHelper.name));
result.add(a);
}
info.put(LoggerHelper.appenderRef, appenderRef);
}
}
return result;
}
}

@ -0,0 +1,164 @@
package com.taobao.arthas.core.command.logger;
import java.lang.reflect.Field;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.ILoggerFactory;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.pattern.PatternLayoutBase;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public class LogbackHelper {
private static boolean Logback = false;
private static Field headField, lengthOptionField;
private static ILoggerFactory loggerFactoryInstance;
static {
try {
Class<?> loggerClass = Class.forName("ch.qos.logback.classic.Logger");
// 这里可能会加载到应用中依赖的logback因此需要判断classloader
if (loggerClass.getClassLoader().equals(LogbackHelper.class.getClassLoader())) {
ILoggerFactory loggerFactory = org.slf4j.LoggerFactory.getILoggerFactory();
if (loggerFactory instanceof LoggerContext) {
loggerFactoryInstance = loggerFactory;
headField = PatternLayoutBase.class.getDeclaredField("head");
headField.setAccessible(true);
lengthOptionField = ThrowableProxyConverter.class.getDeclaredField("lengthOption");
lengthOptionField.setAccessible(true);
Logback = true;
}
}
} catch (Throwable t) {
// ignore
}
}
public static Boolean updateLevel(String name, String level) {
if (Logback) {
try {
Level l = Level.toLevel(level, Level.ERROR);
LoggerContext loggerContext = (LoggerContext) loggerFactoryInstance;
Logger logger = loggerContext.exists(name);
if (logger != null) {
logger.setLevel(l);
return true;
}
} catch (Throwable t) {
// ignore
}
return false;
}
return null;
}
public static Map<String, Map<String, Object>> getLoggers(String name, boolean includeNoAppender) {
Map<String, Map<String, Object>> loggerInfoMap = new LinkedHashMap<String, Map<String, Object>>();
if (Logback) {
LoggerContext loggerContext = (LoggerContext) loggerFactoryInstance;
if (name != null && !name.trim().isEmpty()) {
Logger logger = loggerContext.exists(name);
if (logger != null) {
loggerInfoMap.put(name, doGetLoggerInfo(logger));
}
} else {
// 获取所有logger时如果没有appender则忽略
List<Logger> loggers = loggerContext.getLoggerList();
for (Logger logger : loggers) {
Map<String, Object> info = doGetLoggerInfo(logger);
if (!includeNoAppender) {
List<?> appenders = (List<?>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
loggerInfoMap.put(logger.getName(), info);
}
} else {
loggerInfoMap.put(logger.getName(), info);
}
}
}
}
return loggerInfoMap;
}
private static Map<String, Object> doGetLoggerInfo(Logger logger) {
Map<String, Object> info = new LinkedHashMap<String, Object>();
info.put(LoggerHelper.name, logger.getName());
info.put(LoggerHelper.clazz, logger.getClass());
CodeSource codeSource = logger.getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) {
info.put(LoggerHelper.codeSource, codeSource.getLocation());
}
info.put(LoggerHelper.additivity, logger.isAdditive());
Level level = logger.getLevel(), effectiveLevel = logger.getEffectiveLevel();
if (level != null) {
info.put(LoggerHelper.level, level.toString());
}
if (effectiveLevel != null) {
info.put(LoggerHelper.effectiveLevel, effectiveLevel.toString());
}
List<Map<String, Object>> result = doGetLoggerAppenders(logger.iteratorForAppenders());
info.put(LoggerHelper.appenders, result);
return info;
}
@SuppressWarnings("rawtypes")
private static List<Map<String, Object>> doGetLoggerAppenders(Iterator<Appender<ILoggingEvent>> appenders) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
while (appenders.hasNext()) {
Map<String, Object> info = new LinkedHashMap<String, Object>();
Appender<ILoggingEvent> appender = appenders.next();
info.put(LoggerHelper.name, appender.getName());
info.put(LoggerHelper.clazz, appender.getClass());
if (appender instanceof FileAppender) {
info.put(LoggerHelper.file, ((FileAppender) appender).getFile());
} else if (appender instanceof AsyncAppender) {
AsyncAppender aa = (AsyncAppender) appender;
Iterator<Appender<ILoggingEvent>> iter = aa.iteratorForAppenders();
List<Map<String, Object>> asyncs = doGetLoggerAppenders(iter);
// 异步appender所 ref的 appender参考 https://logback.qos.ch/manual/appenders.html
List<String> appenderRef = new ArrayList<String>();
for (Map<String, Object> a : asyncs) {
appenderRef.add((String) a.get(LoggerHelper.name));
result.add(a);
}
info.put(LoggerHelper.appenderRef, appenderRef);
} else if (appender instanceof ConsoleAppender) {
info.put(LoggerHelper.target, ((ConsoleAppender) appender).getTarget());
}
result.add(info);
}
return result;
}
}

@ -0,0 +1,337 @@
package com.taobao.arthas.core.command.logger;
import static com.taobao.text.ui.Element.label;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.taobao.arthas.common.IOUtils;
import com.taobao.arthas.common.ReflectUtils;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.ClassLoaderUtils;
import com.taobao.arthas.core.util.LogUtil;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import com.taobao.middleware.logger.Logger;
import com.taobao.text.Decoration;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
/**
* logger command
*
* TODO support log4j2
*
* @author hengyunabc 2019-09-04
*
*/
@Name("logger")
@Summary("Print logger info, and update the logger level")
@Description("\nExamples:\n" + " logger\n" + " logger -c 327a647b\n" + " logger --name ROOT --level debug\n"
+ Constants.WIKI + Constants.WIKI_HOME + "logger")
public class LoggerCommand extends AnnotatedCommand {
private static final Logger logger = LogUtil.getArthasLogger();
private static byte[] LoggerHelperBytes;
private static byte[] Log4jHelperBytes;
private static byte[] LogbackHelperBytes;
private static Map<Class<?>, byte[]> classToBytesMap = new HashMap<Class<?>, byte[]>();
static {
LoggerHelperBytes = loadClassBytes(LoggerHelper.class);
Log4jHelperBytes = loadClassBytes(Log4jHelper.class);
LogbackHelperBytes = loadClassBytes(LogbackHelper.class);
classToBytesMap.put(LoggerHelper.class, LoggerHelperBytes);
classToBytesMap.put(Log4jHelper.class, Log4jHelperBytes);
classToBytesMap.put(LogbackHelper.class, LogbackHelperBytes);
}
private String name;
private String hashCode;
private String level;
/**
* include the logger don't have appender, default false.
*/
private boolean includeNoAppender;
/**
* include the arthas logger, default false.
*/
private boolean includeArthasLogger;
@Option(shortName = "n", longName = "name")
@Description("logger name")
public void setName(String name) {
this.name = name;
}
@Option(shortName = "c", longName = "classloader")
@Description("classLoader hashcode")
public void setHashCode(String hashCode) {
this.hashCode = hashCode;
}
@Option(shortName = "l", longName = "level")
@Description("set logger level")
public void setLevel(String level) {
this.level = level;
}
@Option(longName = "include-no-appender", flag = true)
@Description("include the loggers don't have appender, default value false")
public void setHaveAppender(boolean includeNoAppender) {
this.includeNoAppender = includeNoAppender;
}
@Option(longName = "include-arthas-logger", flag = true)
@Description("include the arthas loggers, default value false")
public void setIncludeArthasLogger(boolean includeArthasLogger) {
this.includeArthasLogger = includeArthasLogger;
}
@Override
public void process(CommandProcess process) {
int status = 0;
try {
if (this.name != null && this.level != null) {
level(process);
} else {
loggers(process, name);
}
} finally {
process.end(status);
}
}
public void level(CommandProcess process) {
Instrumentation inst = process.session().getInstrumentation();
boolean result = false;
try {
Boolean updateResult = this.updateLevel(inst, Log4jHelper.class);
if (Boolean.TRUE.equals(updateResult)) {
result = true;
}
} catch (Throwable e) {
logger.error("arthas", "logger command update log4j level error", e);
}
try {
Boolean updateResult = this.updateLevel(inst, LogbackHelper.class);
if (Boolean.TRUE.equals(updateResult)) {
result = true;
}
} catch (Throwable e) {
logger.error("arthas", "logger command update logback level error", e);
}
if (result) {
process.write("update logger level success.\n");
} else {
process.write("update logger level fail.\n");
}
}
public void loggers(CommandProcess process, String name) {
Map<ClassLoader, LoggerTypes> classLoaderLoggerMap = new LinkedHashMap<ClassLoader, LoggerTypes>();
for (Class<?> clazz : process.session().getInstrumentation().getAllLoadedClasses()) {
String className = clazz.getName();
ClassLoader classLoader = clazz.getClassLoader();
// skip the arthas classloader
if (this.includeArthasLogger == false && classLoader != null && this.getClass().getClassLoader().getClass()
.getName().equals(classLoader.getClass().getName())) {
continue;
}
// if special classloader
if (this.hashCode != null && !this.hashCode.equals(StringUtils.classLoaderHash(clazz))) {
continue;
}
if (classLoader != null) {
LoggerTypes loggerTypes = classLoaderLoggerMap.get(classLoader);
if (loggerTypes == null) {
loggerTypes = new LoggerTypes();
classLoaderLoggerMap.put(classLoader, loggerTypes);
}
if ("org.apache.log4j.Logger".equals(className)) {
loggerTypes.addType(LoggerType.LOG4J);
} else if ("ch.qos.logback.classic.Logger".equals(className)) {
loggerTypes.addType(LoggerType.LOGBACK);
}
}
}
for (Entry<ClassLoader, LoggerTypes> entry : classLoaderLoggerMap.entrySet()) {
ClassLoader classLoader = entry.getKey();
LoggerTypes loggerTypes = entry.getValue();
if (loggerTypes.contains(LoggerType.LOG4J)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, Log4jHelper.class);
String renderResult = renderLoggerInfo(loggerInfoMap, process.width());
process.write(renderResult);
}
if (loggerTypes.contains(LoggerType.LOGBACK)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, LogbackHelper.class);
String renderResult = renderLoggerInfo(loggerInfoMap, process.width());
process.write(renderResult);
}
}
}
private String renderLoggerInfo(Map<String, Map<String, Object>> loggerInfos, int width) {
StringBuilder sb = new StringBuilder(8192);
for (Entry<String, Map<String, Object>> entry : loggerInfos.entrySet()) {
Map<String, Object> info = entry.getValue();
TableElement table = new TableElement(2, 10).leftCellPadding(1).rightCellPadding(1);
TableElement appendersTable = new TableElement().rightCellPadding(1);
Class<?> clazz = (Class<?>) info.get(LoggerHelper.clazz);
table.row(label(LoggerHelper.name).style(Decoration.bold.bold()), label("" + info.get(LoggerHelper.name)))
.row(label(LoggerHelper.clazz).style(Decoration.bold.bold()), label("" + clazz.getName()))
.row(label(LoggerHelper.classLoader).style(Decoration.bold.bold()),
label("" + clazz.getClassLoader()))
.row(label(LoggerHelper.classLoaderHash).style(Decoration.bold.bold()),
label("" + StringUtils.classLoaderHash(clazz)))
.row(label(LoggerHelper.level).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.level)))
.row(label(LoggerHelper.effectiveLevel).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.effectiveLevel)))
.row(label(LoggerHelper.additivity).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.additivity)))
.row(label(LoggerHelper.codeSource).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.codeSource)));
@SuppressWarnings("unchecked")
List<Map<String, Object>> appenders = (List<Map<String, Object>>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
for (Map<String, Object> appenderInfo : appenders) {
Class<?> appenderClass = (Class<?>) appenderInfo.get(LoggerHelper.clazz);
appendersTable.row(label(LoggerHelper.name).style(Decoration.bold.bold()),
label("" + appenderInfo.get(LoggerHelper.name)));
appendersTable.row(label(LoggerHelper.clazz), label("" + appenderClass.getName()));
appendersTable.row(label(LoggerHelper.classLoader), label("" + appenderClass.getClassLoader()));
appendersTable.row(label(LoggerHelper.classLoaderHash),
label("" + StringUtils.classLoaderHash(appenderClass)));
if (appenderInfo.get(LoggerHelper.file) != null) {
appendersTable.row(label(LoggerHelper.file), label("" + appenderInfo.get(LoggerHelper.file)));
}
if (appenderInfo.get(LoggerHelper.target) != null) {
appendersTable.row(label(LoggerHelper.target),
label("" + appenderInfo.get(LoggerHelper.target)));
}
if (appenderInfo.get(LoggerHelper.appenderRef) != null) {
appendersTable.row(label(LoggerHelper.appenderRef),
label("" + appenderInfo.get(LoggerHelper.appenderRef)));
}
}
table.row(label("appenders").style(Decoration.bold.bold()), appendersTable);
}
sb.append(RenderUtil.render(table, width)).append('\n');
}
return sb.toString();
}
@SuppressWarnings("unchecked")
private Map<String, Map<String, Object>> loggerInfo(ClassLoader classLoader, Class<?> helperClass) {
Map<String, Map<String, Object>> loggers = Collections.emptyMap();
try {
classLoader.loadClass(helperClass.getName());
} catch (ClassNotFoundException e) {
try {
ReflectUtils.defineClass(helperClass.getName(), classToBytesMap.get(helperClass), classLoader);
} catch (Exception e1) {
// ignore
}
}
try {
Class<?> clazz = classLoader.loadClass(helperClass.getName());
Method getLoggersMethod = clazz.getMethod("getLoggers", new Class<?>[] { String.class, boolean.class });
loggers = (Map<String, Map<String, Object>>) getLoggersMethod.invoke(null,
new Object[] { name, includeNoAppender });
} catch (Throwable e) {
// ignore
}
return loggers;
}
private Boolean updateLevel(Instrumentation inst, Class<?> helperClass) throws Exception {
ClassLoader classLoader = null;
if (hashCode == null) {
classLoader = ClassLoader.getSystemClassLoader();
} else {
classLoader = ClassLoaderUtils.getClassLoader(inst, hashCode);
}
Class<?> clazz = classLoader.loadClass(helperClass.getName());
Method updateLevelMethod = clazz.getMethod("updateLevel", new Class<?>[] { String.class, String.class });
return (Boolean) updateLevelMethod.invoke(null, new Object[] { this.name, this.level });
}
static enum LoggerType {
LOG4J, LOGBACK
}
static class LoggerTypes {
Set<LoggerType> types = new HashSet<LoggerType>();
public Collection<LoggerType> types() {
return types;
}
public void addType(LoggerType type) {
types.add(type);
}
public boolean contains(LoggerType type) {
return types.contains(type);
}
}
private static byte[] loadClassBytes(Class<?> clazz) {
try {
InputStream stream = LoggerCommand.class.getClassLoader()
.getResourceAsStream(clazz.getName().replace('.', '/') + ".class");
return IOUtils.getBytes(stream);
} catch (IOException e) {
// ignore
return null;
}
}
}

@ -0,0 +1,28 @@
package com.taobao.arthas.core.command.logger;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public interface LoggerHelper {
public static final String clazz = "class";
public static final String classLoader = "classLoader";
public static final String classLoaderHash = "classLoaderHash";
public static final String codeSource = "codeSource";
// logger info
public static final String level = "level";
public static final String effectiveLevel = "effectiveLevel";
// type boolean
public static final String additivity = "additivity";
public static final String appenders = "appenders";
// appender info
public static final String name = "name";
public static final String file = "file";
// type List<String>
public static final String appenderRef = "appenderRef";
public static final String target = "target";
}

@ -0,0 +1,184 @@
logger
===
> Print the logger information, update the logger level
### Usage
#### Print the logger information
Take the following `logback.xml` as an example:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>2GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="APPLICATION" />
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n
</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC" />
</root>
</configuration>
```
The result of the `logger` command:
```bash
[arthas@2062]$ logger
name ROOT
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level INFO
effectiveLevel INFO
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
appenders name CONSOLE
class ch.qos.logback.core.ConsoleAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
target System.out
name APPLICATION
class ch.qos.logback.core.rolling.RollingFileAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
file app.log
name ASYNC
class ch.qos.logback.classic.AsyncAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
appenderRef [APPLICATION]
```
In the `appenders` section:
* The target of `CONSOLE` logger is `System.out`
* `APPLICATION` logger is `RollingFileAppender`, the file is `app.log`
* `ASYNC` its `appenderRef` is `APPLICATION`, which means asynchronous output to the file
#### View logger information for the special name
```bash
[arthas@2062]$ logger -n org.springframework.web
name org.springframework.web
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level null
effectiveLevel INFO
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
```
#### View logger information for the special classloader
```bash
[arthas@2062]$ logger -c 2a139a55
name ROOT
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level DEBUG
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
appenders name CONSOLE
class ch.qos.logback.core.ConsoleAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
target System.out
name APPLICATION
class ch.qos.logback.core.rolling.RollingFileAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
file app.log
name ASYNC
class ch.qos.logback.classic.AsyncAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
appenderRef [APPLICATION]
```
#### Update logger level
```bash
[arthas@2062]$ logger --name ROOT --level debug
update logger level success.
```
#### View the logger information without appenders
By default, the `logger` command only prints information about the logger with appenders. If you want to see information about loggers without `appender`, you can use the parameter `--include-no-appender`.
Note that the output will usually be very long.
```bash
[arthas@2062]$ logger --include-no-appender
name ROOT
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level DEBUG
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
appenders name CONSOLE
class ch.qos.logback.core.ConsoleAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
target System.out
name APPLICATION
class ch.qos.logback.core.rolling.RollingFileAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
file app.log
name ASYNC
class ch.qos.logback.classic.AsyncAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
appenderRef [APPLICATION]
name com
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level null
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
name com.alibaba
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level null
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
...
```

@ -0,0 +1,185 @@
logger
===
> 查看logger信息更新logger level
### 使用参考
#### 查看所有logger信息
以下面的`logback.xml`为例:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>2GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="APPLICATION" />
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n
</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC" />
</root>
</configuration>
```
使用`logger`命令打印的结果是:
```bash
[arthas@2062]$ logger
name ROOT
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level INFO
effectiveLevel INFO
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
appenders name CONSOLE
class ch.qos.logback.core.ConsoleAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
target System.out
name APPLICATION
class ch.qos.logback.core.rolling.RollingFileAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
file app.log
name ASYNC
class ch.qos.logback.classic.AsyncAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
appenderRef [APPLICATION]
```
从`appenders`的信息里,可以看到
* `CONSOLE` logger的target是`System.out`
* `APPLICATION` logger是`RollingFileAppender`它的file是`app.log`
* `ASYNC`它的`appenderRef`是`APPLICATION`,即异步输出到文件里
#### 查看指定名字的logger信息
```bash
[arthas@2062]$ logger -n org.springframework.web
name org.springframework.web
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level null
effectiveLevel INFO
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
```
#### 查看指定classloader的logger信息
```bash
[arthas@2062]$ logger -c 2a139a55
name ROOT
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level DEBUG
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
appenders name CONSOLE
class ch.qos.logback.core.ConsoleAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
target System.out
name APPLICATION
class ch.qos.logback.core.rolling.RollingFileAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
file app.log
name ASYNC
class ch.qos.logback.classic.AsyncAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
appenderRef [APPLICATION]
```
#### 更新logger level
```bash
[arthas@2062]$ logger --name ROOT --level debug
update logger level success.
```
#### 查看没有appender的logger的信息
默认情况下,`logger`命令只打印有appender的logger的信息。如果想查看没有`appender`的logger的信息可以加上参数`--include-no-appender`。
注意,通常输出结果会很长。
```bash
[arthas@2062]$ logger --include-no-appender
name ROOT
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level DEBUG
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
appenders name CONSOLE
class ch.qos.logback.core.ConsoleAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
target System.out
name APPLICATION
class ch.qos.logback.core.rolling.RollingFileAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
file app.log
name ASYNC
class ch.qos.logback.classic.AsyncAppender
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
appenderRef [APPLICATION]
name com
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level null
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
name com.alibaba
class ch.qos.logback.classic.Logger
classLoader sun.misc.Launcher$AppClassLoader@2a139a55
classLoaderHash 2a139a55
level null
effectiveLevel DEBUG
additivity true
codeSource file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
...
```
Loading…
Cancel
Save