Skip to content

Commit

Permalink
[#958][#895] updated example for more detailed Log4j2 control
Browse files Browse the repository at this point in the history
  • Loading branch information
remkop committed Feb 19, 2020
1 parent 8a2942c commit 5f7f0fc
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 17 deletions.
@@ -1,8 +1,11 @@
package picocli.examples.logging;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
Expand Down Expand Up @@ -42,7 +45,7 @@
* </p>
*/
public class LoggingMixin {
@Spec(MIXEE) CommandSpec spec; // spec of the command where the @Mixin is used
private @Spec(MIXEE) CommandSpec spec; // spec of the command where the @Mixin is used

private boolean[] verbosity = new boolean[0];

Expand All @@ -67,12 +70,19 @@ public void setVerbose(boolean[] verbosity) {
// that will take the verbosity level that we stored in the top-level command's LoggingMixin
// to configure Log4j2 before executing the command that the user specified.

CommandSpec root = spec;
while (root.parent() != null) { root = root.parent(); }
IOwner owner = root.commandLine().getCommand();
IOwner owner = spec.root().commandLine().getCommand();
owner.getLoggingMixin().verbosity = verbosity;
}

/**
* Returns the verbosity from the LoggingMixin of the top-level command.
* @return the verbosity value
*/
public boolean[] getVerbosity() {
IOwner owner = spec.root().commandLine().getCommand();
return owner.getLoggingMixin().verbosity;
}

/**
* Configures Log4j2 based on the verbosity level of the top-level command's LoggingMixin,
* before invoking the default execution strategy ({@link picocli.CommandLine.RunLast RunLast}) and returning the result.
Expand All @@ -91,7 +101,7 @@ public void setVerbose(boolean[] verbosity) {
* @return the exit code of executing the most specific subcommand
*/
public static int executionStrategy(ParseResult parseResult) {
IOwner owner = parseResult.commandSpec().commandLine().getCommand();
IOwner owner = parseResult.commandSpec().root().commandLine().getCommand();
owner.getLoggingMixin().configureLoggers();

return new CommandLine.RunLast().execute(parseResult);
Expand All @@ -107,22 +117,36 @@ public static int executionStrategy(ParseResult parseResult) {
* </ul>
*/
public void configureLoggers() {
LoggingMixin.initializeLog4j();
configureAppender(LoggerContext.getContext(false), calcLogLevel());
}

if (verbosity.length >= 3) {
Configurator.setRootLevel(Level.TRACE);
} else if (verbosity.length == 2) {
Configurator.setRootLevel(Level.DEBUG);
} else if (verbosity.length == 1) {
Configurator.setRootLevel(Level.INFO);
} else {
Configurator.setRootLevel(Level.WARN);
private Level calcLogLevel() {
switch (getVerbosity().length) {
case 0: return Level.WARN;
case 1: return Level.INFO;
case 2: return Level.DEBUG;
default: return Level.TRACE;
}
}

private void configureAppender(LoggerContext loggerContext, Level level) {
final LoggerConfig rootConfig = loggerContext.getConfiguration().getRootLogger();
for (Appender appender : rootConfig.getAppenders().values()) {
if (appender instanceof ConsoleAppender) {
rootConfig.removeAppender(appender.getName());
rootConfig.addAppender(appender, level, null);
}
}
if (rootConfig.getLevel().isMoreSpecificThan(level)) {
rootConfig.setLevel(level);
}
loggerContext.updateLoggers();
}

// usually you would just have a log4j2.xml config file...
// here we do a quick and dirty programmatic setup
private static void initializeLog4j() {
// IMPORTANT: The below MUST be called BEFORE any call to LogManager.getLogger() is made.
public static LoggerContext initializeLog4j() {
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.ERROR); // show internal log4j2 errors
builder.setConfigurationName("QuickAndDirtySetup");
Expand All @@ -133,8 +157,8 @@ private static void initializeLog4j() {
builder.add(appenderBuilder);
//builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG)
// .add(builder.newAppenderRef("Stdout")).addAttribute("additivity", false));
builder.add(builder.newRootLogger(Level.WARN).add(builder.newAppenderRef("Stdout")));
/*LoggerContext loggerContext = */Configurator.initialize(builder.build());
builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout").addAttribute("level", Level.WARN)));
return Configurator.initialize(builder.build());
}

/**
Expand Down
Expand Up @@ -32,6 +32,9 @@
*/
@Command(name = "app", subcommands = LoggingSub.class)
class MyApp implements Runnable, LoggingMixin.IOwner {
static {
LoggingMixin.initializeLog4j(); // programmatic initialization; must be done before calling LogManager.getLogger()
}
private static Logger logger = LogManager.getLogger(MyApp.class);

@Spec CommandSpec spec;
Expand Down

0 comments on commit 5f7f0fc

Please sign in to comment.