HADOOP-18653. LogLevel servlet to determine log impl before using setLevel (#5456)

The log level can only be set on Log4J log implementations;
probes are used to downgrade to a warning when other
logging back ends are used

Contributed by Viraj Jasani
This commit is contained in:
Viraj Jasani 2023-03-13 05:30:12 -07:00 committed by GitHub
parent 09469bf47d
commit aff840c59c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 5 deletions

View File

@ -34,6 +34,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.classification.VisibleForTesting;
import org.apache.hadoop.thirdparty.com.google.common.base.Charsets;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
@ -44,6 +46,7 @@ import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
import org.apache.hadoop.security.ssl.SSLFactory;
import org.apache.hadoop.util.GenericOptionsParser;
import org.apache.hadoop.util.GenericsUtil;
import org.apache.hadoop.util.ServletUtil;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
@ -338,14 +341,18 @@ public class LogLevel {
out.println(MARKER
+ "Submitted Class Name: <b>" + logName + "</b><br />");
Logger log = Logger.getLogger(logName);
org.slf4j.Logger log = LoggerFactory.getLogger(logName);
out.println(MARKER
+ "Log Class: <b>" + log.getClass().getName() +"</b><br />");
if (level != null) {
out.println(MARKER + "Submitted Level: <b>" + level + "</b><br />");
}
process(log, level, out);
if (GenericsUtil.isLog4jLogger(logName)) {
process(Logger.getLogger(logName), level, out);
} else {
out.println("Sorry, setting log level is only supported for log4j loggers.<br />");
}
}
out.println(FORMS);

View File

@ -20,6 +20,7 @@ package org.apache.hadoop.util;
import java.lang.reflect.Array;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
@ -33,6 +34,14 @@ import org.slf4j.LoggerFactory;
@InterfaceStability.Unstable
public class GenericsUtil {
private static final String SLF4J_LOG4J_ADAPTER_CLASS = "org.slf4j.impl.Log4jLoggerAdapter";
/**
* Set to false only if log4j adapter class is not found in the classpath. Once set to false,
* the utility method should not bother re-loading class again.
*/
private static final AtomicBoolean IS_LOG4J_LOGGER = new AtomicBoolean(true);
/**
* Returns the Class object (of type <code>Class&lt;T&gt;</code>) of the
* argument of type <code>T</code>.
@ -87,12 +96,27 @@ public class GenericsUtil {
if (clazz == null) {
return false;
}
Logger log = LoggerFactory.getLogger(clazz);
return isLog4jLogger(clazz.getName());
}
/**
* Determine whether the log of the given logger is of Log4J implementation.
*
* @param logger the logger name, usually class name as string.
* @return true if the logger uses Log4J implementation.
*/
public static boolean isLog4jLogger(String logger) {
if (logger == null || !IS_LOG4J_LOGGER.get()) {
return false;
}
Logger log = LoggerFactory.getLogger(logger);
try {
Class log4jClass = Class.forName("org.slf4j.impl.Log4jLoggerAdapter");
Class<?> log4jClass = Class.forName(SLF4J_LOG4J_ADAPTER_CLASS);
return log4jClass.isInstance(log);
} catch (ClassNotFoundException e) {
IS_LOG4J_LOGGER.set(false);
return false;
}
}
}

View File

@ -140,7 +140,7 @@ public class TestGenericsUtil {
@Test
public void testIsLog4jLogger() throws Exception {
assertFalse("False if clazz is null", GenericsUtil.isLog4jLogger(null));
assertFalse("False if clazz is null", GenericsUtil.isLog4jLogger((Class<?>) null));
assertTrue("The implementation is Log4j",
GenericsUtil.isLog4jLogger(TestGenericsUtil.class));
}