Spring Boot programmatic logging configuration - java

How can I configure logging programmatically in a spring boot application?
Using an xml or properties file is not flexible enough for my needs.
Update: I want to achieve something like this:
#Value("${logging.level.root}")
private String loggingLevelRoot;
#Value("${logging.level.myApp}")
private String loggingLevelMyApp;
#Value("${logging.file}")
private boolean fileAppenderEnabled;
....
setLevel(Logger.ROOT_LOGGER_NAME, Level.toLevel(loggingLevelRoot)));
setLevel("com.myapp", Level.toLevel(loggingLevelMyApp)));
setLevel("org.springframework", Level.WARN);
setLevel("org.apache.coyote", Level.INFO);
setLevel("org.apache.catalina", Level.INFO);
setLevel("org.apache.catalina.startup.DigesterFactory", Level.ERROR);
setLevel("org.apache.catalina.util.LifecycleMBeanBase", Level.ERROR);
Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
logger.addAppender(createConsoleAppender());
if (fileAppenderEnabled) {
logger.addAppender(createFileAppender());
}
All I have per environment is:
logging.level.root=[INFO, DEBUG, ..]
logging.level.myApp=[INFO, DEBUG, ..]
logging.file=[true | false]
No duplication of XML, Groovy and other formats I really don't want to deal with.
At the end of the day, this is really about achieving the same flexibility for logging as Spring JavaConfig did for beans. XML or other file formats are simply too static, require too much duplication and not integrated well enough with the rest of the configuration of the application.
Why should logging be configured differently than any other bean or service? It makes no sense.

I'm not sure you want or need to disable the default XML configuration of the logging system, but you do want to execute your customization calls after that was done. Fortunately that's pretty easy as it's done as early as possible in the initializer chain for a SpringApplication. The easiest place to put your code is probably a SpringApplicationInitializer (it has to implement ApplicationContextInitializer as well so it can be added to the SpringApplication). E.g.
SpringApplication application = new SpringApplication(MySources.class);
application.addInitializers(new LoggingInitializer());
application.run(args);
You won't be able to do dependency injection into the initializer if you do it that way, but it will ensure that it gets called as early as possible in the lifecycle. If your initializer implements EnvironmentAware then you will also be passed an instance of Environment before the call to SpringApplicationInitializer.initialize() - using that you can resolve the environment dependent pieces in your sample, e.g.
String loggingLevelRoot = environment.getProperty("logging.level.root");
Once you have it working, to avoid having to do the same thing for all apps you can make it declarative by adding a META-INF/spring.factories containing your initializer class:
org.springframework.context.ApplicationContextInitializer=\
my.pkg.for.LoggingInitializer
If you really need dependency injection and #Value resolution I think you are going to have to accept that the ApplicationContext will have fully refreshed before you get a chance to configure anything. If that's an acceptable compromise I recommend just adding a LoggingInitializer to your context and have it implement CommandLineRunner.

Related

Building custom java config annotations - similar to custom XML namespaces

