Log4j notifying on logging level changes - java

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...

Related

Log4j incorrect class using Spring AOP

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.

how to add new log value to all logging statements

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.

Migrating from log4j 1.2 to log4j 2 - how to get list of all appenders and rolling file strategy

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.

IDs for log messages when using SLF4J

I would like to add a unique identifier to log statements, so I am able to add documentation (externally, e.g. a wiki) to every log statement, so a user can quickly access the message related documentation using the id. The logging framework I would like to use is SLF4J/logback.
I was not able to find documentation about related approaches except for some bits regarding auditing frameworks.
There is the Marker concept which I thought could be usable for ID injection, or I could just add the ID to the message text itself.
How would I add IDs to the logging statements "the right way"? Are there possibilities I didn't think of?
EDIT
The term unique ID just states there should be an identifier per log statement. A developer e.g. adds such an ID to a table/enum/whatever manually, which could be done wrong.
Such ID has to be stable, so documentation can be based on it. So the ID itself is not what I am wondering about.
My question is: what would be the right way of pushing the ID to the logger together with the message text? Would Markers be suited for this kind of requirement, should I embed the ID into the message text or is there some other possibility?
So, basically, would I use
logger.info(IDMarkers.DB_CONNECTION_FAILED, "no connection to the database");
or instead just
logger.info("[{}] no connection to the database", LogIDs.DB_CONNECTION_FAILED);
First approach has the advantage that showing the IDs is up to the logging system/its configuration.
Slf4j has http://www.slf4j.org/apidocs/org/slf4j/Marker.html
Unfortunately Markers are advertised for a different purpose. Still you can use them to uniquely mark logging statements.
More cumbersome solution is MDC:
MDC.put("MsgId", "EV-1234");
log.info()
MDC.remove("MsgId");
or with structural logging (requires v2.0.0):
logger.atDebug()
.addKeyValue("MsgId", "EV-1234")
.log("Temperature changed.");
Unique is only unique within some scope. Eventually, even every int or long value will be used.
So think about what "uniqueness" means to you. Then use a wrapper that will ensure your logging is handled with that id inserted.
Note that with slf4j you are dealing with an interface which will make a number of logging APIs consistent. This means you probably won't have the option to sub-class or even inject your implementation of the interface to ensure your consistent logging. Therefore you will be constrained to techniques like wrapping your logging API (preferably through the "consistent" interface).
package mypackage.log;
public class LoggerWrapper implements org.log4j.Logger {
private org.log4j.Logger logger;
public LoggerWrapper(org.log4j.Logger logger) {
this.logger = logger;
}
public String getUniqueId() {
return ...;
}
public void info(String message, Object params...) {
logger.info(String.format("[%d] %s", getUniqueId(), message), params));
}
... implement all the other methods ...
}
And this means that you will have to make your own LoggerFactory interface too
public Logger getLogger(String name) {
return new LoggerWrapper(org.sql4j.LoggerFactory(name));
}
While the code above has a few warts (not actually testing it); hopefully, it will give you an idea.

Separate INFO and ERROR logs from java.util.logging

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.

Categories