001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.logging; 018 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021 022import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; 023import org.springframework.boot.context.event.ApplicationFailedEvent; 024import org.springframework.boot.logging.LogLevel; 025import org.springframework.context.ApplicationContextInitializer; 026import org.springframework.context.ApplicationEvent; 027import org.springframework.context.ConfigurableApplicationContext; 028import org.springframework.context.event.ApplicationContextEvent; 029import org.springframework.context.event.ContextRefreshedEvent; 030import org.springframework.context.event.GenericApplicationListener; 031import org.springframework.context.support.GenericApplicationContext; 032import org.springframework.core.Ordered; 033import org.springframework.core.ResolvableType; 034import org.springframework.util.Assert; 035 036/** 037 * {@link ApplicationContextInitializer} that writes the {@link ConditionEvaluationReport} 038 * to the log. Reports are logged at the {@link LogLevel#DEBUG DEBUG} level. A crash 039 * report triggers an info output suggesting the user runs again with debug enabled to 040 * display the report. 041 * <p> 042 * This initializer is not intended to be shared across multiple application context 043 * instances. 044 * 045 * @author Greg Turnquist 046 * @author Dave Syer 047 * @author Phillip Webb 048 * @author Andy Wilkinson 049 * @author Madhura Bhave 050 */ 051public class ConditionEvaluationReportLoggingListener 052 implements ApplicationContextInitializer<ConfigurableApplicationContext> { 053 054 private final Log logger = LogFactory.getLog(getClass()); 055 056 private ConfigurableApplicationContext applicationContext; 057 058 private ConditionEvaluationReport report; 059 060 private final LogLevel logLevelForReport; 061 062 public ConditionEvaluationReportLoggingListener() { 063 this(LogLevel.DEBUG); 064 } 065 066 public ConditionEvaluationReportLoggingListener(LogLevel logLevelForReport) { 067 Assert.isTrue(isInfoOrDebug(logLevelForReport), "LogLevel must be INFO or DEBUG"); 068 this.logLevelForReport = logLevelForReport; 069 } 070 071 private boolean isInfoOrDebug(LogLevel logLevelForReport) { 072 return LogLevel.INFO.equals(logLevelForReport) 073 || LogLevel.DEBUG.equals(logLevelForReport); 074 } 075 076 public LogLevel getLogLevelForReport() { 077 return this.logLevelForReport; 078 } 079 080 @Override 081 public void initialize(ConfigurableApplicationContext applicationContext) { 082 this.applicationContext = applicationContext; 083 applicationContext 084 .addApplicationListener(new ConditionEvaluationReportListener()); 085 if (applicationContext instanceof GenericApplicationContext) { 086 // Get the report early in case the context fails to load 087 this.report = ConditionEvaluationReport 088 .get(this.applicationContext.getBeanFactory()); 089 } 090 } 091 092 protected void onApplicationEvent(ApplicationEvent event) { 093 ConfigurableApplicationContext initializerApplicationContext = this.applicationContext; 094 if (event instanceof ContextRefreshedEvent) { 095 if (((ApplicationContextEvent) event) 096 .getApplicationContext() == initializerApplicationContext) { 097 logAutoConfigurationReport(); 098 } 099 } 100 else if (event instanceof ApplicationFailedEvent 101 && ((ApplicationFailedEvent) event) 102 .getApplicationContext() == initializerApplicationContext) { 103 logAutoConfigurationReport(true); 104 } 105 } 106 107 private void logAutoConfigurationReport() { 108 logAutoConfigurationReport(!this.applicationContext.isActive()); 109 } 110 111 public void logAutoConfigurationReport(boolean isCrashReport) { 112 if (this.report == null) { 113 if (this.applicationContext == null) { 114 this.logger.info("Unable to provide the conditions report " 115 + "due to missing ApplicationContext"); 116 return; 117 } 118 this.report = ConditionEvaluationReport 119 .get(this.applicationContext.getBeanFactory()); 120 } 121 if (!this.report.getConditionAndOutcomesBySource().isEmpty()) { 122 if (this.getLogLevelForReport().equals(LogLevel.INFO)) { 123 if (this.logger.isInfoEnabled()) { 124 this.logger.info(new ConditionEvaluationReportMessage(this.report)); 125 } 126 else if (isCrashReport) { 127 logMessage("info"); 128 } 129 } 130 else { 131 if (this.logger.isDebugEnabled()) { 132 this.logger.debug(new ConditionEvaluationReportMessage(this.report)); 133 } 134 else if (isCrashReport) { 135 logMessage("debug"); 136 } 137 } 138 } 139 } 140 141 private void logMessage(String logLevel) { 142 this.logger.info( 143 String.format("%n%nError starting ApplicationContext. To display the " 144 + "conditions report re-run your application with '" + logLevel 145 + "' enabled.")); 146 } 147 148 private class ConditionEvaluationReportListener 149 implements GenericApplicationListener { 150 151 @Override 152 public int getOrder() { 153 return Ordered.LOWEST_PRECEDENCE; 154 } 155 156 @Override 157 public boolean supportsEventType(ResolvableType resolvableType) { 158 Class<?> type = resolvableType.getRawClass(); 159 if (type == null) { 160 return false; 161 } 162 return ContextRefreshedEvent.class.isAssignableFrom(type) 163 || ApplicationFailedEvent.class.isAssignableFrom(type); 164 } 165 166 @Override 167 public boolean supportsSourceType(Class<?> sourceType) { 168 return true; 169 } 170 171 @Override 172 public void onApplicationEvent(ApplicationEvent event) { 173 ConditionEvaluationReportLoggingListener.this.onApplicationEvent(event); 174 } 175 176 } 177 178}