how to create a log4j custom appender and control the filename - java

My company uses a software package that reads in log files from our servers, parses them, and spits performance data into a database. We dont have access / permission to modify the source code for the app that reads the files but we do have access to the code that writes the files. I need to change the way the log files are being written and I would like to use log4j (so I can use an AsyncAppender). The program expects a few things:
1). There should be 10 log files that roll and each log file will be one day of logs. The files need to be named 0 through 9 and I need to be able to programatically set the file name and when they roll based on the server time.
2). Essentially when generating the 11th log file it should delete the oldest one and start writing to that one.
3). When a new log file is generated I need to be able to insert a timestamp as the first line of the file (System.currentTimeMillis()).
Is it possible to meet the above requirements with a custom log4j file appender? Ive looked at DailyRollingFileAppender but cant seem to figure out how to control the file names exactly like I need to. Also I cant seem to figure out how to write the first line in the log when it is generated (for example is there some callback function I can register when a new log file gets rolled in)?

I think you can achieve first 2 with
using RollingFileAppender and specifying FixedWindowRollingPolicy for RollingPolicy
as for the #3 you can always write your own handler

For the sake of posterity. I used the below class as my custom rolling policy
import org.apache.log4j.rolling.RollingPolicyBase;
import org.apache.log4j.rolling.RolloverDescription;
import org.apache.log4j.rolling.RolloverDescriptionImpl;
import org.apache.log4j.rolling.TriggeringPolicy;
import org.apache.log4j.Appender;
import org.apache.log4j.spi.LoggingEvent;
public final class CustomRollingPolicy extends RollingPolicyBase
implements TriggeringPolicy
{
private short curFileId = -1;
private String lastFileName = null;
static private final long FILETIMEINTERVAL = 86400000l;
static private final int NUM_FILES = 10;//86400000l;
public String folderName = "";
public String getFolderName() {
return folderName;
}
public void setFolderName(String folderName) {
this.folderName = folderName;
}
private short calculateID(long startTime) {
return (short) ((startTime / FILETIMEINTERVAL) % NUM_FILES);
}
public String getCurrentFileName()
{
StringBuffer buf = new StringBuffer();
buf.append(folderName);
buf.append(calculateID(System.currentTimeMillis()));
return buf.toString();
}
public void activateOptions()
{
super.activateOptions();
this.lastFileName = getCurrentFileName();
}
public RolloverDescription initialize(String currentActiveFile, boolean append)
{
curFileId = this.calculateID(System.currentTimeMillis());
lastFileName = getCurrentFileName();
String fileToUse = activeFileName != null? activeFileName: currentActiveFile != null?currentActiveFile:lastFileName;
return new RolloverDescriptionImpl(fileToUse, append, null, null);
}
public RolloverDescription rollover(String currentActiveFile)
{
curFileId = this.calculateID(System.currentTimeMillis());
String newFileName = getCurrentFileName();
if (newFileName.equals(this.lastFileName))
{
return null;
}
String lastBaseName = this.lastFileName;
String nextActiveFile = newFileName;
if (!currentActiveFile.equals(lastBaseName))
{
nextActiveFile = currentActiveFile;
}
this.lastFileName = newFileName;
return new RolloverDescriptionImpl(nextActiveFile, false, null, null);
}
public boolean isTriggeringEvent(Appender appender, LoggingEvent event, String filename, long fileLength)
{
short fileIdForCurrentServerTime = this.calculateID(System.currentTimeMillis());
return curFileId != fileIdForCurrentServerTime;
}
}
And here is the appender config in my log4j xml file:
<!-- ROLLING FILE APPENDER FOR RUM LOGS -->
<appender name="rumRollingFileAppender" class="org.apache.log4j.rolling.RollingFileAppender">
<rollingPolicy class="com.ntrs.wpa.util.CustomRollingPolicy">
<param name="folderName" value="C:/bea-portal-10.3.2/logs/"/>
<param name="FileNamePattern" value="C:/bea-portal-10.3.2/logs/foo.%d{yyyy-MM}.gz"/>
</rollingPolicy>
<layout class="com.ntrs.els.log4j.AppServerPatternLayout">
<param name="ConversionPattern" value="%m%n" />
</layout>
</appender>

Related

log4j2 - Can one appender manage more than 1 file "simultaneously"?

