Loomer
Loomer

Reputation: 569

Capture javax.net.debug to file

I need to save the javax.net.debug=all output that is created to a file. I'm using log4j and I tried creating a logging proxy as in the code example below; however, it is not picking up the info. I am not sure where the javax.net.debug is being printed to. I tried capturing system.out and system.err this way but neither worked. Thanks for your help.

public class StdOutErrLog {

    private static final Logger logger = Logger.getLogger(StdOutErrLog.class);

    public static void tieSystemOutAndErrToLog() {
        System.setOut(createLoggingProxy(System.out));
        System.setErr(createLoggingProxy(System.err));
    }

    public static PrintStream createLoggingProxy(final PrintStream realPrintStream) {
        return new PrintStream(realPrintStream) {
            public void print(final String string) {
                realPrintStream.print(string);
                logger.info(string);
            }
        };
    }
}

Upvotes: 11

Views: 19710

Answers (3)

diabolusss
diabolusss

Reputation: 29

Dedicated for those who came here seeking for a solution and none of those have worked. I faced this problem with Tomcat 9 application running with jdk8 (source code compliance level is 1.7, configured LOG4J and Tomcat JULI logging), so my solution is based on that, however this tip is universal:

Always check documentation and investigate code sources.

Searching by some specific keywords from ssl trace log i've found out that system property "javax.net.debug" is used in SSLLogger.java (newer jdk, i.e 11) and in Debug.java (older jdk, i.e. 7). I was lucky enough to find jdk11 sources first, so there i've found descriptive comments (while older haven't):

If the system property "javax.net.debug" is not defined, the debug logging is turned off. If the system property "javax.net.debug" is defined as empty, the debug logger is specified by System.getLogger("javax.net.ssl"), and applications can customize and configure the logger or use external logging mechanisms. If the system property "javax.net.debug" is defined and non-empty, a private debug logger implemented in this class is used.

I've added "-Djavax.net.debug" VM argument to Tomcat server and SSL trace log output have slightly changed (and became less verbose).

To control what this package logger will output i've modified Tomcat server logging.properties:

  • add new logging handler (5 is sequence number, i.e. i had 4 logging handlers before):

    5ssl.org.apache.juli.FileHandler.level = FINEST

    5ssl.org.apache.juli.FileHandler.directory = ${catalina.base}/logs

    5ssl.org.apache.juli.FileHandler.prefix = ssl.

    5ssl.org.apache.juli.FileHandler.maxDays = 2

  • add this handler to handlers property:

    handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4host-manager.org.apache.juli.FileHandler, 5ssl.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

  • and, finally, override SSL default logger:

    javax.net.ssl.level = FINEST

    javax.net.ssl.handlers = 5ssl.org.apache.juli.FileHandler

Now my custom logger is (almost) working similarly to native SSLLogger with debug level 'all'.

Important difference is that more verbose messages like 'ssl,trustmanager' or 'ssl,plaintext' are missing.

Native logger output:

javax.net.ssl|FINE|47|http-nio-8081-exec-5|2022-06-28 17:05:43.051 EEST|SSLSocketInputRecord.java:474|Raw read (
  0000: 6B 38 AA 29 C2 5B 2B 22   AD 2B 23 3B A9 EA A8 EA  k8.).[+".+#;....
  0010: E0 BF 64 4A 0E B3 5E 5C   63 2A 63 EC 85 82 40 DA  ..dJ..^\c*c...@.
  0020: 4B 9B 73 BE 49 AE 1C 14   D6 E6 C2 9A 06 96 18 21  K.s.I..........!

Custom logger output:

28-Jun-2022 16:26:09.508 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log READ: TLSv1.2 handshake, length = 5712
28-Jun-2022 16:26:09.508 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log Raw read
28-Jun-2022 16:26:09.508 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log Raw read
28-Jun-2022 16:26:09.509 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log Raw read
28-Jun-2022 16:26:09.509 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log Raw read
28-Jun-2022 16:26:09.510 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log Raw read
28-Jun-2022 16:26:09.523 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log Raw read
28-Jun-2022 16:26:09.524 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log Raw read
28-Jun-2022 16:26:09.524 FINE [http-nio-8081-exec-5] sun.security.ssl.SSLLogger.log READ: TLSv1.2 handshake, length = 5712

After checking sources of few files marked in native logger, i.e. SSLSocketInputRecord.java, X509TrustManagerImpl.java i have noticed that there is additional check for specific logging properties mentioned in logger help (plaintext, record, etc), which is checked like this:

if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) {
            SSLLogger.fine("adding as trusted certificates",
                    (Object[])trustedCerts.toArray(new X509Certificate[0]));
        }

And, while using custom logger SSLLogger.isOn is true, no native logging properties are defined, which means that with custom logger configured no specific verbose messages will be outputted by SSLLogger...

p.s. i couldn't manage to use log4j as a custom logger for SSLLogger. (I suppose to do that i need to configure Tomcat to use log4j rather than java.util.logging for all Tomcat's internal logging as noted in docs.)

Upvotes: 1

mtraut
mtraut

Reputation: 4740

Maybe the subsystem makes its copy of the values and you are too late when switching. Try doing this first in your main.

EDIT

OK - i missed completely your idiom. I think you should not use this inner class. You should define a PrintStream instance on an OutputStream that creates a new log entry upon every "\n". The way you do it now misses a lot of possibilities to "print around" your instance.


package de.mit.stackoverflow;

import java.io.IOException;
import java.io.OutputStream;

public class LogOutputStream extends OutputStream {

    private StringBuilder sb = new StringBuilder();

    @Override
    public void write(int b) throws IOException {
        if (b == '\n') {
            log(sb.toString());
            sb.setLength(0);
        } else {
            sb.append((char) b);
        }
    }

}


and then do

    OutputStream os = new LogOutputStream();
    PrintStream ps = new PrintStream(os);
    System.setOut(ps);

You maybe still want to include a reference to the previous stream - left as excercise :-)

Upvotes: 8

Matt Solnit
Matt Solnit

Reputation: 33582

This is a long shot, but it's possible that overriding print(String) is not enough. For example, there is also print(Object), etc., not to mention the various append() and format() methods.

Upvotes: 3

Related Questions