How to use custom logger with Logback and SLF4J - java

I have existing project with very standart Logback implementation.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class SomeClass{
Logger log = LoggerFactory.getLogger(getClass());
void someMethod(){
log.info("some log message");
}
}
This is used in many places and in code I dont have sources. ch.qos.logback.classic.Logger is used as implementation.
I want to use my own implementation of Logger (I wanted to extend ch.qos.logback.classic.Logger but it's final) and use it applicatio wide without modifying the code (which I don't have anyway).
I would expect that configuration of LoggerFactory in logback.xml would be possible, but it doesn't seem so.
I'm using maven artifacts
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
I could not find straight way of doing it. I came over XLogger and logback-ext but it seems I would need to modify all factory usages.

Related

Lombok not working in springboot application

I want to bean or inject logger so that I don't end up creating an object of logger in each and every class.
So I am trying to integrate Lombok which would help me to resolve lot of things including logger.
Here is my code:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#SpringBootApplication
public class MyApplication {
private static final Logger logger = LoggerFactory.getLogger(MyApplication.class);
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
log.debug("Hello");
logger.info("Info log", MyApplication.class.getSimpleName());
}
}
But this is giving me error when I try to use log directly
IDE error -
log cannot be resolved
Console runtime error -
Exception in thread "main" java.lang.NullPointerException: Cannot
invoke "org.slf4j.Logger.info(String, Object)" because
"com.MyApplication .log" is null at
com.MyApplication.main(MyApplication .java:27)
Pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.4.5</version>
</dependency>
I have also installed lomback to my Eclipse IDE
Looks like you didn't install the Lombok plugin in your IDE.
Take a look at https://projectlombok.org/setup/overview under IDEs. There you can find instructions on how to install the plugin on most common used IDEs.
Also try to remove the logger from your code. This line:
private static final Logger logger = LoggerFactory.getLogger(MyApplication.class);
Because this is what lombok is adding at compile time. And you don't need the logging dependency because it's already part of spring-boot