I want to create an appender that logs each user actions into a different file per user using the MDC / ThreadContext to save the user name and use it to name the file later on.
So for User1 we'd have "web_debug_user1_yyyy-MM-DD", for user2 "web_debug_user2_yyyy-MM-DD".
Those users can be logged in the application at the same time.
Here is the relevant part of the config:
<Properties>
<Property name="logPath">/data/logs</Property>
<Property name="rollingFileName">web_debug</Property>
<Property name="rollingFileNameError">web_Error</Property>
<Property name="patternLog">%d %-5p [%c] %m [SID: %X{sessionId}]%n</Property>
<Property name="patternLogUser">%d %-5p <%X{userId}><%X{customerID}><%X{oid}> [%c] %m [SID: %X{sessionId}]%n</Property>
</Properties>
<Appenders>
<RollingFile name="rollingFileUser"
filePattern="${logPath}/${rollingFileName}_$${ctx:userId}%d{yyyy-MM-dd}_%i.txt">
<PatternLayout pattern="${patternLogUser}" />
<Policies>
<UserLoggingTriggerPolicy />
<TimeBasedTriggeringPolicy interval="1"/>
</Policies>
<DirectWriteRolloverStrategy />
</RollingFile>
And here is the custom policy UserLoggingTriggeringPolicy which sets the fileName property on the manager each time an user event is launched.
#Plugin(name = "UserLoggingTriggerPolicy", category = "Core")
public class UserLoggingTriggerPolicy implements TriggeringPolicy {
private RollingFileManager manager;
private String typelog;
private File[] debugFilesUser;
#Override
public void initialize(RollingFileManager manager) {
this.manager = manager;
this.typelog = manager.getFileName().contains("debug") ? "debug" : "Error";
this.debugFilesUser = null;
}
#Override
public boolean isTriggeringEvent(LogEvent arg0) {
return isRolling();
}
public boolean isRolling() {
boolean roll = false;
if (!this.manager.getFileName().contains(MDC.get("userId"))) {
((DirectFileRolloverStrategy) manager.getRolloverStrategy()).clearCurrentFileName();
((DirectFileRolloverStrategy) manager.getRolloverStrategy()).getCurrentFileName(manager);
}
File f = new File(this.manager.getFileName());
File folder = new File(f.getParent());
if (debugFilesUser == null) {
getFiles(folder);
}
if ((debugFilesUser.length != 0 && debugFilesUser[debugFilesUser.length - 1].length() / 1024 / 1024 > 10)
|| !f.exists()) {
debugFilesUser = null;
roll = true;
}
return roll;
}
private void getFiles(File folder) {
debugFilesUser = folder.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
if (name.contains(MDC.get("userId")) && name.contains(typelog)) {
return true;
}
return false;
}
});
}
#PluginFactory
public static UserLoggingTriggerPolicy createPolicy() {
return new UserLoggingTriggerPolicy();
}
}
Thing is that it does not seem enough to change the FileName of the FileManager given that it still points to the same OutputStream thus logging all different user messages into the file belonging to the first logged in user in the app. After some debugging found out that the OutputStream only changes under 2 circumstances: Initialization of the FileManager and RollOver. Then forced a rollOver in the custom policy when the current user changes but it ended up creating a new file after each roll and not writing in the previously existing ones, so in the span of 10 minutes had like 20-30 different files.
So the question(s) is(are): Is there any way to make an appender use a previous file, let's say "to rollback", and not only create a new one?
Was my approach wrong?
Thanks.

Log final MySQL query in MyBatis Java to execute it manually afterwards on a different server

