Link to home
Start Free TrialLog in
Avatar of GAUTAM
GAUTAMFlag for United States of America

asked on

Using Log4j across classes

Hi Experts...
I use looging from log4j in my java programs.
I get the logger using the below command:
static Logger logger = Logger.getLogger(TestLogging.class.getName());
How do i have a common logger for say more than one java class.
Please help...
Avatar of for_yan
for_yan
Flag of United States of America image


this is from:
http://logging.apache.org/log4j/1.2/manual.html
Calling the getLogger method with the same name will always return a reference to the exact same logger object.

For example, in

   Logger x = Logger.getLogger("wombat");
   Logger y = Logger.getLogger("wombat");

x and y refer to exactly the same logger object.

Thus, it is possible to configure a logger and then to retrieve the same instance somewhere else in the code without passing around references. In fundamental contradiction to biological parenthood, where parents always preceed their children, log4j loggers can be created and configured in any order. In particular, a "parent" logger will find and link to its descendants even if it is instantiated after them.
What do you exactly mean, When I use log4J I always define a new logger, de "default" log4j will get the class and writes the log were ever you configured it

static Logger logger = Logger.getLogger([i]classname[/i].class);

Open in new window


Unless I want to have somecode logged to a special file, then I define other/second logger

static Logger logger = Logger.getLogger("loggerToOtherFile");

Open in new window

Further they point out that it is often convenient and good organization to name by class
but  it is not necessary, do you can invoke the same logger in different classes
and the logeger named by one clas can be clled and used in another calss



I think tyhis is a good explanation of all options

from

http://stackoverflow.com/questions/3038079/using-same-log4j-logger-in-standalone-java-application
(in particuylar option B applies directly to yourt question):
      

A) in Log4J, you have logger hierarchies, not a single logger. This typically boils down to one logger per class, where loggers are identified by classname. The logger is initialized like this:

private static final Logger logger = Logger.getLogger(MyClass.class);

This is a good practice in that it allows you to fine-tune logging behaviour for modules (packages) or even individual classes within your app. So you may disable logging for some packages, log at INFO level in others, and log on DEBUG level for some critical classes e.g. when you want to catch a bug.

B) However, if you want a single logger everywhere, simply use the root logger in every class:

private static final Logger logger = Logger.getLogger();

C) for calls to not too complex methods, the performance difference is likely negligible, as the JIT compiler will agressively inline calls anyway. For complicated methods it is more of an open question.

Note that the method you show does unnecessary work by loading logger configuration - this is done automatically by Log4J if you name your config file log4j.properties and put it on the classpath. However, even if you need to use a nonstandard config file name, you could still load the Log4J configuration in a single place upon startup, and omit the lazy loading of the logger. Then what is left is

private static final Logger logger = Logger.getLogger(ExtractData.class);

private static Logger getLogger() {
    return logger;
}

and this will surely be inlined by the compiler. You may want to retain getLogger nevertheless, to avoid modifying a lot of caller code.

Note that it is unnecessary for getLogger to be public, as all classes are supposed to have their own logger reference anyway.
Avatar of GAUTAM

ASKER

@for_yan:Thanks for the reply.
Considering the case B)
private static final Logger logger = Logger.getLogger();
returns an error stating:  The method getLogger(String) in the type Logger is not applicable for the arguments ().
How do i solve this.
Please help...
I assume they meant this method:

public static Logger getRootLogger()

Avatar of CEHJ
>>How do i have a common logger for say more than one java class.

Why would you want that actually? It's not usually a good way of organizing logging
Avatar of GAUTAM

ASKER

@for_yan:Thanks for replying.
I added the below method and it works fine.But i have to add this method in all the classes of the application.Or do i have an alternative.

@CEHJ:Thanks for the reply.
So how else can i improve this?Or what other option do i have?

Please help...
Avatar of GAUTAM

ASKER

Code added in all classes
public static Logger getLogger() { 
	   if (logger != null) return logger; 
	   try { 
		   
	   PropertyConfigurator.configure("log4j.properties"); 
	   logger = Logger.getLogger(ClassName.class); 
	    
	   } catch (Exception exception) { 
	   exception.printStackTrace(); 
	   } 
	   return logger;
	   }

Open in new window

No, I think once you say in the your MainClass



static Logger mylogger = Logger.getRootLoger();

then everywher youjust use

MainClass.mylogger

as any static variable
>>
So how else can i improve this?Or what other option do i have?

Please help...
>>

Normally speaking, you want logging to be as granular as possible, so that the behaviour of individual classes can be observed in isolation from others. What you're asking for is the opposite. Nearly always you want the following
Logger logger = Logger.getLogger(TestLogging.class);

