Conventions regarding the loading of resource files to a Java project - java

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.

Related

Delete logger lines from final jar while performing maven build or package

I know that it is possible to delete or skip files and folders while doing maven clean.
My problem is that I want to delete some specific lines (log.info lines) from final jar while doing mvn clean package.
My full codebase is filled with logger lines similar to the following codebase:
class MyClass {
def main(args: Array[String]) {
val log = Utils.getLogger
log.info("Application started")
val url = "https://sunilkumar.in"
log.info(s"Trying to fetch content from url: $url")
...
log.info("Successfully fetched content. Doing something now.")
val v1 = "variable1"
val v2 = "variable2"
log.info(s"Starting to do something with v1: $v1 and v2: $v2")
try {
...
log.info("Successfully done something with variables")
}
catch {
case e: Exception =>
log.severe(s"Failed to do something. Got exception with message: ${e.getMessage}")
}
log.info("Continuing the work")
...
log.info("Finished everything")
}
}
These logger information helps me a lot in understanding the problem
and where my codebase failed with reason. So I cannot remove them
because I need them during development.
When I am building the Jar file for my clients (production artifact), I don't need any logging.
If required I can use a pattern to identify logger lines by searching log.info as everywhere I am using the same variable name which is log.
My question is:
I don't know where and how to use this pattern to delete lines from final jar while doing mvn clean build package so that:
there should be no logging.
If I open up Jar file and convert .class
to .java file then there should be no logging codebase.
Note:
My project is a mixture of Java & Scala codebase and I want to delete
all lines where log.info is found in any class be it Java or Scala in final jar.
I am using maven-assembly-plugin to create jar-with-dependencies as final product.
Any other solution is most welcomed.
Are you using slf4j, if so then consider setting the level to error. By setting it to error, none the log.info lines will get printed.
A sample config for reference
<configuration scan="true" scanPeriod="60 seconds">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSSZ,UTC} %X{requestId} %X{req.remoteHost} %X{req.xForwardedFor}
%X{req.requestURI} [%thread] %-5level [%logger{0}] %msg%n
</pattern>
</encoder>
</appender>
<root level="error">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Here, I am printing log.error to console, so info lines will not be printed. By changing the level to info, you could print both info and error statements

Java Separate log file for separate Program Arguements

