logger command support log4j2. #865

This commit is contained in:
hengyunabc 2019-09-20 17:14:19 +08:00
parent 784ec442e5
commit 053ee2044f
5 changed files with 240 additions and 10 deletions

View File

@ -120,6 +120,15 @@
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>

View File

@ -0,0 +1,187 @@
package com.taobao.arthas.core.command.logger;
import java.lang.reflect.Field;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AsyncAppender;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
/**
*
* @author hengyunabc 2019-09-20
*
*/
public class Log4j2Helper {
private static boolean Log4j2 = false;
private static Field configField = null;
static {
try {
Class<?> loggerClass = Class.forName("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);
} catch (Throwable e) {
// ignore
}
} catch (Throwable t) {
}
}
public static boolean hasLength(String str) {
return (str != null && !str.isEmpty());
}
private static LoggerConfig getLoggerConfig(String name) {
if (!hasLength(name) || LoggerConfig.ROOT.equalsIgnoreCase(name)) {
name = LogManager.ROOT_LOGGER_NAME;
}
return getLoggerContext().getConfiguration().getLoggers().get(name);
}
private static LoggerContext getLoggerContext() {
return (LoggerContext) LogManager.getContext(false);
}
public static Boolean updateLevel(String loggerName, String logLevel) {
if (Log4j2) {
Level level = Level.getLevel(logLevel.toUpperCase());
if (level == null) {
return null;
}
LoggerConfig loggerConfig = getLoggerConfig(loggerName);
if (loggerConfig == null) {
loggerConfig = new LoggerConfig(loggerName, level, true);
getLoggerContext().getConfiguration().addLogger(loggerName, loggerConfig);
} else {
loggerConfig.setLevel(level);
}
getLoggerContext().updateLoggers();
return Boolean.TRUE;
}
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 (!Log4j2) {
return loggerInfoMap;
}
Configuration configuration = getLoggerContext().getConfiguration();
if (name != null && !name.trim().isEmpty()) {
LoggerConfig loggerConfig = configuration.getLoggerConfig(name);
if (loggerConfig == null) {
return loggerInfoMap;
}
loggerInfoMap.put(name, doGetLoggerInfo(loggerConfig));
} else {
// 获取所有logger时如果没有appender则忽略
Map<String, LoggerConfig> loggers = configuration.getLoggers();
if (loggers != null) {
for (Entry<String, LoggerConfig> entry : loggers.entrySet()) {
LoggerConfig loggerConfig = entry.getValue();
if (!includeNoAppender) {
if (!loggerConfig.getAppenders().isEmpty()) {
loggerInfoMap.put(entry.getKey(), doGetLoggerInfo(entry.getValue()));
}
} else {
loggerInfoMap.put(entry.getKey(), doGetLoggerInfo(entry.getValue()));
}
}
}
}
return loggerInfoMap;
}
private static Object getConfigField(LoggerConfig loggerConfig) {
try {
if(configField != null) {
return configField.get(loggerConfig);
}
} catch (Throwable e) {
// ignore
}
return null;
}
private static Map<String, Object> doGetLoggerInfo(LoggerConfig loggerConfig) {
Map<String, Object> info = new HashMap<String, Object>();
String name = loggerConfig.getName();
if (name == null || name.trim().isEmpty()) {
name = LoggerConfig.ROOT;
}
info.put(LoggerHelper.name, name);
info.put(LoggerHelper.clazz, loggerConfig.getClass());
CodeSource codeSource = loggerConfig.getClass().getProtectionDomain().getCodeSource();
if (codeSource != null) {
info.put(LoggerHelper.codeSource, codeSource.getLocation());
}
Object config = getConfigField(loggerConfig);
if (config != null) {
info.put(LoggerHelper.config, config);
}
info.put(LoggerHelper.additivity, loggerConfig.isAdditive());
Level level = loggerConfig.getLevel();
if (level != null) {
info.put(LoggerHelper.level, level.toString());
}
List<Map<String, Object>> result = doGetLoggerAppenders(loggerConfig);
info.put(LoggerHelper.appenders, result);
return info;
}
private static List<Map<String, Object>> doGetLoggerAppenders(LoggerConfig loggerConfig) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
Map<String, Appender> appenders = loggerConfig.getAppenders();
for (Entry<String, Appender> entry : appenders.entrySet()) {
Map<String, Object> info = new HashMap<String, Object>();
Appender appender = entry.getValue();
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).getFileName());
} else if (appender instanceof ConsoleAppender) {
info.put(LoggerHelper.target, ((ConsoleAppender) appender).getTarget());
} else if (appender instanceof AsyncAppender) {
AsyncAppender asyncAppender = ((AsyncAppender) appender);
String[] appenderRefStrings = asyncAppender.getAppenderRefStrings();
info.put(LoggerHelper.blocking, asyncAppender.isBlocking());
info.put(LoggerHelper.appenderRef, Arrays.asList(appenderRefStrings));
}
}
return result;
}
}

