starter - spring java log
Mostrar Banner de arranque con Root-Logger WARN (3)
En el entorno de desarrollo y prueba, el nivel de registro ROOT es DEBUG o INFO. El banner de inicio de primavera se muestra al inicio de la aplicación:
2017-03-23 14:31:00,322 [INFO ] -
:: Spring Boot :: (v1.5.2.RELEASE)
:: Application :: AcMe (v1.0-SNAPSHOT)
:: Build :: 2017-03-23 09:53
Pero cuando se ejecuta en un entorno de producción, mi nivel de registrador ROOT normalmente es WARN. Esto hace que el banner no se imprima.
¿Cómo configurar logback para que el banner se muestre también en producción?
Mi suposición era agregar otro registrador, pero la siguiente (y la misma configuración) no funcionó:
<logger name="org.springframework.web" level="INFO" additivity="false">
<appender-ref ref="FILE"/>
</logger>
Aquí mi configuración
application.properties:
spring.main.banner-mode=log
application-devel.properties:
logging.config=classpath:logging-spring-devel.xml
application-production.properties:
logging.config=classpath:logging-spring-production.xml
logging-devel.xml (banner mostrado)
LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}application.log}"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
...
</appender>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
logging-production.xml (banner no mostrado)
LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}application.log}"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
...
</appender>
<root level="WARN">
<appender-ref ref="FILE"/>
</root>
</configuration>
Esto es, lo que se me ocurrió. Se ajusta a la idea de simplemente reemplazar el registrador en la implementación regular.
El problema con el uso de la implementación de registro predeterminada es la forma en que el registro de recursos comunes se adapta a través del puente slf4j.
Este es probablemente uno de los códigos más feos, por lo que esperamos ver una solución en las próximas versiones de arranque de primavera ...
Paso 1: registrar un nuevo oyente de la aplicación
/META-INF/spring.factory
org.springframework.context.ApplicationListener=ac.me.appevents.BannerDisplay
Paso 2: Implemente el oyente de la aplicación
package ac.me.appevents;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.boot.ResourceBanner;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
public class BannerDisplay implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
/**
* Banner location property key.
*/
private static final String BANNER_LOCATION_PROPERTY = "banner.location";
/**
* Default banner location.
*/
private static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt";
private static final Logger LOG = LoggerFactory.getLogger(BannerDisplay.class);
private static final Marker MRK = MarkerFactory.getMarker("Banner");
private ResourceLoader resourceLoader;
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
Environment environment = event.getEnvironment();
String location = environment.getProperty(BANNER_LOCATION_PROPERTY, BANNER_LOCATION_PROPERTY_VALUE);
ResourceLoader resLoader = getResourceLoader();
Resource resource = resLoader.getResource(location);
if (resource.exists()) {
ResourceBanner banner = new ResourceBanner(resource);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
banner.printBanner(environment, deduceMainApplicationClass(), new PrintStream(baos));
String charset = environment.getProperty("banner.charset", "UTF-8");
try {
LOG.info(MRK, baos.toString(charset));
}
catch (UnsupportedEncodingException e) {
LOG.warn(MRK, "Unsupported banner charset encoding.", e);
}
}
}
@NotNull
private ResourceLoader getResourceLoader() {
if (resourceLoader == null) {
this.resourceLoader = new DefaultResourceLoader(ClassUtils.getDefaultClassLoader());
}
return resourceLoader;
}
public void setResourceLoader(final ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
Durante la impresión, un banner Spring Boot utiliza el registrador de la clase org.springframework.boot.SpringApplication
con el nivel INFO
.
La solución más simple sería habilitar el nivel INFO
para esta clase en particular:
<logger name="org.springframework.boot.SpringApplication"
level="INFO" additivity="false">
<appender-ref ref="FILE"/>
</logger>
Primero, debo admitir que no lo he probado, pero al menos puede darte algunas ideas.
Puede eliminar spring.main.banner-mode=log
y proporcionar su propia implementación de contenedor que usará el registrador en lugar del flujo de salida proporcionado. El código debería verse más o menos así:
public class BannerLoggerWrapper implements Banner {
private static final Log logger = LogFactory.getLog(BannerLoggerWrapper.class);
private Banner actual;
public BannerLoggerWrapper(Banner actual) {
this.actual = actual;
}
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
try {
logger.info(createStringFromBanner(environment, sourceClass));
} catch (UnsupportedEncodingException ex) {
logger.warn("Failed to create String for banner", ex);
}
}
private String createStringFromBanner(Environment environment, Class<?> sourceClass) throws UnsupportedEncodingException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
actual.printBanner(environment, sourceClass, new PrintStream(baos));
String charset = environment.getProperty("banner.charset", "UTF-8");
return baos.toString(charset);
}
}
Puede reemplazar logger.info
con logger.warn
en esta clase o puede crear configuraciones adicionales específicamente para este registrador:
<logger name="your.package.name.BannerLoggerWrapper" level="INFO" additivity="false">
<appender-ref ref="FILE"/>
</logger>
De acuerdo con la documentación , puede configurar Spring Boot para usar su implementación de Banner usando SpringApplication.setBanner(…)
.