How to override java.util.logging.Logger to my private logger? - java

I have code fetched from jar that uses java.util.logging.Logger.
Jar contains about 1000 logger usages and each class start from:
private static final Logger LOG = Logger.getLogger(SomeClass.class.getName());
I want to handle all logs there, means, to point them to my Logger usage and not to java.util.logging.Logger.
Therefore I wrote my own logger.
So instead:
LOG.log(Level.SEVERE, "Error sleeping", e);
I can write:
MyLogger.toLog(TLogLevel.WFS_ERROR, "Monkey", "Error sleeping", e );
The problem is I need run over all java files and replace with mine.
Messy way, hmm
Does anyone know how can by easy way to convert java.util.logging.Logger to com.boo.MyLogger?
Thanks,

The SLF4J project has a jul-to-slf4j bridge that can be used to redirect java.util.logging.Logger calls to SLF4J. You could use that (by making your MyLogger implement the interface defined by SLF4J).
Note that, however, unlike all other logging libraries, j.u.l. is hard-wired into the Java class libraries and cannot be bridged without a performance penalty.
Also, I don't know what you are doing with MyLogger, but usually there is no need to write your own. There are plenty of logging implementations to choose from, and they can be configured in many different ways. And even if you do have to write your own Logger implementation, you should use an existing interface (such as SLF4J which seems to most popular these days).

Take a look at SLF4J:
The Simple Logging Facade for Java or (SLF4J) serves as a simple
facade or abstraction for various logging frameworks, e.g.
java.util.logging, log4j and logback, allowing the end user to plug in
the desired logging framework at deployment time.
Using that you could then also use logback (same author) to log to a common logging framework using the various bridges already available. Or, write your own, but either way you would not have to worry about replacing all that code...

Oracle's Java 7 Logger is configurable, its implementation is simply:
public static Logger getLogger(String name) {
// This method is intentionally not a wrapper around a call
// to getLogger(name, resourceBundleName). If it were then
// this sequence:
//
// getLogger("Foo", "resourceBundleForFoo");
// getLogger("Foo");
//
// would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null.
LogManager manager = LogManager.getLogManager();
return manager.demandLogger(name);
}
So you can also via code set a logging level; besides declarative.
LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.INFO);
Lars Vogel has a nice page, also with its own Logger class.
All put together is quite workable, but maybe sometimes somewhat hard to understand.

Related

How to enforce SLF4J usage in Java code with ArchUnit

I wanna use ArchUnit to enforce usage only SLF4J logging framework and avoid simple System.out calls. Also I would like to avoid any other logging frameworks for using. How can I implement the such check?
Currently I write this test
class EnforceSlf4JLoggingTest {
private final JavaClasses importedClasses = new ClassFileImporter()
.withImportOption(new ImportOption.DoNotIncludeTests())
.importPackages("... my packages ...");
#Test
public void classesShouldNotUseJavaUtilLogging() {
NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING.check(importedClasses);
}
#Test
public void classesShouldNotUseSystemOutLogging() {
noClasses()
.should()
.dependOnClassesThat()
.belongToAnyOf(java.lang.System.class)
.because("use SLF4J instead")
.check(importedClasses);
}
}
but it doesn't actually enforce SLF4J usage but only restrict java.unit.logging usage and prevent of having dependency to java.lang.System class (where actually System.out constant is located).
Whether is more elegant solution of my case?
There is no a simple way to avoid usage any other loggig systems beside of SLF4J but it's possible to use this ArchUnit check template
ArchRuleDefinition.noClasses()
.should().dependOnClassesThat()
.resideInAnyPackage(
"java.util.logging..",
"org.apache.logging.."
).as("Please only depend on SLF4J as a logging framework");
In the section resideInAnyPackage should be specified packages of different logging systems have to be avoided.
Also, rule
com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS
has to be used to check absents any plain System.out, System.err and printStackTrace calls in code.

log4j2 extending Logger class

I am trying to migrate from Log4j 1.7 to Log4j2.4
In 1.7, I as creating AppLogger class by extending org.apache.log4j.Logger and using extending debug/error/fatal Methods
e.g.,
public void error(Object message) {
Object error = message;
if (message instanceof Exception) {
Exception e = (Exception) message;
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
error = sw.toString();
}
super.error(error);
}
But in Log4j 2.x, I am not able to extend the class org.apache.logging.log4j.Logger;
What is the Best way to achieve this?
I can understand why you might have wanted to do this with Log4j 1.x, but I cannot figure out why you would want to do this with log4j 2. The best place to do this is in a Layout, but most already do this.
As Ralph pointed out, Log4j2 has a very rich set of functionality, and if you go over the documentation, you often find that you can achieve your goals using the built-in layouts, lookups, filters and other features.
Writing a custom lookup only takes a few lines of code and gives you a powerful hook into the logging subsystem which you can use for many purposes.
If you really want to extend Logger for whatever reason, be aware that some layouts require Log4j to walk the stacktrace to report which class and line in your code from the logger was called from. This mechanism is a bit complex and in general I would recommend you create a Logger wrapper instead of extending it. (Prefer delegation to subclassing.)
Log4j has a tool to make this easy for you: the Custom Logger Generator tool documented in the Custom Log Levels page. Try this and see if it gives you what you need.

