package picocli.examples.logging_mixin_advanced; 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; import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import picocli.CommandLine; import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Option; import picocli.CommandLine.ParseResult; import picocli.CommandLine.Spec; import static picocli.CommandLine.Spec.Target.MIXEE; /** * This is a mixin that adds a {@code --verbose} option to a command. * This class will configure Log4j2, using the specified verbosity: *
* To add the {@code --verbose} option to a command, simply declare a {@code @Mixin}-annotated field with type {@code LoggingMixin} * (if your command is a class), or a {@code @Mixin}-annotated method parameter of type {@code LoggingMixin} if your command * is a {@code @Command}-annotated method. *
** This mixin can be used on multiple commands, on any level in the command hierarchy. *
** Make sure that {@link #configureLoggers} is called before executing any command. * This can be accomplished with: *
* public static void main(String... args) { * new CommandLine(new MyApp()) * .setExecutionStrategy(LoggingMixin::executionStrategy)) * .execute(args); * } **/ public class LoggingMixin { /** * This mixin is able to climb the command hierarchy because the * {@code @Spec(Target.MIXEE)}-annotated field gets a reference to the command where it is used. */ private @Spec(MIXEE) CommandSpec mixee; // spec of the command where the @Mixin is used private boolean[] verbosity = new boolean[0]; // Each subcommand that mixes in the LoggingMixin has its own instance of this class, // so there may be many LoggingMixin instances. // We want to store the verbosity value in a single, central place, so // we find the top-level command, // and store the verbosity level on our top-level command's LoggingMixin. // // In the main method, `LoggingMixin::executionStrategy` should be set as the execution strategy: // 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. private static LoggingMixin getTopLevelCommandLoggingMixin(CommandSpec commandSpec) { return ((MyApp) commandSpec.root().userObject()).loggingMixin; } /** * Sets the specified verbosity on the LoggingMixin of the top-level command. * @param verbosity the new verbosity value */ @Option(names = {"-v", "--verbose"}, description = { "Specify multiple -v options to increase verbosity.", "For example, `-v -v -v` or `-vvv`"}) public void setVerbose(boolean[] verbosity) { getTopLevelCommandLoggingMixin(mixee).verbosity = verbosity; } /** * Returns the verbosity from the LoggingMixin of the top-level command. * @return the verbosity value */ public boolean[] getVerbosity() { return getTopLevelCommandLoggingMixin(mixee).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. *
* Example usage: *
** public void main(String... args) { * new CommandLine(new MyApp()) * .setExecutionStrategy(LoggingMixin::executionStrategy)) * .execute(args); * } ** * @param parseResult represents the result of parsing the command line * @return the exit code of executing the most specific subcommand */ public static int executionStrategy(ParseResult parseResult) { getTopLevelCommandLoggingMixin(parseResult.commandSpec()).configureLoggers(); return new CommandLine.RunLast().execute(parseResult); } /** * Configures the Log4j2 console appender(s), using the specified verbosity: *