I want to use log4j without any configure file.
What I wan to do is something as:
logger = (Logger) LogManager.getLogger(this.getClass());
String pattern = "[%level] %m%n";
//do something to make this logger output to an local file "/xxx/yyy/zzz.log"
I have found this answer: Configuring Log4j Loggers Programmatically.
But the docs of Logger#addAppender
says:
This method is not exposed through the public API and is used primarily for unit testing.
I am not sure if it is the right way to use this method in my code or there is other better solution to solve my problem.
The official documentation shows an example : Programatically Adding to the Current Configuration
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
Layout layout = PatternLayout.createLayout(PatternLayout.SIMPLE_CONVERSION_PATTERN, config, null, null,null, null);
Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true", "false", "false", "4000", layout, null, "false", null, config);
appender.start();
config.addAppender(appender);
AppenderRef ref = AppenderRef.createAppenderRef("File", null, null);
AppenderRef[] refs = new AppenderRef[] {ref};
LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j", "true", refs, null, config, null );
loggerConfig.addAppender(appender, null, null);
config.addLogger("org.apache.logging.log4j", loggerConfig);
ctx.updateLoggers();
With these limitations :
- If the configuration file is changed the configuration will be reloaded and the manual changes will be lost.
- Modification to the running configuration requires that all the methods being called (addAppender and addLogger) be synchronized.
This solution avoids to use method from the core implementation org.apache.logging.log4j.core.Logger
, and it avoids dirty cast like that :
import org.apache.logging.log4j.Logger;
Logger logger = (Logger) LogManager.getLogger(this.getClass());
((org.apache.logging.log4j.core.Logger) logger).addAppender(...); // Bypassing the public API
With the latest version of log4j2
, all create the APIs like
PatternLayout.createLayout,
FileAppender.createAppender,
LoggerConfig.createLogger
have become deprecated, it's better to use a custom log ConfigurationFactory
along with ConfigurationBuilder
for defining the log configuration programmatically.
If I simply respond to your requirement, I can suggest three options. I use the first one for a kind of bootstrap Logger config; however I thought the second would be necessary at first. Your third choice seems cumbersome since you need to call different log4j API-s to get configured.
Using Log4j over the simple logging framework for Java ...
Make a 'minimal' or 'default' log4j.properties file in your resources for the JAR file. Then declare some statics...
private static final URL LOGGER_CONFIG_URL = resolveConfigUrl();
:
private static URL resolveConfigUrl(){
URL url = LogConfig.class.getResource( LOGGER_CONFIG_NAME );
if( null == url ) // Second chance, try for a file.
{
url = FileHelp.resolveUrlNameAsUrlToFile( LOGGER_CONFIG_NAME );
//-- Make this function with: url = tmpFile.toURI().toURL()
// Plus appropriate try/catch and error checks.
}
return url;
}
private static void configureLogger(){
BasicConfigurator.configure();
PropertyConfigurator.configure( LOGGER_CONFIG_URL );
LOG.info( "Logging config done: " + LOGGER_CONFIG_NAME );
}
Write your config to a StreamWriter instead of placing a file in your JAR, and then give the Stream to the log configurator as a StringReader and use the example above (more or less).
You can use the slf4j API to do your log config, rather than write direct to Log4j. Most places I've been prefer the SLF4J route.
Personally, I prefer option #1; it is easy to maintain. simple and you can always re-order the code to accept/look for a file to load first. There are some other lateral avenues you can consider, such as setting environment variables programmatically at start-up. That seems contrived to me.
The way I use #1 is to establish a default / bootstrap logger configuration via the resources file, which itself gets wrapped into the JAR file. You can reconfigure things 'later' while this option gives you a minimalist star-up or bootstrap config. In the early stages I found things weren't (yet) being logged because the logger initialisation was yet to happen on embedded apps. So I kept the simple option for bootstrap (or default) as a basic ever since. Hope this helps.