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 java.util.ArrayList; 020import java.util.Collections; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; 029import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcome; 030import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes; 031import org.springframework.util.ClassUtils; 032import org.springframework.util.LinkedMultiValueMap; 033import org.springframework.util.MultiValueMap; 034import org.springframework.util.StringUtils; 035 036/** 037 * A condition evaluation report message that can logged or printed. 038 * 039 * @author Phillip Webb 040 * @author Andy Wilkinson 041 * @since 1.4.0 042 */ 043public class ConditionEvaluationReportMessage { 044 045 private StringBuilder message; 046 047 public ConditionEvaluationReportMessage(ConditionEvaluationReport report) { 048 this(report, "CONDITIONS EVALUATION REPORT"); 049 } 050 051 public ConditionEvaluationReportMessage(ConditionEvaluationReport report, 052 String title) { 053 this.message = getLogMessage(report, title); 054 } 055 056 private StringBuilder getLogMessage(ConditionEvaluationReport report, String title) { 057 StringBuilder message = new StringBuilder(); 058 message.append(String.format("%n%n%n")); 059 StringBuilder separator = new StringBuilder(); 060 for (int i = 0; i < title.length(); i++) { 061 separator.append("="); 062 } 063 message.append(String.format("%s%n", separator)); 064 message.append(String.format("%s%n", title)); 065 message.append(String.format("%s%n%n%n", separator)); 066 Map<String, ConditionAndOutcomes> shortOutcomes = orderByName( 067 report.getConditionAndOutcomesBySource()); 068 logPositiveMatches(message, shortOutcomes); 069 logNegativeMatches(message, shortOutcomes); 070 logExclusions(report, message); 071 logUnconditionalClasses(report, message); 072 message.append(String.format("%n%n")); 073 return message; 074 } 075 076 private void logPositiveMatches(StringBuilder message, 077 Map<String, ConditionAndOutcomes> shortOutcomes) { 078 message.append(String.format("Positive matches:%n")); 079 message.append(String.format("-----------------%n")); 080 List<Entry<String, ConditionAndOutcomes>> matched = shortOutcomes.entrySet() 081 .stream().filter((entry) -> entry.getValue().isFullMatch()) 082 .collect(Collectors.toList()); 083 if (matched.isEmpty()) { 084 message.append(String.format("%n None%n")); 085 } 086 else { 087 matched.forEach((entry) -> addMatchLogMessage(message, entry.getKey(), 088 entry.getValue())); 089 } 090 message.append(String.format("%n%n")); 091 } 092 093 private void logNegativeMatches(StringBuilder message, 094 Map<String, ConditionAndOutcomes> shortOutcomes) { 095 message.append(String.format("Negative matches:%n")); 096 message.append(String.format("-----------------%n")); 097 List<Entry<String, ConditionAndOutcomes>> nonMatched = shortOutcomes.entrySet() 098 .stream().filter((entry) -> !entry.getValue().isFullMatch()) 099 .collect(Collectors.toList()); 100 if (nonMatched.isEmpty()) { 101 message.append(String.format("%n None%n")); 102 } 103 else { 104 nonMatched.forEach((entry) -> addNonMatchLogMessage(message, entry.getKey(), 105 entry.getValue())); 106 } 107 message.append(String.format("%n%n")); 108 } 109 110 private void logExclusions(ConditionEvaluationReport report, StringBuilder message) { 111 message.append(String.format("Exclusions:%n")); 112 message.append(String.format("-----------%n")); 113 if (report.getExclusions().isEmpty()) { 114 message.append(String.format("%n None%n")); 115 } 116 else { 117 for (String exclusion : report.getExclusions()) { 118 message.append(String.format("%n %s%n", exclusion)); 119 } 120 } 121 message.append(String.format("%n%n")); 122 } 123 124 private void logUnconditionalClasses(ConditionEvaluationReport report, 125 StringBuilder message) { 126 message.append(String.format("Unconditional classes:%n")); 127 message.append(String.format("----------------------%n")); 128 if (report.getUnconditionalClasses().isEmpty()) { 129 message.append(String.format("%n None%n")); 130 } 131 else { 132 for (String unconditionalClass : report.getUnconditionalClasses()) { 133 message.append(String.format("%n %s%n", unconditionalClass)); 134 } 135 } 136 } 137 138 private Map<String, ConditionAndOutcomes> orderByName( 139 Map<String, ConditionAndOutcomes> outcomes) { 140 MultiValueMap<String, String> map = mapToFullyQualifiedNames(outcomes.keySet()); 141 List<String> shortNames = new ArrayList<>(map.keySet()); 142 Collections.sort(shortNames); 143 Map<String, ConditionAndOutcomes> result = new LinkedHashMap<>(); 144 for (String shortName : shortNames) { 145 List<String> fullyQualifiedNames = map.get(shortName); 146 if (fullyQualifiedNames.size() > 1) { 147 fullyQualifiedNames.forEach((fullyQualifiedName) -> result 148 .put(fullyQualifiedName, outcomes.get(fullyQualifiedName))); 149 } 150 else { 151 result.put(shortName, outcomes.get(fullyQualifiedNames.get(0))); 152 } 153 } 154 return result; 155 } 156 157 private MultiValueMap<String, String> mapToFullyQualifiedNames(Set<String> keySet) { 158 LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>(); 159 keySet.forEach((fullyQualifiedName) -> map 160 .add(ClassUtils.getShortName(fullyQualifiedName), fullyQualifiedName)); 161 return map; 162 } 163 164 private void addMatchLogMessage(StringBuilder message, String source, 165 ConditionAndOutcomes matches) { 166 message.append(String.format("%n %s matched:%n", source)); 167 for (ConditionAndOutcome match : matches) { 168 logConditionAndOutcome(message, " ", match); 169 } 170 } 171 172 private void addNonMatchLogMessage(StringBuilder message, String source, 173 ConditionAndOutcomes conditionAndOutcomes) { 174 message.append(String.format("%n %s:%n", source)); 175 List<ConditionAndOutcome> matches = new ArrayList<>(); 176 List<ConditionAndOutcome> nonMatches = new ArrayList<>(); 177 for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) { 178 if (conditionAndOutcome.getOutcome().isMatch()) { 179 matches.add(conditionAndOutcome); 180 } 181 else { 182 nonMatches.add(conditionAndOutcome); 183 } 184 } 185 message.append(String.format(" Did not match:%n")); 186 for (ConditionAndOutcome nonMatch : nonMatches) { 187 logConditionAndOutcome(message, " ", nonMatch); 188 } 189 if (!matches.isEmpty()) { 190 message.append(String.format(" Matched:%n")); 191 for (ConditionAndOutcome match : matches) { 192 logConditionAndOutcome(message, " ", match); 193 } 194 } 195 } 196 197 private void logConditionAndOutcome(StringBuilder message, String indent, 198 ConditionAndOutcome conditionAndOutcome) { 199 message.append(String.format("%s- ", indent)); 200 String outcomeMessage = conditionAndOutcome.getOutcome().getMessage(); 201 if (StringUtils.hasLength(outcomeMessage)) { 202 message.append(outcomeMessage); 203 } 204 else { 205 message.append(conditionAndOutcome.getOutcome().isMatch() ? "matched" 206 : "did not match"); 207 } 208 message.append(" ("); 209 message.append( 210 ClassUtils.getShortName(conditionAndOutcome.getCondition().getClass())); 211 message.append(String.format(")%n")); 212 } 213 214 @Override 215 public String toString() { 216 return this.message.toString(); 217 } 218 219}