resilience4j-spring-boot-2 annotations (#Retry, #CircuitBreaker...) are completely ignored

I spent a whole day trying to find why this does not work so I think it might be useful if I share the question and the answer.
The Resilience4j library provides an elegant annotation-based solution from Spring Boot 2. All you need to do is just annotate a method (or a class) with one of the provided annotations, such as #CircuitBreaker, #Retry, #RateLimiter, #Bulkhead, #Thread and the appropriate resilience pattern is automagically added.
I added the expected dependency to the Maven pom.xml:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>${resilience4j.version}</version>
</dependency>
Now the compiler is happy, so I can add the annotations:
...
import org.springframework.stereotype.Service;
import io.github.resilience4j.retry.annotation.Retry;
...
#Service
public class MyService {
...
#Retry(name = "get-response")
public MyResponse getResponse(MyRequest request) {
...
}
}
The program compiles, runs, however the annotations are completely ignored.
According to the resilience4j-spring-boot2 documentation:
The module expects that spring-boot-starter-actuator and spring-boot-starter-aop are already provided at runtime.
So the whole trick is to add also the missing dependencies to the Maven pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Control myBatis logs destination file and level

I'm working on a spring-based application which has to communicate with a SQL database through mybatis: all right but the logs destination.
For some reason mybatis logs to the wrong file, could you help me to figure out why? Here's my configuration:
log4j.properties:
### Appenders
# Console appender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=WARN
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# Application file appender
log4j.appender.main=org.apache.log4j.RollingFileAppender
log4j.appender.main.File=logs/app.log
log4j.appender.main.layout=org.apache.log4j.PatternLayout
log4j.appender.main.MaxFileSize=10MB
log4j.appender.main.MaxBackupIndex=15
# Libs file appender
log4j.appender.libs=org.apache.log4j.RollingFileAppender
log4j.appender.libs.File=logs/app_libs.log
log4j.appender.libs.layout=org.apache.log4j.PatternLayout
log4j.appender.libs.MaxFileSize=10MB
log4j.appender.libs.MaxBackupIndex=15
### Loggers & additivity
# Application
log4j.additivity.our.company.basepackage=false
log4j.logger.our.company.basepackage=TRACE,main,console
# Root logger
log4j.rootLogger=INFO,libs
pom.xml snippet
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>runtime</scope>
</dependency>
I find TRACE-level rows of mybatis ("org.apache.ibatis.logging.jdbc.BaseJdbcLogger.trace(BaseJdbcLogger.java:145)") in the file "app.log".
I excluded commons-logging from spring-core, and with a dependency tree I don't see commons-logging. Why isn't mybatis logging to the file "app_libs.log"? Why does mybatis not respect the specified level?
Thank you.
Edit 1
The code with which the database gets queried has been generated with mybatis-generator, and the generated code lives somewhere under the package "our.company.basepackage".
Since the question was posted, I didn't stop to think about this, until now: I found the reason of that behaviour.
The decisive suggestion is that "the code has been generated with mybatis-generator", and it has been generated in the same subpackage of the application: this means that the *Mapper classes, used for querying the databaes, effectively are in the application package and so their logs are treated as logs of "our.company.basepackage" and not as logs of "org.apache.ibatis".
The "org.apache.ibatis" in logs rows was misleading me.
After this small insight, I added the following to my log4j.properties:
log4j.additivity.our.company.basepackage.persistence.mybatis=false
log4j.logger.our.company.basepackage.persistence.mybatis=INFO,libs
With these 2 more lines, everything works properly, i.e. no more "org.apache.ibatis" rows in app.log.
I hope that this can be useful to someone other using mybatis-generator.

Can't disable quartz-scheduler logging using SLF4J

After adding quartz-scheduler to a project, Tomcat's server log is being spammed with the following message:
[INFO] [talledLocalContainer] 12:15:06.319
[DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG
o.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers
I'm trying to disable that log message, which repeats every 25-seconds or so. I've been through a number of other answers to this same question, such as:
Disable quartz logging
stop quartz debug logging log4j
Disabling Log4J Output in Java
...and none of the approaches suggested are working.
I have the following dependencies declared in pom.xml:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
And I've added the following log4j.properties settings to my project:
log4j.rootLogger=OFF
log4j.logger.quartz=OFF
log4j.logger.o.quartz=OFF
log4j.logger.org.quartz=OFF
...and also the following simplelogger.properties:
org.slf4j.simpleLogger.defaultLogLevel=error
In addition to attempting the programmatic solution suggested on one of the linked answers, which should disable all logging and goes roughly like:
List<Logger> loggers = Collections.<Logger>list(LogManager.getCurrentLoggers());
loggers.add(LogManager.getRootLogger());
for ( Logger logger : loggers ) {
logger.setLevel(Level.OFF);
}
That seems to disable everything except the log message from quartz when it runs.
Is there a way to get rid of the log message from quartz, short of modifying the quartz source-code to remove it?
The logging entry above doesn't look like Log4J. I think thats ACL or JUL. When you are using Slf4j to Log4J, you also need to redirect these frameworks to Slf4j. Add these dependencies to your project as well:
<!--Redirect Apache Commons Logging to Slf4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.5</version>
</dependency>
<!--Redirect Java Util Logging to Slf4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.5</version>
</dependency>
Read this doc page about redirecting Jul to Slf4J, too. Be sure to exclude any existing commons-logging.jar files from your project, through Maven.
(From the Slf4J documentation page)
And if all else fails, try the following code:
//see if we can find the offending logger through slf4j's LoggerFactory
org.slf4j.Logger logger =
LoggerFactory.getLogger(Class.forName("org.quartz.core.QuartzSchedulerThread"));
if (logger != null && logger instanceof ch.qos.logback.classic.Logger) {
//the slf4j Logger interface doesn't expose any configuration API's, but
//we can cast to a class that does; so cast it and disable the logger
((ch.qos.logback.classic.Logger)logger).setLevel(
ch.qos.logback.classic.Level.OFF);
}

How to make OpenEJB to use slf4j?

Can anyone give an example of pom.xml dependencies configuration that will make OpenEJB to use slf4j logging, instead of JCL (this is what it uses now, as I understand).
see also How to configure OpenEJB logging?
We have support for:
Log4j
java.uti.logging
New implementations of LogStreamFactory can be plugged in by setting the class name as the value of openejb.log.factory system property.
Feel free to copy one of the existing impls and update for either JCL or SLF4J. The project is always accepting new committers if you get the urge to hack on other things as well!
Thanks to David's pointers, this is actually quite easy. You only need to implement two clases:
- Slf4jLogStreamFactory
- Slf4jLogStream
then set your factory :
System.setProperty("openejb.log.factory", "de.glauche.Slf4jLogStreamFactory");
package de.glauche;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.LogStream;
import org.apache.openejb.util.LogStreamFactory;
public class Slf4jLogStreamFactory implements LogStreamFactory {
#Override
public LogStream createLogStream(LogCategory logCategory) {
return new Slf4jLogStream(logCategory);
}
}
and the Slf4jLogStream:
package de.glauche;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.LogStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jLogStream implements LogStream {
private Logger log;
public Slf4jLogStream(LogCategory logCategory) {
log = LoggerFactory.getLogger(logCategory.getName());
}
#Override
public boolean isFatalEnabled() {
return log.isErrorEnabled();
}
#Override
public void fatal(String message) {
log.error(message);
}
#Override
public void fatal(String message, Throwable t) {
log.error(message,t);
}
... (overwrite the remaining methods like this)
With this i'm getting all output nicely formated in my logback logger via slf4j :)
This is how I made OpenEJB to use external logging:
[...]
#Before
public void before() throws Exception {
System.setProperty("openejb.logger.external", "true");
InitialContext ctx = new InitialContext();
}
[...]
Maybe it's possible to move this system property to some global resource, like pom.xml?
If you add the API and JCL dependencies then your logging using the SLF4J API will get directed to the default JCL logs.
Is that what you want? Or do you want to use some other back end for the logging?
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
Not a Maven expert, but from my understanding, you want this:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.6.1</version>
</dependency>
If OpenEJB doesn't support JCL, it would be pointless to add that dependency, so go with JUL or Log4j.
You can find more details at Maven repository.
Just in case you really want to use JCL bridge, use this:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.6.1</version>
</dependency>
If you are executing via Maven's exec plugin, try this:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>your Mainclass here</mainClass>
<systemProperties>
<systemProperty>
<key>openejb.log.factory</key>
<value>slf4j</value>
</systemProperty>
</systemProperties>
</configuration>
</plugin>

Categories