Programmatically log into multiple files using log4j - java

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
}

Related

Initiate Log4j2 RollingFileAppender in Java

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.

Stop creating log file if no logs are printed

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);
}

collecting logs with log4j2 v2.9

i have a question regarding log4j2 with version 2.9.
Basically I want to do the same as described here (log4j), only with 2.9:
Sample log4j v1.x
I need a logger that can be called in any method in the class. This is to collect all subsequent logs recursively from a certain starting point. The collection should be able to be read out in any form later on.
Logger logger = LogManager.getLogger();
public void meth1(){
StringWriter/ List/ String or whatever
logger.add(Collector);
logger.info("Start");
this.meth2();
this.meht3();
logger.info("Stop");
=> do something with the collected logs
}
public void meht2(){
logger.info("meth2: add Collection");
}
public void meth3(){
logger.info("meth3: add Collection");
}
public void meht4(){
logger.info("foo");
}
as soon as the end is set in a form, the following logs should be included in the collection:
Start
meth2: add Collection
meth3: add Collection
Stop
thanks for your help
After a while, I came up with the following solution which works for me using Log4J 2.8. I've added some comments to the code to explain the different steps necessary.
It's important that the logger is requested with the same name (line marked with /* 1 */ as for which the configuration is stored (line marked with /* 2 */. This implies, that the class name cannot be used to get the logger, or that MyClass.class.getName() should be used at /*2*/.
public class LoggingTest {
public static void main(String[] args) {
// define the logger, could also be static in class
final String loggerName = "myCollectingLogger";
Logger logger = LogManager.getLogger(loggerName); /* 1 */
// the log message collector
StringWriter writer = new StringWriter();
// start adapting the logger configuration
LoggerContext ctx = LoggerContext.getContext(false);
Configuration config = ctx.getConfiguration();
// create our appender
PatternLayout layout = PatternLayout.newBuilder().withPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n").build();
WriterAppender writerAppender = WriterAppender.newBuilder().setName("writerAppender").setTarget(writer).setLayout(layout).build();
// add the appender to a LoggerConfig
AppenderRef ref = AppenderRef.createAppenderRef("writerAppender", null, null);
AppenderRef[] refs = new AppenderRef[] { ref };
LoggerConfig loggerConfig = LoggerConfig.createLogger(false, Level.INFO, "example", null, refs, null, config, null);
loggerConfig.addAppender(writerAppender, null, null);
// enable the LoggerConfig in the LoggerContext
config.addLogger(loggerName, loggerConfig); /* 2 */
ctx.updateLoggers();
// use the logger:
logger.info("Start");
logger.warn("foo bar");
logger.error("relax, it's just a test");
logger.info("Stop");
System.out.println("--- the collected log messages: ---");
System.out.println(writer.toString());
}
}
thanks, I've adjusted it to my needs:
public static StringWriter createStringWriter(String classname){
StringWriter stringWriter = new StringWriter();
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
PatternLayout layout = PatternLayout.newBuilder()
.withPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n").build();
WriterAppender writerAppender = WriterAppender.newBuilder()
.setName(classname + "writeLogger")
.setTarget(stringWriter)
.setLayout(layout)
.build();
writerAppender.start();
config.addAppender(writerAppender);
LoggerConfig loggerConfig = config.getLoggerConfig(classname);
loggerConfig.addAppender(writerAppender, null, null);
ctx.updateLoggers();
return stringWriter;
}
public static void removeStringWriter(String classname){
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(classname);
loggerConfig.removeAppender(classname + "writeLogger");
ctx.updateLoggers();
}

Configure log4j2 for use from applet

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);
}

log4j INFO level not logging information

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.

Categories