We're building a framework on top of Spring & Spring MVC. Our framework is quite mature at this point - about 2 years old and is used widely within our organization. Our framework is very modular (much like spring itself is). There are various modules that can be used independently or together. When used together they provide many benefits to the end user. We have built a handful custom spring XML namespaces (NamespaceHandlers, BeanDefinitionParsers, etc). Each module provides their own which brings in its own set of XML configuration elements. This is all working great for us and has been a really big win for us.
What we'd like to do now is move away from XML-based configuration and into java config. My idea/thought is for each module to introduce a set of java config annotations that can be used (something similar to the #EnableCaching, #EnableMBeanExport annotations). My question is this - even if I create my annotations - how do I "wire" them in so that if they are present I can do "stuff"? This would be similar conceptually to the NamespaceHandlers & BeanDefinitionParsers. I can't find any documentation anywhere as to how to get started.
I've thought about creating some custom abstract base classes which do what I need them to do - but the problem is when it comes to the end user's application - they can only extend a single class. I need a flexible way for each module in my framework to expose its own custom configuration that end user applications can use, just like they use our XML namespace elements.
Here's a glimpse as to what we do in XML (not full application context file - just a blurb from it pertaining to our custom XML namespaces):
<atom-web:web/>
<atom-web:logging/>
<atom-web:security entitlementsProvider="XML" xmlRefreshInterval="${cache.refresh.interval.ms}"/>
<atom-profile:profile caching="IN_MEMORY" entryExpiryDelay="${cache.refresh.interval.ms}"/>
<atom-prefs:preferences backingStoreUrl="${pref.backingStore.url}"/>
<atom-content:content contentServerBaseUrl="${content.server.url}" contentServerFileUrl="${content.server.file.url}" site="${site.name}" contentTaskExecutor="contentTaskExecutor" snippetCaching="IN_MEMORY" cacheRefreshInterval="${cache.refresh.interval.ms}"/>
<bean id="contentTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" p:corePoolSize="3" p:maxPoolSize="20"/>
What I'm envisioning is some kind of set of annotations - something like this:
#EnableAtomWebApplication
#EnableAtomWebLogging
#EnableAtomWebSecurity(entitlementsProvider=EntitlementsProvider.XML, xmlRefreshDelay=120000)
#EnableAtomProfile(caching=CachingType.IN_MEMORY, expiryDelay=120000)
// Other annotations for rest of modules
#Configuration
public class ConfigurationClass {
// Rest of configuration in here
}
Any help here would be greatly appreciated. I'm not quite sure where to start and can't really find any documentation anywhere to help me get started.
So after thinking about this a bit I think I've found the correct starting point. I wanted to throw this out there for anyone who might be able to say "yeah thats the right place" or "no you aren't looking in the correct place".
Using my example above
#EnableAtomProfile(caching=CachingType.IN_MEMORY, expiryDelay=120000)
I would create an annotation for the #EnableAtomProfile annotation like this:
#Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
#Target(value={java.lang.annotation.ElementType.TYPE})
#Documented
#Import({AtomProfileBeanDefinitionRegistrar.class})
public #interface EnableAtomProfile {
CachingType caching() default CachingType.NONE;
long expiryDelay default 0;
}
The AtomProfileBeanDefinitionRegistrar class would implement org.springframework.context.annotation.ImportBeanDefinitionRegistrar and do any of the necessary stuff that I'm currently doing in my BeanDefinitionParser
You can have a BeanPostProcessor defined, which would basically:
inspect every single bean created
with reflection check if the object's class is annotated with #YourAnnotation
and if it is, then apply some custom logic - e.g. package the object into some other class or something
Reference:
Spring docs on BeanPostProcessors
source code for RequiredAnnotationBeanPostProcessor, which is a BeanPostProcessor which analyzes annotations

Spring Test: better use XML configuration over annotations?

