Incredibly simple Log4j 2.0 example not working - java

This is a stupidly simple example, and yet for some reason it's not working. I must be missing something obvious.
I am trying to make a very simple log4j 2.0 example program. I have added these two jars to the classpath:
log4j-api-2.0-beta8.jar
log4j-core-2.0-beta8.jar
And have done the simplest example possible, using the default configuration:
package testlog;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TestLog {
static Logger logger = LogManager.getLogger(TestLog.class.getName());
public static void main(String[] args) {
logger.trace("Hello World");
System.out.println("Test over");
}
}
But for some reason, all I get is 'Test over', I never get the Hello World, anywhere I can find anyway. Am I looking in the wrong place? It;s my understanding that with the default configuration it should be printed to the console, with the 'Test Over'. I have changed the log level to info, still the same. I have tried pulling out the Logger functionality into a class, still the same. I am following this tutorial on the log4j documentation page:
http://logging.apache.org/log4j/2.x/manual/configuration.html#AutomaticConfiguration
I can't understand what could possibly be wrong. Could anyone shed any light on what I've done wrong? Thanks in advance.
Update The logger DOES work with logger.error(). This would mean it is a problem with the default filters/level no?

You missed that line in the documentation:
Note that by default Log4j assigns the root logger to Level.ERROR.
Do a
logger.error("Hello World");
and it will be displayed.
If you want to display info and/or trace levels, you have to configure your logger.

Related

Static Context Configuration for Logback Appenders

I've found a few examples (even on Stack Overflow) of some programmatic configuration of Logback logging appenders, but as much as I've incorporated into my own setup hasn't worked for me so far. Some examples produce an actual Logger instance, but considering I've already got a Logger being statically instantiated within my class, I want to be able to programmatically enable an Appender that I've defined for unit testing purposes.
Here is my custom appender:
package org.example.logging;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import java.util.ArrayList;
import java.util.List;
// Credit to https://stackoverflow.com/a/29077499/5476186
public class TestAppender extends AppenderBase<ILoggingEvent> {
private static List<ILoggingEvent> events = new ArrayList<>();
#Override
protected void append(ILoggingEvent e) {
events.add(e);
}
public static List<ILoggingEvent> events() {
return List.copyOf(events);
}
public static void clear() {
events.clear();
}
}
And in my testing code, I'm trying to configure my TestAppender to "kick in" so that, after invoking this method in my test setup, I can capture the logs and validate them:
package org.example.logging;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
// ...
// Mostly modeled after https://stackoverflow.com/a/7825548/5476186
private static void startAppender() {
LoggerContext logCtx = (LoggerContext) LoggerFactory.getILoggerFactory();
TestAppender appender = new TestAppender();
appender.setContext(logCtx);
appender.setName("TEST");
// I was hoping this would statically allow the appender to kick in,
// but all of the examples then attach this appender to a Logger instance.
appender.start();
}
Obviously, this isn't working for me. So I guess I have two contingent questions.
Is this possible and, if so, how can I make it work?
If this is not possible, what's the cleanest way to accomplish what I'm trying to do? (Enable/disable appenders during testing without having to manually mess with a config file.)
In one of the threads linked above, I found this answer which looks like one possible solution is to modify the text in the configuration file and to force a reload, but that doesn't seem super clean to me. Another option would be to create my own wrapper Logger factory which I could use to provide loggers with my TestAppender during test execution with dependency injection. I'll probably be creating a wrapper anyway, even though I'm using SLF4J.
Side note: I know that my test code as currently written is pretty tightly coupled with Logback instead of SLF4J, so I'm open to criticism/advice on that issue, too.
If you're using slf4j in your production code, then there is already a project that can help in testing: Its called slf4j-test
In a nutshell, it provides an API to retrieve a "test logger" in the test that will keep all the logged messages in memory so that you'll be able to verify them.
So that you:
Execute a method that logs something
Retrieve a test logger
call getLoggingEvents() on the test logger and verify the logged events
The link that I've provided contains an example of the API as well as maven integration example.
If, alternatively you would like to use logback directly for the tests or something, there is already a ListAppender shipped as a part of logback distribution that allows retrieval of events that have passed through the appender. You can add it programmatically to the logger and use inside the test.
Here you can find a comprehensive example of doing that

