Protect and operate

Diagnostics

Test SMTP connections, inspect the settings in use, route debug output, understand failures, and read server replies.

§

Logging emails instead of sending

You can configure the Mailer to log emails instead of sending them.

Note: normally emails are always logged on Level.DEBUG level, but in logging mode emails are logged in Level.INFO.

Mailer mailer = MailerBuilder
    .withTransportModeLoggingOnly()
    .(...)
    .buildMailer();

mailer.sendMail(email); // <-- this email will be logged but not sent
or:
simplejavamail.transport.mode.logging.only=true
§

Override envelope-level receivers

For testing purposes, you can change where the email is sent to, without touching the actual email being sent, by overriding the envelope-level receivers. This keeps test routing outside the message content while preserving the original addresses for inspection.

// send the email to your testers
currentEmailBuilder.withOverrideReceivers(tester1Recipient, testGroupInboxRecipient);
§

Enable Jakarta Mail debug logging

By default Jakarta Mail's debug logging is turned off, but you can turn it on for more diagnostic info. You can also route the debug stream to stdout, stderr or SLF4J without touching the internal Session instance directly.

Use withDebugPrinter(PrintStream) when the built-in targets do not fit. Simple Java Mail passes that stream to Jakarta Mail but does not close it. Keep it open for as long as the Mailer may use it, then close it yourself.

A custom PrintStream is a Java-only setting: a live stream cannot be represented by a property or CLI argument. For those forms, choose STDOUT, STDERR, or SLF4J through SessionDebugOutput instead.

Mailer mailer = MailerBuilder
    .withSMTPServer("host", 587, "username", "password")
    .withDebugLogging(true)
    .withDebugOutput(SessionDebugOutput.SLF4J)
    .buildMailer();

// or
mailer.getSession().setDebug(true);

// or
yourSession.setDebug(true);
MailerBuilder.usingSession(yourSession);
or:
simplejavamail.javaxmail.debug=true
simplejavamail.javaxmail.debug.out=SLF4J

In this example both resources have a clear owner. Try-with-resources closes the Mailer first and the debug stream afterwards, so Jakarta Mail cannot write to an already closed stream.

try (PrintStream jakartaMailDebug = new PrintStream(
        Files.newOutputStream(Path.of("jakarta-mail-debug.log")));
     Mailer mailer = MailerBuilder
        .withSMTPServer("host", 587, "username", "password")
        .withDebugLogging(true)
        .withDebugPrinter(jakartaMailDebug)
        .buildMailer()) {

    mailer.testConnection();
}
§

Inspect a mailer or an email

After building a Mailer or an Email instance, you can read everything back from it and see if it is what you expected.

You can verify default values this way as well.

email.getAnythingYouCanSetWithABuilder();

mailer.getSession();
mailer.getTransportStrategy();
mailer.getServerConfig();
mailer.getProxyConfig();
mailer.getOperationalConfig();
// default S/MIME signing, validation criteria etc.
mailer.getEmailGovernance();
§

Catching exceptions

During a synchronous send, Simple Java Mail throws a MailException if preparation, validation or sending fails. This includes checked exceptions translated from the underlying frameworks.

An asynchronous send reports those failures through its CompletableFuture. See Handling asynchronous results for an example. The no-argument sendMail(email) follows the mailer's configured async default.


try {
   mailer.sendMail(email, false);
} catch (MailException e) {
   // handle the exception
}
§

Logging output

Simple Java Mail uses SLF4J to log, which means you can use any logging framework you like that supports it. Also read this excellent summary of how to configure slf4j with log4j2.

Aside from the native Jakarta Mail logging, the following parts of Simple Java Mail may produce additional logging:
  • "org.simplejavamail"
  • "org.simplejavamail.internal.clisupport"
  • "org.simplejavamail.internal.dkimsupport"
  • "org.simplejavamail.internal.smimesupport"
  • "org.simplejavamail.internal.authenticatedsockssupport"
  • "socks5bridge"

The latter two are for the proxy bridge that is used for authenticated proxy connections.

If you wish to know where and how the properties are loaded, you can increase logging for "org.simplejavamail.config.ConfigLoader". This will tell you which properties are loaded by API and which from config file and which properties are without value.

Simple Java Mail logs through SLF4J, leaving the logging backend and its configuration to your application. If you use Log4j 2, log4j2_example.xml provides a starting point that you can copy and adapt. More examples follow below.

§

Example with Log4j 2

Add the matching SLF4J binding for Log4j 2, then place this configuration in log4j2.xml.

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
    <appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss} [%t] %-5level %c{1} - %msg%n" />
        </Console>
        <Console name="simpleConsole" target="SYSTEM_OUT">
            <PatternLayout pattern="%d Simple Java Mail SOCKS5 bridge - %level %m%n" />
        </Console>
    </appenders>
    <Loggers>
        <Logger name="org.simplejavamail" level="trace"/>
        <!-- in case you're using authenticated proxy -->
        <Logger name="socks5bridge" level="info" additivity="false">
            <AppenderRef ref="simpleConsole" />
        </Logger>
        <Logger name="org.simplejavamail.internal.authenticatedsockssupport" level="warn"/>

        <Root level="warn">
            <AppenderRef ref="console" />
        </Root>
    </Loggers>
</configuration>
§

Example with Logback

Add Logback to the classpath, then place this configuration in logback.xml.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %c{1} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="simpleConsole" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d Simple Java Mail SOCKS5 bridge - %level %m%n</pattern>
        </encoder>
    </appender>

    <logger name="org.simplejavamail" level="TRACE"/>
    <!-- in case you're using authenticated proxy -->
    <logger name="socks5bridge" level="INFO" additivity="false">
        <appender-ref ref="simpleConsole" />
    </logger>
    <logger name="org.simplejavamail.internal.authenticatedsockssupport" level="WARN"/>

    <root level="WARN">
        <appender-ref ref="console" />
    </root>
</configuration>