I'm learning Spring and looking at using Spring Test for my web app (JQuery/CXF/Hibernate). I have been using annotations to wire up my beans and noticed that this sometimes got into the way of polymorphism. For example I have a GenericDAO extended by all DAOs. It is also a concrete class used by simple services, so when I wired it for one of those services, ALL other DAOs became candidates for runtime wiring (because of polymorphism). I could solve that by wiring explicitly by type: #Resource(type= GenericDaoImpl.class), but this goes against the best practice of coding to interfaces ..
Now I want to create unit tests using a MockGenericDaoImpl, and integration tests using GenericDaoImpl. How will I override the annotations? I'm guessing the best approach is to stick to XML-based wiring, so that I can have several versions of beans.xml, one for prod, one for unit tests, one for integration tests, and possibly split them into parallel sub-files, as needed. Isn't this approach more flexible than depending on annotations that either scan to guess the appropriate candidate, or constrain polymorphism?
Can someone give me broad directions on how to organize my test configuration setup? Thank you!
How about using #Configuration class? If you're not familiar with this method - you can declare a class that replaces the XML context file and looks something like this:
#Configuration
#ComponentScan({ "com.project.dao"})
public class TestConfiguration {
#Bean
public GenericDao getGenericDao() {
return new MockGenericDaoImpl();
}
}
In the #ComponentScan annotation just put the relevant packages to scan. This way you're very flexible with the beans you're creating and the way to create them. You can injects mock to the beans whatever way you'd like.
Also you can create several test configurations, each configuration for a different purpose. In order to load the context you need to put this annotation on your test class:
#ContextConfiguration(classes={TestConfiguration .class})
Using XML configuration files prevent you from depending on Spring dependencies. You can replace it with another DI framework(maybe Guice, but this is not realistic for big projects I know). Using XML configuration files enables you to code cleanly.
I hate XML but I hate existence of not-business-logic-specific things in my code more.
I see you know how to overcome the test issues using XML configuration files. I will try to show how to overcome duplication of implementations(one real one mock implementation) problems using annotations.
You can define your beans via annotations. You can select implementation using aliases:
project.dev.properties:
my.project.dao.xDao=xDaoJpaBean
project.test.properties:
my.project.dao.xDao=xDaoMockBean
<alias name="${my.project.dao.xDao}" alias="xDao"/>
#Autowired
#Qualifier("xDao")
protected XDao xDao;
So you can select your implementation just using your properties file. If you want to use Annotations purely you can do this as well:
#Autowired
#Qualifier("${my.project.dao.xDao}")
protected XDao xDao;
Your build environment will specify which properties file to load, in return your bean will be assigned.

Java Spring ApplicationContext Configuration

I'm in the process of moving all of my Spring Configurations to Java code. I've run into a problem where I now want to set which profile I am using based on a command line switch or maven profile, etc... I also want to avoid having to place all of the same annotations on each of my test classes. This is not a web application, but rather a functional test suite.
Here is my attempt:
public class CompanyApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
final AnnotationConfigApplicationContext rootContext = new AnnotationConfigApplicationContext();
rootContext.getEnvironment().setActiveProfiles(System.getProperty("spring.profile.active", "local"));
rootContext.register(LocalConfiguration.class, SauceLabsConfiguration.class);
}
}
Then I have my tests annotated with the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = CompanyApplicationContextInitializer.class)
However when I attempt to run my tests, my autowired pieces are not being located. Am I on the right track at all? How can I wire in this class to programatically set my ApplicationContext?
The problem with your example above is that you're passing an ApplicationContextInitializer class #ContextConfiguration#classes. The #classes attribute is intended to accept classes marked with Spring's #Configuration annotation.
ApplicationContextInitializer is intended for use primarily in web applications, where it is difficult to get programmatic access to the WebApplicationContext. The "contextInitializerClasses" init-param can be passed to the Spring DispatcherServlet, and Spring will call your ACI implementation at the right time, allowing you to manipulate the application context prior to #refresh().
In your case, it appears you are concerned only with activating profiles for an integration test. So your ACI is unnecessary. Mark your integration test with Spring's #ActiveProfiles annotation to dictate which profiles are active.
Note that if spring.profiles.active has been set as a JVM system property or environment variable, the specified profile(s) will be activated automatically. i.e. there is no need to call System#getProperty as you do in your ACI implementation. One thing to note, however, is that based on the logic in your ACI implementation, it appears you want to fall back to a profile named "local" if spring.profiles.active is note supplied as a system property or environment variable. You may be interested to know that there is a "reserved default profile" named literally "default". This probably has the same semantics you're looking for with your "local" profile. Consider renaming your 'local' profile to 'default'.
Finally, note that there does exist an open improvement request for providing ApplicationContextInitializer support in #ContextConfiguration classes: https://jira.springsource.org/browse/SPR-9011. You might want to put a watch on that. It would, for example, allow you a simple option for programmatically activating 'local' if no other profiles are active.
Try adding the locations of your app context XML to the second annotation:
#ContextConfiguration(locations = {
"classpath:applicationContext.xml"
})

