Use commons java logging in spring mvc by default - java

I want use the default log system that comes with spring mvc, I have this code.
protected final Log LOGGER = LogFactory.getLog(getClass());
#RequestMapping("/editor")
public String init() {
LOGGER.debug("[EditorController.init] - Inicio");
return TILES + "editor";
}
When you goes to /editor in my webapp, I dont see logged info in any output. I have a commons-loggin.properties in my resources folder (/src/resources) and I have this propertie setted (I tried without any config too)
handlers=java.util.logging.ConsoleHandler
# default log level
.level=INFO
All info that I see on the net, change the method to SLF4J or L4J, I don't want use any new library, my Log use will be very simple, I think that commons-logging can works great.
Any help?

Related

capture logs in a test

I'm trying to capture bean allocation logs in a test - I've got code that I've tested, and will successfully capture logs from my classes - but when I try to do it on spring classes it is seemingly not working - here is the code I use to try and capture:
LoggerContext context = (LoggerContext) (LoggerFactory.getILoggerFactory());
Logger log = context.getLogger("org.springframework.beans.factory.support.DefaultListableBeanFactory");
log.setLevel(Level.DEBUG);
MyAppender appender = new MyAppender();
appender.setContext( context);
log.addAppender( appender );
SpringApplication newApplication = new SpringApplication( Application.class);
newApplication.run( new String [] {});
Now if I trace in and look at the logger that spring is using - it looks like a completely different style of logger - (its hooked to a logmanager, not a loggercontext) - and go into that and it seems like it might be a different context?
Any idea what I'm doing wrong, and how I can in a unit test capture spring bean creation logs?
Spring boot is using Logback logger by default
It uses LogbackLoggingSystem implementation which
extends from AbstractLoggingSystem
Spring boot LoggingSystem runs before context is initialized
To override default properties you can define logback.xml or logback-spring.xml
Or you can use application.yml or properties file to define log configurations :
logging.level.* : It is used as prefix with package name to set log level.
logging.file : It configures a log file name to log message in file. We can also configure file name with absolute path.
logging.path : It only configures path for log file. Spring boot creates a log file with name spring.log
logging.pattern.console : It defines logging pattern in console.
logging.pattern.file: It defines logging pattern in file.
logging.pattern.level: It defines the format to render log level. Default is %5p.
As documentation says:
You can force Spring Boot to use a particular logging system by using the org.springframework.boot.logging.LoggingSystem system property. The value should be the fully qualified class name of a LoggingSystem implementation. You can also disable Spring Boot’s logging configuration entirely by using a value of none.
If you use static Loggers in your Class under Test, you could use Powermock to mock the logger and assert the output, as descirbed in this question.
We use it in our Spring-Tests and formatting and style is the same.
for anyone interested - this finally worked for me:
Logger logger = Logger.getLogger(
"org.springframework.beans.factory.support.DefaultListableBeanFactory");
logger.addHandler( this );
logger.setLevel( java.util.logging.Level.FINE);
_logger = logger;
now I can capture, trace and time all bean allocations.

Java using multiple logger types in the same file skipping log statements

There is an maven OSGI component in which I have a call to EclipseStarter.stop() statement which shuts down the OSGI framework. There are a couple of log statements printed after this line. I have used pax-logging with log4j2 as the logging backend and JCL at the frontend.
Since OSGI framework is down the log messages do not print through pax-logging-log4j2. Therefore I am trying to use java util logging to print these messages. The logger initialization in the java file is done as follows.
private static final Log log = LogFactory.getLog(MyClass.class);
private static final Logger logger = Logger.getLogger(MyClass.class.getName());
log.info("Before shutdown");
EclipseStarter.stop();
logger.log(Level.INFO, "After shutdown JUL");
log.info("After shutdown JCL");
The util logging statement seems to be skipping when I debugged.
In order to see output from a logger you have to attach a Handler subclass. You can add them from code or you can create a logging.properties.

Changing level of logging dynamically

I want to change the level of logging for all (about) of my web application controller classes dynamically. Each class that does logging contains this code:
private static final Logger logger = LoggerFactory
.getLogger(HomeController.class);
I learned that slf4j doesn't offer the functionality of setting a log level, so that one has to use the underlying log4j:
public static void setLogLevel(String level) {
org.apache.log4j.Logger logger4j = org.apache.log4j.Logger.getRootLogger();
logger4j.setLevel(org.apache.log4j.Level.toLevel(level.toUpperCase()));
logger.info("Sample Info After setLevel");
logger.debug("Sample Debug After setLevel");
logger.error("Sample Error After setLevel");
logger.trace("Sample Trace After setLevel");
logger.warn("Sample Warn After setLevel");
}
My idea is to have a controller method which changes the logLevel and maybe stores it in the database. My question is: How I can solve this elegantly without copy and pasting everything in 40+ files and modifying every controller method? Also note that the logger code is static, while my database access is not static.
How I can solve this elegantly without copy and pasting everything in 40+ files and modifying every controller method?
You use a single controller method, taking the logger name and desired log level as a query parameters, and call the string version of LogManager.getLogger(String name).
Also saving to the database and re-loading on restart is of course extra work to be done by you.
Alternatively, the controller can update the Log4j configuration file used at startup, and force Log4j to reload it whenever it's changed, e.g. by calling PropertyConfigurator.configure(String configFilename).
You can use Slf4j and externalize your application.properties file . You can configure the logging level in this file as per your needs.
logging.level.com.test=DEBUG
logging.level.org=INFO
I am not sure why you need to store this logging level in the Database.

