Do I need both SLF4J binding and adapter for log4j? - java

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:

Related

Migration to log4j2

I am trying to migrate some code from log4j 1.x to 2.x. Some things are easy to replace, but I cannot find the way to replace this:
Object obj = event.getMDC("xxx"); //event is org.apache.log4j.spi.LoggingEvent type from log4j 1.x.
I know about the method with the bridge but for now I want to skip it and try to do it like this.
MDC can be replace with ThreadContext and I see some people using org.apache.logging.log4j.core.LogEvent but it doesn't contain the same methods as LoggingEvent (examples):
.getRenderMessage()
.getTrowableInformation()
.getMDC();

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

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.

slf4j always use NOPMDCAdapter

I use slf4j for logging in my project. And i want to use MDC for logging user ID parameters. So i check tutorials and doc, and make code like:
MDC.put("key", userId)
userId actually is a string.
And i use my usual log4j xml with properties like appenders etc. Add there %X{key} but nothing happen. I simply don't see anything in place of %X{key} but other parameters like %-5p or %c works well.
So i used debug to watch what happen in MDC.put() method, and find out that in initialization of MDC used:
MDCAdapter mdcAdapter;
mdcAdapter = StaticMDCBinder.SINGLETON.getMDCA();
Debug in IDEA shows that it have "Log4jMDCAdapter" like one of implementation for MDCAdapter.
But then i look at StaticMDCBinder, and there are code like:
public MDCAdapter getMDCA() {
return new NOPMDCAdapter();
}
So how it possible that slf4j can initialize MDC with proper adapter for example log4j ??? I didnt get it. Because it always use NOPMDCAdapter it cant store anything in MDC, and cant show it in logging. How i can fix it??
In classpath i have:
log4j-1.2.16.jar
slf4j-api-1.6.1.jar
slf4j-api-1.6.2.jar
slf4j-jcl-1.6.2.jar
slf4j-log4j12-1.6.2.jar
SLF4J's MDC is just a facade to switch over the applicable implementation of MDC based on the underlying logger. If it doesn't find any appropriate MDCAdapter from the logger it fallsback to NO-OP adapter.
For this it tries to find class named StaticMDCBinder
https://github.com/qos-ch/slf4j/blob/v_1.7.25/slf4j-api/src/main/java/org/slf4j/MDC.java#L99
So in this case as mentioned in #Wizzard's answer if there are two StaticMDCBinder classes in your class path tomcat will pick one of it.
slf4j-simple does have a StaticMDCBinder which defaults to NO-OP adapter so removing
slf4j-simple jar should solve the issue here.
Problem solved.
It was transcendent dependency from other libraries which used in project. Namely slf4j-jcl, i add part into maven pom, and now it works fine.

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

How to enable FINE logging for a single java class

I'm using java.util.logging.Logger logging in my program. How do I enable FINE logging for a single class, while setting it to WARNING for every other class?
I'd prefer to do this programatically in my main() method rather than needing to set up additional properties files.
I know the OP has asked to do this programatically but here's an example of how to do it in the properties file too.
Caveat: I thought it was worthy of inclusion as the header doesn't indicate programatically and many developers will want to manage it through the logging.properties. Also there isn't really a lot on-line about this, it can be confusing and is slightly different to, say log4j
The root logging level is indicated by the .level config. This dictates which events are by default to be captured and "distributed for" logging. The root logging level is the level used by the "root logger" in the logging hierarchy. See this onjava article for more info on the logging hierarchy.
Below, the root log level is set to WARNING so will ordinarily capture only WARNING events. This is inherited by all child loggers in the hierarchy, unless you configure otherwise (later):
.level=WARNING
This root-logging level only indicates what is captured, not what is "distributed". How a captured event (message) is distributed is down to the handlers associated with the logger. For instance, a ConsoleHandler will output the event to the console. For instance:
java.util.logging.ConsoleHandler.level = WARNING
This ConsoleHandler.level indicates the level for which this handler should distribute - or print - the message. So, if a FINE message is received with the above config then this handler will not print it. It will print any messages with a WARNING log level or above though.
Setting to ALL will ensure that the ConsoleHandler will print all messages to the console (an we also need to configure the root level to ensure all are captured):
.level=ALL
java.util.logging.ConsoleHandler.level = ALL
However, this would create a lot of noise which we also don't want. So, to reduce the FINE-level events to those classes we're interested in, we change the logging level of those specific loggers only:
com.level = WARNING
com.mypackage.MyClass1.level = FINE
com.mypackage.MyClass2.level = FINE
com.mypackage.mysubpackage.MyClass3.level = FINE
Note that in the above, I've explicitly set the level for the "com" logger to WARNING.
Logger log = Logger.getLogger(this.getClass().getName()).setLevel(Level.FINE);
If you do not want to have a logger defined for every single class in question but rather want to share loggers between classes, you can alternatively implement your own java.util.logging.Handler that has its own way of filtering for class names using the information provided by LogRecord.getSourceClassName().
I believe that you can set your log level for your Handler and your specific class Logger to FINE, while keeping all the other Loggers for the rest of your code base to WARNING should do the trick. Both the Logger and the Handler need to pass the level filter for a message to be logged.
Well, here's a method I added to my mail class that's actually working. I would still welcome any improvements from others.
private static void setupLogging() {
// To enable FINE logging in a single class, apparently this bewildering
// maze of statements is required.
Logger.getLogger("").setLevel(Level.FINE);
for (Handler handler : Logger.getLogger("").getHandlers()) {
handler.setLevel(Level.FINE);
}
MyOtherClass.logger.setLevel(Level.FINE);
Logger.getLogger("").setLevel(Level.WARNING);
}

Categories

Resources