Logback - set log file name programmatically - java

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 .

Related

log4j2 properties file for a custom appender

I created a custom appender and it's not getting called when I run my test. Here's what the properties look like:
name=config
appenders=console, myCustomAppender
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
#appender.console.layout.pattern =%d{HH:mm:ss} [%t] %c{1} [%-5level] - %msg%n
appender.console.layout.pattern=%d{dd-MM-yyyy HH:mm:ss} [%-5p] (%F:%L) - %m%n
appender.myCustomAppender = com.myCompany.logging.log4j.WindowsEventLogAppender
appender.myCustomAppender.name = WindowsEventLogAppender
appender.myCustomAppender.type = WindowsEventLogAppender
rootLogger.level=info
rootLogger.appenderRefs=stdout, myCustomAppender
rootLogger.appenderRef.stdout.ref=STDOUT
My appender is called a WindowsEventLogAppender. Any idea what's wrong with my properties file? I see the console test messages but none of the messages from my appender. Right now I'm just doing a System.out.println in my custom appender to verify it's getting called.
BTW, I've found lot's of XML examples out there for log4j2 configurations with custom appenders but none for using a properties file for configuration.
Thanks,
-Mike
I might be quite late here, but I think my answer can help other people looking for answers. Please accept this as an answer if this is correct!
If you have created a custom appender having annotation like this:
#Plugin(name = "MyCustomAppender", category = "Core",
elementType = "appender", printObject = true)
public final class MyCustomAppenderImpl extends AbstractAppender {
// other code for the plugin....
}
The log4j2 manual about Configuring Appenders states that:
"An appender is configured either using the specific appender plugin's name or with an appender element and the type attribute containing the appender plugin's name"
Meaning the type for appender should be Appender Plugin's Name attribute value.
Which in above case is MyCustomAppender (appender.identifierName.type=MyCustomAppender)
So, the Properties file configuration for this to work should be:
(Note : I have added a stdout(console) appender just to show
relevance/similarity of usage with OOTB appenders, and 2 example
usages with RootLogger and a custom logger)
# this packages attribute is important, please put comma seperated package(s) to the
# plugin(s) you have created
packages = com.package.to.your.plugin
# Example: Declare and Define OOTB Console appender, which sends log events to stdout
appender.console.name = stdout
appender.console.type = Console
# Declare and define the custom appender like this
# Note that the "abc" in "appender.abc.type" can be anything
# and the value for "appender.abc.type" should be the same as
# "Name" attribute value given in custom appender plugin which is "MyCustomAppender"
appender.abc.name=arbitrary_name
appender.abc.type=MyCustomAppender
rootLogger.appenderRef.stdout.ref = stdout
rootLogger.appenderRef.abc.ref = arbitrary_name
logger.loggeridentifier.name = com.test.SomeClass
logger.loggeridentifier.appenderRef.stdout.ref = stdout
logger.loggeridentifier.appenderRef.abc.ref = arbitrary_name
# Also note that the value of appenderRef should be the same name given to your
# appender in properties file, which in this case is "arbitrary_name" (as given above)
Try adding the packages property.
Like: packages = com.myCompany
You haven't included the package info
try the below configuration.
name=config
appenders=console, myCustomAppender
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
#appender.console.layout.pattern =%d{HH:mm:ss} [%t] %c{1} [%-5level] - %msg%n
appender.console.layout.pattern=%d{dd-MM-yyyy HH:mm:ss} [%-5p] (%F:%L) - %m%n
appender.myCustomAppender = com.myCompany.logging.log4j.WindowsEventLogAppender
appender.myCustomAppender.name = WindowsEventLogAppender
appender.myCustomAppender.type = WindowsEventLogAppender
rootLogger.level=info
rootLogger.appenderRefs=stdout, myCustomAppender
rootLogger.appenderRef.stdout.ref=STDOUT
rootLogger.com.mycompany.example=INFO,STDOUT

How to log from core libraries

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.

Log4j configured with a properties file doesn´t create a file

I am new configuring log4j for a project, I have used it several times, but it´s the first time i have to configure it.
I am configuring my Log4j, I have imported the log4j-1.2.17.jar library, and I have created a properties that look like this:
log4j.appender.consola = org.apache.log4j.ConsoleAppender
log4j.appender.consola.threshold = INFO
log4j.appender.consola.target = System.out
log4j.appender.consola.layout = org.apache.log4j.EnhancedPatternLayout
log4j.appender.consola.layout.ConversionPattern = %d{dd MMM yyyy - HH:mm:ss} [%-5p] %c{2} - %m%n
log4j.appender.archivo = org.apache.log4j.FileAppender
log4j.appender.archivo.file = archivo.log
log4j.appender.archivo.layout = org.apache.log4j.PatternLayout
log4j.appender.archivo.layout.ConversionPattern = %d [%-5p] %c{2} - %m%n
log4j.rootLogger=TRACE, consola
log4j.logger.com.javatutoriales.log4j.configuracion=WARN, archivo
This properties file called log4j.properties is created in the default package of the project.
When I used this configuration in a class, it returns me the console log properly. I used the following code In a class:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PruebaLog {
/** * Logger. */
private static final Logger logger = LoggerFactory.getLogger(PruebaLog.class);
public static void main (String args[]){
logger.trace("mensaje de trace");
logger.debug("mensaje de debug");
logger.info("mensaje de info");
logger.warn("mensaje de warn");
logger.error("mensaje de error");
}
}
The problem is that this class returns me the console out, but doesn´t create the file archivo.log in the path of my project.
Anybody know the reason of why it doesn´t create this log file???
Or anybody could help me with the configuration of this file in order to have a log of the diferent classes of my project in a log file???
You are not using the file appender as you have set the console appender for the root logger:
log4j.rootLogger=TRACE, consola
You need this instead to use the file appender:
log4j.rootLogger=INFO, archivo
Learn more about the appenders and different log4j properties from the official document:
http://logging.apache.org/log4j/1.2/manual.html
in you properties, you need to add rootLogger file
log4j.rootLogger=INFO, archivo
For more details you can refer to log4j.properties config

log4j rollover at restart

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.

Conventions regarding the loading of resource files to a Java project

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.

Categories