I am developing a command line application in JAVA where, based on some factors, MySQL queries are executed on a sandbox MySQL server and after some manual verification these queries need to be executed on the production server. I want to log these MySQL queries in a .sql file on sandbox server and execute it later using scripts.
I tried searching through stack overflow and found lots of examples for logging queries, but the queries logged are not the final queries executed. For example,
Preparing: INSERT into table_name values(null, ?,?, ?,?,?, ?, ?,?);
DEBUG [pool-4-thread-1] - ==> Parameters: 21655(Integer), 2658413(Integer), 04:05:00(Time), null, 1565.0(Double), 3(String), (String), 0(Integer)
Also, the queries are not logged in directly executable format.
My log4j.properties file:
log4j.rootLogger=INFO, stdout
log4j.logger.com.test.mappers.TableMapper=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
log4j.logger.java.sql.PreparedStatement=DEBUG, stdout
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.com.ibatis=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
Can I log exact final MySQL queries in a .sql file separately?
After trying a lot, I found that this is not possible only with Mybatis and log4j, it needs a proxy driver which can log the request to JDBC driver.
I found the idea of using log4jdbc2 from here with LOG4JDBC_SQL marker to send the SQL queries to a separate file. But, this file will contain approximated time taken for execution and connection number also. So to get rid of them I wrote a custom spy log delegator.
Installation and configuration of log4jdbc2 can be found here.
I will rewrite the steps taken for getting the desired results:
Added maven dependency for log4jdbc2. log4jdbc2 depends on log4j-api and log4j-core, make sure they are included in the project.
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>1.16</version>
</dependency>
Changed JDBC URL from jdbc:mysql://localhost:3306/mydb to jdbc:log4jdbc:mysql://localhost:3306/mydb
Changed the driver used to net.sf.log4jdbc.sql.jdbcapi.DriverSpy
Created an appender and created a logger with LOG4JDBC_SQL marker to filter only sql queries.
<Appenders>
<File name="queryLog" fileName="/Users/sahebpreetsingh/queries.sql"
immediateFlush="false" append="false">
<PatternLayout pattern="%msg%n" />
</File>
</Appenders>
<Loggers>
<logger name="log4jdbc.log4j2" level="info" additivity="false">
<MarkerFilter marker="LOG4JDBC_SQL" onMatch="ACCEPT"
onMismatch="DENY" />
<appender-ref ref="queryLog" />
</logger>
</Loggers>
The marker here specifies to filter sql queries only and ignore the rest of the results.
Extended the existing Log4j2SpyLogDelegator to save time and log only sql queries in executable form.
package com.myapp.utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import net.sf.log4jdbc.Properties;
import net.sf.log4jdbc.log.log4j2.Log4j2SpyLogDelegator;
import net.sf.log4jdbc.sql.Spy;
public class QueryLogDelegator extends Log4j2SpyLogDelegator {
private static final Logger LOGGER = LogManager.getLogger("log4jdbc.log4j2");
private static final Marker SQL_MARKER = MarkerManager.getMarker("LOG4JDBC_SQL");
private static final Marker SELECT_MARKER = MarkerManager.getMarker("LOG4JDBC_SELECT", SQL_MARKER);
private static final Marker INSERT_MARKER = MarkerManager.getMarker("LOG4JDBC_INSERT", SQL_MARKER);
private static final Marker UPDATE_MARKER = MarkerManager.getMarker("LOG4JDBC_UPDATE", SQL_MARKER);
private static final Marker DELETE_MARKER = MarkerManager.getMarker("LOG4JDBC_DELETE", SQL_MARKER);
private static final Marker CREATE_MARKER = MarkerManager.getMarker("LOG4JDBC_CREATE", SQL_MARKER);
private String getSqlOperation(String sql) {
if (sql == null) {
return "";
}
sql = sql.trim();
if (sql.length() < 6) {
return "";
}
return sql.substring(0, 6).toLowerCase();
}
private boolean shouldSqlBeLogged(String operation) {
return ((operation == null) || ((Properties.isDumpSqlSelect()) && ("select".equals(operation)))
|| ((Properties.isDumpSqlInsert()) && ("insert".equals(operation))) || ((Properties.isDumpSqlUpdate()) && ("update".equals(operation)))
|| ((Properties.isDumpSqlDelete()) && ("delete".equals(operation))) || ((Properties.isDumpSqlCreate()) && ("create".equals(operation))));
}
#Override
public void sqlTimingOccurred(Spy spy, long execTime, String methodCall, String sql) {
String operation = getSqlOperation(sql);
if ((Properties.isDumpSqlFilteringOn()) && (!(shouldSqlBeLogged(operation)))) {
return;
}
Marker marker = getStatementMarker(operation);
if ((Properties.isSqlTimingErrorThresholdEnabled()) && (execTime >= Properties.getSqlTimingErrorThresholdMsec())) {
LOGGER.error(marker, sql);
} else if (LOGGER.isWarnEnabled())
if ((Properties.isSqlTimingWarnThresholdEnabled()) && (execTime >= Properties.getSqlTimingWarnThresholdMsec())) {
LOGGER.warn(marker, sql);
} else
LOGGER.info(marker, sql);
}
private Marker getStatementMarker(String operation) {
if (operation == null)
return SQL_MARKER;
if ("select".equals(operation))
return SELECT_MARKER;
if ("insert".equals(operation))
return INSERT_MARKER;
if ("update".equals(operation))
return UPDATE_MARKER;
if ("delete".equals(operation))
return DELETE_MARKER;
if ("create".equals(operation)) {
return CREATE_MARKER;
}
return SQL_MARKER;
}
}
Added the logger to log4jdbc.log4j2.properties
log4jdbc.dump.sql.addsemicolon=true
log4jdbc.spylogdelegator.name=com.myapp.QueryLogDelegator

