001/** 002 * Copyright (c) 2004-2011 QOS.ch 003 * All rights reserved. 004 * 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 * 013 * The above copyright notice and this permission notice shall be 014 * included in all copies or substantial portions of the Software. 015 * 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 * 024 */ 025package org.slf4j.jul; 026 027import java.util.logging.Level; 028import java.util.logging.LogRecord; 029 030import org.slf4j.Logger; 031import org.slf4j.Marker; 032import org.slf4j.event.EventConstants; 033import org.slf4j.event.LoggingEvent; 034import org.slf4j.helpers.AbstractLogger; 035import org.slf4j.helpers.FormattingTuple; 036import org.slf4j.helpers.LegacyAbstractLogger; 037import org.slf4j.helpers.MessageFormatter; 038import org.slf4j.helpers.NormalizedParameters; 039import org.slf4j.helpers.SubstituteLogger; 040import org.slf4j.spi.DefaultLoggingEventBuilder; 041import org.slf4j.spi.LocationAwareLogger; 042 043/** 044 * A wrapper over {@link java.util.logging.Logger java.util.logging.Logger} in 045 * conformity with the {@link Logger} interface. Note that the logging levels 046 * mentioned in this class refer to those defined in the java.util.logging 047 * package. 048 * 049 * @author Ceki Gülcü 050 * @author Peter Royal 051 */ 052public final class JDK14LoggerAdapter extends LegacyAbstractLogger implements LocationAwareLogger { 053 054 private static final long serialVersionUID = -8053026990503422791L; 055 056 transient final java.util.logging.Logger logger; 057 058 // WARN: JDK14LoggerAdapter constructor should have only package access so 059 // that only JDK14LoggerFactory be able to create one. 060 JDK14LoggerAdapter(java.util.logging.Logger logger) { 061 this.logger = logger; 062 this.name = logger.getName(); 063 } 064 065 /** 066 * Is this logger instance enabled for the FINEST level? 067 * 068 * @return True if this Logger is enabled for level FINEST, false otherwise. 069 */ 070 public boolean isTraceEnabled() { 071 return logger.isLoggable(Level.FINEST); 072 } 073 074 /** 075 * Is this logger instance enabled for the FINE level? 076 * 077 * @return True if this Logger is enabled for level FINE, false otherwise. 078 */ 079 public boolean isDebugEnabled() { 080 return logger.isLoggable(Level.FINE); 081 } 082 083 /** 084 * Is this logger instance enabled for the INFO level? 085 * 086 * @return True if this Logger is enabled for the INFO level, false otherwise. 087 */ 088 public boolean isInfoEnabled() { 089 return logger.isLoggable(Level.INFO); 090 } 091 092 /** 093 * Is this logger instance enabled for the WARNING level? 094 * 095 * @return True if this Logger is enabled for the WARNING level, false 096 * otherwise. 097 */ 098 public boolean isWarnEnabled() { 099 return logger.isLoggable(Level.WARNING); 100 } 101 102 /** 103 * Is this logger instance enabled for level SEVERE? 104 * 105 * @return True if this Logger is enabled for level SEVERE, false otherwise. 106 */ 107 public boolean isErrorEnabled() { 108 return logger.isLoggable(Level.SEVERE); 109 } 110 111 // /** 112 // * Log the message at the specified level with the specified throwable if any. 113 // * This method creates a LogRecord and fills in caller date before calling 114 // * this instance's JDK14 logger. 115 // * 116 // * See bug report #13 for more details. 117 // * 118 // * @param level 119 // * @param msg 120 // * @param t 121 // */ 122 // private void log(String callerFQCN, Level level, String msg, Throwable t) { 123 // // millis and thread are filled by the constructor 124 // LogRecord record = new LogRecord(level, msg); 125 // record.setLoggerName(getName()); 126 // record.setThrown(t); 127 // // Note: parameters in record are not set because SLF4J only 128 // // supports a single formatting style 129 // fillCallerData(callerFQCN, record); 130 // logger.log(record); 131 // } 132 133 /** 134 * Log the message at the specified level with the specified throwable if any. 135 * This method creates a LogRecord and fills in caller date before calling this 136 * instance's JDK14 logger. 137 */ 138 @Override 139 protected void handleNormalizedLoggingCall(org.slf4j.event.Level level, Marker marker, String msg, Object[] args, Throwable throwable) { 140 innerNormalizedLoggingCallHandler(getFullyQualifiedCallerName(), level, marker, msg, args, throwable); 141 } 142 143 private void innerNormalizedLoggingCallHandler(String fqcn, org.slf4j.event.Level level, Marker marker, String msg, Object[] args, Throwable throwable) { 144 // millis and thread are filled by the constructor 145 Level julLevel = slf4jLevelToJULLevel(level); 146 String formattedMessage = MessageFormatter.basicArrayFormat(msg, args); 147 LogRecord record = new LogRecord(julLevel, formattedMessage); 148 149 // https://jira.qos.ch/browse/SLF4J-13 150 record.setLoggerName(getName()); 151 record.setThrown(throwable); 152 // Note: parameters in record are not set because SLF4J only 153 // supports a single formatting style 154 // See also https://jira.qos.ch/browse/SLF4J-10 155 fillCallerData(fqcn, record); 156 logger.log(record); 157 } 158 159 @Override 160 protected String getFullyQualifiedCallerName() { 161 return SELF; 162 } 163 164 @Override 165 public void log(Marker marker, String callerFQCN, int slf4jLevelInt, String message, Object[] arguments, Throwable throwable) { 166 167 org.slf4j.event.Level slf4jLevel = org.slf4j.event.Level.intToLevel(slf4jLevelInt); 168 Level julLevel = slf4jLevelIntToJULLevel(slf4jLevelInt); 169 170 if (logger.isLoggable(julLevel)) { 171 NormalizedParameters np = NormalizedParameters.normalize(message, arguments, throwable); 172 innerNormalizedLoggingCallHandler(callerFQCN, slf4jLevel, marker, np.getMessage(), np.getArguments(), np.getThrowable()); 173 } 174 } 175 176 /** 177 * Fill in caller data if possible. 178 * 179 * @param record The record to update 180 */ 181 final private void fillCallerData(String callerFQCN, LogRecord record) { 182 StackTraceElement[] steArray = new Throwable().getStackTrace(); 183 184 int selfIndex = -1; 185 for (int i = 0; i < steArray.length; i++) { 186 final String className = steArray[i].getClassName(); 187 188 if (barrierMatch(callerFQCN, className)) { 189 selfIndex = i; 190 break; 191 } 192 } 193 194 int found = -1; 195 for (int i = selfIndex + 1; i < steArray.length; i++) { 196 final String className = steArray[i].getClassName(); 197 if (!(barrierMatch(callerFQCN, className))) { 198 found = i; 199 break; 200 } 201 } 202 203 if (found != -1) { 204 StackTraceElement ste = steArray[found]; 205 // setting the class name has the side effect of setting 206 // the needToInferCaller variable to false. 207 record.setSourceClassName(ste.getClassName()); 208 record.setSourceMethodName(ste.getMethodName()); 209 } 210 } 211 212 static String SELF = JDK14LoggerAdapter.class.getName(); 213 214 static String SUPER = LegacyAbstractLogger.class.getName(); 215 static String SUPER_OF_SUPER = AbstractLogger.class.getName(); 216 static String SUBSTITUE = SubstituteLogger.class.getName(); 217 static String FLUENT = DefaultLoggingEventBuilder.class.getName(); 218 219 static String[] BARRIER_CLASSES = new String[] { SUPER_OF_SUPER, SUPER, SELF, SUBSTITUE, FLUENT }; 220 221 private boolean barrierMatch(String callerFQCN, String candidateClassName) { 222 if (candidateClassName.equals(callerFQCN)) 223 return true; 224 for (String barrierClassName : BARRIER_CLASSES) { 225 if (barrierClassName.equals(candidateClassName)) { 226 return true; 227 } 228 } 229 return false; 230 } 231 232 private static Level slf4jLevelIntToJULLevel(int levelInt) { 233 org.slf4j.event.Level slf4jLevel = org.slf4j.event.Level.intToLevel(levelInt); 234 return slf4jLevelToJULLevel(slf4jLevel); 235 } 236 237 private static Level slf4jLevelToJULLevel(org.slf4j.event.Level slf4jLevel) { 238 Level julLevel; 239 switch (slf4jLevel) { 240 case TRACE: 241 julLevel = Level.FINEST; 242 break; 243 case DEBUG: 244 julLevel = Level.FINE; 245 break; 246 case INFO: 247 julLevel = Level.INFO; 248 break; 249 case WARN: 250 julLevel = Level.WARNING; 251 break; 252 case ERROR: 253 julLevel = Level.SEVERE; 254 break; 255 default: 256 throw new IllegalStateException("Level " + slf4jLevel + " is not recognized."); 257 } 258 return julLevel; 259 } 260 261 /** 262 * @since 1.7.15 263 */ 264 public void log(LoggingEvent event) { 265 // assumes that the invocation is made from a substitute logger 266 // this assumption might change in the future with the advent of a fluent API 267 Level julLevel = slf4jLevelToJULLevel(event.getLevel()); 268 if (logger.isLoggable(julLevel)) { 269 LogRecord record = eventToRecord(event, julLevel); 270 logger.log(record); 271 } 272 } 273 274 private LogRecord eventToRecord(LoggingEvent event, Level julLevel) { 275 String format = event.getMessage(); 276 Object[] arguments = event.getArgumentArray(); 277 FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); 278 if (ft.getThrowable() != null && event.getThrowable() != null) { 279 throw new IllegalArgumentException("both last element in argument array and last argument are of type Throwable"); 280 } 281 282 Throwable t = event.getThrowable(); 283 if (ft.getThrowable() != null) { 284 t = ft.getThrowable(); 285 throw new IllegalStateException("fix above code"); 286 } 287 288 LogRecord record = new LogRecord(julLevel, ft.getMessage()); 289 record.setLoggerName(event.getLoggerName()); 290 record.setMillis(event.getTimeStamp()); 291 record.setSourceClassName(EventConstants.NA_SUBST); 292 record.setSourceMethodName(EventConstants.NA_SUBST); 293 294 record.setThrown(t); 295 return record; 296 } 297 298}