LoggingAppender causes NPE in Spring Boot on OpenShift
Related to spring-attic/spring-cloud-gcp#1355. I get a NPE from Logback, and the container won't start, when using Spring Cloud GCP Logging 1.1.0.RC2 which uses google-cloud-java 1.54.0. The Spring Boot app is being deployed on an on-prem OpenShift environment. Note that it works when I start the container on my local machine.
The exception itself isn't very informative:
java.lang.IllegalStateException: Logback configuration error detected: -- | ERROR in ch.qos.logback.core.joran.spi.Interpreter@15:14 - RuntimeException in Action for tag [appender] java.lang.NullPointerException | at org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LogbackLoggingSystem.java:169) | at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithConventions(AbstractLoggingSystem.java:82) | at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:60) | at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:117) | at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:293) | at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:266) | at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:229) | at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:202) | at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) | at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) | at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75) | at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54) | at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:347) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:306) | at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:139) | at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:191) | at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:105) | at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:71) | at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) | at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) | at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75) | at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54) | at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:347) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:306) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) | at com.ikea.rix.cloud.sync.crccem.Application.main(Application.java:23) | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) | at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) | at java.base/java.lang.reflect.Method.invoke(Method.java:566) | at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) | at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) | at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) | at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
So, I created a CustomAppender which simply extends LoggingAppender just overriding the LoggingAppender#start method and changed my logback-spring.xml accordingly.
import com.google.cloud.logging.logback.LoggingAppender; public class CustomAppender extends LoggingAppender { @Override public synchronized void start() { try { super.start(); } catch (Exception e) { e.printStackTrace(); } } }
Now I get a more useful exception:
java.lang.NullPointerException -- | at com.google.cloud.MetadataConfig.getZone(MetadataConfig.java:46) | at com.google.cloud.logging.MonitoredResourceUtil.getValue(MonitoredResourceUtil.java:164) | at com.google.cloud.logging.MonitoredResourceUtil.getResource(MonitoredResourceUtil.java:115) | at com.google.cloud.logging.logback.LoggingAppender.getMonitoredResource(LoggingAppender.java:134) | at com.google.cloud.logging.logback.LoggingAppender.start(LoggingAppender.java:180) | at com.ikea.rix.cloud.sync.crccem.logging.CustomAppender.start(CustomAppender.java:10) | at ch.qos.logback.core.joran.action.AppenderAction.end(AppenderAction.java:90) | at ch.qos.logback.core.joran.spi.Interpreter.callEndAction(Interpreter.java:309) | at ch.qos.logback.core.joran.spi.Interpreter.endElement(Interpreter.java:193) | at ch.qos.logback.core.joran.spi.Interpreter.endElement(Interpreter.java:179) | at ch.qos.logback.core.joran.spi.EventPlayer.play(EventPlayer.java:62) | at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:165) | at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:152) | at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:110) | at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:53) | at org.springframework.boot.logging.logback.LogbackLoggingSystem.configureByResourceUrl(LogbackLoggingSystem.java:180) | at org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LogbackLoggingSystem.java:152) | at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithConventions(AbstractLoggingSystem.java:82) | at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:60) | at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:117) | at org.springframework.boot.context.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:293) | at org.springframework.boot.context.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:266) | at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:229) | at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:202) | at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) | at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) | at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75) | at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54) | at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:347) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:306) | at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:139) | at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:191) | at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:105) | at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:71) | at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) | at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) | at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127) | at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75) | at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54) | at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:347) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:306) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) | at com.ikea.rix.cloud.sync.crccem.Application.main(Application.java:23) | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) | at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) | at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) | at java.base/java.lang.reflect.Method.invoke(Method.java:566) | at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) | at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) | at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) | at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
I found that when the environment variable KUBERNETES_SERVICE_HOST is set you will default to container and not global. See https://github.com/googleapis/google-cloud-java/blob/master/google-cloud-clients/google-cloud-logging/src/main/java/com/google/cloud/logging/MonitoredResourceUtil.java#L177. Thus resulting in a NPE in https://github.com/googleapis/google-cloud-java/blob/master/google-cloud-clients/google-cloud-core/src/main/java/com/google/cloud/MetadataConfig.java#L44-L49 when running on a non GKE Kubernetes.
My thinking is that 1) you shouldn't default to container just because KUBERNETES_SERVICE_HOST is set and 2) you probably should try to avoid the NPE in com.google.cloud.MetadataConfig.getZone(MetadataConfig.java:46).