Cannot resolve symbol 'logger' on DuplicateKeyException

In short: the object "logger" is not recognized unlike in many tutorials.
The problem on its own is not very serious and I can easily go around it. However it is very frustrating to see this "logger" stays in red in my intellIj editor. I am going through docs and blogs and I don't see what the problem is.
My snippet:
#Override
public void insertTicketStatut(TicketStatut pTicketStatut) {
String vSQL = "INSERT INTO statut {id, libelle} VALUES {:id, :libelle}";
BeanPropertySqlParameterSource vParams = new BeanPropertySqlParameterSource(pTicketStatut);
NamedParameterJdbcTemplate vJdbcTemplate = new NamedParameterJdbcTemplate(getDataSource());
try {
vJdbcTemplate.update(vSQL, vParams);
} catch (DuplicateKeyException pE){
logger.error("Le TicketStatut existe déjà ! id="+ pTicketStatut.getId(),pE);
}
}
Hovering over logger shows "Cannot resolve symbol 'logger'
Thanks for your help.
Recommend using sl4j
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Then instantiate:
//generic way to declare logger to be able to copy/paste to other classes
//without changing the class name
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
In dependency, include the binding for sl4j implementation ( can be log4j ).
Can refer here:
logging-with-slf4j
The logger needs to be either instantiated or better injected. It seems that you did not do that. When you use some kind of logging framework like log4j the initialisation would look like this:
static Logger logger = Logger.getLogger(MainApp.class.getName());
When you show us the whole class we can see more details and can guess better.
An tutorial for log4j and spring can be found here.
Indeed, I didn't managed properly my imports:
I added:
private final Log logger = LogFactory.getLog(TicketDaoImpl.class);
from
org.apache.commons.logging.Log;
Stupid lack of attention.
I am having a look also at the doc from log4j/ sl4j on Baeldung. Thanks ^^

SLF4J in distributable library, how to obtain Logger

I want to distribute a lib relying on the SLF4J logger interface. What is the best practice way to obtain the logger which integrate nicely into any other project? Sorry for the unstructured question style, I'm still trying to figure out how all this stuff is glued together.
In other projects I always use this piece of code, because I want to obtain a custom logger:
private final static Logger LOGGER = LoggerFactory.getILoggerFactory().getLogger(NAME_OF_APP);
If I create the class org.slf4j.impl.StaticLoggerBinder and have it some other lib, does the therein defined factory get used even if I just call LoggerFactory.getLogger(NAME_OF_APP) or is some default slf4j factory used?
I want the user to be able to use his own factory and logger, so which way is to perfere, and most of all why?
I'm not sure I fully understand what you are trying to do.
SLF4J is composed of two parts. First the API which you use in your lib to code your logging calls. And secondly the implementation which you use during your development to do logging, but DO NOT set as a dependency of the lib.
Because SLF4J looks for the implementations on the class path the developers using our lib can simple include any implementation they want. Sometimes is some quite strange ways :-) They can use a range of prebuilt implementations or code their own. It's up to them.
I don't think you need to do anything more than just use SLF4J's API as is.
From http://slf4j.org/manual.html
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
So, always use LoggerFactory.getLogger(...). The argument allow the logger backend to determine the behavior of the logger returned to you.
I am not sure I fully understand what your scenario is.
But from my viewpoint, what you want is a distributed logging component.
A simple approach to do that is a socket appender. And a full feature component for distributed logging may like Facebook's scribe.
Use Logger static wrapper from jcabi-log:
import com.jcabi.log.Logger;
public class MyLibraryClass {
public void foo() {
Logger.info(this, "some information");
}
}
All logs will be sent through SLF4J.

Filter unwanted INFO-Messages from Logger

