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.classification.VisibleForTesting;
import org.apache.hadoop.thirdparty.com.google.common.base.Charsets; import org.apache.hadoop.thirdparty.com.google.common.base.Charsets;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; 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.authentication.client.KerberosAuthenticator;
import org.apache.hadoop.security.ssl.SSLFactory; import org.apache.hadoop.security.ssl.SSLFactory;
import org.apache.hadoop.util.GenericOptionsParser; import org.apache.hadoop.util.GenericOptionsParser;
import org.apache.hadoop.util.GenericsUtil;
import org.apache.hadoop.util.ServletUtil; import org.apache.hadoop.util.ServletUtil;
import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; import org.apache.hadoop.util.ToolRunner;
@ -338,14 +341,18 @@ public class LogLevel {
out.println(MARKER out.println(MARKER
+ "Submitted Class Name: <b>" + logName + "</b><br />"); + "Submitted Class Name: <b>" + logName + "</b><br />");
Logger log = Logger.getLogger(logName); org.slf4j.Logger log = LoggerFactory.getLogger(logName);
out.println(MARKER out.println(MARKER
+ "Log Class: <b>" + log.getClass().getName() +"</b><br />"); + "Log Class: <b>" + log.getClass().getName() +"</b><br />");
if (level != null) { if (level != null) {
out.println(MARKER + "Submitted Level: <b>" + level + "</b><br />"); 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); out.println(FORMS);

View File

@ -20,6 +20,7 @@ package org.apache.hadoop.util;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
@ -33,6 +34,14 @@ import org.slf4j.LoggerFactory;
@InterfaceStability.Unstable @InterfaceStability.Unstable
public class GenericsUtil { 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 * Returns the Class object (of type <code>Class&lt;T&gt;</code>) of the
* argument of type <code>T</code>. * argument of type <code>T</code>.
@ -87,12 +96,27 @@ public class GenericsUtil {
if (clazz == null) { if (clazz == null) {
return false; 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 { try {
Class log4jClass = Class.forName("org.slf4j.impl.Log4jLoggerAdapter"); Class<?> log4jClass = Class.forName(SLF4J_LOG4J_ADAPTER_CLASS);
return log4jClass.isInstance(log); return log4jClass.isInstance(log);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
IS_LOG4J_LOGGER.set(false);
return false; return false;
} }
} }
} }

View File

@ -140,7 +140,7 @@ public class TestGenericsUtil {
@Test @Test
public void testIsLog4jLogger() throws Exception { 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", assertTrue("The implementation is Log4j",
GenericsUtil.isLog4jLogger(TestGenericsUtil.class)); GenericsUtil.isLog4jLogger(TestGenericsUtil.class));
} }