I just started to use Java Logger. I tried to use its entering() and exiting() methods with hard coded string for class name and method. They both didn't work for me. Just no log entry for them. But other log statements within that methods were logged properly. My logger level is set to CONFIG. I have tried to set to ALL but still cannot see log entry from them.
I found for each entry, there already has a line with class name and method being logged. It seems these two methods are not necessary. But I am still want to know how to make them work for me.
EDIT:
My code followed: these entering() and exiting() are not create an entry in the log file
//class variables
private final static Logger logger = Logger.getLogger(MyClass.class.getName());
private static FileHandler logFileHandler = null;
//within the main() method
Logger thisLogger = Logger.getLogger("");
logFileHandler = new FileHandler(logFileNameStr, false);
logFileHandler.setFormatter(new SimpleFormatter());
thisLogger.addHandler(logFileHandler);
thisLogger.setLevel(Level.CONFIG);
logger.log(Level.INFO, "Logfile Directory = " + logFileNameStr);
//within a constructor of MyClass
logger.entering("MyClass", "MyClass()");
....
logger.info(initMsg);
....
logger.exiting(this.getClass().getSimpleName(), "MyClass()");
Entering, exiting, and throwing are logged at level FINER. You'll have to set your logger to FINER or lower and set your FileHandler to FINER or lower.
thisLogger.addHandler(logFileHandler);
thisLogger.setLevel(Level.FINER);
logFileHandler.setLevel(Level.ALL);
As far as style goes, you should try creating a static final for class name because you'll use it as the name of the logger and reference it for entering, exiting, throwing, and log precise tracing:
private static final String CLASS_NAME = MyClass.class.getName();
You always want to use a class literal to fetch the name (as above) because getClass().getName() can return a subclass name which can falsify your tracing.
For method names don't include () in the name, just use the entering/exiting method that matches the number of arguments.
For constructor method names use "<init>" and for static init blocks use "<clinit>" as the method name since that is what would show up in a stacktrace.
As #jmehrens said, these "convenience" methods are logged at finer and your Logger and Handler objects are likely configured at a much higher level by default.
Here are more convenient entering and exiting replacements for use within the logged method and which you can simply change the level on:
logger.finer("ENTRY"); // compare to .entering(MyClass.class.getName(),"MyMethodName")
...
logger.finer("RETURN"); // less typing than .exiting(CLASS_NAME,"MyMethodName")
Same output as entering and exiting as long as you're providing the real class and method name. Simple enough to change .finer to .info.
To handle the parameterized versions use .log:
logger.log(Level.FINER,"ENTRY {0}", param1);
...
logger.log(Level.FINER,"RETURN {0}", result);
I think the only place entering/exiting makes sense is when you're logging from the caller's side:
logger.entering(thing.getClass().getName(),"DoStuff");
thing.DoStuff();
logger.exiting(thing.getClass().getName(),"DoStuff");
You could do the same with logp have control over what level it logs at, but it is more verbose:
logger.logp(Level.FINER,thing.getClass().getName(),"DoStuff","ENTRY");
thing.DoStuff();
logger.logp(Level.FINER,thing.getClass().getName(),"DoStuff","RETURN");
Related
I am trying to add some convenient methods to java.util.Logger. They are methods that I expect to ease the coding o my next Java application:
public void severeException(Exception e, String msg) {
super.severe(msg);
super.severe(e.getClass().getCanonicalName());
super.severe(e.getMessage());
}
public void severeLoadingFile(Exception e, String filePath) {
super.severe("Could not load file '" + filePath + "'");
super.severe(e.getClass().getCanonicalName());
super.severe(e.getMessage());
}
public void severeLoadingFile(String filePath) {
super.severe("Could not load file '" + filePath + "'");
}
They represent a 1:2 to 1:3 ratio in the number of code lines I have to write whenever I want to report an exception. There are a large bunch of similar methods for well-known exceptions, like IOException loading a file, etc.
So far I have tried extending Logger as CustomLogger, which led me to problems with class loaders and modules when calling getLogger() method. Then I tried initializing CustomLogger by its constructor...
Beyond the problems with constructor and class loaders, this does not seem to be the way, as the caller method gets trapped by my wrapping methods.
I have scanned the Logger source code but did not find an easy way of calling log() passing it the caller's name or something similar.
Any idea?
It is not impossible yet may impact performance.
In your logging method you can find the caller's name via the stack trace element (https://docs.oracle.com/javase/8/docs/api/java/lang/StackTraceElement.html).
System.out.println(Thread.currentThread().getStackTrace()[1]);
I second Anders's idea of making proper use of a logging framework.
I am using slf4j with logback in my project.there is one request_id stored in a ThreadLoacal.
I want to add the value of this request id to all log statements.
Is there any way so that logger implicitly pick up the value of request_id and log it as well, without being pass it in existing log statements?
Slf4j and logback both supports the usage of a mapped diagnostic context (MDC). You can add named values to the MDC, which are passed to the logger. The logging pattern supports tokens for output.
Note that the MDC is stuck to your thread, i.e. with a different thread the context is lost. And with thread reusage, the context will reappear, so cleaning is important in such situations.
You should be able to do this using a custom Converter.
This Answer to another question shows how you can use a Converter to incorporate a thread identifier that has been saves in a thread-local variable. You should be able to adapt this to incorporate a request id.
This section of the logback manual describes how to hook a custom converter into your logback configs.
This can be tedious, But, I am sure it would do it
Firstly configure sl4j logging with the JDK logger.
Write your own formatter, or override the Java SimpleFormatter like so:
package com.mypackage
public class MyFormatter extends SimpleFormatter{
#Override
public String format(LogRecord record){
String simpleFormattedLog = super.format(record);
String simpleFormattedLogWithThreadID = "THREAD_ID(" + getThreadId() + ") _ " + simpleFormattedLog;
return simpleFormattedLogWithThreadID;
}
private String getThreadId(){
return "GET THREAD ID HERE";
}
}
Then specify the formatter in the properties file as
java.util.logging.ConsoleHandler.formatter = com.mypackage.MyFormatter
This should work, all things being equal.
I am using general java.util.logging.Logger with the following initialization line:
private static final Logger _log = Logger.getLogger(Login.class.getName());
Then I use it like this (just an example):
_log.log(Level.FINE, "Session id: {0}.", session.getId());
In the log sometimes I can see the following:
24-Nov-2014 17:26:13.692 FINE [http-nio-80-exec-1] null.null Session id: 18BD6989930169F77565EA2D001A5759.
Most of the times times however it shows me the calling class and function correctly. That happens in other classes and members too. I cannot figure out why does this happen? Is it a bug in the Loggger?
The output looks like it is from the 'org.apache.juli.OneLineFormatter' and the 'null.null' is the source class and source method names. From the LogRecord.getSourceMethodName documentation:
May be null if no information could be obtained.
It is possible it can't be determined or has been forced to null.
Looking at the source code for 'org.apache.juli.AsyncFileHandler' there is a bug where that code is not capturing the original callsite.
You can create and install a filter on the AsynchFileHandler to force compute the method and class names before a the thread hand off. Here is an example of such a filter:
public class InferCallerFilter implements Filter {
public boolean isLoggable(LogRecord record) {
record.getSourceMethodName(); //Infer caller.
return true;
}
}
Even the MemoryHandler included with the JDK gets this wrong. Per the LogRecord documentation:
Therefore, if a logging Handler wants to pass off a LogRecord to another thread, or to transmit it over RMI, and if it wishes to subsequently obtain method name or class name information it should call one of getSourceClassName or getSourceMethodName to force the values to be filled in.
I "solved" this by changing my custom LogFormatter to get the source class name and source method name at the very beginning of the format method.
The return values of these methods seem to have an "expiration date"...
#Override
public String format(LogRecord record)
{
String sourceClassName = record.getSourceClassName();
String sourceMethodName = record.getSourceMethodName();
...
}
So far, after that change I have not observed any null.null entries anymore, when before I had lots of them.
Is there anyway , or any configuration in log4j that tell him to log a line whenever some "logging level" configurations occur ?
I mean someone changed in some package the debug level from INFO to DEBUG , I want that event being logged by log4j.
Thanks
I'm not sure if it's your case, but if you are reloading the configuration file using configureAndWatch you should be able to see the information you need setting the system property -Dlog4j.debug=true.
See
http://logging.apache.org/log4j/1.2/apidocs/index.html?org/apache/log4j/xml/DOMConfigurator.html
There is a LogLog, the self logging of Log4J. I have never used it but if the solution exists it is there. I'd suggest you to download the source code of log4j and try to investigate it where it "knows" that the log level is being changed. Once you found the code see whether it prints something to self log and configure it as you need.
I'd be appreciate if you can post here more concrete answer if you find it. Good luck.
At runtime, if you have a servlet, jsp or a screen on your application where you are able to change log levels, you will most likely be doing something like this
public void changeLogLevel(String className, String logLevel){
Logger logger = Logger.getLogger(className);
Level level = Level.toLevel(logLevel);
logger.setLevel(level);
}
in order to log this event, all you would have to do is add an extra logger statement for this event
private static Logger classLogger = Logger.getLogger(ThisClass.class);
public void changeLogLevel(String className, String logLevel){
Logger logger = Logger.getLogger(className);
Level level = Level.toLevel(logLevel);
logger.setLevel(level);
classLogger.debug("The Level of " + className + " has changed to " + logLevel);
}
Then each time a log level occurs, you can log it here. If you want to get fancy, just send this log to its own file. You may want to advance the method even further to include an IP/username of the user who changed the log level.
If you have control over your application, ensure this is your only point in the application where a user can change your logging levels.
This doesn't answer your question, but the built-in java.util.logging.LogManager implementation has an addPropertyChangeListener() method that does exactly what you want.
I couldn't find anything comparable in Log4J, but I didn't look that hard...
I've seen code that uses log4j, which acquires logger for a given Logger using
static public Logger getLogger(String name)
and
static public Logger getLogger(Class clazz)
with the former api passed explicitly with getSimpleName(), while the latter uses getName() on the passed Class. Is there a difference between these two? Would it affect if I configure various packages to log at different level in log4j.properties file?
Yes there is a huge difference.
I never use simpleName for Logger instance as it strips down the package name.
Apart from having problems when same class name exists in two different packages (leading to both classes getting the same logger instance), you lose the ability to control logger inheritance.
e.g. for two loggers:
com.foo.A
com.foo.B
in properties, i can just have:
log4j.logger.com.foo=DEBUG,CONSOLE
E.g. My class ShapeDemo.java resides in com.test package, and I have written code like below.
System.out.println("Name-->"+ShapeDemo.class.getName());
System.out.println("SimpleName-->"+ShapeDemo.class.getSimpleName());
This will output following
Name-->com.test.ShapeDemo
SimpleName-->ShapeDemo
Using of this.getClass().getName();
Returns : alin.iwin.flickrbrowser.GetRawData
Meanwhile
private String LOG_TAG = this.getClass().getSimpleName();
Return only : GetRawData.
I prefer using the full name (Class.getName()). When packages are organized correctly, this allows tuning log4j to handle differently log messages originating from different parts of the java packages tree.
For example, you can easily configure all classes in packages starting with "com.mycompany.infra" to use a specific appender, and log only messages of level WARN or above.
You might get confused if you have many classes with the same simpleName in different packages. Having many loggers with the same name should not be a problem otherwise - just could be confusing.