How can I use a service from a Java custom log appender in Grails? - spring

The basic problem is that I want to use a service from a Java custom log appender in Grails. I have defined a bean in my resources.groovy:
beans = {
databaseLogAppender(DatabaseLogAppender) {
logMessageService = ref("logMessageService")
}
}
...and I have added my log appender in my Config.groovy:
appenders {
//...
appender new DatabaseLogAppender(name: 'databaseLog', threshold: org.apache.log4j.Level.WARN)
}
root {
warn 'databaseLog'
// ...
}
I can see that my log appender gets called when there is a log of level WARN but my service does not get injected so I get a NPE instead. It seems to me that I should configure the appender in some other way so that I use the bean that I have defined instead of the class directly, but I cannot figure out how to do this. Any ideas?

I figured it out.
I added my appender programatically in the BootStrap.groovy instead of configuring it in my Config.groovy. This way the beans have been properly initialized.
My BootStrap.groovy:
class BootStrap {
def databaseLogAppender
def init = { servletContext ->
Logger.getRootLogger().addAppender(databaseLogAppender)

Related

How to exclude #ConfigurationProperties from reloading in kubernetes configMap

In my application I have ds bean with prefix so I can defined it in application.properties by profile
#Bean
#ConfigurationProperties(prefix = "atomikos.db")
public AbstractDataSourceBean dbDataSource() {
AtomikosNonXADataSourceBean atomikosDataSource = new AtomikosNonXADataSourceBean();
return atomikosDataSource;
}
according this article this bean will be reload when configMap changed but how I can exclude it and still use application.properties to define properties this bean according to profile ? In production system I just can not recreate connection to db
According to the latest documentation, you should set
spring.cloud.refresh.never-refreshable=my.package.ClassName
where my.package.ClassName is the type of bean you don't want refreshed.

How to disable SpringBoot autoconfiguration for TomcatServletWebServerFactory in order for a custom spring-starter to provide it?

so I was writing my own SpringBootStarter which was supposed to enable the JNDI lookup in the embedded tomcat of a SpringBoot application.
My sample SpringBoot application has a dependency of my custom SpringBootStarter, which in turn has a dependency on the spring-boot-starter-web. If I create a Configuration class like the following inside the sample SpringBoot application everything works perfectly:
#Configuration
public class SampleSpringBootAppConfig {
#Bean
public TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
#Override
protected TomcatWebServer getTomcatWebServer(org.apache.catalina.startup.Tomcat tomcat) {
System.out.println("CONFIGURING CUSTOM TOMCAT WEB SERVER FACTORY ");
tomcat.enableNaming();
return super.getTomcatWebServer(tomcat);
}
#Override
protected void postProcessContext(Context context) {
ContextResource resource = new ContextResource();
resource.setName("myDataSource");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", "org.postgresql.Driver");
resource.setProperty("url", "jdbc:postgresql://localhost:5432/postgres");
resource.setProperty("username", "postgres");
resource.setProperty("password", "postgres");
context.getNamingResources()
.addResource(resource);
}
};
}
Because SpringBoot finds a custom Bean, there won't be an autoconfigured default one / it is overridden and the JNDI is successfully enabled.
However, as soon as I extract this Bean configuration into my auto-configure module of my custom SpringBoot Starter, the following exception is thrown while trying to start the sample SpringBoot application:
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : tomcatServletWebServerFactory,tomcatFactory
I reckon this is due to SpringBoot not finding a customized Bean and therefore creating an autoconfigured default one which also won't be overridden. So now there will be two ServletWebServerFactory beans, the default one and the one from my auto- configure module.
What i tried so far (to no avail) :
annotating my custom Bean with #Primary
setting spring.main.allow-bean-definition-overriding to true
Is there any way to make SpringBoot not initialize a autoconfigured default bean, or any other possible solution to this?
try this
#AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class)
I was able to solve this myself by excluding the responsible AutoConfiguration class:
#SpringBootApplication ( exclude = ServletWebServerFactoryAutoConfiguration.class)

Determine Springs bean instantiation order in Grails 3.3 with GORM 6.1.x

I am using a Grails 3.3 application that uses GORM 6.1.6.RELEASE, Spring-Boot 1.5.4.RELEASE and Spring Core 4.3.9.RELEASE behind the scene. I am trying to declare a Spring bean that get initialized just before Hibernate starts to validate the underlying database schema.
Here is what I like to do. I want to register my Flyway as a Spring bean and inject the dataSource bean into it. In order to have Flyway run before Hibernate starts to validate the current database schema, I add my flyway bean as a dependency onto the sessionFactory bean. The order would be as follows:
dataSource bean
flyway bean
hiberateDatastore bean
GORM 6.1 uses org.grails.orm.hibernate.HibernateDatastore as a Spring bean to initialize the Hibernate ORM and the database. The sessionFactory bean declares the hibernateDatastore#getSessionFactory as factory class.
Therefore the hibernateDatastore always is created first.
What is the way in Grails 3.3 to create a custom Spring bean that has to run after the connection to the database is available but before the Hibernate stuff gets initialized?
In previous versions of Grails 3.x it was possible to declare it in resources.groovy like this.
beans = {
if (Environment.current == Environment.PRODUCTION) {
flyway(Flyway) { bean ->
bean.initMethod = 'migrate'
dataSource = ref('dataSource')
locations = 'classpath:db/h2'
baselineOnMigrate = true
}
BeanDefinition sessionFactoryBeanDef = getBeanDefinition('hibernateDatastore')
if (sessionFactoryBeanDef) {
def dependsOnList = ['flyway'] as Set
if (sessionFactoryBeanDef.dependsOn?.length > 0) {
dependsOnList.addAll(sessionFactoryBeanDef.dependsOn)
}
sessionFactoryBeanDef.dependsOn = dependsOnList as String[]
}
}
}
I don't think Spring provides a visualisation of a 'bean instantion tree' however you could set the log level for org.springframework.beans.factory.support.DefaultListableBeanFactory to DEBUG and you'll get output like this:
Creating shared instance of singleton bean '...fully qualified class name...'
Returning cached instance of singleton bean '...fully qualified class name...'
You could review this log output for beans from the Hibernate namespace.
I presume you'll use the results to declare a DependsOn relationship so just for completeness this would look like:
#Bean
public SomeHibernateClass createHibernate() {
...
}
#Bean
#DependsOn("createHibernate")
public MyClass createMine() {
...
}
Grails 3.3.0 changed the mechanism of the dataSource bean creation. The Grails project lead stated in the related issue:
Previous versions of Grails created the dataSource bean separately from the session factory. We would need to restore this behaviour I guess so the dataSource can be referenced without triggering the creation of the sessionFactory
After upgrading to Grails 3.3.1 the dataSource bean is again available before the session factory gets created. This solves the problem.

Fixing "The web application [ROOT] created a ThreadLocal with key of type [com.netflix.hystrix.Hystrix$1]"

I'm using Netflix cloud in a spring-boot API Gateway and I building the spring-boot app as a WAR that can be run standalone or deployed to a Tomcat container. When the spring-boot app is redeployed in Tomcat, for instance, using the Cargo Maven plugin, Tomcat MemoryLeakDetection complains about "The web application [ROOT] created a ThreadLocal with key of type [com.netflix.hystrix.Hystrix$1]".
After redeploying my spring-boot app 9 times Tomcat runs out of memory. How do I remove the Hystrix ThreadLocal so that it doesn't cause the WebappClassLoader instance to stick around every time I redeploy my spring-boot app. The result is that each redeploy leaves an instance of WebappClassLoader that can't be Garbage Collected because of the Hystrix ThreadLocal?
Here's the out-of-memory stacktrace:
29-Apr-2016 12:21:50.721 SEVERE [http-apr-8080-exec-38] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [my-spring-boot-app] created a ThreadLocal with key of type [com.netflix.hystrix.Hystrix$1] (value [com.netflix.hystrix.Hystrix$1#8302924]) and a value of type [java.util.LinkedL
ist] (value [[]]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
29-Apr-2016 12:22:00.484 INFO [http-apr-8080-exec-38] org.apache.catalina.startup.HostConfig.undeploy Undeploying context [/my-spring-boot-app]
29-Apr-2016 12:22:09.353 INFO [http-apr-8080-exec-33] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive D:\apache-tomcat-8.0.28\webapps\my-spring-boot-app.war
12:23:08,056 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
12:23:08,089 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Setting ReconfigureOnChangeFilter scanning period to 30 seconds
12:23:08,089 |-INFO in ReconfigureOnChangeFilter{invocationCounter=0} - Will scan for changes in [[D:\apache-tomcat-8.0.28\webapps\my-spring-boot-app\WEB-INF\classes\logba
ck.xml]] every 30 seconds.
12:23:08,089 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Adding ReconfigureOnChangeFilter as a turbo filter
12:23:08,106 |-INFO in ch.qos.logback.core.joran.action.StatusListenerAction - Added status listener of type [ch.qos.logback.core.status.OnConsoleStatusListener]
12:23:08,118 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
12:23:08,136 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [CONSOLE]
12:23:08,176 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
java.lang.OutOfMemoryError: Metaspace
Dumping heap to java_pid15972.hprof ...
Heap dump file created [206407579 bytes in 1.201 secs]
29-Apr-2016 12:23:10.870 SEVERE [http-apr-8080-exec-33] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method check
java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2496)
at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:860)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1302)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1167)
at org.slf4j.impl.StaticLoggerBinder.init(StaticLoggerBinder.java:97)
at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:55)
at org.slf4j.LoggerFactory.bind(LoggerFactory.java:141)
at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:120)
at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:331)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:283)
at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:155)
at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:132)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:655)
at org.springframework.boot.context.web.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:84)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:175)
Ok, I think I solved my own problem such that Tomcat MemoryLeakDetection no longer complains about the Hystrix ThreadLocal anymore. What I did was I added a custom ServletContextListener and in the "contextDestroyed()" method I found a way to forcibly remove the private static ThreadLocal from the com.netflix.hystrix.Hystrix class. This appears to solve the problem. Here's an excerpt from my custom servlet listener:
/**
* The listener interface for receiving ServletContext events.
* The class that is interested in processing a ServletContext
* event implements this interface, and the object created
* with that class is registered with a component using the
* component's <code>addServletContextListener<code> method. When
* the ServletContext event occurs, that object's appropriate
* method is invoked.
*
* #see ServletContextEvent
*/
#Component
public class GatewayServletContextListener implements ServletContextListener{
private static final Logger LOG = LoggerFactory.getLogger(GatewayServletContextListener.class);
#Override
public void contextInitialized(ServletContextEvent arg0) {
LOG.info("Servlet context listener observed context initialized");
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
LOG.info("Servlet context listener observed context destroyed");
cleanupThreadLocals();
}
/**
* Cleanup thread locals.
*/
private void cleanupThreadLocals() {
try {
LOG.info("Cleaning up ThreadLocals ...");
Field currentCommandField = ReflectionUtils.findField(Hystrix.class, "currentCommand");
Preconditions.checkNotNull(currentCommandField);
ReflectionUtils.makeAccessible(currentCommandField);
#SuppressWarnings("rawtypes")
ThreadLocal currentCommand = (ThreadLocal)currentCommandField.get(null);
Preconditions.checkNotNull(currentCommand);
currentCommand.remove();
LOG.info("Forcibly removed Hystrix 'currentCommand' ThreadLocal");
LOG.info("Done cleaning up ThreadLocals");
} catch(Exception e) {
LOG.warn(e.getMessage(), e);
}
}
}
And here's the Tomcat log on my spring-boot app redeploy:
2016-05-03/10:11:08.646/PDT [http-apr-8080-exec-3] INFO x.y.z.listeners.GatewayServletContextListener - Servlet context listener observed context destroyed
2016-05-03/10:11:08.648/PDT [http-apr-8080-exec-3] INFO x.y.z.listeners.GatewayServletContextListener - Cleaning up ThreadLocals ...
2016-05-03/10:11:08.652/PDT [http-apr-8080-exec-3] INFO x.y.z.listeners.GatewayServletContextListener - Forcibly removed Hystrix 'currentCommand' ThreadLocal
2016-05-03/10:11:08.654/PDT [http-apr-8080-exec-3] INFO x.y.z.listeners.GatewayServletContextListener - Done cleaning up ThreadLocals

