- * To generate source code for an extended logger that adds custom log levels to the existing ones:
- * {@code java org.apache.logging.log4j.core.tools.Generate$ExtendedLogger =
+ * To generate source code for an extended logger that adds custom log levels to the existing ones run:
+ *
- * To generate source code for a custom logger that replaces the existing log levels with custom ones:
- * {@code java org.apache.logging.log4j.core.tools.Generate$CustomLogger =
+ * To generate source code for a custom logger that replaces the existing log levels with custom ones run:
+ *
- * Example of creating a custom logger:
- * {@code java org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450
- * DEFCON3=550}
+ * Example:
+ *
+ * {@code java -jar log4j-generator.jar customLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550}
*/
+@Command(name = "generate")
public final class Generate {
// Implementation note:
// The generated code is in the user's namespace which has its own versioning scheme, so
@@ -62,7 +84,7 @@ String imports() {
+ "import org.apache.logging.log4j.Marker;%n"
+ "import org.apache.logging.log4j.message.Message;%n"
+ "import org.apache.logging.log4j.message.MessageFactory;%n"
- + "import org.apache.logging.log4j.spi.ExtendedLogger;%n"
+ + "import org.apache.logging.log4j.spi.AbstractLogger;%n"
+ "import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;%n"
+ "import org.apache.logging.log4j.util.MessageSupplier;%n"
+ "import org.apache.logging.log4j.util.Supplier;%n"
@@ -91,16 +113,11 @@ String constructor() {
return ""
+ "%n"
+ " private %s(final Logger logger) {%n"
- + " this.logger = new ExtendedLoggerWrapper((ExtendedLogger) logger, logger.getName(), "
+ + " this.logger = new ExtendedLoggerWrapper((AbstractLogger) logger, logger.getName(), "
+ "logger.getMessageFactory());%n"
+ " }%n";
// @formatter:on
}
-
- @Override
- Class> generator() {
- return CustomLogger.class;
- }
},
EXTEND {
@Override
@@ -113,7 +130,7 @@ String imports() {
+ "import org.apache.logging.log4j.Marker;%n"
+ "import org.apache.logging.log4j.message.Message;%n"
+ "import org.apache.logging.log4j.message.MessageFactory;%n"
- + "import org.apache.logging.log4j.spi.ExtendedLogger;%n"
+ + "import org.apache.logging.log4j.spi.AbstractLogger;%n"
+ "import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;%n"
+ "import org.apache.logging.log4j.util.MessageSupplier;%n"
+ "import org.apache.logging.log4j.util.Supplier;%n"
@@ -143,16 +160,11 @@ String constructor() {
return ""
+ "%n"
+ " private %s(final Logger logger) {%n"
- + " super((ExtendedLogger) logger, logger.getName(), logger.getMessageFactory());%n"
+ + " super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());%n"
+ " this.logger = this;%n"
+ " }%n";
// @formatter:on
}
-
- @Override
- Class> generator() {
- return ExtendedLogger.class;
- }
};
abstract String imports();
@@ -160,8 +172,6 @@ Class> generator() {
abstract String declaration();
abstract String constructor();
-
- abstract Class> generator();
}
static final String FQCN_FIELD = "" + " private static final String FQCN = %s.class.getName();%n";
@@ -985,152 +995,136 @@ Class> generator() {
+ " }%n";
// @formatter:on
+ @Option(names = "-f", description = "Output file.")
+ private Path outputFile;
+
+ @Spec
+ private CommandSpec spec;
+
private Generate() {}
- /**
- * Generates source code for custom logger wrappers that only provide convenience methods for the specified custom
- * levels, not for the standard built-in levels.
- */
- public static final class CustomLogger {
- /**
- * Generates source code for custom logger wrappers that only provide convenience methods for the specified
- * custom levels, not for the standard built-in levels.
- *
- * @param args className of the custom logger to generate, followed by a NAME=intLevel pair for each custom log
- * level to generate convenience methods for
- */
- public static void main(final String[] args) {
- generate(args, Type.CUSTOM);
+ public static class LevelInfo {
+ final String name;
+ final int intLevel;
+
+ LevelInfo(final String name, final int intLevel) {
+ this.name = name;
+ this.intLevel = intLevel;
}
+ }
- private CustomLogger() {}
+ public static void main(final String[] args) {
+ final Generate command = new Generate();
+ new CommandLine(command)
+ .registerConverter(LevelInfo.class, command::convertLevelInfo)
+ .execute(args);
}
- /**
- * Generates source code for extended logger wrappers that provide convenience methods for the specified custom
- * levels, and by extending {@code org.apache.logging.log4j.spi.ExtendedLoggerWrapper}, inherit the convenience
- * methods for the built-in levels provided by the {@code Logger} interface.
- */
- public static final class ExtendedLogger {
- /**
- * Generates source code for extended logger wrappers that provide convenience methods for the specified custom
- * levels.
- *
- * @param args className of the custom logger to generate, followed by a NAME=intLevel pair for each custom log
- * level to generate convenience methods for
- */
- public static void main(final String[] args) {
- generate(args, Type.EXTEND);
+ private LevelInfo convertLevelInfo(final String value) {
+ final String[] parts = value.split("=", 2);
+ if (parts.length == 2) {
+ try {
+ final int intLevel = Integer.parseInt(parts[1]);
+ return new LevelInfo(parts[0], intLevel);
+ } catch (final NumberFormatException ignored) {
+ // Fall through to throw instruction
+ }
}
-
- private ExtendedLogger() {}
+ throw new ParameterException(
+ spec.commandLine(),
+ String.format("Invalid level value '%s'; expected form CUSTOMLEVEL=WEIGHT%nE.g. AUDIT=50", value));
}
- static class LevelInfo {
- final String name;
- final int intLevel;
-
- LevelInfo(final String description) {
- final String[] parts = description.split("=");
- name = parts[0];
- intLevel = Integer.parseInt(parts[1]);
+ private Writer getWriter() throws IOException {
+ if (outputFile != null) {
+ return Files.newBufferedWriter(outputFile, UTF_8);
}
+ return new FilterWriter(new OutputStreamWriter(System.out, UTF_8)) {
- public static List parse(final List values, final Class> generator) {
- final List result = new ArrayList<>(values.size());
- for (int i = 0; i < values.size(); i++) {
- try {
- result.add(new LevelInfo(values.get(i)));
- } catch (final Exception ex) {
- System.err.println("Cannot parse custom level '" + values.get(i) + "': " + ex.toString());
- usage(System.err, generator);
- System.exit(-1);
- }
+ @Override
+ public void close() throws IOException {
+ // Don't close the standard output.
}
- return result;
- }
+ };
}
- private static void generate(final String[] args, final Type type) {
- generate(args, type, System.out);
+ private void validateLevels(final List levels) {
+ if (levels == null || levels.isEmpty()) {
+ throw new ParameterException(spec.commandLine(), "At least one level parameter is required");
+ }
}
/**
* Generates source code for extended logger wrappers that provide convenience methods for the specified custom
* levels.
*
- * @param args className of the custom logger to generate, followed by a NAME=intLevel pair for each custom log
+ * @param classNameFQN className of the custom logger to generate
+ * @param levels a list of NAME=intLevel pair for each custom log
* level to generate convenience methods for
- * @param printStream the stream to write the generated source code to
*/
- public static void generateExtend(final String[] args, final PrintStream printStream) {
- generate(args, Type.EXTEND, printStream);
+ @Command(name = "extendedLogger", description = "Generates a logger with additional log levels.")
+ public void generateExtend(
+ final @Parameters(description = "Class name to generate", paramLabel = "") String classNameFQN,
+ final @Parameters(description = "Additional log levels", paramLabel = "") List levels)
+ throws IOException {
+ validateLevels(levels);
+ try (final Writer writer = getWriter()) {
+ generateSource(Type.EXTEND, classNameFQN, levels, writer);
+ writer.flush();
+ }
}
/**
* Generates source code for custom logger wrappers that only provide convenience methods for the specified
* custom levels, not for the standard built-in levels.
*
- * @param args className of the custom logger to generate, followed by a NAME=intLevel pair for each custom log
+ * @param classNameFQN className of the custom logger to generate
+ * @param levels a list of NAME=intLevel pair for each custom log
* level to generate convenience methods for
- * @param printStream the stream to write the generated source code to
*/
- public static void generateCustom(final String[] args, final PrintStream printStream) {
- generate(args, Type.CUSTOM, printStream);
- }
-
- static void generate(final String[] args, final Type type, final PrintStream printStream) {
- if (!validate(args)) {
- usage(printStream, type.generator());
- System.exit(-1);
+ @Command(name = "customLogger", description = "Generates a logger with custom log methods.")
+ public void generateCustom(
+ final @Parameters(description = "Class name to generate", paramLabel = "") String classNameFQN,
+ final @Parameters(description = "Log levels", paramLabel = "") List levels)
+ throws IOException {
+ validateLevels(levels);
+ try (final Writer writer = getWriter()) {
+ generateSource(Type.CUSTOM, classNameFQN, levels, writer);
+ writer.flush();
}
- final List values = new ArrayList<>(Arrays.asList(args));
- final String classFQN = values.remove(0);
- final List levels = LevelInfo.parse(values, type.generator());
- printStream.println(generateSource(classFQN, levels, type));
- }
-
- static boolean validate(final String[] args) {
- return args.length >= 2;
- }
-
- private static void usage(final PrintStream out, final Class> generator) {
- out.println("Usage: java " + generator.getName() + " className LEVEL1=intLevel1 [LEVEL2=intLevel2...]");
- out.println(" Where className is the fully qualified class name of the custom/extended logger");
- out.println(" to generate, followed by a space-separated list of custom log levels.");
- out.println(" For each custom log level, specify NAME=intLevel (without spaces).");
}
@SuppressFBWarnings(
value = "FORMAT_STRING_MANIPULATION",
justification = "The format strings come from constants. The replacement is done for readability.")
- static String generateSource(final String classNameFQN, final List levels, final Type type) {
- final StringBuilder sb = new StringBuilder(10000 * levels.size());
+ static String generateSource(
+ final Type type, final String classNameFQN, final List levels, final Writer writer)
+ throws IOException {
final int lastDot = classNameFQN.lastIndexOf('.');
final String pkg = classNameFQN.substring(0, Math.max(lastDot, 0));
if (!pkg.isEmpty()) {
- sb.append(String.format(PACKAGE_DECLARATION, pkg));
+ writer.append(String.format(PACKAGE_DECLARATION, pkg));
}
- sb.append(String.format(type.imports(), ""));
+ writer.append(String.format(type.imports(), ""));
final String className = classNameFQN.substring(classNameFQN.lastIndexOf('.') + 1);
final String javadocDescr = javadocDescription(levels);
- sb.append(String.format(type.declaration(), javadocDescr, className));
- sb.append(String.format(FQCN_FIELD, className));
+ writer.append(String.format(type.declaration(), javadocDescr, className));
+ writer.append(String.format(FQCN_FIELD, className));
for (final LevelInfo level : levels) {
- sb.append(String.format(LEVEL_FIELD, level.name, level.name, level.intLevel));
+ writer.append(String.format(LEVEL_FIELD, level.name, level.name, level.intLevel));
}
- sb.append(String.format(type.constructor(), className));
- sb.append(String.format(FACTORY_METHODS.replaceAll("CLASSNAME", className), ""));
+ writer.append(String.format(type.constructor(), className));
+ writer.append(String.format(FACTORY_METHODS.replaceAll("CLASSNAME", className), ""));
for (final LevelInfo level : levels) {
final String methodName = camelCase(level.name);
final String phase1 = METHODS.replaceAll("CUSTOM_LEVEL", level.name);
final String phase2 = phase1.replaceAll("methodName", methodName);
- sb.append(String.format(phase2, ""));
+ writer.append(String.format(phase2, ""));
}
- sb.append('}');
- sb.append(System.getProperty("line.separator"));
- return sb.toString();
+ writer.append('}');
+ writer.append(System.getProperty("line.separator"));
+ return writer.toString();
}
static String javadocDescription(final List levels) {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/package-info.java b/log4j-codegen/src/main/java/org/apache/logging/log4j/codegen/package-info.java
similarity index 95%
rename from log4j-core/src/main/java/org/apache/logging/log4j/core/tools/package-info.java
rename to log4j-codegen/src/main/java/org/apache/logging/log4j/codegen/package-info.java
index 8cbd2eb..028b41b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/package-info.java
+++ b/log4j-codegen/src/main/java/org/apache/logging/log4j/codegen/package-info.java
@@ -19,7 +19,7 @@
*/
@Export
@Version("2.20.1")
-package org.apache.logging.log4j.core.tools;
+package org.apache.logging.log4j.codegen;
import org.osgi.annotation.bundle.Export;
import org.osgi.annotation.versioning.Version;
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/tools/GenerateCustomLoggerTest.java b/log4j-codegen/src/test/java/org/apache/logging/log4j/codegen/GenerateCustomLoggerTest.java
similarity index 82%
rename from log4j-core-test/src/test/java/org/apache/logging/log4j/core/tools/GenerateCustomLoggerTest.java
rename to log4j-codegen/src/test/java/org/apache/logging/log4j/codegen/GenerateCustomLoggerTest.java
index 5932d11..a953f83 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/tools/GenerateCustomLoggerTest.java
+++ b/log4j-codegen/src/test/java/org/apache/logging/log4j/codegen/GenerateCustomLoggerTest.java
@@ -14,17 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.logging.log4j.core.tools;
+package org.apache.logging.log4j.codegen;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.io.BufferedWriter;
import java.io.File;
-import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -38,7 +41,8 @@
import javax.tools.ToolProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Marker;
-import org.apache.logging.log4j.core.test.TestConstants;
+import org.apache.logging.log4j.codegen.Generate.LevelInfo;
+import org.apache.logging.log4j.codegen.Generate.Type;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.test.TestLogger;
@@ -52,7 +56,7 @@
@Tag("functional")
@SetSystemProperty(
- key = TestConstants.LOGGER_CONTEXT_FACTORY,
+ key = "log4j2.loggerContextFactory",
value = "org.apache.logging.log4j.test.TestLoggerContextFactory")
public class GenerateCustomLoggerTest {
@@ -77,13 +81,12 @@ public void testGenerateSource() throws Exception {
final String CLASSNAME = "org.apache.logging.log4j.core.MyCustomLogger";
// generate custom logger source
- final List values = Arrays.asList("DEFCON1=350 DEFCON2=450 DEFCON3=550".split(" "));
- final List levels = Generate.LevelInfo.parse(values, Generate.CustomLogger.class);
- final String src = Generate.generateSource(CLASSNAME, levels, Generate.Type.CUSTOM);
- final File f = new File(TEST_SOURCE);
- f.getParentFile().mkdirs();
- try (final FileOutputStream out = new FileOutputStream(f)) {
- out.write(src.getBytes(Charset.defaultCharset()));
+ final List levels = Arrays.asList(
+ new LevelInfo("DEFCON1", 350), new LevelInfo("DEFCON2", 450), new LevelInfo("DEFCON3", 550));
+ final Path testSource = Paths.get(TEST_SOURCE);
+ Files.createDirectories(testSource.getParent());
+ try (final BufferedWriter writer = Files.newBufferedWriter(testSource, UTF_8)) {
+ Generate.generateSource(Type.CUSTOM, CLASSNAME, levels, writer);
}
// set up compiler
@@ -92,7 +95,7 @@ public void testGenerateSource() throws Exception {
final List errors = new ArrayList<>();
try (final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
final Iterable extends JavaFileObject> compilationUnits =
- fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(f));
+ fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(testSource.toFile()));
final String classPath = System.getProperty("jdk.module.path");
final List optionList = new ArrayList<>();
if (Strings.isNotBlank(classPath)) {
@@ -163,19 +166,24 @@ public void testGenerateSource() throws Exception {
});
}
- // now see if it actually works...
- final Method create = cls.getDeclaredMethod("create", String.class);
- final Object customLogger = create.invoke(null, "X.Y.Z");
- int n = 0;
- for (final String name : logMethods) {
- final Method method = cls.getDeclaredMethod(name, String.class);
- method.invoke(customLogger, "This is message " + n++);
- }
-
final TestLogger underlying = (TestLogger) LogManager.getLogger("X.Y.Z");
- final List lines = underlying.getEntries();
- for (int i = 0; i < lines.size(); i++) {
- assertEquals(" " + levels.get(i).name + " This is message " + i, lines.get(i));
+
+ try {
+ // now see if it actually works...
+ final Method create = cls.getDeclaredMethod("create", String.class);
+ final Object customLogger = create.invoke(null, "X.Y.Z");
+ int n = 0;
+ for (final String name : logMethods) {
+ final Method method = cls.getDeclaredMethod(name, String.class);
+ method.invoke(customLogger, "This is message " + n++);
+ }
+
+ final List lines = underlying.getEntries();
+ for (int i = 0; i < lines.size(); i++) {
+ assertEquals(" " + levels.get(i).name + " This is message " + i, lines.get(i));
+ }
+ } finally {
+ underlying.getEntries().clear();
}
}
}
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/tools/GenerateExtendedLoggerTest.java b/log4j-codegen/src/test/java/org/apache/logging/log4j/codegen/GenerateExtendedLoggerTest.java
similarity index 78%
rename from log4j-core-test/src/test/java/org/apache/logging/log4j/core/tools/GenerateExtendedLoggerTest.java
rename to log4j-codegen/src/test/java/org/apache/logging/log4j/codegen/GenerateExtendedLoggerTest.java
index cb8f49a..9795dd8 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/tools/GenerateExtendedLoggerTest.java
+++ b/log4j-codegen/src/test/java/org/apache/logging/log4j/codegen/GenerateExtendedLoggerTest.java
@@ -14,17 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.logging.log4j.core.tools;
+package org.apache.logging.log4j.codegen;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.io.BufferedWriter;
import java.io.File;
-import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -38,7 +41,8 @@
import javax.tools.ToolProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Marker;
-import org.apache.logging.log4j.core.test.TestConstants;
+import org.apache.logging.log4j.codegen.Generate.LevelInfo;
+import org.apache.logging.log4j.codegen.Generate.Type;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.ExtendedLogger;
@@ -53,7 +57,7 @@
@Tag("functional")
@SetSystemProperty(
- key = TestConstants.LOGGER_CONTEXT_FACTORY,
+ key = "log4j2.loggerContextFactory",
value = "org.apache.logging.log4j.test.TestLoggerContextFactory")
public class GenerateExtendedLoggerTest {
@@ -78,21 +82,21 @@ public void testGenerateSource() throws Exception {
final String CLASSNAME = "org.apache.logging.log4j.core.MyExtendedLogger";
// generate custom logger source
- final List values = Arrays.asList("DIAG=350 NOTICE=450 VERBOSE=550".split(" "));
- final List levels = Generate.LevelInfo.parse(values, Generate.ExtendedLogger.class);
- final String src = Generate.generateSource(CLASSNAME, levels, Generate.Type.EXTEND);
- final File f = new File(TEST_SOURCE);
- f.getParentFile().mkdirs();
- try (final FileOutputStream out = new FileOutputStream(f)) {
- out.write(src.getBytes(Charset.defaultCharset()));
+ final List levels =
+ Arrays.asList(new LevelInfo("DIAG", 350), new LevelInfo("NOTICE", 450), new LevelInfo("VERBOSE", 550));
+ final Path testSource = Paths.get(TEST_SOURCE);
+ Files.createDirectories(testSource.getParent());
+ try (final BufferedWriter writer = Files.newBufferedWriter(testSource, UTF_8)) {
+ Generate.generateSource(Type.EXTEND, CLASSNAME, levels, writer);
}
+ // set up compiler
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector diagnostics = new DiagnosticCollector<>();
final List errors = new ArrayList<>();
try (final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
final Iterable extends JavaFileObject> compilationUnits =
- fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(f));
+ fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(testSource.toFile()));
final String classPath = System.getProperty("jdk.module.path");
final List optionList = new ArrayList<>();
if (Strings.isNotBlank(classPath)) {
@@ -172,29 +176,34 @@ public void testGenerateSource() throws Exception {
method.invoke(extendedLogger, "This is message " + n++);
}
- // This logger extends o.a.l.log4j.spi.ExtendedLogger,
- // so all the standard logging methods can be used as well
- final ExtendedLogger logger = (ExtendedLogger) extendedLogger;
- logger.trace("trace message");
- logger.debug("debug message");
- logger.info("info message");
- logger.warn("warn message");
- logger.error("error message");
- logger.fatal("fatal message");
-
final TestLogger underlying = (TestLogger) LogManager.getLogger("X.Y.Z");
- final List lines = underlying.getEntries();
- for (int i = 0; i < lines.size() - 6; i++) {
- assertEquals(" " + levels.get(i).name + " This is message " + i, lines.get(i));
- }
- // test that the standard logging methods still work
- int i = lines.size() - 6;
- assertEquals(" TRACE trace message", lines.get(i++));
- assertEquals(" DEBUG debug message", lines.get(i++));
- assertEquals(" INFO info message", lines.get(i++));
- assertEquals(" WARN warn message", lines.get(i++));
- assertEquals(" ERROR error message", lines.get(i++));
- assertEquals(" FATAL fatal message", lines.get(i++));
+ try {
+ // This logger extends o.a.l.log4j.spi.ExtendedLogger,
+ // so all the standard logging methods can be used as well
+ final ExtendedLogger logger = (ExtendedLogger) extendedLogger;
+ logger.trace("trace message");
+ logger.debug("debug message");
+ logger.info("info message");
+ logger.warn("warn message");
+ logger.error("error message");
+ logger.fatal("fatal message");
+
+ final List lines = underlying.getEntries();
+ for (int i = 0; i < lines.size() - 6; i++) {
+ assertEquals(" " + levels.get(i).name + " This is message " + i, lines.get(i));
+ }
+
+ // test that the standard logging methods still work
+ int i = lines.size() - 6;
+ assertEquals(" TRACE trace message", lines.get(i++));
+ assertEquals(" DEBUG debug message", lines.get(i++));
+ assertEquals(" INFO info message", lines.get(i++));
+ assertEquals(" WARN warn message", lines.get(i++));
+ assertEquals(" ERROR error message", lines.get(i++));
+ assertEquals(" FATAL fatal message", lines.get(i++));
+ } finally {
+ underlying.getEntries().clear();
+ }
}
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/BasicCommandLineArguments.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/BasicCommandLineArguments.java
deleted file mode 100644
index bacc03f..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/BasicCommandLineArguments.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.tools;
-
-import org.apache.logging.log4j.core.tools.picocli.CommandLine.Option;
-
-public class BasicCommandLineArguments {
-
- @Option(
- names = {"--help", "-?", "-h"},
- usageHelp = true,
- description = "Prints this help and exits.")
- private boolean help;
-
- public boolean isHelp() {
- return help;
- }
-
- public void setHelp(final boolean help) {
- this.help = help;
- }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/CustomLoggerGenerator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/CustomLoggerGenerator.java
deleted file mode 100644
index 8f61116..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/CustomLoggerGenerator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.tools;
-
-/**
- * Wrapper around {@link Generate.CustomLogger}.
- */
-public class CustomLoggerGenerator {
- /**
- * Delegates to {@link Generate.CustomLogger#main(String[])}
- * @param args the command line arguments to pass on
- */
- public static void main(final String[] args) {
- Generate.CustomLogger.main(args);
- }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/ExtendedLoggerGenerator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/ExtendedLoggerGenerator.java
deleted file mode 100644
index 339f9cc..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/ExtendedLoggerGenerator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.tools;
-
-/**
- * Wrapper around {@link Generate.ExtendedLogger}.
- */
-public class ExtendedLoggerGenerator {
- /**
- * Delegates to {@link Generate.ExtendedLogger#main(String[])}
- * @param args the command line arguments to pass on
- */
- public static void main(final String[] args) {
- Generate.ExtendedLogger.main(args);
- }
-}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java
deleted file mode 100644
index 37b5b46..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/tools/picocli/CommandLine.java
+++ /dev/null
@@ -1,5619 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.tools.picocli;
-
-import static org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Column.Overflow.SPAN;
-import static org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Column.Overflow.TRUNCATE;
-import static org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Column.Overflow.WRAP;
-import static org.apache.logging.log4j.util.Strings.toRootLowerCase;
-import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.io.File;
-import java.io.PrintStream;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.WildcardType;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.sql.Time;
-import java.text.BreakIterator;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.Stack;
-import java.util.TreeSet;
-import java.util.UUID;
-import java.util.concurrent.Callable;
-import java.util.regex.Pattern;
-import org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Ansi.IStyle;
-import org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Ansi.Style;
-import org.apache.logging.log4j.core.tools.picocli.CommandLine.Help.Ansi.Text;
-
-/**
- *
- * CommandLine interpreter that uses reflection to initialize an annotated domain object with values obtained from the
- * command line arguments.
- *
Example
- *
import static picocli.CommandLine.*;
- *
- * @Command(header = "Encrypt FILE(s), or standard input, to standard output or to the output file.",
- * version = "v1.2.3")
- * public class Encrypt {
- *
- * @Parameters(type = File.class, description = "Any number of input files")
- * private List<File> files = new ArrayList<File>();
- *
- * @Option(names = { "-o", "--out" }, description = "Output file (default: print to console)")
- * private File outputFile;
- *
- * @Option(names = { "-v", "--verbose"}, description = "Verbosely list files processed")
- * private boolean verbose;
- *
- * @Option(names = { "-h", "--help", "-?", "-help"}, usageHelp = true, description = "Display this help and exit")
- * private boolean help;
- *
- * @Option(names = { "-V", "--version"}, versionHelp = true, description = "Display version info and exit")
- * private boolean versionHelp;
- * }
- *
- *
- * Use {@code CommandLine} to initialize a domain object as follows:
- *
- * public static void main(String... args) {
- * Encrypt encrypt = new Encrypt();
- * try {
- * List<CommandLine> parsedCommands = new CommandLine(encrypt).parse(args);
- * if (!CommandLine.printHelpIfRequested(parsedCommands, System.err, Help.Ansi.AUTO)) {
- * runProgram(encrypt);
- * }
- * } catch (ParameterException ex) { // command line arguments could not be parsed
- * System.err.println(ex.getMessage());
- * ex.getCommandLine().usage(System.err);
- * }
- * }
- *
- * Invoke the above program with some command line arguments. The below are all equivalent:
- *
- */
-public class CommandLine {
- /** This is picocli version {@value}. */
- public static final String VERSION = "2.0.3";
-
- private final Tracer tracer = new Tracer();
- private final Interpreter interpreter;
- private String commandName = Help.DEFAULT_COMMAND_NAME;
- private boolean overwrittenOptionsAllowed = false;
- private boolean unmatchedArgumentsAllowed = false;
- private final List unmatchedArguments = new ArrayList();
- private CommandLine parent;
- private boolean usageHelpRequested;
- private boolean versionHelpRequested;
- private final List versionLines = new ArrayList();
-
- /**
- * Constructs a new {@code CommandLine} interpreter with the specified annotated object.
- * When the {@link #parse(String...)} method is called, fields of the specified object that are annotated
- * with {@code @Option} or {@code @Parameters} will be initialized based on command line arguments.
- * @param command the object to initialize from the command line arguments
- * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation
- */
- public CommandLine(final Object command) {
- interpreter = new Interpreter(command);
- }
-
- /** Registers a subcommand with the specified name. For example:
- *
- * CommandLine commandLine = new CommandLine(new Git())
- * .addSubcommand("status", new GitStatus())
- * .addSubcommand("commit", new GitCommit();
- * .addSubcommand("add", new GitAdd())
- * .addSubcommand("branch", new GitBranch())
- * .addSubcommand("checkout", new GitCheckout())
- * //...
- * ;
- *
- *
- *
The specified object can be an annotated object or a
- * {@code CommandLine} instance with its own nested subcommands. For example:
- *
- * CommandLine commandLine = new CommandLine(new MainCommand())
- * .addSubcommand("cmd1", new ChildCommand1()) // subcommand
- * .addSubcommand("cmd2", new ChildCommand2())
- * .addSubcommand("cmd3", new CommandLine(new ChildCommand3()) // subcommand with nested sub-subcommands
- * .addSubcommand("cmd3sub1", new GrandChild3Command1())
- * .addSubcommand("cmd3sub2", new GrandChild3Command2())
- * .addSubcommand("cmd3sub3", new CommandLine(new GrandChild3Command3()) // deeper nesting
- * .addSubcommand("cmd3sub3sub1", new GreatGrandChild3Command3_1())
- * .addSubcommand("cmd3sub3sub2", new GreatGrandChild3Command3_2())
- * )
- * );
- *
- *
The default type converters are available on all subcommands and nested sub-subcommands, but custom type
- * converters are registered only with the subcommand hierarchy as it existed when the custom type was registered.
- * To ensure a custom type converter is available to all subcommands, register the type converter last, after
- * adding subcommands.
- *
See also the {@link Command#subcommands()} annotation to register subcommands declaratively.
- *
- * @param name the string to recognize on the command line as a subcommand
- * @param command the object to initialize with command line arguments following the subcommand name.
- * This may be a {@code CommandLine} instance with its own (nested) subcommands
- * @return this CommandLine object, to allow method chaining
- * @see #registerConverter(Class, ITypeConverter)
- * @since 0.9.7
- * @see Command#subcommands()
- */
- public CommandLine addSubcommand(final String name, final Object command) {
- final CommandLine commandLine = toCommandLine(command);
- commandLine.parent = this;
- interpreter.commands.put(name, commandLine);
- return this;
- }
- /** Returns a map with the subcommands {@linkplain #addSubcommand(String, Object) registered} on this instance.
- * @return a map with the registered subcommands
- * @since 0.9.7
- */
- public Map getSubcommands() {
- return new LinkedHashMap(interpreter.commands);
- }
- /**
- * Returns the command that this is a subcommand of, or {@code null} if this is a top-level command.
- * @return the command that this is a subcommand of, or {@code null} if this is a top-level command
- * @see #addSubcommand(String, Object)
- * @see Command#subcommands()
- * @since 0.9.8
- */
- public CommandLine getParent() {
- return parent;
- }
-
- /** Returns the annotated object that this {@code CommandLine} instance was constructed with.
- * @param the type of the variable that the return value is being assigned to
- * @return the annotated object that this {@code CommandLine} instance was constructed with
- * @since 0.9.7
- */
- public T getCommand() {
- return (T) interpreter.command;
- }
-
- /** Returns {@code true} if an option annotated with {@link Option#usageHelp()} was specified on the command line.
- * @return whether the parser encountered an option annotated with {@link Option#usageHelp()}.
- * @since 0.9.8 */
- public boolean isUsageHelpRequested() {
- return usageHelpRequested;
- }
-
- /** Returns {@code true} if an option annotated with {@link Option#versionHelp()} was specified on the command line.
- * @return whether the parser encountered an option annotated with {@link Option#versionHelp()}.
- * @since 0.9.8 */
- public boolean isVersionHelpRequested() {
- return versionHelpRequested;
- }
-
- /** Returns whether options for single-value fields can be specified multiple times on the command line.
- * The default is {@code false} and a {@link OverwrittenOptionException} is thrown if this happens.
- * When {@code true}, the last specified value is retained.
- * @return {@code true} if options for single-value fields can be specified multiple times on the command line, {@code false} otherwise
- * @since 0.9.7
- */
- public boolean isOverwrittenOptionsAllowed() {
- return overwrittenOptionsAllowed;
- }
-
- /** Sets whether options for single-value fields can be specified multiple times on the command line without a {@link OverwrittenOptionException} being thrown.
- *
The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its
- * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added
- * later will have the default setting. To ensure a setting is applied to all
- * subcommands, call the setter last, after adding subcommands.
- * @param newValue the new setting
- * @return this {@code CommandLine} object, to allow method chaining
- * @since 0.9.7
- */
- public CommandLine setOverwrittenOptionsAllowed(final boolean newValue) {
- this.overwrittenOptionsAllowed = newValue;
- for (final CommandLine command : interpreter.commands.values()) {
- command.setOverwrittenOptionsAllowed(newValue);
- }
- return this;
- }
-
- /** Returns whether the end user may specify arguments on the command line that are not matched to any option or parameter fields.
- * The default is {@code false} and a {@link UnmatchedArgumentException} is thrown if this happens.
- * When {@code true}, the last unmatched arguments are available via the {@link #getUnmatchedArguments()} method.
- * @return {@code true} if the end use may specify unmatched arguments on the command line, {@code false} otherwise
- * @see #getUnmatchedArguments()
- * @since 0.9.7
- */
- public boolean isUnmatchedArgumentsAllowed() {
- return unmatchedArgumentsAllowed;
- }
-
- /** Sets whether the end user may specify unmatched arguments on the command line without a {@link UnmatchedArgumentException} being thrown.
- *
The specified setting will be registered with this {@code CommandLine} and the full hierarchy of its
- * subcommands and nested sub-subcommands at the moment this method is called. Subcommands added
- * later will have the default setting. To ensure a setting is applied to all
- * subcommands, call the setter last, after adding subcommands.
- * @param newValue the new setting. When {@code true}, the last unmatched arguments are available via the {@link #getUnmatchedArguments()} method.
- * @return this {@code CommandLine} object, to allow method chaining
- * @since 0.9.7
- * @see #getUnmatchedArguments()
- */
- public CommandLine setUnmatchedArgumentsAllowed(final boolean newValue) {
- this.unmatchedArgumentsAllowed = newValue;
- for (final CommandLine command : interpreter.commands.values()) {
- command.setUnmatchedArgumentsAllowed(newValue);
- }
- return this;
- }
-
- /** Returns the list of unmatched command line arguments, if any.
- * @return the list of unmatched command line arguments or an empty list
- * @see #isUnmatchedArgumentsAllowed()
- * @since 0.9.7
- */
- public List getUnmatchedArguments() {
- return unmatchedArguments;
- }
-
- /**
- *
- * Convenience method that initializes the specified annotated object from the specified command line arguments.
- *
- *
- * @param command the object to initialize. This object contains fields annotated with
- * {@code @Option} or {@code @Parameters}.
- * @param args the command line arguments to parse
- * @param the type of the annotated object
- * @return the specified annotated object
- * @throws InitializationException if the specified command object does not have a {@link Command}, {@link Option} or {@link Parameters} annotation
- * @throws ParameterException if the specified command line arguments are invalid
- * @since 0.9.7
- */
- public static T populateCommand(final T command, final String... args) {
- final CommandLine cli = toCommandLine(command);
- cli.parse(args);
- return command;
- }
-
- /** Parses the specified command line arguments and returns a list of {@code CommandLine} objects representing the
- * top-level command and any subcommands (if any) that were recognized and initialized during the parsing process.
- *
- * If parsing succeeds, the first element in the returned list is always {@code this CommandLine} object. The
- * returned list may contain more elements if subcommands were {@linkplain #addSubcommand(String, Object) registered}
- * and these subcommands were initialized by matching command line arguments. If parsing fails, a
- * {@link ParameterException} is thrown.
- *
- *
- * @param args the command line arguments to parse
- * @return a list with the top-level command and any subcommands initialized by this method
- * @throws ParameterException if the specified command line arguments are invalid; use
- * {@link ParameterException#getCommandLine()} to get the command or subcommand whose user input was invalid
- */
- public List parse(final String... args) {
- return interpreter.parse(args);
- }
- /**
- * Represents a function that can process a List of {@code CommandLine} objects resulting from successfully
- * {@linkplain #parse(String...) parsing} the command line arguments. This is a
- * functional interface
- * whose functional method is {@link #handleParseResult(List, PrintStream, CommandLine.Help.Ansi)}.
- *
- * Implementations of this functions can be passed to the {@link #parseWithHandlers(IParseResultHandler, PrintStream, Help.Ansi, IExceptionHandler, String...) CommandLine::parseWithHandler}
- * methods to take some next step after the command line was successfully parsed.
- *
- * @see RunFirst
- * @see RunLast
- * @see RunAll
- * @since 2.0 */
- public interface IParseResultHandler {
- /** Processes a List of {@code CommandLine} objects resulting from successfully
- * {@linkplain #parse(String...) parsing} the command line arguments and optionally returns a list of results.
- * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments
- * @param out the {@code PrintStream} to print help to if requested
- * @param ansi for printing help messages using ANSI styles and colors
- * @return a list of results, or an empty list if there are no results
- * @throws ExecutionException if a problem occurred while processing the parse results; use
- * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed
- */
- List