I'm using java.util.logging to log in my Java application. I'm also using javax.xml.ws.Endpoint to publish a SOAP-interface.
Over the time I added more and more exceptions which all turn up at startup with a log-entry like this:
Jan 24, 2011 12:29:27 PM com.sun.xml.internal.ws.model.RuntimeModeler getExceptionBeanClass
INFO: Dynamically creating exception bean Class de.wi08e.myhome.frontend.jaxws.NotLoggedInBean
I tried following filter to block them, but I'm not sure which class to get with getLogger:
/* Filter ExceptionBeanClass logs */
Logger loggerInfo = Logger.getLogger("javax.xml.ws.Endpoint");
loggerInfo.setFilter(new Filter() {
#Override
public boolean isLoggable(LogRecord l) {
System.out.println(l.getMessage());
if (l.getMessage().startsWith("Dynamically creating exception bean Class"))
return false;
return true;
}
});
Does anyone know how to find out which class creates this log-entries? Is there another way to filter out this nerving messages?
EDIT: I also tried Logger.getLogger("com.sun.xml.internal.ws.model.RuntimeModeler"), but it's still not working...
You should be able to just run the java program with the following flag: -Djava.util.logging.config.file=<mylogging.properties> where mylogging.properties is a file with the following contents instead of doing it in code.
javax.enterprise.resource.webservices.jaxws.server.level = WARN
From http://www.docjar.com/html/api/com/sun/xml/internal/ws/model/RuntimeModeler.java.html
186 private static final Logger logger =
187 Logger.getLogger(
188 com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server");
and from Constants
public static final java.lang.String LoggingDomain = "javax.enterprise.resource.webservices.jaxws";
Edit: Clement P answered this question right long before me; all thumbs-up should go to him!
I keep my answer here because it uses a slightly other way.
I've now solved it myself. In short, this is the solution:
Logger.getLogger("javax.enterprise.resource.webservices.jaxws.server").setLevel(Level.WARNING);
If anyone's interested, this is how I found it:
The method getExceptionBeanClass in com.sun.xml.internal.ws.model.RuntimeModeler is responsible for the messages. It uses a static Logger instanciated with the function
Logger.getLogger(com.sun.xml.ws.util.Constants.LoggingDomain + ".server");
Google-ing for com.sun.xml.ws.util.Constants leads here where you can copy-and-past the LoggingDomain constant.
But keep in mind: This solution won't work on every JRE, because it depends on Sun's 'private' namespace and implementation.
The log entry seems to indicate that it's the com.sun.xml.internal.ws.model.RuntimeModeler class which generates this log message. You should be able to filter it out by raisong the level for this logger.
Alternatively, you could just create the missing class.
Actually, that's the standard procedure that I follow when creating webservices.
If you create this class (i.e. de.wi08e.myhome.frontend.jaxws.NotLoggedInBean),
then it will just use the existing class, without trying to create it at runtime.
Actually, you don't even have to create these classes yourself. You can generate them using the wsgen tool. The wsgen.exe is part of the java jdk. So, you can just run this from the commandline.
wsgen.exe -verbose -keep -cp . de.wi08e.myhome.frontend.WebService -d source
Next move the created files to the correct project source folder.

Get LogLevel for current class/bean

I am using the following approach when logging from my class:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
...
private static Log log = LogFactory.getLog(MyClass.class);
...
log.debug("...");
It has come to my attention that all log statements are always executed no matter what loglevel is applied. I do not want to have debug-related statements executed when I do not need it (performance is an issue here).
So I am looking for something like this:
if (LogLevel == debug) {
log.debug("...");
...
}
How can I obtain the current LogLevel being used for that class?
Each Log instance in the commons logging package can tell you if the specific level is enabled. Use this code:
if ( log.isDebugEnabled() )
{
// Debug log statements
}
Check the Commons Logging JavaDoc for more information.
Use slf4j as the logging facade for your logging framework, since it solves the problem you mentioned by using parametrized logging for improved performance.
If you want an even faster logging framework, use Logback.

Categories

Resources