I am using log4j ogging for my project. Unfortunately, i am not able to log the info levels in the application that i havelogged manually.. though it is logging the database logs perfectly!
Here is my properties file :
log4j.rootLogger=OFF
log4j.appender.serviceLog=org.apache.log4j.RollingFileAppender
log4j.appender.serviceLog.File=C:/Users/prateekg/Desktop/log4j/log
log4j.appender.dbLog=org.apache.log4j.RollingFileAppender
log4j.appender.dbLog.File=C:/Users/prateekg/Desktop/log4j/dbLog
log4j.appender.serviceLog.MaxFileSize=1MB
log4j.appender.serviceLog.MaxBackupIndex=1
log4j.appender.serviceLog.layout=org.apache.log4j.PatternLayout
log4j.appender.serviceLog.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.appender.dbLog.layout=org.apache.log4j.PatternLayout
log4j.appender.dbLog.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.category.serviceLog=INFO,serviceLog
log4j.category.org.springframework.jdbc=DEBUG, dbLog
EDIT:
This is my log code:
public static Logger logger = Logger.getLogger(Controller.class);
Boolean commit=false;
public Controller(DashBoardDAO dao,MessageProcessor mp, HandlerFactory hf, ToXML xml)
{
this.dao=dao;
this.mp=mp;
this.hf=hf;
this.xml=xml;
PropertyConfigurator.configure("log4j.properties");
}
public Boolean controlFlow(Message message)
{
Handler handler=null;
String data[]=null;
Boolean flag=false;
int count=0;
handler=initializeHandlerFactory(message);
data=getMessage(handler,message);
flag=validate(data);
count=getEventCount(data);
//commit=mp.flag1;
//System.out.println("The flag is "+flag);
if(flag)
{
System.out.println("inside flag");
String elementNames[]=dao.getElementNames(Integer.parseInt(data[0]));
System.out.println("No of tags "+elementNames.length);
logger.info("No of tags in the generated XML is "+elementNames.length);
File xmlFile=xml.convertToXML(elementNames, data);
logger.info("Temporary XML File created");
logger.info("Data sent for insertion");
dao.insert(count, data,xmlFile);
Boolean f=xmlFile.delete();
System.out.println("Temp XML file Deleted: "+f);
if(f)
{
logger.info("Temporary XML File deleted");
commit=mp.flag1;
}
else
logger.info("Error in deleting temporary XML File");
}
You either need to define your logger as Logger.getLogger("serviceLog") or define a custom appender matching your Class
log4j.category.my.package.Controller=INFO,serviceLog
or your package
log4j.category.my.package=INFO,serviceLog
Also, calling PropertyConfigurator.configure("log4j.properties"); is not necessary, as this is the default configuration file name being looked up by log4j at startup.
Related
I'm using log4j2.
I want to create a RollingFileAppender which rotates the log file on a daily basis.
The name of the logfile is unknown until the application has started (the logfile name is assembled from the application config).
That is why I need to add a RollingFileAppender at runtime.
I have the following code:
public static final ConfigurationBuilder<BuiltConfiguration> BUILDER = ConfigurationBuilderFactory.newConfigurationBuilder
public void initFileLoggerWithFilePattern(final String pattern) {
final LoggerComponentBuilder logger = BUILDER.newLogger("FileLogger", Level.DEBUG);
final AppenderComponentBuilder appender = createFileAppenderWithFilePattern(pattern);
BUILDER.add(appender);
logger.add(BUILDER.newAppenderRef("RollingFileAppender"));
BUILDER.add(logger);
Configurator.initialize(BUILDER.build());
}
public AppenderComponentBuilder createFileAppenderWithFilePattern(final String pattern) {
final AppenderComponentBuilder acb = BUILDER.newAppender("RollingFileAppender", "RollingFile");
acb.addAttribute("fileName", pattern);
acb.addAttribute("filePattern", pattern);
acb.addComponent(createPatternLayout());
acb.addComponent(createTimeBasedTriggeringPolicy());
return acb;
}
public LayoutComponentBuilder createPatternLayout() {
final LayoutComponentBuilder lcb = BUILDER.newLayout("PatternLayout");
lcb.addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss.SSS}{GMT}Z %m");
return lcb;
}
public ComponentBuilder createTimeBasedTriggeringPolicy() {
final ComponentBuilder policies = BUILDER.newComponent("Policies");
final ComponentBuilder policy = BUILDER.newComponent("TimeBasedTriggeringPolicy");
policies.addComponent(policy);
return policies;
}
The problem is that this code changes absolutely nothing. No appender as well as no Logger is being added to the configuration. The "FileLogger" that was created programmatically is not available.
I used this code to print the loggers and Appenders after executing the code above.
private void printLog4jConfig() {
final LoggerContext context = (LoggerContext) LogManager.getContext(false);
final Configuration config = context.getConfiguration();
// Print appenders
for(Appender app : config.getAppenders().values()) {
System.out.println(app.getName());
}
// Print Loggers and their Appenders
for(LoggerConfig lc : config.getLoggers().values()) {
System.out.println(lc);
for(Appender app : lc.getAppenders().values()) {
System.out.println(" " + app);
}
}
}
Output:
Appenders
-------------
STDOUT
Loggers
-------------
root
STDOUT
Console
STDOUT
My Question:
What is wrong with my code? Why is my Appender as well as my Logger not added? Respectively why is the configuration not being refreshed / updated ?
How can I add a RollingFileAppender as well as a logger to the log4j2 configuration during runtime?
I found the solution to my problem.
Instead using
Configurator.initialize(BUILDER.build())
I had to use
Configurator.reconfigure(BUILDER.build());
This reloaded the configuration and i was able to see and use my appender and logger.
I have the following simple Hello World:
public class App {
private static final Logger log = Logger.getLogger(App.class);
public static void main(String[] args) {
System.out.println("Hello World!");
// log.info("Test info log");
}
}
And have the following in log4j.properties:
# Root logger option
log4j.rootLogger=DEBUG, file
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=log.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
Whenever I run the code, the empty log.log file is created even though the logging is commented out.
How to make it create a log file only if logs are printed?
This may give a solution for this if you know where the first log written to file. You can create the file appender when you need to log.
public static void addFileAppender() {
FileAppender fileAppender = new FileAppender();
fileAppender.setName("FileLogger");
fileAppender.setFile("feature.log");
fileAppender.setLayout(new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"));
fileAppender.setThreshold(Level.DEBUG);
fileAppender.setAppend(true);
fileAppender.activateOptions();
Logger.getRootLogger().addAppender(fileAppender);
}
Log4j creates log file because you created logger private static final Logger log = Logger.getLogger(App.class);. You are telling him that you want to write a log message, but because you commented usage of log he doesn't know what appender you will need and creates all of them.
For example:
log4j2.properties
rootLogger.level=trace
rootLogger.appenderRef.file-info.ref = FileInfo
rootLogger.appenderRef.file-debug.ref=FileDebug
appender.file-info.type=File
appender.file-info.name=FileInfo
appender.file-info.fileName=info.log
appender.file-info.layout.type=PatternLayout
appender.file-info.layout.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.file-info.filter.filter-info.type=LevelRangeFilter
appender.file-info.filter.filter-info.minLevel=info
appender.file-info.filter.filter-info.maxLevel=info
appender.file-debug.type=File
appender.file-debug.name=FileDebug
appender.file-debug.fileName=debug.log
appender.file-debug.layout.type=PatternLayout
appender.file-debug.layout.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
appender.file-debug.filter.filter-debug.type=LevelRangeFilter
appender.file-debug.filter.filter-debug.minLevel=debug
appender.file-debug.filter.filter-debug.maxLevel=debug
case 1: will create only one file info.log with one message
public class App {
private static final Logger log = LogManager.getLogger(App.class);
public static void main(String[] args) {
log.info("Test info log");
}
}
case 2: will create two empty files info.log, debug.log
public class App {
private static final Logger log = LogManager.getLogger(App.class);
public static void main(String[] args) {
}
}
case 3: will create two files info.log (one message), debug.log (empty)
public class App {
private static final Logger log = LogManager.getLogger(App.class);
public static void main(String[] args) {
log.info("Test info log");
Doc doc = new Doc();
}
}
public class Doc {
private static final Logger log = LogManager.getLogger(Doc.class);
}
I want to write logs in separate files Programmatically,
public class ProgLoggerMultipaleFiles {
HashMap<LogCategory, Logger> myLogHashMap = new HashMap<LogCategory, Logger>();
public ProgLoggerMultipaleFiles() {
JLogger jlog = new JLogger();
jlog.startFileLog("mylog");
jlog.startFileLog("HC");
jlog.startFileLog("MC");
jlog.startFileLog("DC");
myLogHashMap.put(LogCategory.mylog, Logger.getLogger("mylog"));
myLogHashMap.put(LogCategory.HC, Logger.getLogger("HC"));
myLogHashMap.put(LogCategory.MC, Logger.getLogger("MC"));
myLogHashMap.put(LogCategory.DC, Logger.getLogger("DC"));
String parameter = "Hello";
log(LogCategory.mylog,Priority.DEBUG,"This is debug : " + parameter);
log(LogCategory.mylog,Priority.INFO,"This is info : " + parameter);
log(LogCategory.mylog,Priority.WARN,"This is warn : " + parameter);
log(LogCategory.mylog,Priority.ERROR,"This is error : " + parameter);
log(LogCategory.mylog,Priority.FATAL,"This is fatal : " + parameter);
log(LogCategory.HC,Priority.FATAL,"HC");
log(LogCategory.MC,Priority.FATAL,"MC");
log(LogCategory.DC,Priority.FATAL,"DC");
}
public void log(LogCategory category,Priority priority,String msg){
myLogHashMap.get(category).log(priority, msg);
}
public enum LogCategory{
mylog,HC,MC,DC
}
public static void main(String[] args) {
ProgLoggerMultipaleFiles plog = new ProgLoggerMultipaleFiles();
}
}
And I initialize loggers and Appenders in this class,
public class JLogger {
public JLogger() {
startConsolLog();
}
public void startConsolLog(){
ConsoleAppender console = new ConsoleAppender(); //create appender
//configure the appender
String PATTERN = "%d [%p|%c|%C{1}] %m%n";
console.setLayout(new PatternLayout(PATTERN));
console.setThreshold(Level.FATAL);
console.activateOptions();
//add appender to any Logger (here is root)
Logger.getRootLogger().addAppender(console);
}
public void startFileLog(String fileName){
FileAppender fa = new FileAppender();
fa.setName(fileName);
fa.setFile(fileName+".log");
fa.setLayout(new PatternLayout("%d %-5p [%c{1}] %m%n"));
fa.setThreshold(Level.DEBUG);
fa.setAppend(true);
fa.activateOptions();
//add appender to any Logger (here is root)
Logger.getRootLogger().setAdditivity(false);
Logger.getRootLogger().addAppender(fa);
//repeat with all other desired appenders
}
}
When I run this code creates 4 diff files but all messages are logged into all files.
Thanks in advance.
Log4j Loggers work like a tree. When you get Loggers like LogManager.getLogger(MyClass.class) and MyClass is in org.my.company namespace, log4j will traverse the configuration and look for "org.my.company.MyClass" then "org.my.company" and so on until it finds one. If not it uses the rootlogger.
So you can "tap" that tree with appenders. For example you want all classes in "org.my.company.api" to log into a special file: Configure a logger named "org.my.company.api" , add that fileappender to it and get the Loggers with *.class.
In your case it's a bit different. You get Loggers with a specific name that is probably not in the namespace tree. So if there is no logger with that special name, the root logger is used. Thus all messages go everywhere.
So what you have to do is configure not only appenders, but also Loggers with those specific names - "mylog" for example and add the respective appender to only that logger.
Your tree:
root ← Appender Console, mylog, HC, MC, ...
but you need actually:
root ← Appender Console
|- mylog ← Appender for mylog.log
|- MC ← Appender for MC.log
|- ...
Try:
public void startFileLog(String fileName){
FileAppender fa = new FileAppender();
fa.setName(fileName);
fa.setFile(fileName+".log");
fa.setLayout(new PatternLayout("%d %-5p [%c{1}] %m%n"));
fa.setThreshold(Level.DEBUG);
fa.setAppend(true);
fa.activateOptions();
//add appender to any Logger (here is NOT root)
Logger.getLogger(fileName).setAdditivity(false); // messages will not go to root logger anymore!
Logger.getLogger(fileName).addAppender(fa);
//repeat with all other desired appenders
}
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.
Where log4j2.xml should be placed for use in applet? Can it log both to Java Console and to files on user computer?
I placed in applet resources conf/log4j2.xml and read it from applet. Applet loads it incorrectly, so i fix the fields from the applet code:
public static Logger getLogger(Class className) {
//get logger configuration
LoggerContext loggerContext = Configurator.initialize("client", className.getClassLoader(), className.getClassLoader().getResource("conf/log4j2.xml").getFile());
Configuration configuration = loggerContext.getConfiguration();
//set root logger to desired level
LoggerConfig loggerConfig = configuration.getLoggerConfig("");
loggerConfig.setLevel(Level.INFO);
//obtain appender
Appender appender = obtainAppender(configuration);
//get logger for required class
org.apache.logging.log4j.core.Logger loggerForClass = loggerContext.getLogger(className.getName());
//associate logger for required class with just created appender
configuration.addLoggerAppender(loggerForClass, appender);
return loggerForClass;
}
private static Appender obtainAppender(Configuration configuration) {
//create appender
TriggeringPolicy[] triggeringPolicies = {OnStartupTriggeringPolicy.createPolicy(), TimeBasedTriggeringPolicy.createPolicy("5", "true"), SizeBasedTriggeringPolicy.createPolicy("5 MB")};
TriggeringPolicy triggeringPolicy = CompositeTriggeringPolicy.createPolicy(triggeringPolicies);
return RollingFileAppender.createAppender(CLIENT_LOG_PATH + FileUtils.FILE_SEPARATOR + "my_client.log",
CLIENT_LOG_PATH + FileUtils.FILE_SEPARATOR + "/$${date:yyyy-MM}/my_client-%d{MM-dd-yyyy-HH-mm}-%i.log",
"", APPLET_APPENDER,
"true", "true",
triggeringPolicy, null,
PatternLayout.createLayout("%d{dd/MM/yyyy HH:mm:ss} %-5p [%t] [%c{1}] %m%n", configuration, null, "UTF-8"),
null, "true", configuration);
}