Variable Substitution in logback groovy

I was working with the logback.xml and the variable were load in string like :
<FileNamePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.%i.html</FileNamePattern>
where logDirectory and logFileName were set in .bat file before calling my jar.
set logFileName=foobar
But now, I deal with groovy. It's awesome and ridiculously more readable than xml. But the variable are no longer expand.
appender("FILE", RollingFileAppender) {
file = "${logDirectory}/${logFileName}.html"
rollingPolicy(TimeBasedRollingPolicy) {
fileNamePattern = "${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.%i.html"
...
}
...
}
The path is now null/null :'(
WORST : the following test throw an exception :
if ("${logConsole}" == "true") {
Anyone see how to make it work ?
-- EDIT add the full logback.groovy
/*
* The configuration use the following variables :
* logDirectory => The log folder
* logFileName => The log file name
* logConsole => true console activation.
**/
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.html.HTMLLayout
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.encoder.LayoutWrappingEncoder
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy
import static ch.qos.logback.classic.Level.INFO
import static ch.qos.logback.classic.Level.OFF
def LOG_DIRECTORY = System.getProperty("logDirectory")
def LOG_FILE_NAME = System.getProperty("logFileName")
def LOG_CONSOLE = System.getProperty("logConsole")
appender("FILE", RollingFileAppender) {
file = "${LOG_DIRECTORY}/${LOG_FILE_NAME}.html"
rollingPolicy(TimeBasedRollingPolicy) {
fileNamePattern = "${LOG_DIRECTORY}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}.%i.html"
timeBasedFileNamingAndTriggeringPolicy(SizeAndTimeBasedFNATP) {
maxFileSize = "100MB"
}
maxHistory = 5
}
encoder(LayoutWrappingEncoder) {
layout(HTMLLayout) {
pattern = "%level%date%logger{36}%msg"
}
}
}
appender("STDOUT", ConsoleAppender) {
withJansi = true
encoder(PatternLayoutEncoder) {
pattern = "%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n"
}
}
logger("org", INFO)
logger("net", INFO)
logger("freemarker", INFO)
// We don't care about the bean creation at least it's more than a warn
logger("org.springframework.beans.factory", WARN)
root(OFF, ["FILE"])
if (LOG_CONSOLE == "true") {
root(OFF, ["FILE","STDOUT"]);
}
The System.getProperty() call retrieves properties set with -D on the java command line whereas in the .bat file you are setting shell/environment variables.
Try using System.getenv() in the logback.groovy file.

Redirect Log4j to specific file

In a batch application that read and parse multiple files, the specifications ask me to output logs for each file separately.
How can I do this?
Example:
for(File f : allFiles) {
//TODO after this line all log should be output to "<f.getName()>.log"
LOGGER.debug('Start processing '+f.getName());
// process the file by calling librairies (JPA, spring, whatever ...)
LOGGER.debug('End processing '+f.getName());
}
So that, if I have 3 files to process, in the end, I want to have 3 log files.
What I have done so far is the following class.
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
public final class LoggerHelper {
/**
* Functional logger
*/
private static final Logger LOGGER = Logger.getLogger("BATCH_LOGGER");
/**
* Pattern for the layout of the logger
*/
private static final String PATTERN_LAYOUT = "%d{yyyy-MM-dd [HH:mm:ss]} %m%n";
/**
* Constructor
*/
private LoggerHelper() {
}
/**
* Initialize the loggers
*
* #param filename
* the name of the file where the logs will be written
* #throws IOException
* if a problem occur when instantiate a file appender
*/
public static void initLoggers(String filename) throws IOException {
// change functional appender
LOGGER.removeAllAppenders();
LOGGER.addAppender(new FileAppender(new PatternLayout(PATTERN_LAYOUT), filename));
LOGGER.setLevel(Level.DEBUG);
}
/**
* Get the batch logger
*
* #return the batch Logger
*/
public static Logger getLogger() {
return LOGGER;
}
}
But I have to replace all LOGGER calls with LoggerHelper.getLogger().debug(...).
And with this solution, I can't log frameworks logs.
for(File f : allFiles) {
//TODO after this line all log should be output to "<f.getName()>.log"
LoggerHelper.initLoggers(f.getName());
LoggerHelper.getLogger().debug('Start processing '+f.getName());
// process the file by calling librairies (JPA, spring, whatever ...)
LoggerHelper.getLogger().debug('End processing '+f.getName());
}
How can I do this?
You are already on a good track. I guess your misstake is to create new loggers. The solution might be to add different appenders to the same logger. So your logger helper just have to replace the appender (as you already did at your code):
private static final class LoggerHelper {
private static final String PATTERN_LAYOUT = "%d{yyyy-MM-dd [HH:mm:ss]} %m%n";
private static final Layout LAYOUT = new PatternLayout(PATTERN_LAYOUT);
public static final void setFileOutputOfLogger(Logger log, String fileName) throws IOException {
log.removeAllAppenders();
log.addAppender(new FileAppender(LAYOUT, fileName));
}
}
That is something you can call once within your loop.
Logger log = Logger.getLogger(FileStuff.class);
for(File f : allFiles) {
LoggerHelper.setFileOutputOfLogger(log, f.getName());
All the framework output will not be touched.
That's the solution I finally implemented.
I share it here, if this can help others...
First, the helper class that reload the log4j configuration.
Note that it (re)set some System properties. Those properties will be used in the log4j file directly.
import org.apache.log4j.xml.DOMConfigurator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public final class LogHelper {
private final static String LOG4J_XML_FILENAME = "log4j.xml";
private final static String LOG_APPLI_DIRECTORY = "LOG_APPLI_DIRECTORY";
private final static String FILENAME = "FILENAME";
public static void initLogsForCurrentFile(String currentFile, String logDir) {
Assert.hasLength(currentFile);
Assert.doesNotContain(currentFile, File.pathSeparator);
ClassPathResource log4jxml = new ClassPathResource(LOG4J_XML_FILENAME);
if (!log4jxml.exists()) {
throw new IllegalArgumentException(
"The [log4j.xml] configuration file has not been found on the classpath.");
}
// TODO Define variables that could be used inside the log4j
// configuration file
System.setProperty(FILENAME, FileUtils.removeExtension(currentFile));
System.setProperty(LOG_APPLI_DIRECTORY, logDir);
// Reload the log4j configuration
try {
DOMConfigurator.configure(log4jxml.getURL());
} catch (Exception e) {
throw new IllegalArgumentException(
"A problem occured while loading the log4j configuration.",
e);
}
}
}
And the corresponding log4j file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- This log4j file will be reloaded multiple times -->
<!-- so that each files processed by the applicatin will have their own log file -->
<!-- ${LOG_APPLI_DIRECTORY} = the log directory -->
<!-- ${FILENAME} = the basename of the current file processed by the batch -->
<appender name="batch-appender" class="org.apache.log4j.RollingFileAppender">
<param name="file"
value="${LOG_APPLI_DIRECTORY}/batch-${FILENAME}.log" />
<param name="MaxFileSize" value="1MB" />
<param name="MaxBackupIndex" value="3" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p %-40.40c{1} - %m%n" />
</layout>
</appender>
<!-- ================ -->
<!-- Root logger -->
<!-- ================ -->
<root>
<priority value="info" />
<appender-ref ref="batch-appender" />
</root>
</log4j:configuration>
With such solution, we stay as close as possible to what we usually do to configure log4j.
And moreover this solution keeps the configuration in the log4j file with no configuration in the java source code.

Configure log4j2 programmatically using ConfigurationFactory

I'm trying to configure and set up Log4j2 only through using ConfigurationFactory and this reference. The code I'm using is as follows:
public class LoggingConfiguration {
public static final String PATTERN_LAYOUT = "[%d] [%t] [%-5level] - %msg (%logger{1}:%L) %n%throwable";
public static final String LOG_FILE_NAME = "app.log";
public static final String LOG_FILE_NAME_PATTERN = LOG_FILE_NAME + "-yyyy.MM.dd";
static {
ConfigurationFactory.setConfigurationFactory(new Log4j2ConfigurationFactory());
}
/**
* Just to make JVM visit this class to initialize the static parts.
*/
public static void configure() {
}
#Plugin(category = "ConfigurationFactory", name = "Log4j2ConfigurationFactory")
#Order(0)
public static class Log4j2ConfigurationFactory extends ConfigurationFactory {
#Override
protected String[] getSupportedTypes() {
return null;
}
#Override
public Configuration getConfiguration(ConfigurationSource source) {
return new Log4j2Configuration();
}
#Override
public Configuration getConfiguration(String name, URI configLocation) {
return new Log4j2Configuration();
}
}
private static class Log4j2Configuration extends DefaultConfiguration {
public Log4j2Configuration() {
setName("app-log4j2");
String root = System.getProperty("APP_ROOT", "/tmp");
if (!root.endsWith("/")) {
root += "/";
}
// MARKER
Layout<? extends Serializable> layout = PatternLayout.createLayout(PATTERN_LAYOUT, null, null, null, null);
String oneDay = TimeUnit.DAYS.toMillis(1) + "";
String oneMB = (1024 * 1024) + "";
final TimeBasedTriggeringPolicy timeBasedTriggeringPolicy = TimeBasedTriggeringPolicy.createPolicy(oneDay,
"true");
final SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy = SizeBasedTriggeringPolicy.createPolicy(oneMB);
final CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(timeBasedTriggeringPolicy,
sizeBasedTriggeringPolicy);
final DefaultRolloverStrategy strategy = DefaultRolloverStrategy.createStrategy("7", "1", null,
Deflater.DEFAULT_COMPRESSION + "", this);
Appender appender = RollingFileAppender.createAppender(root + LOG_FILE_NAME, LOG_FILE_NAME_PATTERN, "true",
"app-log-file-appender", "true", "true", policy, strategy, layout, null, null, null, null, null);
addAppender(appender);
getRootLogger().addAppender(appender, Level.INFO, null);
}
}
}
Note that
it extends BaseConfiguration that already configures console by default
it tries to add a rolling file appender to the root logger
I get the following exception:
Exception in thread "main" java.lang.IllegalStateException: Pattern does not contain a date
at org.apache.logging.log4j.core.appender.rolling.PatternProcessor.getNextTime(PatternProcessor.java:91)
at org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy.initialize(TimeBasedTriggeringPolicy.java:49)
at org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy.initialize(CompositeTriggeringPolicy.java:43)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.<init>(RollingFileManager.java:58)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager$RollingFileManagerFactory.createManager(RollingFileManager.java:297)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager$RollingFileManagerFactory.createManager(RollingFileManager.java:267)
at org.apache.logging.log4j.core.appender.AbstractManager.getManager(AbstractManager.java:71)
at org.apache.logging.log4j.core.appender.OutputStreamManager.getManager(OutputStreamManager.java:65)
at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.getFileManager(RollingFileManager.java:78)
at org.apache.logging.log4j.core.appender.RollingFileAppender.createAppender(RollingFileAppender.java:175)
at com.narmnevis.papyrus.LoggingConfiguration$Log4j2Configuration.<init>(LoggingConfiguration.java:79)
at com.narmnevis.papyrus.LoggingConfiguration$Log4j2ConfigurationFactory.getConfiguration(LoggingConfiguration.java:55)
at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:377)
at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:149)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:85)
at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:34)
at org.apache.logging.log4j.LogManager.getContext(LogManager.java:200)
at org.slf4j.helpers.Log4jLoggerFactory$PrivateManager.getContext(Log4jLoggerFactory.java:104)
at org.slf4j.helpers.Log4jLoggerFactory.getContext(Log4jLoggerFactory.java:90)
at org.slf4j.helpers.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:46)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:270)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:281)
at com.narmnevis.papyrus.Main.<init>(Main.java:12)
at com.narmnevis.papyrus.Main.main(Main.java:21)
If I comment out the code after MARKER in above code, it works but it seems that I'm missing something to configure a rolling file appender. What should I do to fix this?
In log4j 2.x you have to specify the date format in this way
public static final String LOG_FILE_NAME_PATTERN = LOG_FILE_NAME + "-%d{dd-MM-yyy}";
% marks the beginning of a format
d means that it is a date format (you can also use date)
within the curly braces {} you define the formatter's options. In this case the date format. You can use everything that a SimpleDateFormat would accept.
In addition you can also use:
%d{ABSOLUTE} -> HH:mm:ss,SSS
%d{COMPACT} -> yyyyMMddHHmmssSSS
%d{DATE} -> dd MMM yyyy HH:mm:ss,SSS
%d{ISO8601_BASIC} -> yyyyMMdd HHmmss,SSS
%d{ISO8601} -> yyyy-MM-dd HH:mm:ss,SSS
Note: This information is based on log4j 2.0-beta9 (the current release). Since it is a beta version it might change slightly.

Categories