如何为Java HttpURLConnection流量启用线路日志记录?
2022-08-31 19:49:27
我在另一个项目中使用了Jakarta commons HttpClient,我想要相同的线路日志记录输出,但使用“标准”HttpUrlConnection。
我已经使用Fiddler作为代理,但我想直接从java记录流量。
捕获连接输入和输出流的内容是不够的,因为 HTTP 标头是由 HttpUrlConnection 类编写和使用,因此我将无法记录标头。
我在另一个项目中使用了Jakarta commons HttpClient,我想要相同的线路日志记录输出,但使用“标准”HttpUrlConnection。
我已经使用Fiddler作为代理,但我想直接从java记录流量。
捕获连接输入和输出流的内容是不够的,因为 HTTP 标头是由 HttpUrlConnection 类编写和使用,因此我将无法记录标头。
根据Sun的HttpURLConnection来源,通过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
这将记录到控制台,根据需要进行调整,例如.log到文件。
输出示例:
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。
还有 系统属性 。但它主要用于 SSL 调试。-Djavax.net.debug=all
我已经能够在默认的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();
}
}
}
}