I have a single file which sends SMS to different mobile networks according to command line arguements passed to it. Vodafone, 3 network, T-Mobile,O2.. etc. So, when arguement is passed
$Run SMSDaemon 3Network // sends sms to 3 networks's mobile users
$Run SMSDaemon Vodafone // sends sms to Vodafone mobile users
$Run SMSDaemon TMobile // sends sms to TMobile mobile users
$Run SMSDaemon O2 // sends sms to O2 mobile users
Now, I want to create a separate log files for separate network such that I can have log info,debug message separately according to mobile networks.
Since, I have only a single file SMSDaemon I have tried the following but it's not writing anything to file. It's only writing to a file which have been defined in log4j.properties file. So, I am confused what to write in log4j.properties and what to write in this java file??
public static void main(String[] args) {
if( args.length != 1 { return;}
networkUnique = args[0];
setLogProps(networkUnique);
//other codes and in setLogProps method
private static void setLogProps(String networkUnique)
{
log = Logger.getLogger(networkUnique);
Properties logprops=new Properties();
logprops.setProperty("log4j.appender.file","org.apache.log4j.FileAppender");
logprops.setProperty("log4j.appender.file.maxFileSize","300MB");
logprops.setProperty("log4j.appender.file.maxBackupIndex","100");
logprops.setProperty("log4j.appender.file.File","/etc/sms/"+networkUnique+".log");
logprops.setProperty("log4j.appender.file.threshold","debug");
logprops.setProperty("log4j.appender.file.layout","org.apache.log4j.PatternLayout");
logprops.setProperty("log4j.appender.file.layout.ConversionPattern","%d [%t] %-5p [%-35F : %-25M : %-6L] %-C -%m%n");
logprops.setProperty("log4j.appender.stdout","org.apache.log4j.FileAppender");
PropertyConfigurator.configure(logprops);
log.info("Log message Starting for "+ networkUnique);
}
In log4j.properties file following is written. All the logs are written to network.log file which is defined in this properties.. which is not according to my requirement. I need separate log for separate network which is defined in SMSDaemon setLogProps method.
# Define the root logger with appender file
#log = /etc/sms/
log4j.rootLogger = info, FILE
# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
#log4j.appender.FILE.File=${log}/network.log
# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%m%n
Please suggest.
Thanks in advance.
You need to create different appenders to achieve the goal.
Try this configurations :
<appender name="3Network" class="org.apache.log4j.FileAppender">
<param name="file" value="logs/3Network.log" />
<param name="append" value="true" />
</appender>
<appender name="Vodafone" class="org.apache.log4j.FileAppender">
<param name="file" value="logs/Vodafone.log" />
<param name="append" value="true" />
</appender>
<appender name="TMobile" class="org.apache.log4j.FileAppender">
<param name="file" value="logs/TMobile.log" />
<param name="append" value="true" />
</appender>
The same you can write as :
log4j.appender.TMobile=org.apache.log4j.FileAppender
log4j.appender.TMobile.File=logs/TMobile.log..
and so on..
Hope this will help you to get your issue resolved.
Also see this - reference
I would advice to create different Logger classes based on your network e.g.
class com.netwrok.Network1Logger{
public void logMessage(String className, String message, String Level);
}
class com.netwrok.Network2Logger{
public void logMessage(String className, String message, String Level);
}
Then update log4j.properties as below:
# Root logger option
log4j.rootLogger=INFO, RootFileAppender
#Network1 Logger option
log4j.logger.com.netwrok.Network1Logger=INFO,Network1FileAppender
#Network2 Logger option
log4j.logger.com.netwrok.Network2Logger=INFO,Network2FileAppender
# RootFileAppender - used to log messages in the root.log file.
log4j.appender.RootFileAppender=org.apache.log4j.FileAppender
log4j.appender.RootFileAppender.File=root.log
log4j.appender.RootFileAppender.MaxFileSize=100MB
log4j.appender.RootFileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.RootFileAppender.layout.ConversionPattern= %5p [%t] (%F:%L) - %m%n
# Network1FileAppender- used to log messages in the shunted.log file.
log4j.appender.Network1FileAppender=org.apache.log4j.FileAppender
log4j.appender.Network1FileAppender.File=shunted.log
log4j.appender.Network1FileAppender.MaxFileSize=10MB
log4j.appender.Network1FileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.Network1FileAppender.layout.ConversionPattern= %5p [%t] (%F:%L) - %m%n
# Network2FileAppender- used to log messages in the shunted.log file.
log4j.appender.Network2FileAppender=org.apache.log4j.FileAppender
log4j.appender.Network2FileAppender.File=shunted.log
log4j.appender.Network2FileAppender.MaxFileSize=10MB
log4j.appender.Network2FileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.Network2FileAppender.layout.ConversionPattern= %5p [%t] (%F:%L) - %m%n
If you don't desire, root logger, shut it off.

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 .

log4j Could not read configuration file

I am adding logging to a java web project I am working on. I have run into an error that I am unable to figure out.
The error I am getting from tomcat is:
log4j:ERROR Could not read configuration file [log4j.properties].
java.io.FileNotFoundException: log4j.properties (No such file or directory)
I have this simple method in my class:
#RemotingInclude
public UserAccount save(UserAccount dataObject)
{
PropertyConfigurator.configure("log4j.properties");
logger.debug(dataObject.toString());
return dao.save(dataObject);
}
When I look in my webapps//WEB-INF/class folder I do see my log4j.properties file. When I deploy to my tomcat server and restart tomcat, I do see my admin.log file created, but nothing is written to it. Even after hitting the method above. Any help with this is greatly appreciated.
This is the current contents of my log4j.properties file:
log4j.appender.AdminFileAppender=org.apache.log4j.FileAppender
log4j.appender.AdminFileAppender.File=admin.log
log4j.appender.AdminFileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.AdminFileAppender.layout.ConversionPattern= %-4r [%t] %-5p %c %x - %m%n.
log4j.appender.ReportFileAppender=org.apache.log4j.FileAppender
log4j.appender.ReportFileAppender.File=report.log
log4j.appender.ReportFileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.ReportFileAppender.layout.ConversionPattern= %-4r [%t] %-5p %c %x - %m%n
log4j.logger.com.rottmanj.services=WARN,AdminFileAppender
That approach of bootstraping the Log4j is wrong. This is usually the way that is implemented:
import org.apache.log4j.Logger;
public class MyService {
public UserAccount save(UserAccount dataObject) {
logger.debug(dataObject.toString());
return dao.save(dataObject);
}
private static Logger logger = Logger.getLogger(MyService.class);
}
This way Log4j will automatically lookup for the log4j.properties in the root of the classpath.

Categories