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.logging; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025/** 026 * Deferred {@link Log} that can be used to store messages that shouldn't be written until 027 * the logging system is fully initialized. 028 * 029 * @author Phillip Webb 030 * @since 1.3.0 031 */ 032public class DeferredLog implements Log { 033 034 private volatile Log destination; 035 036 private final List<Line> lines = new ArrayList<>(); 037 038 @Override 039 public boolean isTraceEnabled() { 040 synchronized (this.lines) { 041 return (this.destination != null) ? this.destination.isTraceEnabled() : true; 042 } 043 } 044 045 @Override 046 public boolean isDebugEnabled() { 047 synchronized (this.lines) { 048 return (this.destination != null) ? this.destination.isDebugEnabled() : true; 049 } 050 } 051 052 @Override 053 public boolean isInfoEnabled() { 054 synchronized (this.lines) { 055 return (this.destination != null) ? this.destination.isInfoEnabled() : true; 056 } 057 } 058 059 @Override 060 public boolean isWarnEnabled() { 061 synchronized (this.lines) { 062 return (this.destination != null) ? this.destination.isWarnEnabled() : true; 063 } 064 } 065 066 @Override 067 public boolean isErrorEnabled() { 068 synchronized (this.lines) { 069 return (this.destination != null) ? this.destination.isErrorEnabled() : true; 070 } 071 } 072 073 @Override 074 public boolean isFatalEnabled() { 075 synchronized (this.lines) { 076 return (this.destination != null) ? this.destination.isFatalEnabled() : true; 077 } 078 } 079 080 @Override 081 public void trace(Object message) { 082 log(LogLevel.TRACE, message, null); 083 } 084 085 @Override 086 public void trace(Object message, Throwable t) { 087 log(LogLevel.TRACE, message, t); 088 } 089 090 @Override 091 public void debug(Object message) { 092 log(LogLevel.DEBUG, message, null); 093 } 094 095 @Override 096 public void debug(Object message, Throwable t) { 097 log(LogLevel.DEBUG, message, t); 098 } 099 100 @Override 101 public void info(Object message) { 102 log(LogLevel.INFO, message, null); 103 } 104 105 @Override 106 public void info(Object message, Throwable t) { 107 log(LogLevel.INFO, message, t); 108 } 109 110 @Override 111 public void warn(Object message) { 112 log(LogLevel.WARN, message, null); 113 } 114 115 @Override 116 public void warn(Object message, Throwable t) { 117 log(LogLevel.WARN, message, t); 118 } 119 120 @Override 121 public void error(Object message) { 122 log(LogLevel.ERROR, message, null); 123 } 124 125 @Override 126 public void error(Object message, Throwable t) { 127 log(LogLevel.ERROR, message, t); 128 } 129 130 @Override 131 public void fatal(Object message) { 132 log(LogLevel.FATAL, message, null); 133 } 134 135 @Override 136 public void fatal(Object message, Throwable t) { 137 log(LogLevel.FATAL, message, t); 138 } 139 140 private void log(LogLevel level, Object message, Throwable t) { 141 synchronized (this.lines) { 142 if (this.destination != null) { 143 logTo(this.destination, level, message, t); 144 } 145 else { 146 this.lines.add(new Line(level, message, t)); 147 } 148 } 149 } 150 151 /** 152 * Switch from deferred logging to immediate logging to the specified destination. 153 * @param destination the new log destination 154 * @since 2.1.0 155 */ 156 public void switchTo(Class<?> destination) { 157 switchTo(LogFactory.getLog(destination)); 158 } 159 160 /** 161 * Switch from deferred logging to immediate logging to the specified destination. 162 * @param destination the new log destination 163 * @since 2.1.0 164 */ 165 public void switchTo(Log destination) { 166 synchronized (this.lines) { 167 replayTo(destination); 168 this.destination = destination; 169 } 170 } 171 172 /** 173 * Replay deferred logging to the specified destination. 174 * @param destination the destination for the deferred log messages 175 */ 176 public void replayTo(Class<?> destination) { 177 replayTo(LogFactory.getLog(destination)); 178 } 179 180 /** 181 * Replay deferred logging to the specified destination. 182 * @param destination the destination for the deferred log messages 183 */ 184 public void replayTo(Log destination) { 185 synchronized (this.lines) { 186 for (Line line : this.lines) { 187 logTo(destination, line.getLevel(), line.getMessage(), 188 line.getThrowable()); 189 } 190 this.lines.clear(); 191 } 192 } 193 194 /** 195 * Replay from a source log to a destination log when the source is deferred. 196 * @param source the source logger 197 * @param destination the destination logger class 198 * @return the destination 199 */ 200 public static Log replay(Log source, Class<?> destination) { 201 return replay(source, LogFactory.getLog(destination)); 202 } 203 204 /** 205 * Replay from a source log to a destination log when the source is deferred. 206 * @param source the source logger 207 * @param destination the destination logger 208 * @return the destination 209 */ 210 public static Log replay(Log source, Log destination) { 211 if (source instanceof DeferredLog) { 212 ((DeferredLog) source).replayTo(destination); 213 } 214 return destination; 215 } 216 217 private static void logTo(Log log, LogLevel level, Object message, 218 Throwable throwable) { 219 switch (level) { 220 case TRACE: 221 log.trace(message, throwable); 222 return; 223 case DEBUG: 224 log.debug(message, throwable); 225 return; 226 case INFO: 227 log.info(message, throwable); 228 return; 229 case WARN: 230 log.warn(message, throwable); 231 return; 232 case ERROR: 233 log.error(message, throwable); 234 return; 235 case FATAL: 236 log.fatal(message, throwable); 237 return; 238 } 239 } 240 241 private static class Line { 242 243 private final LogLevel level; 244 245 private final Object message; 246 247 private final Throwable throwable; 248 249 Line(LogLevel level, Object message, Throwable throwable) { 250 this.level = level; 251 this.message = message; 252 this.throwable = throwable; 253 } 254 255 public LogLevel getLevel() { 256 return this.level; 257 } 258 259 public Object getMessage() { 260 return this.message; 261 } 262 263 public Throwable getThrowable() { 264 return this.throwable; 265 } 266 267 } 268 269}