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.
Related
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.
My application manages devices in a network. Those are represented in my application by objects and compositions of objects.
Each of these devices has an ID and each object related to a device knows about its ID.
When configuring logging, in addition to set the log level per the package hierarchy, I would like to be able to set it per device - that is, all instances related to one device should be eg. put in DEBUG level.
How can I set the log level by the package hierarchy and also by an instance ID?
My thoughts so far:
Currently my loggers are created the "standard" way, by the class type
public class Thermometer extends AnalogDevice {
private static final Logger logger = LoggerFactory.getLogger(Thermometer.class);
...
}
But I want to be able to choose the log level for specific devices. My current idea would be to use the ID in the logger name, like this:
public class Thermometer extends AnalogDevice {
private final Logger logger;
public Thermometer(String deviceId){
logger = LoggerFactory.getLogger(deviceId+"."+Thermometer.class);
...
}
...
}
(and the same for other classes in the hierarchy and other classes bound to this device) This would allow to configure log4j to have all messages for device "mydevice123" on DEBUG level.
log4j.logger.mydevice123=DEBUG
But this will potentially create many loggers (per device / per class):
mydevice123.com.example.dev.Thermometer.class
mydevice123.com.example.dev.AnalogDevice.class
mydevice123.com.example.dev.SomeOtherDeviceSpecific.class
...
Also I now lost the possibility to set the level on a package hierarchy. This won't work anymore.
log4j.logger.com.example.dev=DEBUG
What's a better way?
Use logback as your logging implementation.
Set your device id in the MDC (Mapped Diagnostic Context) (*)
Set up a DynamicThresholdFilter
(*) IMHO a good approach to set the device id in the MDC is using an aspect applied on every business method which will get the device id from the targeted object (exemple with spring aspects), set the id in context before the method invocation and remove it after
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");
I have two loggers :
root and
test1
If I use getlogger("test1"), it will use test1, is that right?
when using getlogger(typeof(Program)), will it use root?
The logger that is used is defined in your log4j.properties/log4j.xml and that is based on the name of your package.
The getLogger(name) is used to log the name of the current class.
If you input "test1" as a parameter, the log4j log line for your call will contain the "test1".
When you do getLogger("test1") it will use the logger named test1.
When using getLogger(class), it will retrieve the logger that has been named using the class name.
If you are trying to get root logger, use getRootLogger() instead.
See http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html#getLogger%28java.lang.String%29 for more
Your class should use root by default depending on your configuration. See https://logging.apache.org/log4j/1.2/manual.html
"As a side note, let me mention that in log4j child loggers link only to their existing ancestors. In particular, the logger named com.foo.Bar is linked directly to the root logger, thereby circumventing the unused com or com.foo loggers. This significantly increases performance and reduces log4j's memory footprint." - from the manual
I'm configuring the logging for a Java application. What I'm aiming for is two logs: one for all messages and one for just messages above a certain level.
The app uses the java.util.logging.* classes: I'm using it as is, so I'm limited to configuration through a logging.properties file.
I don't see a way to configure two FileHandlers differently: the docs and examples I've seen set properties like:
java.util.logging.FileHandler.level = INFO
While I want two different Handlers logging at different levels to different files.
Any suggestions?
http://java.sun.com/j2se/1.4.2/docs/guide/util/logging/overview.html is helpful. You can only set one Level for any individual logger (as you can tell from the setLevel() method on the logger). However, you can take the lowest of the two common levels, and then filter programmatically.
Unfortunately, you can't do this just with the configuration file. To switch with just the configuration file you would have to switch to something like log4j, which you've said isn't an option.
So I would suggest altering the logging in code, with Filters, with something like this:
class LevelFilter implements Filter {
private Level Level;
public LevelFilter(Level level) {
this.level = level;
}
public boolean isLoggable(LogRecord record) {
return level.intValue() < record.getLevel().intValue();
}
}
And then on the second handler, do setFilter(new LevelFilter(Level.INFO)) or whatever. If you want it file configurable you could use a logging properties setting you've made up yourself, and use the normal Properties methods.
I think the configuration code for setting up the two file handlers ad the programmatic code is fairly simple once you have the design, but if you want more detail add a comment and I'll edit.
I think you should be able to just subclass a handler and then override the methods to allow output to go to multiple files depending on the level of the message. This would be done by overriding the publish() method.
Alternatively, if you have to use the system-provided FileHandler, you could do a setFilter() on it to inject your own filter into the mix and, in that filter code, send ALL messages to your other file and return true if the LogRecord level if INFO or higher, causing the FileHandler.publish() to write it to the real file.
I'm not sure this is the way you should be using filters but I can't see why it won't work.