我在另一个项目中使用了Jakarta commons HttpClient,我希望输出相同wire logging,但使用“标准”HttpUrlConnection。
我使用Fiddler作为代理,但我想直接从java记录流量。
捕获连接输入和输出流的内容是不够的,因为HTTP标头是由HttpUrlConnection类编写和使用的,因此我将无法记录标头。
答案 0 :(得分:44)
根据Sun's HttpURLConnection source,JUL提供了一些日志记录支持。
设置(根据需要调整路径):
-Djava.util.logging.config.file=/full/path/to/logging.properties
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
。但它主要是useful for SSL debugging。
答案 1 :(得分:15)
我已经能够在默认值之上记录实现我自己的SSLSocketFactory的所有SSL流量。
这对我有用,因为我们所有的连接都使用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();
}
}
}
}
答案 2 :(得分:9)
解决方案#1:使用装饰器模式
您必须在Decorator pattern类上使用HttpURLConnection来扩展其功能。然后覆盖所有HttpURLConnection方法并将操作委托给组件指针,并捕获所需信息并记录它。
还要确保覆盖父类URLConnection.getOutputStream():OutputStream和URLConnection.html#getInputStream():InputStream方法,以将装饰的OutputStream和InputStream对象作为好。
解决方案#2:使用自定义内存中的http代理
编写一个简单的http代理服务器,并在应用程序启动和初始化期间从它的单独线程开始。请参阅Example simple proxy server。
将您的应用程序配置为使用上面的HTTP代理来处理您的所有请求。请参阅configuring Java to use Proxies。
现在你所有的流量都在代理上方,就像它在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 | |
| | | |
| --------------------------------- |
| |
================================================
答案 3 :(得分:3)
在Linux中,您可以在strace:
下运行VMstrace -o strace.out -s 4096 -e trace = network -f java ...
答案 4 :(得分:2)
我认为您不能自动执行此操作,但您可以将FilterOutputStream和FilterInputStream与HttpUrlConnection
的输出和输入流作为参数进行子类化。然后,当写入/读取字节时,记录它们并将它们传递给底层流。
答案 5 :(得分:2)
如何使用AspectJ插入Pointcut来为方法添加日志记录建议?我相信AspectJ可以编织它进入私有/受保护的方法。
sun.net.www.protocol.http.HttpURLConnection.writeRequest似乎可以调用sun.net.www.http.HttpClient.writeRequest,它将MessageHeader对象作为输入,以便成为你的目标。
最终这可能会起作用,但会非常脆弱,只能在Sun JVM上运行;而且你真的只能信任你正在使用的确切版本。
答案 6 :(得分:2)
关于机会,你只想获取线上的内容(标题,正文等),你可能想给wireshark一个。
这样做的好处是无需更改任何代码,但如果启用了代码登录功能,则此答案不适用。
答案 7 :(得分:0)
要在Java 8环境中刷新:
@sleske回答
System.setProperty("javax.net.debug","all");
为我开箱即用。
也是@weberjn的建议
strace -o strace.out -s 4096 -e trace=network -f java
但在处理SSL流量时不起作用,因为它会转储编码流。
所有其他代码技巧都对我不起作用,但可能没有足够努力。