I'm using a log4j FileAppender in my project to collect certain results. When my application restarts, I'd like to keep the previous resultsfile and start a new one. Is that possible?
For example:
Start application, write results to results.log
Application ends
Restart application, write results to results_1.log
...
I'v checked out the DailyRollingFileAppender, but that's not really what I need because that will automatically rollover on a certain date setting. I need to rollover when the application restarts.
Log4j2's RollingFileAppender has policy called OnStartupTriggeringPolicy.
As documentation states:
The OnStartupTriggeringPolicy policy causes a rollover if the log file is older than the current JVM's start time and the minimum file size is met or exceeded.
Example of xml configuration (only Policies):
<Policies>
<OnStartupTriggeringPolicy /> # restart on startup of JVM
<SizeBasedTriggeringPolicy size="20 MB" /> # restart file if log file reaches 20MB
<TimeBasedTriggeringPolicy /> # restart if currend date don't mach date in log file name
</Policies>
More informations and documentation:
https://logging.apache.org/log4j/2.x/manual/appenders.html#RollingFileAppender
How about having your application set the log file dynamically? You can do this by creating a file appender programatically when your application starts up and attaching it to your current logger.
For example, the following code will create new log files based on the current time. e.g. results_1336482894.log, results_1336486780.log
Date now = new Date();
FileAppender myFileAppender = new FileAppender();
myFileAppender.setName("myFileAppender");
myFileAppender.setFile("results_" + now.getTime() + ".log");
myFileAppender.setLayout(new PatternLayout("%d %-5p [%t]: %m%n"));
myFileAppender.setThreshold(Level.DEBUG);
myFileAppender.activateOptions();
Logger myLogger = Logger.getLogger("name of your logger"); //Or use getRootLogger() instead
myLogger.addAppender(myFileAppender);
I have solved this by writing my own appender:
import org.apache.log4j.RollingFileAppender;
/**
This appender rolls over at program start.
This is for creating a clean boundary between log data of different runs.
*/
public class RunRolledFileAppender
extends RollingFileAppender
{
public RunRolledFileAppender() { }
#Override
public void activateOptions() {
super.activateOptions();
super.rollOver();
}
#Override
public void rollOver() { }
}
Note the ugly disabling of rollOver() was necessary, since the MaxFileSize setting did not work and that Appender otherwise also rolled over every 10MB, which I didn't need.
You could use the ExternallyRolledFileAppender and the Roller classes to rollover the log file when your application starts up.
Here's an example class:
import org.apache.log4j.Logger;
import org.apache.log4j.varia.Roller;
public class Test {
private static final Logger log = Logger.getLogger(Test.class);
public static void main(final String[] args) {
Roller.main(new String[] {"localhost", "9999"});
log.debug("Started application!");
}
}
And an example log4j.properties file:
log4j.appender.file=org.apache.log4j.varia.ExternallyRolledFileAppender
log4j.appender.file.File=app.log
log4j.appender.file.Port=9999
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-5p: %t: %c: %m%n
log4j.rootLogger=DEBUG, file
Do heed the warning in ExternallyRolledFileAppender:
Note that the initiator is not authenticated. Anyone can trigger a rollover. In production environments, it is recommended that you add some form of protection to prevent undesired rollovers.
If this does not suit your needs, it should be trivial to create your own similar Appender implementation.
Related
It is possible so simple, but I've wasted already a lot of time to find any solution.
I have
package net.rubyeye.xmemcached;
...
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
public class XMemcachedClient implements XMemcachedClientMBean, MemcachedClient {
private static final Logger log = LoggerFactory
.getLogger(XMemcachedClient.class);
....
With Log4j I get all logs from apache-servicemix.
I've tried something like
log4j.logger.net.rubyeye.xmemcached.XMemcachedClient=All, xmemcachedLog
log4j.appender.xmemcachedLog=org.apache.log4j.RollingFileAppender
log4j.appender.xmemcachedLog.File=${karaf.data}/log/spring/xmemcachedLog.log
log4j.appender.xmemcachedLog.ImmediateFlush=true
log4j.appender.xmemcachedLog.maxFileSize = 10MB
log4j.appender.xmemcachedLog.maxBackupIndex = 10
log4j.appender.xmemcachedLog.layout=org.apache.log4j.PatternLayout
log4j.appender.xmemcachedLog.layout.ConversionPattern=%d{dd-MM-yyyy_HH:mm:ss} %-5p [%t] - %m%n
But I don't get anything. I want to get information about exception which I get on the 1335th line
key = this.preProcessKey(key);
Actually, it doesn't matter that I want to log exactly that class. In my application I also have other classes which have LoggerFactory.getLogger(...);
And the main question is
How to get logs from Logger log = LoggerFactory
.getLogger(SomeClass.class);
Now, my rootLogger looks like
# Root logger
log4j.rootLogger=info, out, sift, osgi:VmLogAppender
log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer
You should have a logback.xml somewhere which decide to show your log or not if you're on a java EE application.
Try to add this line of code into it:
<logger name="net.rubyeye.xmemcached" level="DEBUG"/>
It will activate DEBUGĀ logs for all the classes in this package.
If it still doesn't work maybe you don't have the file in your classpath and you might have to add it in the jvm parameter.
There is no problem in my logger. I've just hadn't any log.error() or log.smth() so I haven't got any lines in my files.
So it would work, for example, in that method inside XMemcachedClient
public void setTimeoutExceptionThreshold(int timeoutExceptionThreshold) {
if (timeoutExceptionThreshold <= 0) {
throw new IllegalArgumentException(
"Illegal timeoutExceptionThreshold value "
+ timeoutExceptionThreshold);
}
if (timeoutExceptionThreshold < 100) {
log.warn("Too small timeoutExceptionThreshold value may cause connections disconnect/reconnect frequently.");
}
this.timeoutExceptionThreshold = timeoutExceptionThreshold;
}
It shows me "Too small timeoutExceptionThreshold value may cause connections disconnect/reconnect frequently." in my ${karaf.data}/log/spring/xmemcachedLog.log when timeoutExceptionThreshold < 100
please check the attached image of project structure, let me know if i positioned log4j2.properties right.
also have a look at versions of jars I am using.
I wrote a simple program to print logs on console. in order to achieve this I wrote log4j2.properties file as follows.
Root logger option
log4j.rootLogger=INFO, file
log4j.appender.file= org.apache.logging.log4j.core.appender.ConsoleAppender
log4j.appender.file.Target=System.out
log4j.appender.file.Layout=org.apache.logging.log4j.core.layout.PatternLayout
log4j.appender.file.Layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
Main program is as follows.(also shown in image)
package goldensource.track.logs;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.PropertiesUtil;
public class TestLogger {
private Logger logger;
private PropertiesUtil pu;
TestLogger()
{
System.setProperty("log4j.configurationFile","log4j2.properties");
logger = LogManager.getLogger(TestLogger.class);
logger.info("Yes I am there!");
logger.debug("I am debugging!");
logger.warn("giving you a warner!");
}
public static void main(String[] args) {
TestLogger z = new TestLogger();
}}
I have created a reference of PropertiesUtil but anyways I am not using it.
when I am executing this program nothing is shown on console. as I could make out, I am not able to load properties file properly.
suggest me any modifications or alternatives with examples.
Thanks in advance!
Without any feedback on the error you are getting, I can guess that one of the problems is with the file name. You should specify the absolute path to your log4j2.properties file when you are setting the system property:
System.setProperty("log4j.configurationFile","/absolute/path/to/log4j2.properties");
In this way the logger system will know exactly where to find your file.
I am uisng log4j in one of my applications, the code is as follows;
log=/var/lib/openshift/5372745b4382ec49cb0000d5/app-root/runtime/dependencies/jbossas/deployments/Logs/AppName.log
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
log4j.rootLogger=DEBUG, FILE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.append=true log4j.appender.FILE.file=${log}
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d %-5p %c - %m%n
This application also will use some core application code which is shared between various apps - How do i therefore log from this core code base as it will be shared amongst many applications - this means i can't provide a concrete log= value
Would something like this suffice?
log=/var/lib/openshift/5372745b4382ec49cb0000d5/app-root/runtime/dependencies/jbossas/deployments/Logs/*.log
One approach would be to
have different File appenders for each appplication
Load those appeanders in the respective Application Logger.
protected static final Logger LOGGER = Logger.getLogger(X.class);
static
{
if (null == LOGGER.getAppender("TEST_LOG_APPENDER"))
{
RollingFileAppender fa = new RollingFileAppender();
fa.setName("TEST_LOG_APPENDER");
fa.setFile("/test-output/" + "App_1.log");
fa.setLayout(new PatternLayout("%d %-5p [%c{1}] %m%n"));
fa.setThreshold(Level.DEBUG);
fa.setAppend(true);
fa.setMaxFileSize("10MB");
fa.activateOptions();
LOGGER.addAppender(fa);
}
}
Note that here you need not to create new appender, rather remove others and use only one.
Also this will work if you have a common app level logger.
Not sure if this is a right way of doing it. But just a thought.
I am using logback, and I am trying to set the log file name programmatically within my Java program (similar to Setting Logback Appender path programmatically), and I tried to adapt that solution as follows:
In logback-test.xml:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>log/${log_file_name}.log</file>
...
And then again in my Java program:
String logFileName = "" + System.currentTimeMillis(); // just for example
System.setProperty("log_file_name", logFileName);
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
ContextInitializer ci = new ContextInitializer(lc);
lc.reset();
try
{
// I prefer autoConfig() over JoranConfigurator.doConfigure() so I
// wouldn't need to find the file myself.
ci.autoConfig();
}
catch (JoranException e)
{
// StatusPrinter will try to log this
e.printStackTrace();
}
StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
However the result is two logs, one full and named as I wanted, e.g., "1319041145343.log", and the other is empty and named "log_file_name_IS_UNDEFINED.log". How do I stop this other empty log file from being created?
I believe the following to be closer to what you want.
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
public class Main {
public static void main(String[] args) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
FileAppender fileAppender = new FileAppender();
fileAppender.setContext(loggerContext);
fileAppender.setName("timestamp");
// set the file name
fileAppender.setFile("log/" + System.currentTimeMillis()+".log");
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext);
encoder.setPattern("%r %thread %level - %msg%n");
encoder.start();
fileAppender.setEncoder(encoder);
fileAppender.start();
// attach the rolling file appender to the logger of your choice
Logger logbackLogger = loggerContext.getLogger("Main");
logbackLogger.addAppender(fileAppender);
// OPTIONAL: print logback internal status messages
StatusPrinter.print(loggerContext);
// log something
logbackLogger.debug("hello");
}
}
If all you need is to add a timestamp of the log file name, logback already supports the timestamp element. Thus, you actually don't need any custom code at all.
To separate/sift log messages to different files depending on a runtime attribute, you might want to use ch.qos.logback.classic.sift.SiftingAppender.
In a nutshell, this allows you to set up a FileAppender (or any other appender) with <file>${userid}.log</file> where ${userId} is substituted based on the MDC (Mapped Diagnostic Context) (e.g., MDC.put("userid", "Alice");). See the first link for the complete example.
Here is what you can do to ignore those extra file creation.Below is the config file
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- "application-name" is a variable -->
<File>c:/logs/${application-name}.log</File>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d %p %t %c - %m%n</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="FILE"/>
</root>
</configuration>
Here is the java part,
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator jc = new JoranConfigurator();
jc.setContext(context);
context.reset(); // override default configuration
// inject the name of the current application as "application-name"
// property of the LoggerContext
context.putProperty("application-name", NAME_OF_CURRENT_APPLICATION);
jc.doConfigure("/path/to/the/above/configuration/file.xml");
I got this from here http://logback.qos.ch/faq.html#sharedConfiguration
Looks like the logger is initialized twice. First time, probably when the app loads and it couldn't resolve the ${log_file_name}. If you start the app with -Dlog_file_name=*something* you can verify this behavior if it creates another log file with the name *something*
Already some useful answers here for sure, and it seems that the OP was satisfied, which is great. But I came looking for an answer to the more precise question that was posed originally, which I believe was "how do I set the file name of AN EXISTING file appender programmatically?". For reasons I won't go into, this is what I had to figure out how to do.
I did figure it out without too much fuss. In case someone comes along looking for the same answer I was seeking, here is how to set the log file name of an existing FileAppender programmatically
String logFileName = "" + System.currentTimeMillis(); // just for example
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger logbackLogger = (ch.qos.logback.classic.Logger) loggerContext.getLogger("ROOT");
FileAppender<ILoggingEvent> fileAppender = (FileAppender<ILoggingEvent>)logbackLogger.getAppender("FILE");
fileAppender.setFile(logFileName);
Note that this code looks for the appender in the "ROOT" logger, the logger that is the parent of all loggers. This is where you'll usually find existing appenders, and by making changes to this logger, you are changing the behavior of all of the logging in your system, which is usually what you want .
Given that I'm having issues trying to apply an external log4j configuration via log4j.xml I'm now interested in adopting a convention regarding the loading of resource files to Java projects.
Despite the code works displaying the message without warnings or errors, I suspect the configuration is not being really applied as changing the ConversionPattern makes no difference to the console output.
Program.java
package the.project.path;
import java.io.File;
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.PropertyConfigurator;
class Program {
static final Logger logger = Logger.getLogger("SampleLogger");
static final File config = new File("the.project.path/conf/log4j.xml");
static final String message = "The quick brown fox jumps over the lazy dog.";
public static void main(String[] args) {
if (config.exists()) {
PropertyConfigurator.configure(config.getPath());
} else {
BasicConfigurator.configure();
}
try {
logger.debug(message);
logger.info(message);
logger.warn(message);
logger.error(message);
logger.fatal(message);
} catch (Exception exception) {
System.out.println(exception.toString());
}
}
}
log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="SampleConsoleAppender" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<!--
TTCC is a message format used by log4j.
TTCC is acronym for Time Thread Category Component.
It uses the following pattern: %r [%t] %-5p %c %x - %m%n
-->
<param name="ConversionPattern" value="[%t] [%-5p] - %m%n" />
</layout>
</appender>
<root>
<appender-ref ref="SampleConsoleAppender" />
</root>
</log4j:configuration>
Any advice will be really appreciated. Thanks much in advance.
Try using the DOMConfigurator, thus:
// Load log4j config file...
String path = "/path/to/log4j.xml");
DOMConfigurator.configure(path);
// Start Logging
LOG = Logger.getLogger(Program.class);
(code ripped from my current project, so I know it works) ;-)
As well as the above use of DOMConfigurator, you can optionally set a watch period, whereby Log4J will poll the xml file for changes, every xx millis, and then reload the changes:
// Load log4j config file...
String path = "/path/to/log4j.xml");
DOMConfigurator.configureAndWatch(path, 30000); // 30sec poll
// Start Logging
LOG = Logger.getLogger(Program.class);
HTH
You can try setting -Dlog4j.debug=true to see which options are being applied to log4j.
It doesn't answer your resource loading question, but it is helpful to know that usually.