Conditionally creating beans in spring

Right now I'm exposing the service layer of my application using spring remoting's RMI/SOAP/JMS/Hessian/Burlap/HttpInvoker exporters. What I'd like is to allow the user to somehow define which of these remoting mechanisms they'd like enabled (rather than enabling all of them), then only create those exporter beans.
I was hoping that spring's application context xml's had support for putting in conditional blocks around portions of the xml. However, from what I've seen so far there's nothing in the standard spring distribution that allows you to do something like this.
Are there any other ways to achieve what I'm trying to do?
I am going to assume that you are looking to configure your application based on your environment, as in... for production I want to use this beans, in dev these other ...
As Ralph is saying, since Spring 3.1 you have profiles... But the key, is that you understand that you should put your environment based beans in different configuration files... so you could have something like dev-beans.xml, prod-beans.xml... Then in your main spring file, then you just invoke the appropriate one based on the environment that you are using... So profiles are only technique to do so... But you can also use other techniques, like have a system environmental variable, or pass a parameter in your build to decide which beans you want to use
You could realize this by using a Spring #Configuration bean, so you can construct your beans in java code. (see http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-java)
#Configuration
public class AppConfig {
#Bean
public MyService myService() {
if ( userSettingIshessian ) {
return new HessianExporter();
}else {
return new BurlapExporter();
}
}
}
Of course you need to get the user setting from somewhere, a system parameter would be easy, or config file, or something else.
Spring 3.1 has the concept of Profiles. My you can use them.

How to detect application type in Java code

Imagine we have a java package. This package can be used anywhere. However, there are some codes in this package which are context dependant. For instance, if the application which uses this package is a web app we need to perform some tasks by calling a function while performing other tasks if the application was a console application by calling the very same function.
Here is my question:
Is there any way in java that within the code we can detect if the application was run as a web app or a console?
I appreciate any help :)
As a real world example, the ways we load properties files are different for web and console applications.
For web applications we probably use this.getClass().getClassLoader().getResourceAsStream(url) and for console apps we use new FileInputStream(physical path).
It might be better to set a build property somewhere rather then trying to detect your application type, because I don't think there is a reliable way to do that.
Moreover you shouldn't try to detect application type because your view layer (either web, desktop or console) should be easily interchangeable according to modern architectural principles.
In response to your last comment.
As user384706 said DI is the correct choice here IMO.
I will give an example with spring.
In both your console and web app parts you can have:
public class WebOrConsoleServiceImpl {
private PropertyProvider propertyProvider = new NullPropertyProvider();
// and
public void setPropertyProvider(PropertyProvider impl) {
this.propertyProvider = impl;
}
// and in your service logic
public void logic() {
final Properties props = propertyProvider.loadProperties();
// do stuff
}
}
Where your loadProperties() method would be overriden for different implementations of your PropertyProvider.
And in your spring context you can have:
<bean id="consolePropertyProvider" class="com.company.ConsolePropertyProvider"/>
<bean id="myConsoleService" class="com.company.MyConsoleService">
<property name="propertyProvider" ref="consolePropertyProvider" />
</bean>
And the same pair of bean definitions for your WebService and WebPropertyProvider.
Just use dependency injection
Just place all the appropriate parameters to configure your library via setters and let the container or application configure it accordingly using DI.
So you will not need any checks which IMHO is a bad approach
The J2EE way. If your package is used in a web application then, I am assuming that you are in a J2EE container then, you can add a Reference in the naming at deploy time. You can also register an MDB that can listen to the changes to this reference and modify behavior of your code at runtime. Sweet right?
Other standard way in which you can pass the context of the caller is through a parametrized Factory or through properties.
One non-standard way - for fun is to get the stack trace and look for who the caller is or look for j2ee context etc.

Categories