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.
Related
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?
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)
Im using Log4j and i have the following problem: is there a way to add one more logger from your code ? (not root logger).
In my config file, i set up the following two loggers :
log4j.rootCategory=INFO, ALogFile
log4j.logger.BLog=INFO,BLog
I would like to remove second line from config file and add BLog from code. Is that possible?
The equivalent code should be:
Logger logger = Logger.getLogger("BLog");
logger.setLevel(Level.INFO);
logger.addAppender(Logger.getRootLogger().getAppender("BLog")); // see notes below
I.e. you obtain a Logger object for the name BLog, you then set the level to INFO and attach the appender you've presumable defined elsewhere in your config file, called BLog.
Note: based on the answers from this related question, you may need to attach the appender to a logger in order to be able to reference it, as my code does above. Or just define the appender in your code.
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);
}
I use a property file for java.util.logging and want to log all classes under package:
aaa.bbb.ccc.*
the normal way (i.e. info, fine, finer) but class
aaa.bbb.ccc.ddd.MyClass
in its own logging file "My Class.log" with level finer.
The config should be done only via a properties file. How would this look like?
I tried various ways (e.g. different handlers) but not any succeeded: It never worked that both log files are written to.
To make the problem more concrete - the config I tried:
handler.performance.class=com.logging.handler.FileHandler
handler.performance.file=${LOGGING_ROOT}/performance.log
handler.performance.level=FINE
handler.fine.class=com.logging.handler.FileHandler
handler.fine.file=${LOGGING_ROOT}/finer.log
handler.fine.level=FINE
handler.async.class=com.logging.handler.AsyncBufferHandler
handler.async.level=ALL
handler.async.targets=fine
handler.asyncperf.class=com.logging.handler.AsyncBufferHandler
handler.asyncperf.level=ALL
handler.asyncperf.targets=performance
com.myapp.handlers=async,console
com.myapp.useParentHandlers=false
com.myapp.common.logging.handlers=asyncperf
com.myapp.common.logging.useParentHandlers=false
The class I want to log to this separate performance log is located beneath com.myapp.common.logging...
Found the solution - it was a wrong initialization:
The logger should be initialized with:
Logger.getLogger(MyClass.class.getName())
Then the config:
com.myapp.common.logging.MyClass.handlers=asyncperf
com.myapp.common.logging.MyClass.useParentHandlers=false
logs all logging messages of this class in the specified separate file as desired!
Define two File appenders for the two target files
Define one root logger to use the first appender
Define a second logger for the special class, to use the other appender
set additivity of the logger to false in order to make any message go to one but not both files
I think that you have problem because you can't configure 2 default FileHandlers, only one of them. So try to implement your personal subclass of FileHandler and configure it as a separate handler.
I don't remember if we can configure logger for separate class or only for package, so try also configure handlers to package of MyClass.