如何为java HttpURLConnection流量启用线路记录?

Ser*_*ipc 44 java logging http httpurlconnection

我在另一个项目中使用了Jakarta commons HttpClient,我希望使用相同的线路日志输出,但使用"标准"HttpUrlConnection.

我使用Fiddler作为代理,但我想直接从java记录流量.

捕获连接输入和输出流所发生的事情是不够的,因为HTTP标头是由HttpUrlConnection类编写和使用的,因此我将无法记录标头.

Vad*_*zim 48

根据Sun的HttpURLConnection源代码,JUL提供了一些日志记录支持.

设置(根据需要调整路径):

-Djava.util.logging.config.file=/full/path/to/logging.properties
Run Code Online (Sandbox Code Playgroud)

logging.properties:

handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINEST
sun.net.www.protocol.http.HttpURLConnection.level=ALL
Run Code Online (Sandbox Code Playgroud)

这将登录到控制台,根据需要进行调整,例如登录文件.

示例输出:

2010-08-07 00:00:31 sun.net.www.protocol.http.HttpURLConnection writeRequests
FIN: sun.net.www.MessageHeader@16caf435 pairs: {GET /howto.html HTTP/1.1: null}{User-Agent: Java/1.6.0_20}{Host: www.rgagnon.com}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive}
2010-08-07 00:00:31 sun.net.www.protocol.http.HttpURLConnection getInputStream
FIN: sun.net.www.MessageHeader@5ac0728 pairs: {null: HTTP/1.1 200 OK}{Date: Sat, 07 Aug 2010 04:00:33 GMT}{Server: Apache}{Accept-Ranges: bytes}{Content-Length: 17912}{Keep-Alive: timeout=5, max=64}{Connection: Keep-Alive}{Content-Type: text/html}
Run Code Online (Sandbox Code Playgroud)

请注意,这仅打印没有正文的标题.

有关详细信息,请参阅http://www.rgagnon.com/javadetails/java-debug-HttpURLConnection-problem.html.

还有系统属性-Djavax.net.debug=all.但它主要用于SSL调试.

  • 以编程方式,您可以使用`sun.util.logging.PlatformLogger .getLogger("sun.net.www.protocol.http.HttpURLConnection").setLevel(PlatformLogger.Level.ALL)执行此操作; (10认同)

Ser*_*ipc 16

我已经能够记录所有SSL流量,在默认的SSLSocketFactory之上实现我自己的SSLSocketFactory.

这对我有用,因为我们所有的连接都使用HTTPS,我们可以使用方法HttpsURLConnection.setSSLSocketFactory设置套接字工厂.

可以在http://www.javaspecialists.eu/archive/Issue169.html找到一个能够监控所有套接字的更完整的解决方案. 感谢Lawrence Dol指向使用Socket.setSocketImplFactory的正确方向

这是我尚未准备好生产代码:

public class WireLogSSLSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory delegate;

    public WireLogSSLSocketFactory(SSLSocketFactory sf0) {
        this.delegate = sf0;
    }

    public Socket createSocket(Socket s, String host, int port,
            boolean autoClose) throws IOException {
        return new WireLogSocket((SSLSocket) delegate.createSocket(s, host, port, autoClose));
    }

    /*
    ...
    */

    private static class WireLogSocket extends SSLSocket {

        private SSLSocket delegate;

        public WireLogSocket(SSLSocket s) {
            this.delegate = s;
        }

        public OutputStream getOutputStream() throws IOException {
            return new LoggingOutputStream(delegate.getOutputStream());
        }

        /*
        ...
        */

        private static class LoggingOutputStream extends FilterOutputStream {
            private static final Logger logger = Logger.getLogger(WireLogSocket.LoggingOutputStream.class);
            //I'm using a fixed charset because my app always uses the same. 
            private static final String CHARSET = "ISO-8859-1";
            private StringBuffer sb = new StringBuffer();

            public LoggingOutputStream(OutputStream out) {
                super(out);
            }

            public void write(byte[] b, int off, int len)
                    throws IOException {
                sb.append(new String(b, off, len, CHARSET));
                logger.info("\n" + sb.toString());
                out.write(b, off, len);
            }

            public void write(int b) throws IOException {
                sb.append(b);
                logger.info("\n" + sb.toString());
                out.write(b);
            }

            public void close() throws IOException {
                logger.info("\n" + sb.toString());
                super.close();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Serhill:尝试使用这个类;添加了所有必要的覆盖并将 HttpsURLConnection socketFactory 设置为此类的实例(使用默认 SocketFactory 实例化)。但是,它在调用 HttpsURLConnection.getOutputStream() 时不会连接,而此时它会使用默认 SSLSocketFactory 进行连接。我肯定错过了什么。有任何想法吗? (2认同)

Gla*_*boz 9

解决方案#1:使用装饰器模式

您必须在HttpURLConnection 类上使用Decorator模式来扩展它的功能.然后覆盖所有HttpURLConnection方法并将操作委托给组件指针,以及捕获所需信息并记录它.

还要确保覆盖父类URLConnection.getOutputStream():OutputStreamURLConnection.html#getInputStream():InputStream方法也返回装饰的OutputStreamInputStream对象.

.

解决方案#2:使用自定义的内存中http代理

编写一个简单的http代理服务器,让它在应用程序启动和初始化期间从它的单独线程开始.请参见以下是一个简单的代理服务器.

将您的应用程序配置为使用上述HTTP代理来处理所有请求.请参阅配置Java以使用代理.

现在你的所有流量都在代理上方,就像它在小提琴手中发生的那样.因此,您可以访问 "从客户端到服务器"的原始http流以及"从服务器返回到客户端".您必须解释此原始信息并根据需要进行记录.

更新: 使用HTTP代理作为基于SSL的Web服务器的适配器.

  == Client System =============================== 
  |                                              | 
  |    ------------------------------            | 
  |   |                              |           | 
  |   |    Java process              |           | 
  |   |                       ----   |           | 
  |   |        ----------    |    |  |           | 
  |   |       |          |    -O  |  |           | 
  |   |       |  Logging |        |  |           | 
  |   |       |   Proxy <---HTTP--   |    -----  | 
  |   |       |  Adapter |           |   |     | | 
  |   |       |  Thread o------------------>   | | 
  |   |       |        o |           |   |     | | 
  |   |        --------|-            |   | Log | | 
  |   |                |             |    -----  | 
  |    ----------------|-------------            | 
  |                    |                         | 
  =====================|========================== 
                       |                           
                       |                           
                     HTTPS                         
                      SSL                          
                       |                           
  == Server System ====|========================== 
  |                    |                         | 
  |    ----------------|----------------         | 
  |   |                V                |        | 
  |   |                                 |        | 
  |   |   Web Server                    |        | 
  |   |                                 |        | 
  |    ---------------------------------         | 
  |                                              | 
  ================================================ 
Run Code Online (Sandbox Code Playgroud)


小智 7

要刷新 Java 8 环境:

关注 @sleske 的回答

System.setProperty("javax.net.debug","all");
Run Code Online (Sandbox Code Playgroud)

开箱即用为我工作。

还有@weberjn 的建议

strace -o strace.out -s 4096 -e trace=network -f java
Run Code Online (Sandbox Code Playgroud)

但如果处理 SSL 流量则没有用,因为它会转储编码流。

所有其他代码技巧对我来说都不起作用,但可能没有足够努力。


web*_*rjn 5

在Linux中,您可以在strace下运行VM:

strace -o strace.out -s 4096 -e trace = network -f Java ...