Reputation: 1190
I am trying to setup log4j2 to write logs using the RollingFileAppender
. I want to configure the logging system programmatically instead of using XML files.
Here is what I tried (mostly the same as the docs at https://logging.apache.org/log4j/2.x/manual/customconfig.html#Configurator):
public static void configure(String rootLevel, String packageLevel) {
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory
.newConfigurationBuilder();
builder.setConfigurationName("RollingBuilder");
builder.setStatusLevel(Level.TRACE);
// Create a rolling file appender
LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %c{1}:%L - %m%n");
ComponentBuilder triggeringPolicy =
builder
.newComponent("Policies")
.addComponent(
builder
.newComponent("SizeBasedTriggeringPolicy")
.addAttribute("size", "200M")
);
AppenderComponentBuilder appenderBuilder =
builder
.newAppender("rolling", "RollingFile")
.addAttribute("fileName", "log")
.addAttribute("filePattern", "log.%d.gz")
.add(layoutBuilder)
.addComponent(triggeringPolicy);
builder.add(appenderBuilder);
// Create new logger
LoggerComponentBuilder myPackageLoggerBuilder =
builder.newLogger("com.mypackage", packageLevel)
.add(builder.newAppenderRef("rolling"))
.addAttribute("additivity", false);
builder.add(myPackageLoggerBuilder);
RootLoggerComponentBuilder rootLoggerBuilder =
builder
.newRootLogger(rootLevel)
.add(builder.newAppenderRef("rolling"));
builder.add(rootLoggerBuilder);
// Initialize logging
Configurator.initialize(builder.build());
}
I call the configure()
method at the start of the main method. A file named log
is created when I run my program, but all log output goes to standard out and the log
file remains empty.
Can someone help figure out what is wrong with my config?
I am not using any log4j configuration file, if that makes a difference. Also using the slf4j API in my code. Dependencies -
org.apache.logging.log4j:log4j-api:2.11.1
org.apache.logging.log4j:log4j-core:2.11.1
org.apache.logging.log4j:log4j-slf4j-impl:2.11.1
org.slf4j:slf4j-api:1.7.25
Upvotes: 3
Views: 5466
Reputation: 31
Please find the sample Root log and finally needs to call reconfigure function.
RootLoggerComponentBuilder rootLogger = builder
.newRootLogger(Level.ALL)
.add(builder.newAppenderRef("LogToRollingFile"));
LoggerComponentBuilder logger = builder
.newLogger("MyClass",Level.ALL)
.addAttribute("additivity", false);
Configurator.reconfigure(builder.build());
Upvotes: 3
Reputation: 4713
First, this answer is in response to the additional information provided in your comment.
My requirement is that I want to control log levels for different packages via command line flags when I launch my program
Since you want to use program arguments to control your log level I suggest you take a look at the Main Arguments Lookup and the Routing Appender. Using these two features together you can set up your logging configuration to send log events to the appropriate appender based on program arguments.
I will provide a simple example to help guide you.
First here is an example Java class that generates some log events:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.lookup.MainMapLookup;
public class SomeClass {
private static Logger log = LogManager.getLogger();
public static void main(String[] args){
MainMapLookup.setMainArguments(args);
if(log.isDebugEnabled())
log.debug("This is some debug!");
log.info("Here's some info!");
log.error("Some error happened!");
}
}
Next the configuration file for log4j2 (see comments in the code for details):
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Routing name="myRoutingAppender">
<!-- log events are routed to appenders based on the logLevel program argument -->
<Routes pattern="$${main:logLevel}">
<!-- If the logLevel argument is followed by DEBUG this route is used -->
<Route ref="DebugFile" key="DEBUG" />
<!-- If the logLevel argument is omitted or followed by any other value this route is used -->
<Route ref="InfoFile" />
</Routes>
</Routing>
<!-- This appender is not necessary, was used to test the config -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<!-- Below are the 2 appenders used by the Routing Appender from earlier -->
<File name="DebugFile" fileName="logs/Debug.log" immediateFlush="true"
append="false">
<PatternLayout
pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</File>
<File name="InfoFile" fileName="logs/Info.log" immediateFlush="true"
append="false">
<PatternLayout
pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<LevelRangeFilter minLevel="FATAL" maxLevel="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</File>
</Appenders>
<Loggers>
<!-- Root logger is set to DEBUG intentionally so that debug events are generated.
However, events may be ignored by the LevelRangeFilter depending on where they
are routed by the Routing Appender
-->
<Root level="DEBUG">
<AppenderRef ref="Console" />
<AppenderRef ref="myRoutingAppender" />
</Root>
</Loggers>
</Configuration>
Using this configuration if you do not provide a "logLevel" argument then by default log events are routed to the "InfoFile" appender and any events that are more specific than INFO are ignored via the LevelRangeFilter
.
If a "logLevel" argument is provided and is followed by "DEBUG" then log events are routed to the "DebugFile" appender and none of the events are ignored.
Note that I did try to use lookups to set the log level but it appears that log level parameters can't be configured via lookups. That is why I had to use this alternative approach. My one concern with this approach is that as I noted in the comments within the configuration file the log level must be kept at DEBUG which means you always generate the DEBUG events even if you don't use them. This might impact performance. A workaround would be to use your program argument to determine whether you need to generate the debug events. For example:
Normally you would use:
if(log.isDebugEnabled())
log.debug("This is some debug!");
but when using the configuration above you would use something like:
if("DEBUG".equals(args[1]))
log.debug("This is some debug!");
and you could make that more efficient using an enum (maybe even use the Level
class provided by log4j2) if you needed to.
I hope this helps get you started.
Upvotes: 1