Do I need both SLF4J binding and adapter for log4j?

I am confused about the difference between log4j2-slf4j binding and log4j2-slf4j adapter.
Can someone explain in plain English what the differences are? The links I put up say the adapter "routes" to SLF4J and the binding "allows SLF4J to use Log4j", but I don't really understand what this means. Also, do I need both, or just one of them?
You must use only one. If you put both, you will cause an endless cycle. The following may give you an idea:
Log4j 2 SLF4J Binding
If you have code in your application like this:
// Instance of SLF4J
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(MyClass.class);
And you want to use Log4J 2 (log4j2.xml), then you might need:
Log4j 2 to SLF4J Adapter
If you have code in your application like this:
// Instance of Log4J 2
private static final org.apache.logging.log4j.Logger LOG =
org.apache.logging.log4j.LogManager.getLogger(MyClass.class);
And you want to use SLF4J (e.g. logback.xml), then you might need:

Custom Log Level

In my existing application "org.apache.log4j" API's have been used in java code.
Requirement :
I have to log some statement(say xyz) in log file in any case and should not dependent of log levels.For example : if my log level is error then also xyz should print, if my log level is debug then also xyz should print.
I cannot make log statement of xyz is debug because if i do this, other log statements apart from xyz will also start printing.
For this, I believe, I have to add some custom log level.Please help how to do it and how to set its level ordering so that in any case it should print.
Thanks in advance.
Best Regards
What you could do is create a different Logger for those statements (you are not restricted to use classes names when defining a logger)
// Standard logger
private static Logger log = Logger.getLogger(MyClass.class)
// XYZ logger
private static Logger logXYZ = Logger.getLogger("logs.xyz");
You can access the same logger from several class, you just have to pass the same label.
Then, in the configuration file, you can define a different log level for that category, and even output these logs in a different appender (different file, processing, etc.)
You could "hijack" the protected method Logger#forcedLog() to always print to the log.
You must place the hijacker class in the same package as Logger.
package org.apache.log4j;
/**
* #author maba, 2012-08-23
*/
public class LogOverride {
public static void print(Logger logger, String message) {
logger.forcedLog(logger.getName(), Priority.INFO, message, null);
}
}
And from your calling code
log.setLevel(Level.OFF); // Make sure logging is turned off
log.info("Normal logging"); // Will not be seen anywhere
LogOverride.print(log, "Overriding logger"); // Will still make it to your appender
This is what the log4j FAQ says about custom levels:
How do I add a custom level?
It is possible, but rarely appropriate. The request is commonly for a level named something like "audit" that doesn't obviously fit in the progression "trace", "debug", "info", "warn", "error" and "fatal". In that case, the request for a level is really a request for a mechanism to specify a different audience. The appropriate mechanism is to use a distinct logger name (or tree) for "audit" related messages.
So if you want to go with that suggestion then you should look at the answer from SJuan76.
If you do decide to go with the idea of creating a custom, you would need to create a subclass of Level to do this, because the Level constructor is protected.
/**
* Instantiate a Level object.
*/
protected Level(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
It looks like you should then chain to the Level constructor, passing it a suitable level value. Note that the larger the level number the higher the priority is. So for a Level that won't be blocked at any of the existing named levels, you want a value that is greater than Priority.FATAL_INT which is 50000.
(However, I'm not convinced that this is the right approach. For a start, you probably won't be able to refer to your custom level by name in a logging config file.)

How to find which library slf4j has bound itself to?

I am using slf4j for logging in my application.
I get the purpose of slf4j. I would like to know how to find out which logging-library slf4j is currently binding to. I have log4j in my referenced libraries. I am assuming that slf4j has bound itself to log4j.
What I would like to know is, is there any way to explicitly confirm this binding?
Just do what SLF4J does to discover the binding:
final StaticLoggerBinder binder = StaticLoggerBinder.getSingleton();
Now you can try to find out what is the actual implementation logback in my case:
System.out.println(binder.getLoggerFactory());
System.out.println(binder.getLoggerFactoryClassStr());
This prints:
ch.qos.logback.classic.LoggerContext[default]
ch.qos.logback.classic.selector.DefaultContextSelector
The StaticLoggerBinder's getLoggerFactoryClassStr() method is probably what you're looking for.
Easy. Put a breakpoint on .. say.. LOG.info(...). Once debugger stops there, step into.. and viola.. you will find yourself in the code of the actual logger... say log4j or logback.. whatever.
It's possible to do this using the main slf4j public API (i.e. without the internal StaticLoggerBinder), e.g. to detect if slf4j has bpound to log4j2:
if ("org.apache.logging.slf4j.Log4jLoggerFactory".equals(
org.slf4j.LoggerFactory.getILoggerFactory().getClass().getName()
)
{ ... }
Or, avoiding the need to have StaticLoggerBinder (which is not part of slf4j-api):
log.info(log.getClass().getName());
In my case this prints
ch.qos.logback.classic.Logger

Categories

Resources