Make embedded Tomcat log to certain file with log4j in Maven project - java

Introduction
I've been looking for this fix for some time now but no luck so far. I'm building a Java webapp which uses embedded Tomcat as servlet. I have set log4j as my main logger and the logs post to the file specified successfully. Because the Tomcat is embedded I cannot find this catalina.out file that I've seen in so many answers through SO and other forums.
Problem
What I want is to make Tomcat log with log4j to the same file I use for logging for the whole webapp. How can I do this? Is there a way to do this programmatically?
P.S. :
I'm using Windows as an OS and the Tomcat version is 8.0.30

By default tomcat uses [CATALINA_HOME]/conf/logging.properties. In there edit the below properties:
1catalina.org.apache.juli.AsyncFileHandler.level = FINE
1catalina.org.apache.juli.AsyncFileHandler.directory = **YOUR_PATH_HERE**
1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina.

Turns out it was way simpler than I thought, so I'm just leaving the answer here for anyone who might have the same problem.
I made a function initializeLogging()
private static void initializeLogging()
{
//Getting the current timestamp to use in log file naming
Calendar calendar = Calendar.getInstance();
java.text.DateFormat df = new java.text.SimpleDateFormat("yyMMddHHmmss");
String timestamp = df.format(calendar.getTime());
ConfigurationBuilder< BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.ALL);
builder.setConfigurationName("RollingBuilder");
// create a console appender
AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target",
ConsoleAppender.Target.SYSTEM_OUT);
appenderBuilder.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "<%d{yyyy/MM/dd HH:mm:ss}> %p (%t). %m%n"));
builder.add(appenderBuilder);
// create a rolling file appender
LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout")
.addAttribute("pattern", "<%d{yyyy/MM/dd HH:mm:ss}> %p (%t). %m%n");
ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
.addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M"));
//specifying log file name as well ss log file name if the first exceeds maximum size => 100M
appenderBuilder = builder.newAppender("rolling", "RollingFile")
.addAttribute("fileName", "logs/myLogging" + "_" + timestamp + ".log")
.addAttribute("FilePattern","logs/myLogging"+"_"+ timestamp + "%d{yyyyMMddHHmmss}" + ".log")
.add(layoutBuilder)
.addComponent(triggeringPolicy);
builder.add(appenderBuilder);
// create the new logger
builder.add(builder.newLogger("myLogger", Level.ALL)
.add(builder.newAppenderRef("rolling"))
.addAttribute("additivity", false));
builder.add(builder.newRootLogger(Level.ALL)
.add(builder.newAppenderRef("rolling")));
LoggerContext ctx = Configurator.initialize(builder.build());
}
so what this function does is to give a pattern at each entry to the logs and direct the logs to a file specified by me. Now every time I want to log something I just have to call Logger logger = LogManager.getLogger('myLogger)
So there it is a quick programmatic only log4j initialization.

Related

Java Process Not Able To Write Logs To Console

I am implementing log4j2 in my java application where I trying to write data to console using log4j2 configuration file. I have created a jar called Interface.jar . Soft Links are created for this jar and are run with different arguments . 2 soft links created are Interface1 and Interface2 with different arguments . Interface1 and Interface2 have different log4j2 configuration file path.
System.setProperty("log4j.configurationFile", System.getenv("LOG4J_INTERFACE1"));
System.setProperty("log4j.configurationFile", System.getenv("LOG4J_INTERFACE2"));
Log4j Configuration File For Interface1 is :
status = warn
appenders=console
property.LOG_PATTERN = %d{yyyy-MM-dd} %d{HH:mm:ss,SSS zzz}|%p|INTERFACE1||||||%m%n
appender.console.type = Console
appender.console.name = LogToConsole
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = ${LOG_PATTERN}
logger.Log4JExample.name = Log4JExample
logger.Log4JExample.level = debug
logger.Log4JExample.additivity = false
logger.Log4JExample.appenderRef.console.ref = LogToConsole
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = LogToConsole
Log4j Configuration File For Interface2 is :
status = warn
appenders=console
property.LOG_PATTERN = %d{yyyy-MM-dd} %d{HH:mm:ss,SSS zzz}|%p|INTERFACE2||||||%m%n
appender.console.type = Console
appender.console.name = LogToConsole2
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = ${LOG_PATTERN}
logger.Log4JExample.name = Log4JExample
logger.Log4JExample.level = debug
logger.Log4JExample.additivity = false
logger.Log4JExample.appenderRef.console.ref = LogToConsole2
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = LogToConsole2
The issue is that when I start both the process , Interface1 is able to write to console but Interface2 is not able to do so. Both the process are running fine. One challenge I see is that both these jars have save main class Interface.class .
LogManager.getLogger(Interface.class);
Can anyone shed some light on how can I stream the data to console from multiple processes using soft links.
Need to have 2 appenders. You can use give all the appenders with comma separated.
I can see both loggers have different format and both needs to write the console. In those cases you need to use Marker filter for different loggers.
below is the sample log4j2.properties file
appenders = console, console1
appender.console.type = Console
appender.console.name = LogToConsole
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd} %d{HH:mm:ss,SSS zzz}|%p|INTERFACE1||||||%m%n
appender.console1.type = Console
appender.console1.name = LogToConsole2
appender.console1.layout.type = PatternLayout
appender.console1.layout.pattern = %d{yyyy-MM-dd} %d{HH:mm:ss,SSS zzz}|%p|INTERFACE2||||||%m%n
loggers = rolling, interface2
logger.rolling.name = org.apache.logging.log4j.core.appender.rolling
#configure rootLogger and attach all the appenders to it
rootLogger.level = info
rootLogger.appenderRef.console.ref = LogToConsole
logger.interface2.name = Interface2
logger.interface2.additivity = false
logger.interface2.level = debug
logger.interface2.appenderRef.console1.ref = LogToConsole2
It seems you are setting a system property like so:
System.setProperty("log4j.configurationFile", System.getenv("LOG4J_INTERFACE1"));
while you should be setting log4j2.configurationFile.
Notice the log4j2 prefix.
I also recommend setting another system property
java -Dlog4j2.debug=true ...
just to see more what log4j2 is doing. You can turn it off once you are happy.

