简体   繁体   English

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

[英]How to enable wire logging for a java HttpURLConnection traffic?

I've used Jakarta commons HttpClient in another project and I would like the same wire logging output but using the "standard" HttpUrlConnection. 我在另一个项目中使用了Jakarta commons HttpClient ,我希望使用相同的线路日志输出,但使用“标准”HttpUrlConnection。

I've used Fiddler as a proxy but I would like to log the traffic directly from java. 我使用Fiddler作为代理,但我想直接从java记录流量。

Capturing what goes by the connection input and output streams is not enough because the HTTP headers are written and consumed by the HttpUrlConnection class, so I will not be able to log the headers. 捕获连接输入和输出流所发生的事情是不够的,因为HTTP标头是由HttpUrlConnection类编写和使用的,因此我将无法记录标头。

According to Sun's HttpURLConnection source there is some logging support via JUL . 根据Sun的HttpURLConnection源代码JUL提供了一些日志记录支持。

Setup (adjust path as required): 设置(根据需要调整路径):

-Djava.util.logging.config.file=/full/path/to/logging.properties

logging.properties: logging.properties:

handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINEST
sun.net.www.protocol.http.HttpURLConnection.level=ALL

This will log to the console, adjust as required to eg log to a file. 这将登录到控制台,根据需要进行调整,例如登录文件。

Example output: 示例输出:

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}

Note that this prints only headers without body. 请注意,这仅打印没有正文的标题。

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

There is also system property -Djavax.net.debug=all . 还有系统属性-Djavax.net.debug=all But it's mainly useful for SSL debugging . 但它主要用于SSL调试

I've been able to log all SSL traffic implementing my own SSLSocketFactory on top of the default one. 我已经能够记录所有SSL流量,在默认的SSLSocketFactory之上实现我自己的SSLSocketFactory

This worked for me because all of our connections are using HTTPS and we can set the socket factory with the method HttpsURLConnection.setSSLSocketFactory . 这对我有用,因为我们所有的连接都使用HTTPS,我们可以使用方法HttpsURLConnection.setSSLSocketFactory设置套接字工厂。

A more complete solution that enables monitoring on all sockets can be found at http://www.javaspecialists.eu/archive/Issue169.html Thanks to Lawrence Dol for pointing in the right direction of using Socket.setSocketImplFactory 可以在http://www.javaspecialists.eu/archive/Issue169.html找到一个能够监控所有套接字的更完整的解决方案感谢Lawrence Dol指向使用Socket.setSocketImplFactory的正确方向

Here is my not ready for production code: 这是我尚未准备好生产代码:

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();
            }
        }
    }
}

Solution#1: Use Decorator Pattern 解决方案#1:使用装饰器模式

You will have to use Decorator pattern on HttpURLConnection class to extend it's functionality. 您必须在HttpURLConnection类上使用Decorator模式来扩展它的功能。 Then override all HttpURLConnection method(s) and delegate operation to Component pointer as well as capture required information and log it. 然后覆盖所有HttpURLConnection方法并将操作委托给组件指针,并捕获所需信息并记录它。

Also make sure you override parent class URLConnection.getOutputStream() : OutputStream and URLConnection.html#getInputStream() : InputStream methods to return decorated OutputStream and InputStream objects as well. 还要确保覆盖父类URLConnection.getOutputStream()OutputStreamURLConnection.html#getInputStream()InputStream方法也返回装饰的OutputStreamInputStream对象。

.

Solution#2: Use custom, in-memory http proxy 解决方案#2:使用自定义的内存中http代理

Write a simple http proxy server and have it start in it's separate thread during application startup and initialization. 编写一个简单的http代理服务器 ,让它在应用程序启动和初始化期间从它的单独线程开始。 See Example simple proxy server . 请参见示例简单代理服务器

Have your application configured to use above HTTP proxy for all your requests. 将您的应用程序配置为使用上述HTTP代理来处理所有请求。 See configuring Java to use Proxies . 请参阅配置Java以使用代理

