jakarta.mail.MailLogger Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.mail;
import java.io.PrintStream;
import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A simplified logger used by Jakarta Mail to handle logging to a
* PrintStream and logging through a java.util.logging.Logger.
* If debug is set, messages are written to the PrintStream and
* prefixed by the specified prefix (which is not included in
* Logger messages).
* Messages are logged by the Logger based on the configuration
* of the logging system.
*/
/*
* It would be so much simpler to just subclass Logger and override
* the log(LogRecord) method, as the javadocs suggest, but that doesn't
* work because Logger makes the decision about whether to log the message
* or not before calling the log(LogRecord) method. Instead, we just
* provide the few log methods we need here.
*/
final class MailLogger {
/**
* For log messages.
*/
private final Logger logger;
/**
* For debug output.
*/
private final String prefix;
/**
* Produce debug output?
*/
private final boolean debug;
/**
* Stream for debug output.
*/
private final PrintStream out;
/**
* Construct a new MailLogger using the specified Logger name,
* debug prefix (e.g., "DEBUG"), debug flag, and PrintStream.
*
* @param name the Logger name
* @param prefix the prefix for debug output, or null for none
* @param debug if true, write to PrintStream
* @param out the PrintStream to write to
*/
public MailLogger(String name, String prefix, boolean debug,
PrintStream out) {
logger = Logger.getLogger(name);
this.prefix = prefix;
this.debug = debug;
this.out = out != null ? out : System.out;
}
/**
* Construct a new MailLogger using the specified class' package
* name as the Logger name,
* debug prefix (e.g., "DEBUG"), debug flag, and PrintStream.
*
* @param clazz the Logger name is the package name of this class
* @param prefix the prefix for debug output, or null for none
* @param debug if true, write to PrintStream
* @param out the PrintStream to write to
*/
public MailLogger(Class> clazz, String prefix, boolean debug,
PrintStream out) {
String name = packageOf(clazz);
logger = Logger.getLogger(name);
this.prefix = prefix;
this.debug = debug;
this.out = out != null ? out : System.out;
}
/**
* Construct a new MailLogger using the specified class' package
* name combined with the specified subname as the Logger name,
* debug prefix (e.g., "DEBUG"), debug flag, and PrintStream.
*
* @param clazz the Logger name is the package name of this class
* @param subname the Logger name relative to this Logger name
* @param prefix the prefix for debug output, or null for none
* @param debug if true, write to PrintStream
* @param out the PrintStream to write to
*/
public MailLogger(Class> clazz, String subname, String prefix, boolean debug,
PrintStream out) {
String name = packageOf(clazz) + "." + subname;
logger = Logger.getLogger(name);
this.prefix = prefix;
this.debug = debug;
this.out = out != null ? out : System.out;
}
/**
* Construct a new MailLogger using the specified Logger name and
* debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream
* from the Session.
*
* @param name the Logger name
* @param prefix the prefix for debug output, or null for none
* @param session where to get the debug flag and PrintStream
*/
@Deprecated
public MailLogger(String name, String prefix, Session session) {
this(name, prefix, session.getDebug(), session.getDebugOut());
}
/**
* Construct a new MailLogger using the specified class' package
* name as the Logger name and the specified
* debug prefix (e.g., "DEBUG"). Get the debug flag and PrintStream
* from the Session.
*
* @param clazz the Logger name is the package name of this class
* @param prefix the prefix for debug output, or null for none
* @param session where to get the debug flag and PrintStream
*/
@Deprecated
public MailLogger(Class> clazz, String prefix, Session session) {
this(clazz, prefix, session.getDebug(), session.getDebugOut());
}
/**
* Create a MailLogger that uses a Logger with the specified name
* and prefix. The new MailLogger uses the same debug flag and
* PrintStream as this MailLogger.
*
* @param name the Logger name
* @param prefix the prefix for debug output, or null for none
* @return a MailLogger for the given name and prefix.
*/
public MailLogger getLogger(String name, String prefix) {
return new MailLogger(name, prefix, debug, out);
}
/**
* Create a MailLogger using the specified class' package
* name as the Logger name and the specified prefix.
* The new MailLogger uses the same debug flag and
* PrintStream as this MailLogger.
*
* @param clazz the Logger name is the package name of this class
* @param prefix the prefix for debug output, or null for none
* @return a MailLogger for the given name and prefix.
*/
public MailLogger getLogger(Class> clazz, String prefix) {
return new MailLogger(clazz, prefix, debug, out);
}
/**
* Create a MailLogger that uses a Logger whose name is composed
* of this MailLogger's name plus the specified sub-name, separated
* by a dot. The new MailLogger uses the new prefix for debug output.
* This is used primarily by the protocol trace code that wants a
* different prefix (none).
*
* @param subname the Logger name relative to this Logger name
* @param prefix the prefix for debug output, or null for none
* @return a MailLogger for the given name and prefix.
*/
public MailLogger getSubLogger(String subname, String prefix) {
return new MailLogger(logger.getName() + "." + subname, prefix,
debug, out);
}
/**
* Create a MailLogger that uses a Logger whose name is composed
* of this MailLogger's name plus the specified sub-name, separated
* by a dot. The new MailLogger uses the new prefix for debug output.
* This is used primarily by the protocol trace code that wants a
* different prefix (none).
*
* @param subname the Logger name relative to this Logger name
* @param prefix the prefix for debug output, or null for none
* @param debug the debug flag for the sub-logger
* @return a MailLogger for the given name and prefix.
*/
public MailLogger getSubLogger(String subname, String prefix,
boolean debug) {
return new MailLogger(logger.getName() + "." + subname, prefix,
debug, out);
}
/**
* Log the message at the specified level.
*
* @param level the log level.
* @param msg the message.
*/
public void log(Level level, String msg) {
ifDebugOut(msg);
if (logger.isLoggable(level)) {
final StackTraceElement frame = inferCaller();
logger.logp(level, frame.getClassName(), frame.getMethodName(), msg);
}
}
/**
* Log the message at the specified level.
*
* @param level the log level.
* @param msg the message.
* @param param1 the additional parameter.
*/
public void log(Level level, String msg, Object param1) {
if (debug) {
msg = MessageFormat.format(msg, param1);
debugOut(msg);
}
if (logger.isLoggable(level)) {
final StackTraceElement frame = inferCaller();
logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, param1);
}
}
/**
* Log the message at the specified level.
*
* @param level the log level.
* @param msg the message.
* @param params the message parameters.
*/
public void log(Level level, String msg, Object... params) {
if (debug) {
msg = MessageFormat.format(msg, params);
debugOut(msg);
}
if (logger.isLoggable(level)) {
final StackTraceElement frame = inferCaller();
logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, params);
}
}
/**
* Log the message at the specified level using a format string.
*
* @param level the log level.
* @param msg the message format string.
* @param params the message parameters.
* @since JavaMail 1.5.4
*/
public void logf(Level level, String msg, Object... params) {
msg = String.format(msg, params);
ifDebugOut(msg);
logger.log(level, msg);
}
/**
* Log the message at the specified level.
*
* @param level the log level.
* @param msg the message.
* @param thrown the throwable to log.
*/
public void log(Level level, String msg, Throwable thrown) {
if (debug) {
if (thrown != null) {
debugOut(msg + ", THROW: ");
thrown.printStackTrace(out);
} else {
debugOut(msg);
}
}
if (logger.isLoggable(level)) {
final StackTraceElement frame = inferCaller();
logger.logp(level, frame.getClassName(), frame.getMethodName(), msg, thrown);
}
}
/**
* Log a message at the CONFIG level.
*
* @param msg the message.
*/
public void config(String msg) {
log(Level.CONFIG, msg);
}
/**
* Log a message at the FINE level.
*
* @param msg the message.
*/
public void fine(String msg) {
log(Level.FINE, msg);
}
/**
* Log a message at the FINER level.
*
* @param msg the message.
*/
public void finer(String msg) {
log(Level.FINER, msg);
}
/**
* Log a message at the FINEST level.
*
* @param msg the message.
*/
public void finest(String msg) {
log(Level.FINEST, msg);
}
/**
* If "debug" is set, or our embedded Logger is loggable at the
* given level, return true.
*
* @param level the log level.
* @return true if loggable.
*/
public boolean isLoggable(Level level) {
return debug || logger.isLoggable(level);
}
/**
* Common code to conditionally log debug statements.
*
* @param msg the message to log.
*/
private void ifDebugOut(String msg) {
if (debug)
debugOut(msg);
}
/**
* Common formatting for debug output.
*
* @param msg the message to log.
*/
private void debugOut(String msg) {
if (prefix != null)
out.println(prefix + ": " + msg);
else
out.println(msg);
}
/**
* Return the package name of the class.
* Sometimes there will be no Package object for the class,
* e.g., if the class loader hasn't created one (see Class.getPackage()).
*
* @param clazz the class source.
* @return the package name or an empty string.
*/
private String packageOf(Class> clazz) {
Package p = clazz.getPackage();
if (p != null)
return p.getName(); // hopefully the common case
String cname = clazz.getName();
int i = cname.lastIndexOf('.');
if (i > 0)
return cname.substring(0, i);
// no package name, now what?
return "";
}
/**
* A disadvantage of not being able to use Logger directly in Jakarta Mail
* code is that the "source class" information that Logger guesses will
* always refer to this class instead of our caller. This method
* duplicates what Logger does to try to find *our* caller, so that
* Logger doesn't have to do it (and get the wrong answer), and because
* our caller is what's wanted.
*
* @return StackTraceElement that logged the message. Treat as read-only.
*/
private StackTraceElement inferCaller() {
// Get the stack trace.
StackTraceElement[] stack = (new Throwable()).getStackTrace();
// First, search back to a method in the Logger class.
int ix = 0;
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (isLoggerImplFrame(cname)) {
break;
}
ix++;
}
// Now search for the first frame before the "Logger" class.
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (!isLoggerImplFrame(cname)) {
// We've found the relevant frame.
return frame;
}
ix++;
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
return new StackTraceElement(MailLogger.class.getName(), "log",
MailLogger.class.getName(), -1);
}
/**
* Frames to ignore as part of the MailLogger to JUL bridge.
*
* @param cname the class name.
* @return true if the class name is part of the MailLogger bridge.
*/
private boolean isLoggerImplFrame(String cname) {
return MailLogger.class.getName().equals(cname);
}
}