I am trying to make an trace logger using AOP programming, simular to this. The problem is that even after getting a new logger like this:
final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass().getName());
All logs are still like this:
TRACE 2015-11-05 14:35:43,588 LoggingAspect.logMethod(LoggingAspect.java:42) - Some log
Instead of:
TRACE 2015-11-05 14:35:43,588 MyClass.SomeMethod(MyClass.java:10) - Some log
The class to log from does not change. Any suggestions?
joinPoint.getTarget()
returns the target object the method is executed on. This may be null if your object was executed in a static context, I think. But it may be null. So do not use it. And as you can see, It behaves a little bit strange (I think it has to do with proxying or subclassing).
You should better use
joinPoint.getSignature()
to retrieve the signature of the method, containing the defining class. So better use
joinPoint.getSignature().getDeclaringType()
to retrieve the class for the logger.
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.
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 am in the process of migrating my application from log4j 1.2 to log4j 2.0
I have existing code:
Enumeration appenders = logger.getAllAppenders();
.
.
.
fileBackupIndex = rollingFileAppender.getMaxBackupIndex();
In log4j 2.0 I could not find way to replace above java code. How to get list of all appenders and how to get the max value defined for RollingFile appender programatically?
With log4j2, there is a separation between API and CORE. This allows the team to make changes to the implementation without breaking client code.
So, if your code depends on implementation details, be aware that in the future this may change and your code may break.
That said, you can get a map of the appenders like this:
Logger logger = LogManager.getLogger();
Map<String, Appender> appenderMap =
((org.apache.logging.log4j.core.Logger) logger).getAppenders();
You can loop over the map until you find a RollingFileAppender. From this point it gets really ugly... The information you want is all in private fields, so you would need to use reflection to do the following:
get the fileAppender's "manager" field and cast it to RollingFileManager
get the manager's "strategy" field and cast it to DefaultRolloverStrategy
get the defaultRolloverStrategy's "maxIndex" field
This would obviously be pretty fragile... If you really need this you can request this feature on the log4j-dev mailing list or create a JIRA ticket. The quickest way to get this feature is if you provide a patch with the feature request.
I've added accessors for
minIndex, maxIndex, and compressionLevel.
triggeringPolicy and rolloverStrategy.
See our SVN trunk.
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...