How to get Environment properties from application.properties into logback.groovy in Spring Boot project?

Trying to inject properties defined in application.properties/application.yml into logback.groovy script in Spring Boot project.
I cannot Inject Environment or ApplicationContext into groovy scripts.
Are there any workarounds?
I am not looking for solutions like System.getProperty('spring.profiles.active')
src/main/resources/logback.groovy
import org.springframework.core.env.Environment
#Inject private Environment env; //this is not working. how to get env here?
println "spring.profiles.active : ${env.getProperty('spring.profiles.active')}"
appender("STDOUT", ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = "%green(%d{HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n"
}
}
if(System.getProperty("spring.profiles.active")?.equalsIgnoreCase("prod")) {
root INFO, ["STDOUT", "FILE"]
} else {
root INFO, ["STDOUT"]
}
src/main/resources/application.yml
---
spring:
profiles:
active: development
logback.groovy needs to be evaluated very early because otherwise the code for loading the spring configuration, instantiating beans, etc. could not log anything. That's why #Inject can't work.
I see 2 options:
You could easily load application.properties in logback.groovy, but it's far from trivial to take all the configuration override mechanisms that spring provides into account
Create a spring bean that changes the logback configuration programmaticaly
when initialized
A different approach is to externalize the logback configuration on production with -Dlogback.configurationFile=/path/to/config.groovy. Putting the config file in a well known location (like /etc/my-app) makes it easy to change log levels in production without re-deploying (or even re-starting).
Sorry to resurrect this thread but while since this is thread is what I found while looking for a solution, I wanted to share a workaround.
I have used a Custom converter and conversion rule to pull out properties in classpath resource (i.e. application.properties):
In logback.groovy:
conversionRule('springApplicationName', CustomSpringApplicationNameConverter)
def patternExpression = '%green([%d{YYYY-MM-dd HH:mm:ss.SSS}]) [%t] %highlight(%-5level) %magenta([%springApplicationName]) - %cyan(%logger{36}) -- %msg%n'
and then 'patternExpression' used in desired appender
and my custom converter class (in groovy):
class CustomSpringApplicationNameConverter extends ClassicConverter {
#Override
String convert(ILoggingEvent event) {
ClassPathResource classPathResource = new ClassPathResource('application.properties')
Properties applicationProperties = new Properties()
applicationProperties.load(classPathResource.inputStream)
String springApplicationName = applicationProperties.getProperty('spring.application.name')
if (!springApplicationName) {
System.err.println('Could not find entry for \'spring.application.name\' in \'application.properties\'')
}
springApplicationName
}
}

Resources