Open in new window

Avatar of GAUTAM

ASKER

@for_yan::Thanks for replying.
But is i do that i will not know the class in which any exception occurs since the logger was defined in the parent class.
I have my appender status as log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n and the class displayed is the main class in which it was defined as static and public and not the class in which exception occured.
Please help...
You still haven't said WHY you want a common logger...
but I guess you still can have printStackTrace with full information where exception occurred
Avatar of chinhw
chinhw

@gaugeta
I do not recommend you use log4J in this manner. You can make use of the "Named Hierarchy" nature of log4J to archieve what you are trying to do.

Suppose you have 2 java classes.
1) A.B.C.D1
2) A.B.C.D2

In (1)
static Logger logger = Logger.getLogger("A.B.C.D1");
In (2)
static Logger logger = Logger.getLogger("A.B.C.D2");

In your log4J settings.
Set a logger for "A.B.C"

Log4J in (1) and (2) will use logger "A.B.C" because of "Name Hierarchy".

Alternatively.
You can also define 2 logger in log4J settings.
One for "A.B.C.D1" and another for "A.B.C.D2".
The logger for (1) and (2) uses the same appender.

--------------------------
The benefit of this is that you will have more control over what you are going to log without changing code.
Example: You can choose not to log (2) by just changing the log4J settings without having the need to change code.



tht's how you can put stacktrace of exception to logger:
http://stackoverflow.com/questions/4347797/how-to-send-a-stacktrace-to-log4j
Avatar of GAUTAM

ASKER

@for_yan:Thanks for the reply.
I have the following two java files and the log4j properties files as well.
When the exception occurs i get the message as shown below as well.
I want to retain the same format but add a user defined message at each function as well in which exception occurs.
How do i achieve this.
Please help...

import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

 public class TestLogging {

   //static Logger logger = Logger.getLogger(TestLogging.class.getName());
   public static Logger logger =TestLogging.getLogger();


   public static void main(String[] args) {
	   PropertyConfigurator.configure("log4j.properties");
try
{
	TestLogging2.main1();
	//String s=args[4];
     // BasicConfigurator replaced with PropertyConfigurator.
     

     logger.debug("Entering application.");
     logger.info("Exiting application.");
}
catch(Exception e)
{
	  
	  String stackStr = null;
	  StackTraceElement[] stk = e.getStackTrace();
	  for (int i = 0; i < stk.length ; i++)
	  {                                               
		  stackStr += "Level [" + Integer.toString(i) + "] - File Name: '" + stk[i].getFileName() + "' Method Name: '" + stk[i].getMethodName() +"' Line Number: '" + stk[i].getLineNumber()+"' Message: '" + e.toString() + "'\n";
	  }   
	  
	  logger.debug("Exception occured "+stackStr);
	  logger.debug("Exception ended");
}
   }
   public static Logger getLogger() 
   { 
	   if (logger != null) return logger; 
	   try 
	   { 
		   PropertyConfigurator.configure("log4j.properties"); 
		   logger = Logger.getLogger(TestLogging.class); 
	    
	   } 
	   catch (Exception exception) 
	   { 
		   exception.printStackTrace(); 
	   } 
	   return logger;
   } 


 }

Open in new window

import java.util.Properties;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

 public class TestLogging2  {

	 


   public static void main1() throws Exception {
	   PropertyConfigurator.configure("log4j.properties");

	main2();
     // BasicConfigurator replaced with PropertyConfigurator.
     

     TestLogging.logger.debug("Entering application.");
     TestLogging.logger.info("Exiting application.");


   }
   
   public static void main2() throws Exception
   {
	 
	   String args[]={"hello"};
		String s=args[4];
	     // BasicConfigurator replaced with PropertyConfigurator.
	     

	     TestLogging.logger.debug("Entering application.");
	     TestLogging.logger.info("Exiting application.");
	 
	  
   }
   
   }

Open in new window

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%m%n

Open in new window

Exception occured nullLevel [0] - File Name: 'TestLogging2.java' Method Name: 'main2' Line Number: '30' Message: 'java.lang.ArrayIndexOutOfBoundsException: 4'
Level [1] - File Name: 'TestLogging2.java' Method Name: 'main1' Line Number: '16' Message: 'java.lang.ArrayIndexOutOfBoundsException: 4'
Level [2] - File Name: 'TestLogging.java' Method Name: 'main' Line Number: '18' Message: 'java.lang.ArrayIndexOutOfBoundsException: 4'

Exception ended

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
You also need to read up on formatting - you don't need to do anything but use the correct format string:

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
:)