mirror of
https://gitee.com/arthas/arthas.git
synced 2024-11-29 18:58:37 +08:00
logger command support log4j2. #865
This commit is contained in:
parent
784ec442e5
commit
053ee2044f
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user