Set root logger level for CLIs (#123742)

All CLIs in elasticsearch support command line flags for controlling the
output level. When --silent is used, the expectation is that normal
logging is omitted. Yet the log4j logger is still configured to output
error level logs. This commit sets the appropriate log level for log4j
depending on the Terminal log level.
This commit is contained in:
Ryan Ernst 2025-03-02 09:55:09 -08:00 committed by GitHub
parent 3a1aa8b36d
commit 39a2e88964
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 82 additions and 5 deletions

View file

@ -12,6 +12,7 @@ apply plugin: 'elasticsearch.publish'
dependencies {
api 'net.sf.jopt-simple:jopt-simple:5.0.2'
api project(':libs:core')
api project(':libs:logging')
testImplementation(project(":test:framework")) {
exclude group: 'org.elasticsearch', module: 'cli'

View file

@ -11,6 +11,8 @@
module org.elasticsearch.cli {
requires jopt.simple;
requires org.elasticsearch.base;
requires java.logging;
requires org.elasticsearch.logging;
exports org.elasticsearch.cli;
}

View file

@ -15,6 +15,8 @@ import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.logging.Level;
import org.elasticsearch.logging.internal.spi.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
@ -84,12 +86,16 @@ public abstract class Command implements Closeable {
return;
}
LoggerFactory loggerFactory = LoggerFactory.provider();
if (options.has(silentOption)) {
terminal.setVerbosity(Terminal.Verbosity.SILENT);
loggerFactory.setRootLevel(Level.OFF);
} else if (options.has(verboseOption)) {
terminal.setVerbosity(Terminal.Verbosity.VERBOSE);
loggerFactory.setRootLevel(Level.DEBUG);
} else {
terminal.setVerbosity(Terminal.Verbosity.NORMAL);
loggerFactory.setRootLevel(Level.INFO);
}
execute(terminal, options, processInfo);

View file

@ -9,5 +9,5 @@
module org.elasticsearch.logging {
exports org.elasticsearch.logging;
exports org.elasticsearch.logging.internal.spi to org.elasticsearch.server;
exports org.elasticsearch.logging.internal.spi to org.elasticsearch.server, org.elasticsearch.cli;
}

View file

@ -9,6 +9,7 @@
package org.elasticsearch.logging.internal.spi;
import org.elasticsearch.logging.Level;
import org.elasticsearch.logging.Logger;
/**
@ -26,6 +27,10 @@ public abstract class LoggerFactory {
public abstract Logger getLogger(Class<?> clazz);
public abstract void setRootLevel(Level level);
public abstract Level getRootLevel();
public static void setInstance(LoggerFactory INSTANCE) {
LoggerFactory.INSTANCE = INSTANCE;
}

View file

@ -9,20 +9,51 @@
package org.elasticsearch.common.logging.internal;
import static org.apache.logging.log4j.Level.ALL;
import static org.apache.logging.log4j.Level.DEBUG;
import static org.apache.logging.log4j.Level.ERROR;
import static org.apache.logging.log4j.Level.FATAL;
import static org.apache.logging.log4j.Level.INFO;
import static org.apache.logging.log4j.Level.OFF;
import static org.apache.logging.log4j.Level.TRACE;
import static org.apache.logging.log4j.Level.WARN;
public final class LevelUtil {
private LevelUtil() {}
public static org.apache.logging.log4j.Level log4jLevel(final org.elasticsearch.logging.Level level) {
return switch (level) {
case OFF -> org.apache.logging.log4j.Level.OFF;
case FATAL -> org.apache.logging.log4j.Level.FATAL;
case OFF -> OFF;
case FATAL -> FATAL;
case ERROR -> org.apache.logging.log4j.Level.ERROR;
case WARN -> org.apache.logging.log4j.Level.WARN;
case WARN -> WARN;
case INFO -> org.apache.logging.log4j.Level.INFO;
case DEBUG -> org.apache.logging.log4j.Level.DEBUG;
case TRACE -> org.apache.logging.log4j.Level.TRACE;
case TRACE -> TRACE;
case ALL -> org.apache.logging.log4j.Level.ALL;
};
}
public static org.elasticsearch.logging.Level elasticsearchLevel(final org.apache.logging.log4j.Level log4jLevel) {
// we can't use a switch because log4j levels are not an enum
if (log4jLevel == OFF) {
return org.elasticsearch.logging.Level.OFF;
} else if (log4jLevel == FATAL) {
return org.elasticsearch.logging.Level.FATAL;
} else if (log4jLevel == ERROR) {
return org.elasticsearch.logging.Level.ERROR;
} else if (log4jLevel == WARN) {
return org.elasticsearch.logging.Level.WARN;
} else if (log4jLevel == INFO) {
return org.elasticsearch.logging.Level.INFO;
} else if (log4jLevel == DEBUG) {
return org.elasticsearch.logging.Level.DEBUG;
} else if (log4jLevel == TRACE) {
return org.elasticsearch.logging.Level.TRACE;
} else if (log4jLevel == ALL) {
return org.elasticsearch.logging.Level.ALL;
}
throw new AssertionError("unknown log4j level [" + log4jLevel + "]");
}
}

View file

@ -10,6 +10,8 @@
package org.elasticsearch.common.logging.internal;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.logging.Level;
import org.elasticsearch.logging.Logger;
import org.elasticsearch.logging.internal.spi.LoggerFactory;
@ -30,4 +32,15 @@ public class LoggerFactoryImpl extends LoggerFactory {
// classloader a class comes from, we will use the root logging config.
return getLogger(clazz.getName());
}
@Override
public void setRootLevel(Level level) {
var log4jLevel = LevelUtil.log4jLevel(level);
Loggers.setLevel(LogManager.getRootLogger(), log4jLevel);
}
@Override
public Level getRootLevel() {
return LevelUtil.elasticsearchLevel(LogManager.getRootLogger().getLevel());
}
}

View file

@ -121,6 +121,7 @@ import org.elasticsearch.index.analysis.TokenizerFactory;
import org.elasticsearch.indices.IndicesModule;
import org.elasticsearch.indices.analysis.AnalysisModule;
import org.elasticsearch.jdk.RuntimeVersionFeature;
import org.elasticsearch.logging.internal.spi.LoggerFactory;
import org.elasticsearch.plugins.AnalysisPlugin;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.scanners.StablePluginsRegistry;
@ -575,6 +576,23 @@ public abstract class ESTestCase extends LuceneTestCase {
}
}
private static org.elasticsearch.logging.Level capturedLogLevel = null;
// just capture the expected level once before the suite starts
@BeforeClass
public static void captureLoggingLevel() {
capturedLogLevel = LoggerFactory.provider().getRootLevel();
}
@AfterClass
public static void restoreLoggingLevel() {
if (capturedLogLevel != null) {
// log level might not have been captured if suite was skipped
LoggerFactory.provider().setRootLevel(capturedLogLevel);
capturedLogLevel = null;
}
}
@Before
public final void before() {
LeakTracker.setContextHint(getTestName());

View file

@ -49,6 +49,7 @@ module org.elasticsearch.security {
requires oauth2.oidc.sdk;
requires org.slf4j;
requires unboundid.ldapsdk;
requires org.elasticsearch.logging;
exports org.elasticsearch.xpack.security.action to org.elasticsearch.server;
exports org.elasticsearch.xpack.security.action.apikey to org.elasticsearch.server;