51

別のプロジェクトでJakarta commons HttpClientを使用しましたが、「標準」の HttpUrlConnection を使用して、同じワイヤ ログ出力が必要です。

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

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

4

8 に答える 8

60

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 のデバッグに役立ちます。

于 2012-09-09T14:00:40.793 に答える
16

デフォルトのものの上に独自のSSLSocketFactoryを実装するすべての SSL トラフィックをログに記録できました。

すべての接続が HTTPS を使用しており、メソッドHttpsURLConnection.setSSLSocketFactoryでソケット ファクトリを設定できるため、これはうまくいきました。

すべてのソケットの監視を可能にするより完全なソリューションは、http: //www.javaspecialists.eu/archive/Issue169.html にあります。Socket.setSocketImplFactoryを使用する正しい方向を示してくれたLawrence Dolに 感謝します。

ここに私の本番用コードの準備ができていません:

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();
            }
        }
    }
}
于 2010-02-16T11:02:48.693 に答える
10

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

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

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

.

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

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

すべてのリクエストに対して上記の HTTP プロキシを使用するようにアプリケーションを構成します。Proxies を使用するための 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                    |        | 
  |   |                                 |        | 
  |    ---------------------------------         | 
  |                                              | 
  ================================================ 
于 2010-02-18T19:40:43.877 に答える
2

偶然にも、ワイヤー上のコンテンツ(ヘッダー、ボディなど)にのみ興味がある場合は、wiresharkを試してみることをお勧めします。

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

于 2010-02-23T14:49:30.103 に答える
2

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

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

最終的にこれは機能するかもしれませんが、非常に壊れやすく、Sun JVM でしか機能しません。実際には、使用している正確なバージョンしか信頼できません。

于 2010-02-19T17:33:58.273 に答える
2

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

于 2009-09-18T17:44:04.813 に答える