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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1445919/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 12:22:20  来源:igfitidea点击:

How to enable wire logging for a java HttpURLConnection traffic?

javahttplogginghttpurlconnection

提问by Serxipc

I've used Jakarta commons HttpClientin another project and I would like the same wire loggingoutput but using the "standard" HttpUrlConnection.

我在另一个项目中使用了Jakarta commons HttpClient,我想要相同的线路日志输出,但使用“标准”HttpUrlConnection。

I've used Fiddleras 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 类编写和使用的,所以我将无法记录标头。

采纳答案by Serxipc

I've been able to log all SSL traffic implementing my own SSLSocketFactoryon top of the default one.

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

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.htmlThanks to Lawrence Dolfor 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();
            }
        }
    }
}

回答by Adam Batkin

I don't think you can do that automatically, but you could subclass FilterOutputStreamand FilterInputStreamwith the HttpUrlConnection's output and input streams as parameters. Then as bytes are written/read, log them as well as pass them through to the underlying streams.

我不认为你可以自动做到这一点,但你可以继承FilterOutputStream中FilterInputStream中的HttpUrlConnection的输出和输入流作为参数。然后在写入/读取字节时,记录它们并将它们传递到底层流。

回答by Gladwin Burboz

Solution#1: Use Decorator Pattern

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

You will have to use Decorator patternon HttpURLConnectionclass to extend it's functionality. Then override all HttpURLConnection method(s) and delegate operation to Component pointer as well as capture required information and log it.

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

Also make sure you override parent class URLConnection.getOutputStream(): OutputStreamand URLConnection.html#getInputStream(): InputStreammethods to return decorated OutputStreamand InputStreamobjects as well.

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

.

.

Solution#2: Use custom, in-memory http proxy

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

Write a simple http proxy serverand have it start in it's separate thread during application startup and initialization. See Example simple proxy server.

编写一个简单的 http 代理服务器,并在应用程序启动和初始化期间让它在单独的线程中启动。请参阅示例简单代理服务器

Have your application configured to use above HTTP proxyfor all your requests. See configuring Java to use Proxies.

将您的应用程序配置为对所有请求使用上述 HTTP 代理。请参阅配置 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". You will have to interpret this raw information and log it as required.

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

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                    |        | 
  |   |                                 |        | 
  |    ---------------------------------         | 
  |                                              | 
  ================================================ 

回答by danpaq

What about using AspectJ to insert a Pointcut to add logging advice around a method? I believe AspectJ can weave it's way into private/protected methods.

如何使用 AspectJ 插入一个切入点来围绕方法添加日志记录建议?我相信 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; and really you could only trust the exact version you are using.

最后这可能会起作用,但会非常脆弱,并且只能在 Sun JVM 上运行;实际上,您只能信任您正在使用的确切版本。

回答by beny23

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 wiresharka go.

如果您只对获取线路上的内容(标题、正文等)感兴趣,那么您可能想试一试wireshark

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.

这具有不必更改任何代码的优点,但如果您想要通过代码启用日志记录,则此答案不适用。

回答by Vadzim

According to Sun's HttpURLConnection sourcethere 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 e.g. 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.htmlfor details.

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

There is also system property -Djavax.net.debug=all. But it's mainly useful for SSL debugging.

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

回答by weberjn

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 ...

回答by Lukasz Bulak

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.

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