How to write a test case to verify log4j logs are generated in proper format

I want to write a testng test case to verify that the logs (log4j) generated are following exactly the given conversion pattern from the log4j.properties file. As an example if I have conversion pattern : [%d] %-5p [%t]: %m%n a sample log would looks like [2015-07-07 16:42:09,937] DEBUG [main]: Message 1 I want to make sure that the log follows the exact pattern.
So for now what I'm doing is first read all the logs in to a String array and loop that array to find whether the expected log without the date i.e. DEBUG [main]: Message 1 contains in the recorded.
Is this way of testing log records is correct ?
If you have any idea about a good way to test logs rather than this please point out.
There are any solutions. The first in my mind is:
Configure your log4j to write on System.out and use the SystemOutRule.
public void MyTest {
#Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
#Test
public void writesTextToSystemOut() {
System.out.print("hello world");
assertEquals("hello world", systemOutRule.getLog());
}
}
See: https://stefanbirkner.github.io/system-rules/index.html
This solution is logger implementation independent if the logger writes to System.out and/or System.err.
More log4J aware:
Use a mock framework - #slartidan says - and mock the appender implementation.
Based on your preferred mock framework and log4J 1.x or log4J 2.x the solution is quite different.
Log4J 1.2
Create a mock logger appender and add this appender on logger:
Logger.getLogger("...").addAppender( mockedAppender );
See: https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Category.html#addAppender%28org.apache.log4j.Appender%29
Log4J 2.x
Extending ConfigurationFactory and Configuration (only in test artefact) and add the mocked appender.
The easiest way to create a custom Configuration is to extend one of the standard Configuration classes (XMLConfiguration, JSONConfiguration) and then create a new ConfigurationFactory for the extended class. After the standard configuration completes the custom configuration can be added to it.
http://logging.apache.org/log4j/2.x/manual/customconfig.html (includes complete example)

Logback defaulting to debug mode for some third party libraries

We use slf4j and logback for logging in our java ee web application. Recently in order to move these libraries to a common location in the glassfish app server (to glassfish\lib directory specifically) we made a couple of configuration changes. We made changes in the web.xml file to add JNDI entries and renamed the logback.xml to logback-<context-name>.xml as specified in here. The logback-<context-name>.xml is placed in WEB-INF\classes directory.
After this change, logging for hibernate and http-client libraries is defaulting to debug mode and hence there is lot of logging being done. The root logger has INFO log level and there are no specific loggers defined for hibernate & http-client libraries.
If I revert this change i.e. remove the jndi entry in web.xml and rename the configuration file to logback.xml, only INFO logs are logged as expected.
Any suggestions on what could be the issue?
Thanks.
Update
On troubleshooting further I released that hibernate and apache client libraries use the default logger context created during the initialization time. This logger context has the root log level set to DEBUG.
We use JNDI as the context selector.
It is strange that the context name is not found at runtime when the ConnectionManager class (hibernate class - org.hibernate.jdbc.ConnectionManager) tries to instantiate a logger. Logger instance creation calls ContextJNDISelector.getLoggerContext() method. This method does a JNDI lookup but does not find the entry.
I troubleshooted further by spawning a thread in a servlet context listener to print the JNDI entry (java:comp/env/logback/context-name) every 3 seconds. The logs from the thread indicate that the JNDI entries are always present.
Any ideas why is contextName is not found in JNDI when queried by ContextJNDISelector?
This seemed to be a problem because the application used EJB's. The application server (glassfish) loaded the EJB's before the logging context was actually set. Hence the log messages for some of the libraries got logged under DEBUG level. Using a wrapper class around the logger resolved the issue. The wrapper class delayed the logger creation until its first use.
public class LogWrapper {
private Class loggerClass;
private Logger logger;
public LogWrapper(Class loggerClass) {
this.loggerClass = loggerClass; //lazy logging context creation (to avoid issues with static instances in EJBs which get loaded on startup before logging ctx name is actually set)
}
/**
* For lazy init of logger, on first actual use, so the logger context will be correctly set even when used by EJBs (loaded by classloader too soon, before logging context is actually set)
*/
private Logger getLogger() {
if (logger == null) {
logger = LoggerFactory.getLogger(loggerClass);
loggerClass = null;
}
return logger;
}
public void info(String message) {
getLogger().info(message);
}

Categories