如何为java HttpURLConnection流量启用连线日志记录?

如何为java HttpURLConnection流量启用连线日志记录?,java,http,logging,httpurlconnection,Java,Http,Logging,Httpurlconnection,我在另一个项目中使用过,我希望使用相同的输出,但使用“标准”HttpUrlConnection 我使用了代理,但我想直接从java记录流量 捕获连接输入和输出流的内容是不够的,因为HTTP头是由HttpUrlConnection类编写和使用的,因此我将无法记录头。我认为您无法自动执行此操作,但您可以使用HttpUrlConnection的输出和输入流作为参数来子类化和。然后在写入/读取字节时,记录它们并将它们传递到底层流。我已经能够在默认的SSL流量之上记录所有实现我自己的SSL流量 这对我来说

我在另一个项目中使用过,我希望使用相同的输出,但使用“标准”HttpUrlConnection

我使用了代理,但我想直接从java记录流量


捕获连接输入和输出流的内容是不够的,因为HTTP头是由HttpUrlConnection类编写和使用的,因此我将无法记录头。

我认为您无法自动执行此操作,但您可以使用
HttpUrlConnection
的输出和输入流作为参数来子类化和。然后在写入/读取字节时,记录它们并将它们传递到底层流。

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

这对我来说很有效,因为我们所有的连接都使用HTTPS,我们可以用这个方法设置套接字工厂

可以在以下位置找到一个更完整的解决方案,该解决方案支持对所有套接字进行监控: 感谢您指出了正确的使用方向

以下是我的尚未准备就绪的生产代码:

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();
            }
        }
    }
}
解决方案#1:使用装饰图案

您必须在类上使用以扩展其功能。然后重写所有HttpURLConnection方法,将操作委托给组件指针,并捕获所需信息并记录

还要确保重写父类:和:方法以返回装饰对象和对象

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

编写一个简单的http代理服务器,并在应用程序启动和初始化期间在其单独的线程中启动。看

将应用程序配置为对所有请求使用上述HTTP代理。看

现在你们所有的流量都通过上面的代理,就像在fiddler中发生的一样。因此,您可以访问原始http流“从客户端到服务器”以及“从服务器返回到客户端”。您必须解释这些原始信息,并根据需要将其记录下来

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

  == Client System =============================== 
  |                                              | 
  |    ------------------------------            | 
  |   |                              |           | 
  |   |    Java process              |           | 
  |   |                       ----   |           | 
  |   |        ----------    |    |  |           | 
  |   |       |          |    -O  |  |           | 
  |   |       |  Logging |        |  |           | 
  |   |       |   Proxy <---HTTP--   |    -----  | 
  |   |       |  Adapter |           |   |     | | 
  |   |       |  Thread o------------------>   | | 
  |   |       |        o |           |   |     | | 
  |   |        --------|-            |   | Log | | 
  |   |                |             |    -----  | 
  |    ----------------|-------------            | 
  |                    |                         | 
  =====================|========================== 
                       |                           
                       |                           
                     HTTPS                         
                      SSL                          
                       |                           
  == Server System ====|========================== 
  |                    |                         | 
  |    ----------------|----------------         | 
  |   |                V                |        | 
  |   |                                 |        | 
  |   |   Web Server                    |        | 
  |   |                                 |        | 
  |    ---------------------------------         | 
  |                                              | 
  ================================================ 
==客户端系统==========================================================
|                                              | 
|    ------------------------------            | 
|   |                              |           | 
|| Java进程||
|   |                       ----   |           | 
|   |        ----------    |    |  |           | 
|| | | |-O | | |
|| | | | | | |
|| |代理| |
|| | o | | | | |
||------------| | |日志| |
|   |                |             |    -----  | 
|    ----------------|-------------            | 
|                    |                         | 
=====================|========================== 
|                           
|                           
HTTPS
SSL
|                           
==服务器系统===============================================================
|                    |                         | 
|    ----------------|----------------         | 
|| V | |
|   |                                 |        | 
|| Web服务器||
|   |                                 |        | 
|    ---------------------------------         | 
|                                              | 
================================================ 

使用AspectJ插入切入点,在方法周围添加日志记录建议,怎么样?我相信AspectJ可以将它编织成私有/受保护的方法

sun.net.www.protocol.http.HttpURLConnection.writeRequest可能会调用sun.net.www.http.HttpClient.writeRequest,它将MessageHeader对象作为输入,从而成为您的目标


最终,这可能会起作用,但会非常脆弱,只能在Sun JVM上工作;实际上,你只能相信你正在使用的确切版本。

如果你只对在线内容(标题、正文等)感兴趣,你可能想试一试

这样做的好处是不必更改任何代码,但如果您所追求的是通过代码启用日志记录,则此答案不适用。

据了解,通过提供一些日志记录支持

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

logging.properties:

handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINEST
sun.net.www.protocol.http.HttpURLConnection.level=ALL
这将记录到控制台,根据需要进行调整,例如记录到文件

示例输出:

2010-08-07 00:00:31 sun.net.www.protocol.http.HttpURLConnection writeRequests
FIN:sun.net.www。MessageHeader@16caf435配对:{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配对:{null:HTTP/1.1200 OK}{Date:Sat,2010年8月7日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}
请注意,这只打印标题而不打印正文

有关详细信息,请参阅


还有一个系统属性
-Djavax.net.debug=all
。但主要是。

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


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

要使用java 8环境刷新:

下面是@sleske的答案

System.setProperty("javax.net.debug","all");
对我来说,这是开箱即用的

还有@weberjn的建议

strace -o strace.out -s 4096 -e trace=network -f java
但如果处理
strace -o strace.out -s 4096 -e trace=network -f java