问题
We are using a fairly simple log4j2.xml configuration file to log to stdout. However there are cases where we want to change this configuration programmatically after the start of the application to use a log file that is handed over on the command line.
For this I followed the suggestion on the log4j2 homepage and wrote the following method
static void divertLogging(String logFile, Level level) {
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
AppenderComponentBuilder appenderBuilder
= builder.newAppender("File", "FILE").addAttribute("fileName", logFile).addAttribute("append", "false");
appenderBuilder.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));
builder.add(appenderBuilder);
builder.add(builder.newRootLogger(level).add(builder.newAppenderRef("File")));
try {
builder.writeXmlConfiguration(System.out);
} catch (IOException e) {
throw new ApplicationException(e);
}
BuiltConfiguration configuration = builder.build();
Configurator.initialize(configuration);
((LoggerContext)LogManager.getContext(false)).updateLoggers(configuration);
}
We get the following output
<?xml version="1.0" ?>
<Configuration>
<Appenders>
<FILE name="File" fileName="test.log" append="false">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
</FILE>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
and then the log message
ERROR Attempted to append to non-started appender File
After that logging continues to be output to stdout and the desired log file stays empty.
Anyone having an idea what I am doing wrong?
回答1:
You don't need programmatic configuration to do what you want, and I would strongly discourage you from using it since that would make your code depend on the log4j2 implementation rather than its public interface.
To change the file dynamically at runtime you can use the RoutingAppender
together with a lookup. See the log4j2 FAQ page for details.
Here is a sample log4j2 configuration:
<?xml version="1.0" ?>
<Configuration>
<Appenders>
<Routing name="myAppender">
<Routes pattern="$${main:0}">
<!-- This route is chosen if there is no value for main argument 0 -->
<Route key="$${main:0}">
<File
fileName="logs/default.log"
name="myAppender-default">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
</File>
</Route>
<!-- This route is chosen if there is a value for main argument 0 -->
<Route>
<File
fileName="logs/${main:0}.log"
name="myAppender-${main:0}">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
</File>
</Route>
</Routes>
</Routing>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="myAppender"/>
</Root>
</Loggers>
</Configuration>
Here is some sample Java code to generate some logs:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.lookup.MainMapLookup;
public class SomeClass {
private static final Logger LOG = LogManager.getLogger();
public static void main(String[] args){
MainMapLookup.setMainArguments(args);
LOG.info("This should appear in default.log");
args = new String[]{"specialFile"};
MainMapLookup.setMainArguments(args);
LOG.info("This should appear in specialFile.log");
}
}
When the above code is executed without passing a program argument, 2 logs are generated each with 1 entry. The default.log contains the first log entry and the specialFile.log contains the second. If you pass a program argument it will be used as the log file name in which case no entry would appear in default.log - as illustrated by the second log where we simulate passing a single argument by creating the new String
array.
Hope this helps!
回答2:
try this
/**
*
* @param logType 0 = console, 1 = file
* @param logFile
* @param level
*/
private void divertLogging(int logType, String logFile, Level level) {
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
AppenderComponentBuilder appenderBuilder;
if (logType == 0)
appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
else
appenderBuilder = builder.newAppender("File", "FILE").addAttribute("fileName", logFile).addAttribute("append", "false");
appenderBuilder.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));
builder.add(appenderBuilder);
if (logType == 1)
builder.add(builder.newRootLogger(level).add(builder.newAppenderRef("File")));
try {
builder.writeXmlConfiguration(System.out);
} catch (IOException e) {
e.printStackTrace();
}
BuiltConfiguration configuration = builder.build();
Configurator.initialize(configuration);
((LoggerContext)LogManager.getContext(false)).updateLoggers(configuration);
if (logType == 1)
configuration.getAppender("File").start();
}
public static void main(String[] args) {
Log4j2Test test = new Log4j2Test();
test.divertLogging(0, null, Level.ALL);
logger.error("Log to console 1");
test.divertLogging(1, "C:\\Java\\test\\output\\test.log", Level.ALL);
logger.error("Log to file 2");
test.divertLogging(0, null, Level.ALL);
logger.error("Log to console 3");
test.divertLogging(1, "C:\\Java\\test\\output\\test.log", Level.ALL);
logger.error("Log to file 4");
}
Why my answer is downvoted?
来源:https://stackoverflow.com/questions/58058227/log4j2-dynamic-file-name-based-on-command-line-argument