我用在另一個項目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