web-dev-qa-db-ja.com

Java HttpURLConnectionトラフィックのワイヤログを有効にする方法は?

別のプロジェクトで Jakarta commons HttpClient を使用しましたが、同じ wire logging の出力を使用したいのですが、「標準」のHttpUrlConnectionを使用します。

Fiddler をプロキシとして使用しましたが、Javaから直接トラフィックを記録したいと思います。

HTTPヘッダーはHttpUrlConnectionクラスによって書き込まれ、消費されるため、接続の入出力ストリームの内容をキャプチャするだけでは十分ではありません。したがって、ヘッダーをログに記録することはできません。

41
Serxipc

独自の 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();
            }
        }
    }
}
15
Serxipc

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

これはコンソールに記録し、必要に応じて調整します。ファイルに記録します。

出力例:

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デバッグに便利 です。

44
Vadzim

解決策1:デコレータパターンを使用する

その機能を拡張するには、 装飾パターン on HttpURLConnection クラスを使用する必要があります。次に、すべてのHttpURLConnectionメソッドをオーバーライドし、操作をコンポーネントポインターに委任し、必要な情報をキャプチャしてログに記録します。

また、必ず親クラスをオーバーライドしてください RLConnection.getOutputStream()OutputStream および RLConnection.html#getInputStream()InputStream 装飾された OutputStream および InputStream オブジェクトも返すメソッド。

解決策2:カスタムのメモリ内HTTPプロキシを使用する

単純なhttpプロキシサーバーを記述するを起動し、アプリケーションの起動と初期化中に別のスレッドで起動します。 単純なプロキシサーバーの例 を参照してください。

すべてのリクエストに対して上記のHTTPプロキシを使用に設定してください。 configuring Java を参照してください。

これで、フィドラーで発生するのと同じように、すべてのトラフィックがプロキシを通過します。したがって、未加工のhttpストリームへのアクセス権「クライアントからサーバーへ」および「サーバーからクライアントへ」。この生の情報を解釈し、必要に応じて記録する必要があります。

更新: SSLベースのWebサーバーへのアダプターとしてHTTPプロキシを使用します。

  == Client System =============================== 
  |                                              | 
  |    ------------------------------            | 
  |   |                              |           | 
  |   |    Java process              |           | 
  |   |                       ----   |           | 
  |   |        ----------    |    |  |           | 
  |   |       |          |    -O  |  |           | 
  |   |       |  Logging |        |  |           | 
  |   |       |   Proxy <---HTTP--   |    -----  | 
  |   |       |  Adapter |           |   |     | | 
  |   |       |  Thread o------------------>   | | 
  |   |       |        o |           |   |     | | 
  |   |        --------|-            |   | Log | | 
  |   |                |             |    -----  | 
  |    ----------------|-------------            | 
  |                    |                         | 
  =====================|========================== 
                       |                           
                       |                           
                     HTTPS                         
                      SSL                          
                       |                           
  == Server System ====|========================== 
  |                    |                         | 
  |    ----------------|----------------         | 
  |   |                V                |        | 
  |   |                                 |        | 
  |   |   Web Server                    |        | 
  |   |                                 |        | 
  |    ---------------------------------         | 
  |                                              | 
  ================================================ 
9
Gladwin Burboz

Linuxでは、straceの下でVMを実行できます。

strace -o strace.out -s 4096 -e trace = network -f Java ...

4
weberjn

自動的にこれを行うことはできないと思いますが、サブクラス FilterOutputStream および FilterInputStreamHttpUrlConnectionの出力および入力ストリームをパラメーターとして使用できます。次に、バイトの書き込み/読み取りが行われたら、それらをログに記録するとともに、基礎となるストリームに渡します。

2
Adam Batkin

オフの機会に、ワイヤー上のコンテンツ(ヘッダー、ボディなど)を取得することにのみ興味がある場合は、 wireshark を試してください。

これには、コードを変更する必要がないという利点がありますが、コードを介してログを有効にすることが目的であれば、この回答は適用されません。

2
beny23

AspectJを使用してPointcutを挿入し、メソッドの周りにロギングアドバイスを追加するのはどうですか? AspectJは、プライベート/保護されたメソッドに組み込むことができると信じています。

Sun.net.www.protocol.http.HttpURLConnection.writeRequestは、MessageHeaderオブジェクトを入力として受け取るSun.net.www.http.HttpClient.writeRequestを呼び出し、それがターゲットになるようです。

結局、これは動作するかもしれませんが、非常に壊れやすく、Sun JVMでのみ動作します。実際に使用しているのは正確なバージョンのみです。

2
danpaq

Java 8環境で更新するには:

@sleskeの回答に従う

System.setProperty("javax.net.debug","all");

箱から出してすぐに私のために働いた。

@weberjnの提案もありました

strace -o strace.out -s 4096 -e trace=network -f Java

ただし、エンコードされたストリームをダンプするため、SSLトラフィックで処理する場合は役に立ちません。

他のすべてのコードトリックはうまくいきませんでしたが、おそらく十分な努力をしていませんでした。

0
Lukasz Bulak