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.
Related
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.
By the below post i can able to configure log4j to log in N different file.
Creating multiple log files of different content with log4j
log4j: Log output of a specific class to a specific appender
But i question is FOO.java should able to log in 2 different files. Normal debug/infos in general logger and some statistics in different logger.
I use slf4j and log4j.. I can able to change the logging framework if needed.
Normally, loggers are named after the class, but you don't have to do that. You can name the logger something totally different, or use the class name with some prefix or suffix, e.g. for class org.example.Foo:
org.example.Foo <-- Standard logger name
org.example.Foo.stats
stats.org.example.Foo
Foo.stats
stats.Foo
stats.Bar
Using a prefix will allow you to redirect statistics from all sources (classes) to a separate file, in one config entry.
You decide what is appropriate for you.
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?
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.
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.