Get SLF4J loggers for given package - spring-boot

For my current application, I am attempting to get the loggers for the given package. However, after multiple attempts still I am unable to do that. I will add the sample code block for what I've been trying so far.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Logger logger;
private TestUtils(String thePackageOrLoggerName) {
ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
this.logger = iLoggerFactory.getLogger(thePackageOrLoggerName);
}
However, the logger I get from the above code does not work. It has the name attribute with the package I've given but it doesn't contain any loggers within the package. In the above example I am only based on SLF4J and does not relay on Log4j2 or Logback because I want this implementation to be independent of underlying logging framework.
However, I could accomplish what I want to do in my previous attempt but I did use logback-core library. I will also add the code block for that as well for a better understanding of what I am going to do.
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
private final Logger logger;
private testUtils(String thePackageOrLoggerName) {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
this.logger = context.getLogger(thePackageOrLoggerName);
}
With the above implementation, my logger contains all the logger instances for the given package. I want to accomplish the same behaviour without using the logback-core and only using SLF4J related libraries. Can anyone give me insights to proceed further and it is much appreciated.

Try using lombok instead and add #Slf4j above the class.
Now you can do like:
LOGGER.info("Text");
LOGGER.getName();
LOGGER...{etc}
Will make things super easy. You just need to add #Slf4j above any class you want a logger in. And it will do the rest for you.
If you only wanted a single logger. You can make a single logger class this way. And just create methods to call the logger inside the class
#Slf4j
#NoArgsConstructor
#Component
public class TestLogger() {
public void logWarning(String warn){
LOGGER.warn(warn);
}
public void logError(String error){
LOGGER.error(error);
}
}
Then just Autowire this class into each area you need it.
All code is untested and was written in the text field provided here.
https://mvnrepository.com/artifact/org.projectlombok/lombok

Related

Spring Boot #Value Properties

I have a Spring Boot application and in one of the classes, I try to reference a property from the application.properties file using #Value. But, the property does not get resolved. I have looked at similar posts and tried following the suggestions, but that didn't help. The class is:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class PrintProperty {
#Value("${file.directory}")
private String fileDirectory;
public void print() {
System.out.println(fileDirectory);
}
}
I have the property file.directory in application.properties. I have other fields as well.
I had the same problem like you. Here's my error code.
#Component
public class GetExprsAndEnvId {
#Value("hello")
private String Mysecret;
public GetExprsAndEnvId() {
System.out.println("construct");
}
public void print(){
System.out.println(this.Mysecret);
}
public String getMysecret() {
return Mysecret;
}
public void setMysecret(String mysecret) {
Mysecret = mysecret;
}
}
This is no problem like this, but
we need to use it like this:
#Autowired
private GetExprsAndEnvId getExprsAndEnvId;
not like this:
getExprsAndEnvId = new GetExprsAndEnvId();
Here, the field annotated with #Value is null because Spring doesn't know about the copy of GetExprsAndEnvId that is created with new and didn't know to how to inject values in it.
Make sure your application.properties file is under src/main/resources/application.properties. Is one way to go. Then add #PostConstruct as follows
Sample Application.properties
file.directory = somePlaceOverHere
Sample Java Class
#ComponentScan
public class PrintProperty {
#Value("${file.directory}")
private String fileDirectory;
#PostConstruct
public void print() {
System.out.println(fileDirectory);
}
}
Code above will print out "somePlaceOverhere"
I´d like to mention, that I used spring boot version 1.4.0 and since this version you can only write:
#Component
public class MongoConnection {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private int mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
}
Then inject class whenever you want.
EDIT:
From nowadays I would use #ConfigurationProperties because you are able to inject property values in your POJOs. Keep hierarchical sort above your properties. Moreover, you can put validations above POJOs attributes and so on. Take a look at the link
To read the values from application.properties we need to just annotate our main class with #SpringBootApplication and the class where you are reading with #Component or variety of it. Below is the sample where I have read the values from application.properties and it is working fine when web service is invoked. If you deploy the same code as is and try to access from http://localhost:8080/hello you will get the value you have stored in application.properties for the key message.
package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
public class DemoApplication {
#Value("${message}")
private String message;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping("/hello")
String home() {
return message;
}
}
Try and let me know
You haven't included package declarations in the OP but it is possible that neither #SpringBootApplication nor #ComponentScan are scanning for your #Component.
The #ComponentScan Javadoc states:
Either basePackageClasses or basePackages (or its alias value) may be
specified to define specific packages to scan. If specific packages
are not defined, scanning will occur from the package of the class
that declares this annotation.
ISTR wasting a lot of time on this before and found it easiest to simply move my application class to the highest package in my app's package tree.
More recently I encountered a gotcha were the property was being read before the value insertion had been done. Jesse's answer helped as #PostConstruct seems to be the earliest you can read the inserted values, and of course you should let Spring call this.
I had the similar issue and the above examples doesn't help me to read properties. I have posted the complete class which will help you to read properties values from application.properties file in SpringBoot application in the below link.
Spring Boot - Environment #Autowired throws NullPointerException
Your problem is that you need a static PropertySourcesPlaceholderConfigurer Bean definition in your configuration. I say static with emphasis, because I had a non-static one and it didn't work.
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
I had the same issue get value for my property in my service class. I resolved it by using #ConfigurationProperties instead of #Value.
create a class like this:
import org.springframework.boot.context.properties.ConfigurationProperties;
#ConfigurationProperties(prefix = "file")
public class FileProperties {
private String directory;
public String getDirectory() {
return directory;
}
public void setDirectory(String dir) {
this.directory = dir;
}
}
add the following to your BootApplication class:
#EnableConfigurationProperties({
FileProperties.class
})
Inject FileProperties to your PrintProperty class, then you can get hold of the property through the getter method.