Now all your traffic is going through above proxy, just like how it happens in fiddler. 现在你的所有流量都在代理上方,就像它在小提琴手中发生的那样。 Hence you have access to raw http stream "from client to server" as well as "back from server to client". 因此, 您可以访问 “从客户端到服务器”的原始http流以及“从服务器返回到客户端”。 You will have to interpret this raw information and log it as required. 您必须解释此原始信息并根据需要进行记录。

Update: Use HTTP Proxy as Adapter to SSL based Web Server. 更新:使用HTTP代理作为基于SSL的Web服务器的适配器。

  == Client System =============================== 
  |                                              | 
  |    ------------------------------            | 
  |   |                              |           | 
  |   |    Java process              |           | 
  |   |                       ----   |           | 
  |   |        ----------    |    |  |           | 
  |   |       |          |    -O  |  |           | 
  |   |       |  Logging |        |  |           | 
  |   |       |   Proxy <---HTTP--   |    -----  | 
  |   |       |  Adapter |           |   |     | | 
  |   |       |  Thread o------------------>   | | 
  |   |       |        o |           |   |     | | 
  |   |        --------|-            |   | Log | | 
  |   |                |             |    -----  | 
  |    ----------------|-------------            | 
  |                    |                         | 
  =====================|========================== 
                       |                           
                       |                           
                     HTTPS                         
                      SSL                          
                       |                           
  == Server System ====|========================== 
  |                    |                         | 
  |    ----------------|----------------         | 
  |   |                V                |        | 
  |   |                                 |        | 
  |   |   Web Server                    |        | 
  |   |                                 |        | 
  |    ---------------------------------         | 
  |                                              | 
  ================================================ 

In Linux you can run the VM under strace: 在Linux中,您可以在strace下运行VM:

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

I don't think you can do that automatically, but you could subclass FilterOutputStream and FilterInputStream with the HttpUrlConnection 's output and input streams as parameters. 我不认为您可以自动执行此操作,但您可以使用HttpUrlConnection的输出和输入流作为参数将FilterOutputStreamFilterInputStream子类化。 Then as bytes are written/read, log them as well as pass them through to the underlying streams. 然后在写入/读取字节时,记录它们并将它们传递给底层流。

What about using AspectJ to insert a Pointcut to add logging advice around a method? 使用AspectJ插入一个Pointcut来为方法添加日志记录建议怎么样? I believe AspectJ can weave it's way into private/protected methods. 我相信AspectJ可以编织私有/受保护的方法。

It appears that sun.net.www.protocol.http.HttpURLConnection.writeRequest may call sun.net.www.http.HttpClient.writeRequest which takes the MessageHeader Object as an input so that would be your target. 似乎sun.net.www.protocol.http.HttpURLConnection.writeRequest可以调用sun.net.www.http.HttpClient.writeRequest,它将MessageHeader对象作为输入,这将是你的目标。

In the end this might work but will be awfully fragile and only work on the Sun JVM; 最终这可能会起作用,但会非常脆弱,只能在Sun JVM上运行; and really you could only trust the exact version you are using. 而且你真的只能信任你正在使用的确切版本。

On the off chance, that you're only interested in getting at the content on the wire (headers, body etc), you might want to give wireshark a go. 关于机会,你只想获得线上的内容(标题,正文等),你可能想给wirehark一个机会。

This has the advantage of not having to change any code, though if enabling logging through code was what you're after, this answer is not applicable. 这样做的好处是不必更改任何代码,但如果启用了代码登录,那么这个答案就不适用了。

To refresh with Java 8 environment: 要使用Java 8环境进行刷新:

Following @sleske answer 关注@sleske的回答

System.setProperty("javax.net.debug","all");

worked for me out of the box. 为我工作开箱即用。

Also was @weberjn suggestion of 也是@weberjn的建议

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

but not useful if handling with SSL traffic as it dumps encoded stream. 但如果在转储编码流时使用SSL流量进行处理,则无用。

All other code tricks did not work for me, but maybe not trying enough hard. 所有其他代码技巧对我来说都不起作用,但可能没有那么努力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM