我用在另一个项目Jakarta commons HttpClient,我想同样的wire logging输出,但使用“标准” HttpURLConnection类。如何为java HttpURLConnection流量启用线路日志记录?
我用Fiddler作为代理,但我想直接从Java记录的流量。
捕获连接输入和输出流所发生的事情是不够的,因为HTTP头由HttpUrlConnection类写入和使用,所以我将无法记录头。
我用在另一个项目Jakarta commons HttpClient,我想同样的wire logging输出,但使用“标准” HttpURLConnection类。如何为java HttpURLConnection流量启用线路日志记录?
我用Fiddler作为代理,但我想直接从Java记录的流量。
捕获连接输入和输出流所发生的事情是不够的,因为HTTP头由HttpUrlConnection类写入和使用,所以我将无法记录头。
我已经能够登录默认的基础上实现我自己的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();
}
}
}
}
我不认为你可以自动做到这一点,但你可以将FilterOutputStream和FilterInputStream的子集与HttpUrlConnection
的输出和输入流作为参数一起使用。然后,当字节被写入/读取时,记录它们并将它们传递给底层流。
解决方案#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。
现在所有的流量正在经历上面的代理,就像它在小提琴手是如何发生的。因此,您可以访问原始http流“从客户端到服务器”以及“从服务器返回客户端”。您必须解释这些原始信息并根据需要进行记录。
更新: 使用HTTP代理作为基于SSL的Web服务器的适配器。
== Client System ===============================
| |
| ------------------------------ |
| | | |
| | Java process | |
| | ---- | |
| | ---------- | | | |
| | | | -O | | |
| | | Logging | | | |
| | | Proxy <---HTTP-- | ----- |
| | | Adapter | | | | |
| | | Thread o------------------> | |
| | | o | | | | |
| | --------|- | | Log | |
| | | | ----- |
| ----------------|------------- |
| | |
=====================|==========================
|
|
HTTPS
SSL
|
== Server System ====|==========================
| | |
| ----------------|---------------- |
| | V | |
| | | |
| | Web Server | |
| | | |
| --------------------------------- |
| |
================================================
查看JDK6源头是在私有方法sun.net.www.protocol.http.HttpURLConnection.writeRequests中发送的。使用修饰的HttpURLConnection,我将能够记录方法调用和流内容,但是我将无法访问负责创建套接字并写入它的底层sun.net.HttpClient实例。 – Serxipc 2010-02-19 12:12:07
增加解决方案#2:使用自定义的内存http代理 – 2010-02-19 16:31:53
自定义代理无法处理ssl。这就是我正在寻找直接从java中记录流量而不是使用fiddler的原因。 – Serxipc 2010-02-22 18:33:57
怎么样使用AspectJ插入一个切入点来绕方法添加日志记录的建议?我相信AspectJ可以编入私有/受保护的方法。
看来,sun.net.www.protocol.http.HttpURLConnection.writeRequest可能会调用sun.net.www.http.HttpClient.writeRequest,它将MessageHeader对象作为输入,这将成为您的目标。
最终这可能会工作,但会非常脆弱,只能在Sun JVM上运行;真的你只能相信你使用的确切版本。
取决于未发布的,未公开的私有API是**糟糕的主意。 – 2010-02-23 04:01:47
@劳伦斯代尔:确实。但是,对于单个调试会话,这可能是好的。 ' – sleske 2017-07-12 11:54:27
如果你只想获得线上的内容(头文件,正文等),那么你可能想要给wireshark一个提示。
这具有无需更改任何代码的优点,但如果启用通过代码进行日志记录是您所追求的内容,则此答案不适用。
根据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: [email protected] 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: [email protected] 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。
程序可以使用 'sun.util.logging.PlatformLogger .getLogger( “sun.net.www.protocol.http.HttpURLConnection”) .setLevel(PlatformLogger.Level.ALL)做到这一点引用应该已经存在的文件“JRE_HOME \ lib \ logging.properties”。如果没有看到任何输出,日志记录级别可能太低,找到'java.util.logging.ConsoleHandler.level'行并将其设置为'FINEST'。 – 2016-02-12 22:38:04
这将; – user3792852 2016-08-30 08:51:26
@ user3792852:好点。我冒昧地将其添加到答案中,希望你不介意。 – sleske 2017-07-12 12:00:00
在Linux中,你可以使用strace下运行VM:
strace的-o strace.out -s -e 4096一丝=网络-f java的...
Socket.setSocketImplFactory(),对于非SSL,可能? – 2010-02-23 04:06:15
@Serhill:一些详细的例子和/或代码摘录可能会让你接受。 – 2010-02-23 04:07:24
我认为javaspecialist的“SSL流量实现我自己的SSLSocketFactory的默认值”解决方案是最好的。 – 2010-02-24 02:27:43