#WebMvcTest mapperscan conflict

My project used spring-boot (1.4.0.release) and mybatis-spring-boot-starter. When I try to have some test code for controller, I always get a exception
Caused by: java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.mybatis.spring.support.SqlSessionDaoSupport.checkDaoConfig(SqlSessionDaoSupport.java:75)
at org.mybatis.spring.mapper.MapperFactoryBean.checkDaoConfig(MapperFactoryBean.java:74)
at org.springframework.dao.support.DaoSupport.afterPropertiesSet(DaoSupport.java:44)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
... 42 more`
But when I comment #MapperScan("com.toby.mapper"), it runs very well.
Here is my example class:
#MapperScan("com.toby.mapper")
#EnableTransactionManagement
#EnableConfigurationProperties(AppConfig.class)
#SpringBootApplication(scanBasePackages = "com.toby.configuration,com.toby.web.controller,com.toby.service,com.toby.dao")
public class Example {
public static void main(String[] args) throws Exception {
//new SpringApplicationBuilder().sources(Example.class).run(args);
SpringApplication application=new SpringApplication(Example.class);
application.addInitializers(new PropertyPasswordDecodingContextInitializer());
application.run(args);
}
}
Here is my test code:
package com.toby.web.controller;
import com.toby.common.config.AppConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
/**
* Created by Toby on 2016/8/10.
*/
#RunWith(SpringRunner.class)
#WebMvcTest(value = MyRestController.class)
public class MyRestControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private AppConfig appConfig;
#Test
public void testHome() throws Exception {
/*this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk()).andExpect(content().string("Honda Civic"));*/
}
}
I guess you've updated the description or I didn't read it properly the first time. #MapperScan is a mybatis specific annotation that triggers something but is missing some guard of some sort.
We had the same problem in boot actually. Let's say you put #EnableCaching on your main app. Because slicing disables all auto-configurations but a list of specific ones, the cache auto-configuration would not kick in and you'll get an exception because the CacheManager isn't found. To fix that issue, we've started to create some annotation to easily enable those. If you look at WebMbcTest you'll see it's annotated with AutoConfigureCache that's going to provide a dummy no-op cache manager unless specified otherwise.
Your problem is that the mybatis support is a third party integration and there isn't any support for that. Some solutions:
Change #WebMbvcTest to provide the class of another configuration class, effectivly disabling the use of your main spring boot app. Of course that class shouldn't define the #MapperScan annotation
Move the MapperScan (and anything that's not required with slicing) to another Configuration class. It could be a class in the same package as your app. Slicing won't scan those by default so you'll be fine. It's by far the easiest
Create an issue in the mybatis support so that they improve the auto-configuration to back-off (prevent this exception). I am not sure that's possible actually
Long story short, since #MapperScan is a way to tell mybatis to scan your entities, maybe you shouldn't add it on your main boot app if you use slicing. Because your #WebMbcTest doesn't want to use that obviously.

How to make logging components visible across whole Spring Boot application

I'm running Spring Boot and I want to add logging to my application. All examples have the following for the main Application entrypoint:
private static final Logger log = LoggerFactory.getLogger(Application.class);
However, I want the same log variable visible (as Singleton) across all my application components (services, controllers, etc.). How would I do that? Thanks!
You can use springs IoC container to achieve this.
just configure a bean like this in a #Configuration cass
#Bean
public Logger log() {
return LoggerFactory.getLogger(AnyClassYouWant.class);
}
and inject it with #Autowired in your class
class WhatEver {
#Autowired
Logger log;
//...
}
You can create a static class.. Which contain:
private static final Logger log = LoggerFactory.getLogger(StaticLog.class);
And will expose methods to write logs..
Hope that answer your needs

how to load additional bean configuration file in spring at run time

I have a spring app, and in the future we will develop more classes, and for these class we will also use additional configuration files (not overwrite the existing ones) to define the beans. Then how to dynamically load them? I know there is an interface of ApplicationContextAware, I could have a bean running checking whether new configuration files are available, if they come, I could run the
setApplicationContext(ApplicationContext applicationContext)
But then how to use ApplicationContext to load the additional configuration file?
update:
If the app is loaded from XML then I could convert ApplicationContext to ClassPathXmlApplicationContext and then use the load method,but what if AnnotationConfigApplicationContext, it only has scan method to scan package, but what if I want to load from xml?
update:
The following is the code I want to use, it used spring integration to monitor a fold, at run time I could put jar file on the class path, and then put the xml configuration in that folder, this will trigger the loadAdditionBeans function to run, and the xml File object will be passed in, what need to do is to add the context in that File to the current context but not create a child context.
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import java.io.File;
#MessageEndpoint
public class FolderWatcher implements ApplicationContextAware {
//private ApplicationContext ctx;
private AnnotationConfigApplicationContext ctx; // it's a spring boot,so the ctx is AnnotationConfigApplicationContext
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx=(AnnotationConfigApplicationContext)applicationContext;
}
#ServiceActivator
public void loadAdditionBeans(File file){
/*
file is an xml configuration file, how to load the beans defined it into the currect context,
I don't what to have another hierarchy, since that will make the beans defined in the file not
available in parent.
*/
}
}
PathMatchingResourcePatternResolver pmrl = new PathMatchingResourcePatternResolver(context.getClassLoader());
Resource[] resources = pmrl.getResources(
"classpath*:com/mycompany/**/applicationContext.xml"
);
for (Resource r : resources) {
GenericApplicationContext createdContext = new GenericApplicationContext(context);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(createdContext);
int i = reader.loadBeanDefinitions(r);
}
Have a look at the above code and let me know if it helps to resolve your problem.
If you're using the classpath scanning but you still want o load additional configurations from XML, you can simply use the #ImportResource annotation on your #Configuration class and import the XML resource you need:
#Configuration
#ImportResource( { "classpath*:/rest_config.xml" } )
public class MyConfig{
...
}
That way it's easy to mix in legacy XML configuration with newer, Java configs and you don't have to - for example - migrate your entire configuration in one go.
Hope that helps.

In Spring how do I configure java.util.Logging so it can be autowired?

In our webapp we're using java.util.Logging (JULI, actually, since we deploy to Tomcat 6). Logging is configured by a logging.properties file in WEB-INF/classes, a la this.
I'd like to configure the logger so it can be autowired, something like:
#Autowired
private Logger mylogger;
I've searched the Spring forums, the web, and of course Stack Overflow and I can't find how to set this up. I'd appreciate any help on this.
Thanks!
One way would be to use the Java Config style, so you'd have one bean like this:
#Configuration
public class LoggerProvider {
#Bean
public Logger logger() {
return Logger.getLogger("foobar.whatever");
}
}
That could then be autowired into the rest of the app as normal.
In order to use #Autowired on something (a bean) you must make that bean spring-controlled. There are many ways to do this and they depend on the logging framework you want to use.
I'm afraid there isn't a 'one-size-fits-all' solution.
Usually, you would use a static initializer provided by the logging framework of your choice or some abstraction over it (e.g. commons-logging).
I found one reference in which a #Logger annotation is introduced, maybe that points you into a direction of your liking:
http://jgeeks.blogspot.com/2008/10/auto-injection-of-logger-into-spring.html
In order to make Logger be injectable with #Autowired, you must have a configuration class where you have configured all the Beans with which you use #Autowired. That class will be marked with #Configuration. There you must put the following #Bean in your configuration:
#Configuration
public class WebConfiguration {
#Bean
#Scope("prototype")
public Logger produceLogger(InjectionPoint injectionPoint) {
Class<?> classOnWired = injectionPoint.getMember().getDeclaringClass();
return LoggerFactory.getLogger(classOnWired);
}
}

Resources