如何为 java HttpURLConnection 流量启用线路日志记录?
-
22-07-2019 - |
题
我用过 雅加达公共 HttpClient 在另一个项目中,我也想要同样的 电线记录 输出但使用“标准”HttpUrlConnection。
我用过 提琴手 作为代理,但我想直接从 java 记录流量。
捕获连接输入和输出流的内容是不够的,因为 HTTP 标头是由 HttpUrlConnection 类写入和使用的,因此我将无法记录标头。
解决方案 2
我已经能够记录所有实现我自己的 SSL 流量 SSLSocketFactory 在默认值之上。
这对我有用,因为我们所有的连接都使用 HTTPS,我们可以使用该方法设置套接字工厂 HttpsURLConnection.setSSLSocketFactory.
可以在以下位置找到可以监视所有套接字的更完整的解决方案: http://www.javaspecialists.eu/archive/Issue169.html 谢谢 劳伦斯·多尔 指向正确的使用方向 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();
}
}
}
}
其他提示
根据 Sun 的 HttpURLConnection 源 有一些日志支持通过 七月.
设置(根据需要调整路径):
-Djava.util.logging.config.file=/full/path/to/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 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}
请注意,这仅打印标题而不打印正文。
看 http://www.rgagnon.com/javadetails/java-debug-HttpURLConnection-problem.html 了解详情。
还有系统属性 -Djavax.net.debug=all
. 。但主要是 对于 SSL 调试很有用.
解决方案#1:使用装饰器模式
你将不得不使用 装饰者模式 在 HttpURL连接 类来扩展它的功能。然后重写所有 HttpURLConnection 方法并将操作委托给组件指针以及捕获所需信息并记录它。
还要确保您覆盖父类 URLConnection.getOutputStream() : 输出流 和 URLConnection.html#getInputStream() : 输入流 返回装饰的方法 输出流 和 输入流 对象也是如此。
.
解决方案#2:使用自定义的内存中 http 代理
写一个简单的http代理服务器 并在应用程序启动和初始化期间让它在单独的线程中启动。看 简单代理服务器示例.
将您的应用程序配置为 使用上面的HTTP代理 满足您的所有要求。看 配置 Java 使用代理.
现在你的所有流量都通过上面的代理,就像在 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 | |
| | | |
| --------------------------------- |
| |
================================================
在Linux可以下strace的运行VM:
strace的-o strace.out -s 4096 -e跟踪=网络-f的java ...
关于使用AspectJ插入一个切入点来绕方法添加日志记录的建议是什么?我相信的AspectJ可以编织它的方式进入私人/保护的方法。
似乎sun.net.www.protocol.http.HttpURLConnection.writeRequest可以调用sun.net.www.http.HttpClient.writeRequest它接受的MessageHeader对象作为输入,以便将目标
在最后,这可能会奏效,但将是非常脆弱的,只有在Sun JVM工作;真的,你只能相信你正在使用的确切版本。
在关闭的机会,你是只在上线(头,体等)的含量越来越有兴趣,你可能想给的 Wireshark的一个去。
这并没有改变任何代码,但如果能够通过代码记录了你以后,这个答案是不适用的优势。
要与Java刷新8环境:
继@sleske答案
System.setProperty("javax.net.debug","all");
为我工作开箱。
的同样是@weberjn建议
strace -o strace.out -s 4096 -e trace=network -f java
但不有用的,如果使用SSL通讯处理,因为它转储编码流。
所有其他代码的技巧并没有为我工作,但也许并不想努力不够。