View File

@ -1,5 +1,6 @@
package com.taobao.arthas.core.command.logger;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
@ -26,9 +27,8 @@ public class Log4jHelper {
static {
try {
Class<?> loggerClass = Class.forName("org.apache.log4j.Logger");
// 这里可能会加载到应用中依赖的log4j因此需要判断classloader
// 这里可能会加载到其它上游ClassLoader的log4j因此需要判断是否当前classloader
if (loggerClass.getClassLoader().equals(Log4jHelper.class.getClassLoader())) {
LogManager.getLoggerRepository();
Log4j = true;
}
} catch (Throwable t) {
@ -106,6 +106,10 @@ public class Log4jHelper {
Map<String, Object> info = new HashMap<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.getAdditivity());
Level level = logger.getLevel(), effectiveLevel = logger.getEffectiveLevel();

View File

@ -36,8 +36,6 @@ import com.taobao.text.util.RenderUtil;
/**
* logger command
*
* TODO support log4j2
*
* @author hengyunabc 2019-09-04
*
*/
@ -51,6 +49,7 @@ public class LoggerCommand extends AnnotatedCommand {
private static byte[] LoggerHelperBytes;
private static byte[] Log4jHelperBytes;
private static byte[] LogbackHelperBytes;
private static byte[] Log4j2HelperBytes;
private static Map<Class<?>, byte[]> classToBytesMap = new HashMap<Class<?>, byte[]>();
@ -58,10 +57,12 @@ public class LoggerCommand extends AnnotatedCommand {
LoggerHelperBytes = loadClassBytes(LoggerHelper.class);
Log4jHelperBytes = loadClassBytes(Log4jHelper.class);
LogbackHelperBytes = loadClassBytes(LogbackHelper.class);
Log4j2HelperBytes = loadClassBytes(Log4j2Helper.class);
classToBytesMap.put(LoggerHelper.class, LoggerHelperBytes);
classToBytesMap.put(Log4jHelper.class, Log4jHelperBytes);
classToBytesMap.put(LogbackHelper.class, LogbackHelperBytes);
classToBytesMap.put(Log4j2Helper.class, Log4j2HelperBytes);
}
private String name;
@ -145,6 +146,15 @@ public class LoggerCommand extends AnnotatedCommand {
logger.error("arthas", "logger command update logback level error", e);
}
try {
Boolean updateResult = this.updateLevel(inst, Log4j2Helper.class);
if (Boolean.TRUE.equals(updateResult)) {
result = true;
}
} catch (Throwable e) {
logger.error("arthas", "logger command update log4j2 level error", e);
}
if (result) {
process.write("update logger level success.\n");
} else {
@ -180,6 +190,8 @@ public class LoggerCommand extends AnnotatedCommand {
loggerTypes.addType(LoggerType.LOG4J);
} else if ("ch.qos.logback.classic.Logger".equals(className)) {
loggerTypes.addType(LoggerType.LOGBACK);
} else if ("org.apache.logging.log4j.Logger".equals(className)) {
loggerTypes.addType(LoggerType.LOG4J2);
}
}
}
@ -202,6 +214,12 @@ public class LoggerCommand extends AnnotatedCommand {
process.write(renderResult);
}
if (loggerTypes.contains(LoggerType.LOG4J2)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, Log4j2Helper.class);
String renderResult = renderLoggerInfo(loggerInfoMap, process.width());
process.write(renderResult);
}
}
}
@ -223,11 +241,19 @@ public class LoggerCommand extends AnnotatedCommand {
.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)))
label("" + info.get(LoggerHelper.level)));
if (info.get(LoggerHelper.effectiveLevel) != null) {
table.row(label(LoggerHelper.effectiveLevel).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.effectiveLevel)));
}
if (info.get(LoggerHelper.config) != null) {
table.row(label(LoggerHelper.config).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.config)));
}
table.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)));
@ -307,7 +333,7 @@ public class LoggerCommand extends AnnotatedCommand {
}
static enum LoggerType {
LOG4J, LOGBACK
LOG4J, LOGBACK, LOG4J2
}
static class LoggerTypes {

View File

@ -14,6 +14,10 @@ public interface LoggerHelper {
// logger info
public static final String level = "level";
public static final String effectiveLevel = "effectiveLevel";
// log4j2 only
public static final String config = "config";
// type boolean
public static final String additivity = "additivity";
public static final String appenders = "appenders";