001/** 002 * Copyright (c) 2004-2022 QOS.ch 003 * All rights reserved. 004 * <p> 005 * Permission is hereby granted, free of charge, to any person obtaining 006 * a copy of this software and associated documentation files (the 007 * "Software"), to deal in the Software without restriction, including 008 * without limitation the rights to use, copy, modify, merge, publish, 009 * distribute, sublicense, and/or sell copies of the Software, and to 010 * permit persons to whom the Software is furnished to do so, subject to 011 * the following conditions: 012 * <p> 013 * The above copyright notice and this permission notice shall be 014 * included in all copies or substantial portions of the Software. 015 * <p> 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 021 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 023 */ 024package org.slf4j.spi; 025 026import java.util.List; 027import java.util.function.Supplier; 028 029import org.slf4j.Logger; 030import org.slf4j.Marker; 031import org.slf4j.event.DefaultLoggingEvent; 032import org.slf4j.event.KeyValuePair; 033import org.slf4j.event.Level; 034import org.slf4j.event.LoggingEvent; 035import org.slf4j.helpers.Reporter; 036 037/** 038 * Default implementation of {@link LoggingEventBuilder} 039 */ 040public class DefaultLoggingEventBuilder implements LoggingEventBuilder, CallerBoundaryAware { 041 042 043 // The caller boundary when the log() methods are invoked, is this class itself. 044 045 static String DLEB_FQCN = DefaultLoggingEventBuilder.class.getName(); 046 047 protected DefaultLoggingEvent loggingEvent; 048 protected Logger logger; 049 050 public DefaultLoggingEventBuilder(Logger logger, Level level) { 051 this.logger = logger; 052 loggingEvent = new DefaultLoggingEvent(level, logger); 053 } 054 055 /** 056 * Add a marker to the current logging event being built. 057 * <p> 058 * It is possible to add multiple markers to the same logging event. 059 * 060 * @param marker the marker to add 061 */ 062 @Override 063 public LoggingEventBuilder addMarker(Marker marker) { 064 loggingEvent.addMarker(marker); 065 return this; 066 } 067 068 @Override 069 public LoggingEventBuilder setCause(Throwable t) { 070 loggingEvent.setThrowable(t); 071 return this; 072 } 073 074 @Override 075 public LoggingEventBuilder addArgument(Object p) { 076 this.loggingEvent.addArgument(p); 077 return this; 078 } 079 080 @Override 081 public LoggingEventBuilder addArgument(Supplier<?> objectSupplier) { 082 this.loggingEvent.addArgument(objectSupplier.get()); 083 return this; 084 } 085 086 @Override 087 public LoggingEventBuilder addKeyValue(String key, Object value) { 088 loggingEvent.addKeyValue(key, value); 089 return this; 090 } 091 092 @Override 093 public LoggingEventBuilder addKeyValue(String key, Supplier<Object> value) { 094 loggingEvent.addKeyValue(key, value.get()); 095 return this; 096 } 097 098 @Override 099 public void setCallerBoundary(String fqcn) { 100 this.loggingEvent.setCallerBoundary(fqcn); 101 } 102 103 @Override 104 public void log() { 105 log(this.loggingEvent); 106 } 107 108 @Override 109 public LoggingEventBuilder setMessage(String message) { 110 this.loggingEvent.setMessage(message); 111 return this; 112 } 113 114 @Override 115 public LoggingEventBuilder setMessage(Supplier<String> messageSupplier) { 116 this.loggingEvent.setMessage(messageSupplier.get()); 117 return this; 118 } 119 120 @Override 121 public void log(String message) { 122 this.loggingEvent.setMessage(message); 123 log(this.loggingEvent); 124 } 125 126 @Override 127 public void log(String message, Object arg) { 128 this.loggingEvent.setMessage(message); 129 this.loggingEvent.addArgument(arg); 130 log(this.loggingEvent); 131 } 132 133 @Override 134 public void log(String message, Object arg0, Object arg1) { 135 this.loggingEvent.setMessage(message); 136 this.loggingEvent.addArgument(arg0); 137 this.loggingEvent.addArgument(arg1); 138 log(this.loggingEvent); 139 } 140 141 @Override 142 public void log(String message, Object... args) { 143 this.loggingEvent.setMessage(message); 144 this.loggingEvent.addArguments(args); 145 146 log(this.loggingEvent); 147 } 148 149 @Override 150 public void log(Supplier<String> messageSupplier) { 151 if(messageSupplier == null) { 152 log((String) null); 153 } else { 154 log(messageSupplier.get()); 155 } 156 } 157 158 protected void log(LoggingEvent aLoggingEvent) { 159 if(aLoggingEvent.getCallerBoundary() == null) { 160 setCallerBoundary(DLEB_FQCN); 161 } 162 163 if(logger instanceof LoggingEventAware) { 164 ((LoggingEventAware) logger).log(aLoggingEvent); 165 } else if(logger instanceof LocationAwareLogger) { 166 logViaLocationAwareLoggerAPI((LocationAwareLogger) logger, aLoggingEvent); 167 } else { 168 logViaPublicSLF4JLoggerAPI(aLoggingEvent); 169 } 170 } 171 172 private void logViaLocationAwareLoggerAPI(LocationAwareLogger locationAwareLogger, LoggingEvent aLoggingEvent) { 173 List<Marker> markerList = aLoggingEvent.getMarkers(); 174 Marker firstMarker = (markerList != null && !markerList.isEmpty()) ? markerList.get(0) : null; 175 String mergedMessage = mergeMarkersAndKeyValuePairsAndMessage(aLoggingEvent); 176 locationAwareLogger.log(firstMarker, aLoggingEvent.getCallerBoundary(), aLoggingEvent.getLevel().toInt(), 177 mergedMessage, 178 aLoggingEvent.getArgumentArray(), aLoggingEvent.getThrowable()); 179 } 180 181 private void logViaPublicSLF4JLoggerAPI(LoggingEvent aLoggingEvent) { 182 Object[] argArray = aLoggingEvent.getArgumentArray(); 183 int argLen = argArray == null ? 0 : argArray.length; 184 185 Throwable t = aLoggingEvent.getThrowable(); 186 int tLen = t == null ? 0 : 1; 187 188 Object[] combinedArguments = new Object[argLen + tLen]; 189 190 if(argArray != null) { 191 System.arraycopy(argArray, 0, combinedArguments, 0, argLen); 192 } 193 if(t != null) { 194 combinedArguments[argLen] = t; 195 } 196 197 String mergedMessage = mergeMarkersAndKeyValuePairsAndMessage(aLoggingEvent); 198 199 200 switch(aLoggingEvent.getLevel()) { 201 case TRACE: 202 logger.trace(mergedMessage, combinedArguments); 203 break; 204 case DEBUG: 205 logger.debug(mergedMessage, combinedArguments); 206 break; 207 case INFO: 208 logger.info(mergedMessage, combinedArguments); 209 break; 210 case WARN: 211 logger.warn(mergedMessage, combinedArguments); 212 break; 213 case ERROR: 214 logger.error(mergedMessage, combinedArguments); 215 break; 216 } 217 } 218 219 220 /** 221 * Prepend markers and key-value pairs to the message. 222 * 223 * @param aLoggingEvent 224 * 225 * @return 226 */ 227 private String mergeMarkersAndKeyValuePairsAndMessage(LoggingEvent aLoggingEvent) { 228 StringBuilder sb = mergeMarkers(aLoggingEvent.getMarkers(), null); 229 sb = mergeKeyValuePairs(aLoggingEvent.getKeyValuePairs(), sb); 230 final String mergedMessage = mergeMessage(aLoggingEvent.getMessage(), sb); 231 return mergedMessage; 232 } 233 234 private StringBuilder mergeMarkers(List<Marker> markerList, StringBuilder sb) { 235 if(markerList == null || markerList.isEmpty()) 236 return sb; 237 238 if(sb == null) 239 sb = new StringBuilder(); 240 241 for(Marker marker : markerList) { 242 sb.append(marker); 243 sb.append(' '); 244 } 245 return sb; 246 } 247 248 private StringBuilder mergeKeyValuePairs(List<KeyValuePair> keyValuePairList, StringBuilder sb) { 249 if(keyValuePairList == null || keyValuePairList.isEmpty()) 250 return sb; 251 252 if(sb == null) 253 sb = new StringBuilder(); 254 255 for(KeyValuePair kvp : keyValuePairList) { 256 sb.append(kvp.key); 257 sb.append('='); 258 // Same protection as MessageFormatter.safeObjectAppend: a failing 259 // toString() (including StackOverflowError) must not abort logging. 260 // See https://github.com/qos-ch/slf4j/issues/448 261 safeObjectAppend(sb, kvp.value); 262 sb.append(' '); 263 } 264 return sb; 265 } 266 267 /** 268 * Append {@code o} to {@code sb}, catching any {@link Throwable} thrown by 269 * {@link Object#toString()} and substituting {@code [FAILED toString()]}. 270 * Mirrors {@code MessageFormatter.safeObjectAppend}. 271 */ 272 private static void safeObjectAppend(StringBuilder sb, Object o) { 273 if (o == null) { 274 sb.append("null"); 275 return; 276 } 277 try { 278 sb.append(o.toString()); 279 } catch (Throwable t) { 280 Reporter.error("Failed toString() invocation on an object of type [" + o.getClass().getName() + "]", t); 281 sb.append("[FAILED toString()]"); 282 } 283 } 284 285 private String mergeMessage(String msg, StringBuilder sb) { 286 if(sb != null) { 287 sb.append(msg); 288 return sb.toString(); 289 } else { 290 return msg; 291 } 292 } 293 294 295 296 297 298 299}