1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 2012-2018 Oracle and/or its affiliates. All rights reserved. 5 * 6 * The contents of this file are subject to the terms of either the GNU 7 * General Public License Version 2 only ("GPL") or the Common Development 8 * and Distribution License("CDDL") (collectively, the "License"). You 9 * may not use this file except in compliance with the License. You can 10 * obtain a copy of the License at 11 * https://oss.oracle.com/licenses/CDDL+GPL-1.1 12 * or LICENSE.txt. See the License for the specific 13 * language governing permissions and limitations under the License. 14 * 15 * When distributing the software, include this License Header Notice in each 16 * file and include the License file at LICENSE.txt. 17 * 18 * GPL Classpath Exception: 19 * Oracle designates this particular file as subject to the "Classpath" 20 * exception as provided by Oracle in the GPL Version 2 section of the License 21 * file that accompanied this code. 22 * 23 * Modifications: 24 * If applicable, add the following below the License Header, with the fields 25 * enclosed by brackets [] replaced by your own identifying information: 26 * "Portions Copyright [year] [name of copyright owner]" 27 * 28 * Contributor(s): 29 * If you wish your version of this file to be governed by only the CDDL or 30 * only the GPL Version 2, indicate your decision by adding "[Contributor] 31 * elects to include this software in this distribution under the [CDDL or GPL 32 * Version 2] license." If you don't indicate a single choice of license, a 33 * recipient has the option to distribute your version of this file under 34 * either the CDDL, the GPL Version 2 or to extend the choice of license to 35 * its licensees as provided above. However, if you add GPL Version 2 code 36 * and therefore, elected the GPL Version 2 license, then the option applies 37 * only if the new code is made subject to such option by the copyright 38 * holder. 39 */ 40 41 package com.sun.mail.util; 42 43 import java.io.PrintStream; 44 import java.text.MessageFormat; 45 import java.util.logging.Level; 46 import java.util.logging.Logger; 47 import javax.mail.Session; 48 49 /** 50 * A simplified logger used by JavaMail to handle logging to a 51 * PrintStream and logging through a java.util.logging.Logger. 52 * If debug is set, messages are written to the PrintStream and 53 * prefixed by the specified prefix (which is not included in 54 * Logger messages). 55 * Messages are logged by the Logger based on the configuration 56 * of the logging system. 57 */ 58 59 /* 60 * It would be so much simpler to just subclass Logger and override 61 * the log(LogRecord) method, as the javadocs suggest, but that doesn't 62 * work because Logger makes the decision about whether to log the message 63 * or not before calling the log(LogRecord) method. Instead, we just 64 * provide the few log methods we need here. 65 */ 66 67 public final class MailLogger { 68 /** 69 * For log messages. 70 */ 71 private final Logger logger; 72 /** 73 * For debug output. 74 */ 75 private final String prefix; 76 /** 77 * Produce debug output? 78 */ 79 private final boolean debug; 80 /** 81 * Stream for debug output. 82 */ 83 private final PrintStream out; 84 85 /** 86 * Construct a new MailLogger using the specified Logger name, 87 * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. 88 * 89 * @param name the Logger name 90 * @param prefix the prefix for debug output, or null for none 91 * @param debug if true, write to PrintStream 92 * @param out the PrintStream to write to 93 */ MailLogger(String name, String prefix, boolean debug, PrintStream out)94 public MailLogger(String name, String prefix, boolean debug, 95 PrintStream out) { 96 logger = Logger.getLogger(name); 97 this.prefix = prefix; 98 this.debug = debug; 99 this.out = out != null ? out : System.out; 100 } 101 102 /** 103 * Construct a new MailLogger using the specified class' package 104 * name as the Logger name, 105 * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. 106 * 107 * @param clazz the Logger name is the package name of this class 108 * @param prefix the prefix for debug output, or null for none 109 * @param debug if true, write to PrintStream 110 * @param out the PrintStream to write to 111 */ MailLogger(Class<?> clazz, String prefix, boolean debug, PrintStream out)112 public MailLogger(Class<?> clazz, String prefix, boolean debug, 113 PrintStream out) { 114 String name = packageOf(clazz); 115 logger = Logger.getLogger(name); 116 this.prefix = prefix; 117 this.debug = debug; 118 this.out = out != null ? out : System.out; 119 } 120 121 /** 122 * Construct a new MailLogger using the specified class' package 123 * name combined with the specified subname as the Logger name, 124 * debug prefix (e.g., "DEBUG"), debug flag, and PrintStream. 125 * 126 * @param clazz the Logger name is the package name of this class 127 * @param subname the Logger name relative to this Logger name 128 * @param prefix the prefix for debug output, or null for none 129 * @param debug if true, write to PrintStream 130 * @param out the PrintStream to write to 131 */ MailLogger(Class<?> clazz, String subname, String prefix, boolean debug, PrintStream out)132 public MailLogger(Class<?> clazz, String subname, String prefix, boolean debug, 133 PrintStream out) { 134 String name = packageOf(clazz) + "." + subname; 135 logger = Logger.getLogger(name); 136 this.prefix = prefix; 137 this.debug = debug; 138 this.out = out != null ? out : System.out; 139 } 140 141 /** 142 * Construct a new MailLogger using the specified Logger name and 143 * debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream 144 * from the Session. 145 * 146 * @param name the Logger name 147 * @param prefix the prefix for debug output, or null for none 148 * @param session where to get the debug flag and PrintStream 149 */ 150 @Deprecated MailLogger(String name, String prefix, Session session)151 public MailLogger(String name, String prefix, Session session) { 152 this(name, prefix, session.getDebug(), session.getDebugOut()); 153 } 154 155 /** 156 * Construct a new MailLogger using the specified class' package 157 * name as the Logger name and the specified 158 * debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream 159 * from the Session. 160 * 161 * @param clazz the Logger name is the package name of this class 162 * @param prefix the prefix for debug output, or null for none 163 * @param session where to get the debug flag and PrintStream 164 */ 165 @Deprecated MailLogger(Class<?> clazz, String prefix, Session session)166 public MailLogger(Class<?> clazz, String prefix, Session session) { 167 this(clazz, prefix, session.getDebug(), session.getDebugOut()); 168 } 169 170 /** 171 * Create a MailLogger that uses a Logger with the specified name 172 * and prefix. The new MailLogger uses the same debug flag and 173 * PrintStream as this MailLogger. 174 * 175 * @param name the Logger name 176 * @param prefix the prefix for debug output, or null for none 177 * @return a MailLogger for the given name and prefix. 178 */ getLogger(String name, String prefix)179 public MailLogger getLogger(String name, String prefix) { 180 return new MailLogger(name, prefix, debug, out); 181 } 182 183 /** 184 * Create a MailLogger using the specified class' package 185 * name as the Logger name and the specified prefix. 186 * The new MailLogger uses the same debug flag and 187 * PrintStream as this MailLogger. 188 * 189 * @param clazz the Logger name is the package name of this class 190 * @param prefix the prefix for debug output, or null for none 191 * @return a MailLogger for the given name and prefix. 192 */ getLogger(Class<?> clazz, String prefix)193 public MailLogger getLogger(Class<?> clazz, String prefix) { 194 return new MailLogger(clazz, prefix, debug, out); 195 } 196 197 /** 198 * Create a MailLogger that uses a Logger whose name is composed 199 * of this MailLogger's name plus the specified sub-name, separated 200 * by a dot. The new MailLogger uses the new prefix for debug output. 201 * This is used primarily by the protocol trace code that wants a 202 * different prefix (none). 203 * 204 * @param subname the Logger name relative to this Logger name 205 * @param prefix the prefix for debug output, or null for none 206 * @return a MailLogger for the given name and prefix. 207 */ getSubLogger(String subname, String prefix)208 public MailLogger getSubLogger(String subname, String prefix) { 209 return new MailLogger(logger.getName() + "." + subname, prefix, 210 debug, out); 211 } 212 213 /** 214 * Create a MailLogger that uses a Logger whose name is composed 215 * of this MailLogger's name plus the specified sub-name, separated 216 * by a dot. The new MailLogger uses the new prefix for debug output. 217 * This is used primarily by the protocol trace code that wants a 218 * different prefix (none). 219 * 220 * @param subname the Logger name relative to this Logger name 221 * @param prefix the prefix for debug output, or null for none 222 * @param debug the debug flag for the sub-logger 223 * @return a MailLogger for the given name and prefix. 224 */ getSubLogger(String subname, String prefix, boolean debug)225 public MailLogger getSubLogger(String subname, String prefix, 226 boolean debug) { 227 return new MailLogger(logger.getName() + "." + subname, prefix, 228 debug, out); 229 } 230 231 /** 232 * Log the message at the specified level. 233 * @param level the log level. 234 * @param msg the message. 235 */ log(Level level, String msg)236 public void log(Level level, String msg) { 237 ifDebugOut(msg); 238 if (logger.isLoggable(level)) { 239 final StackTraceElement frame = inferCaller(); 240 logger.logp(level, frame.getClassName(), frame.getMethodName(), msg); 241 } 242 } 243 244 /** 245 * Log the message at the specified level. 246 * @param level the log level. 247 * @param msg the message. 248 * @param param1 the additional parameter. 249 */ log(Level level, String msg, Object param1)250 public void log(Level level, String msg, Object param1) { 251 if (debug) { 252 msg = MessageFormat.format(msg, new Object[] { param1 }); 253 debugOut(msg); 254 } 255 256 if (logger.isLoggable(level)) { 257 final StackTraceElement frame = inferCaller(); 258 logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, param1); 259 } 260 } 261 262 /** 263 * Log the message at the specified level. 264 * @param level the log level. 265 * @param msg the message. 266 * @param params the message parameters. 267 */ log(Level level, String msg, Object... params)268 public void log(Level level, String msg, Object... params) { 269 if (debug) { 270 msg = MessageFormat.format(msg, params); 271 debugOut(msg); 272 } 273 274 if (logger.isLoggable(level)) { 275 final StackTraceElement frame = inferCaller(); 276 logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, params); 277 } 278 } 279 280 /** 281 * Log the message at the specified level using a format string. 282 * @param level the log level. 283 * @param msg the message format string. 284 * @param params the message parameters. 285 * 286 * @since JavaMail 1.5.4 287 */ logf(Level level, String msg, Object... params)288 public void logf(Level level, String msg, Object... params) { 289 msg = String.format(msg, params); 290 ifDebugOut(msg); 291 logger.log(level, msg); 292 } 293 294 /** 295 * Log the message at the specified level. 296 * @param level the log level. 297 * @param msg the message. 298 * @param thrown the throwable to log. 299 */ log(Level level, String msg, Throwable thrown)300 public void log(Level level, String msg, Throwable thrown) { 301 if (debug) { 302 if (thrown != null) { 303 debugOut(msg + ", THROW: "); 304 thrown.printStackTrace(out); 305 } else { 306 debugOut(msg); 307 } 308 } 309 310 if (logger.isLoggable(level)) { 311 final StackTraceElement frame = inferCaller(); 312 logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, thrown); 313 } 314 } 315 316 /** 317 * Log a message at the CONFIG level. 318 * @param msg the message. 319 */ config(String msg)320 public void config(String msg) { 321 log(Level.CONFIG, msg); 322 } 323 324 /** 325 * Log a message at the FINE level. 326 * @param msg the message. 327 */ fine(String msg)328 public void fine(String msg) { 329 log(Level.FINE, msg); 330 } 331 332 /** 333 * Log a message at the FINER level. 334 * @param msg the message. 335 */ finer(String msg)336 public void finer(String msg) { 337 log(Level.FINER, msg); 338 } 339 340 /** 341 * Log a message at the FINEST level. 342 * @param msg the message. 343 */ finest(String msg)344 public void finest(String msg) { 345 log(Level.FINEST, msg); 346 } 347 348 /** 349 * If "debug" is set, or our embedded Logger is loggable at the 350 * given level, return true. 351 * @param level the log level. 352 * @return true if loggable. 353 */ isLoggable(Level level)354 public boolean isLoggable(Level level) { 355 return debug || logger.isLoggable(level); 356 } 357 358 /** 359 * Common code to conditionally log debug statements. 360 * @param msg the message to log. 361 */ ifDebugOut(String msg)362 private void ifDebugOut(String msg) { 363 if (debug) 364 debugOut(msg); 365 } 366 367 /** 368 * Common formatting for debug output. 369 * @param msg the message to log. 370 */ debugOut(String msg)371 private void debugOut(String msg) { 372 if (prefix != null) 373 out.println(prefix + ": " + msg); 374 else 375 out.println(msg); 376 } 377 378 /** 379 * Return the package name of the class. 380 * Sometimes there will be no Package object for the class, 381 * e.g., if the class loader hasn't created one (see Class.getPackage()). 382 * @param clazz the class source. 383 * @return the package name or an empty string. 384 */ packageOf(Class<?> clazz)385 private String packageOf(Class<?> clazz) { 386 Package p = clazz.getPackage(); 387 if (p != null) 388 return p.getName(); // hopefully the common case 389 String cname = clazz.getName(); 390 int i = cname.lastIndexOf('.'); 391 if (i > 0) 392 return cname.substring(0, i); 393 // no package name, now what? 394 return ""; 395 } 396 397 /** 398 * A disadvantage of not being able to use Logger directly in JavaMail 399 * code is that the "source class" information that Logger guesses will 400 * always refer to this class instead of our caller. This method 401 * duplicates what Logger does to try to find *our* caller, so that 402 * Logger doesn't have to do it (and get the wrong answer), and because 403 * our caller is what's wanted. 404 * @return StackTraceElement that logged the message. Treat as read-only. 405 */ inferCaller()406 private StackTraceElement inferCaller() { 407 // Get the stack trace. 408 StackTraceElement stack[] = (new Throwable()).getStackTrace(); 409 // First, search back to a method in the Logger class. 410 int ix = 0; 411 while (ix < stack.length) { 412 StackTraceElement frame = stack[ix]; 413 String cname = frame.getClassName(); 414 if (isLoggerImplFrame(cname)) { 415 break; 416 } 417 ix++; 418 } 419 // Now search for the first frame before the "Logger" class. 420 while (ix < stack.length) { 421 StackTraceElement frame = stack[ix]; 422 String cname = frame.getClassName(); 423 if (!isLoggerImplFrame(cname)) { 424 // We've found the relevant frame. 425 return frame; 426 } 427 ix++; 428 } 429 // We haven't found a suitable frame, so just punt. This is 430 // OK as we are only committed to making a "best effort" here. 431 return new StackTraceElement(MailLogger.class.getName(), "log", 432 MailLogger.class.getName(), -1); 433 } 434 435 /** 436 * Frames to ignore as part of the MailLogger to JUL bridge. 437 * @param cname the class name. 438 * @return true if the class name is part of the MailLogger bridge. 439 */ isLoggerImplFrame(String cname)440 private boolean isLoggerImplFrame(String cname) { 441 return MailLogger.class.getName().equals(cname); 442 } 443 } 444