How to disable error logs being printed to the console during unit tests execution with log4j2?

After upgrading to Log4j2, error logs are printing to console during unit tests execution.
I tried to add status=FATAL in the configuration file to avoid error printing on console.
# ----------------------------------------------------------------
# LOGGING
# ----------------------------------------------------------------
# Note - this section is similar to the log4j2 properties syntax
# https://logging.apache.org/log4j/2.x/manual/configuration.html#Properties
status=FATAL;
test.*.log4j2.rootLogger.level=INFO;
*.*.log4j2.rootLogger.level=INFO;
*.*.log4j2.rootLogger.appenderRefs=(APPLICATION);
*.*.log4j2.appenders=(APPLICATION);
*.*.log4j2.appender.APPLICATION.type=AmazonRollingRandomAccessFile;
*.*.log4j2.appender.APPLICATION.name=APPLICATION;
*.*.log4j2.appender.APPLICATION.filePattern="var/output/logs/$APP.%d{yyyy-MM-dd-HH}";
*.*.log4j2.appender.APPLICATION.layout.type=PatternLayout;
# Standard log format but with the purchase id appended after the message.
# This location is to not break rtla processing, which parses everything up-to & including the logger (%c)
*.*.log4j2.appender.APPLICATION.layout.pattern="%d{DATE} [%p] %X{RequestId} (%t) %c: %m [Purchase: %X{PurchaseId}]%n";
The reference doc.
But seems it is not taking status into consideration and it is still printing logs on console.
I tried with all the options below as well, but no luck:
status=FATAL; ..log4j2.status=INFO; ..log4j2.status=ERROR;
The status of the Configuration only relates to the internal logging of log4j2 itself, i.e. you can use it to debug your configuration of log4j2, but it is not used for your actual application.
If you want to disable all logging for your application, you can set the ThresholdFilter of your configuration to off:
filter.threshold.type = ThresholdFilter
filter.threshold.level = off
or you can set your root logger to level = off (provided you don't have any other loggers defined):
rootLogger.level = off
A complete example of something that would work:
status = warn
name = TestConfig
filter.threshold.type = ThresholdFilter
filter.threshold.level = off
appender.list.type = List
appender.list.name = List
appender.list.filter.threshold.type = ThresholdFilter
appender.list.filter.threshold.level = trace
logger.whatever.name = com.relentlesscoding.logging
logger.whatever.level = trace
logger.whatever.additivity = false
logger.whatever.appenderRef.whatever.ref = List
rootLogger.level = trace

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.

Logback - set log file name programmatically

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 .

Different Logger in the same class using Log4J

I want specific messages generated from within the same class to be logged separately. So, how can I create 2 different types of loggers within the same class. Currently, the Properties file looks like
log4j.rootCategory=DEBUG, O
# Stdout
log4j.appender.O=org.apache.log4j.ConsoleAppender
log4j.appender.O.layout=org.apache.log4j.PatternLayout
log4j.appender.O.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x - %C.%M(%F:%L) - %m%n
# File
log4j.appender.MESSAGE=org.apache.log4j.RollingFileAppender
log4j.appender.MESSAGE.File=target/logs/messages.log
# Control the maximum log file size
log4j.appender.MESSAGE.MaxFileSize=1000KB
# Archive log files (one backup file here)
log4j.appender.MESSAGE.MaxBackupIndex=100
log4j.appender.MESSAGE.layout=org.apache.log4j.PatternLayout
log4j.appender.MESSAGE.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x - %C.%M (% F:% L) - %m%n
log4j.appender.MESSAGE.
log4j.category.failedMessagesLog=INFO, MESSAGE
I'm using the logging in my code as: –
/** Logger. */
Logger logger = Logger.getLogger(MyClass.class);
Logger msgLogger = Logger.getLogger("MESSAGE");
Upon testing, I get an empty log file (messages.log) created.
Any suggestions??
Create two loggers with different names. You can configure them on a per name basis.
A simple way to do this is to add a suffix to you class name. e.g.
Log log1 = LogFactory.getLog(getClass().getName()+".log1");
Log log2 = LogFactory.getLog(getClass().getName()+".log2");
in your properties file.
log4j.category.mypackage.myclass.log1=INFO, MESSAGE1
log4j.category.mypackage.myclass.log2=INFO, MESSAGE2
log4j.rootCategory=DEBUG, O
log4j.appender.O=org.apache.log4j.ConsoleAppender
log4j.appender.O.layout=org.apache.log4j.PatternLayout
log4j.appender.O.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x - %C.%M(%F:%L) - %m%n
log4j.appender.MESSAGE=org.apache.log4j.RollingFileAppender
log4j.appender.MESSAGE.File=target/logs/messages.log
log4j.appender.MESSAGE.MaxFileSize=1000KB
log4j.appender.MESSAGE.MaxBackupIndex=100
log4j.appender.MESSAGE.layout=org.apache.log4j.PatternLayout
log4j.appender.MESSAGE.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x - %C.%M
log4j.appender.**MESSAGE2**=org.apache.log4j.RollingFileAppender
log4j.appender.**MESSAGE2**.File=target/logs/**messages2**.log
log4j.appender.**MESSAGE2**.MaxFileSize=1000KB
log4j.appender.**MESSAGE2**.MaxBackupIndex=100
log4j.appender.**MESSAGE2**.layout=org.apache.log4j.PatternLayout
log4j.appender.**MESSAGE2**.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x - %C.%M
log4j.category.failedMessagesLog=INFO, MESSAGE , **MESSAGE2**
"failedMessagesLog" is the java file to which appender (INFO,MESSAGE, MESSAGE1) is applied.
I have just reused existing RollingFileAppender. you can use any other appender ( like fileAppender).
You should use the right Class name Logger logger = Logger.getLogger(MyClass.class)
should be changed to private static final Logger log = Logger.getLogger( **failedMessagesLog.class** );
Make sure you are using log4j's logging ie
import **org.apache.log4j.Logger**;

Categories