【问题标题】:Jsoup http loggingJsoup http 日志记录
【发布时间】:2014-06-22 09:24:55
【问题描述】:

有没有办法记录 http 请求和响应? 让我们假设以下请求

Connection.Response res = Jsoup.connect("LOGIN_URL_HERE")
            .data("user", "USER", "pass", "PASS")
            .method(Connection.Method.POST)
            .execute();

如何记录 http 请求和响应?请注意,我需要 HTTP 而不仅仅是要解析的 HTML。

【问题讨论】:

标签: java html http logging jsoup


【解决方案1】:

默认情况下,jsoup 使用java.net.HttpURLConnection 的实现,所以我想您需要为该实现打开日志记录(可能是:sun.net.www.protocol.http.HttpURLConnection)java.net

有一个系统属性可以为 java net utils 启用日志记录

-Djavax.net.debug=all

【讨论】:

    【解决方案2】:

    由于Jsoup 缺少日志记录(我正在使用的版本:1.12.1)并且使用-Djavax.net.debug=all JVM 参数日志太冗长,我发现最好的方法是装饰HttpConnection 类,所以可以自定义记录的内容。为了实现这一点,execute 方法调用需要被记录 Connection.RequestConnection.Response 的属性所包围。

    使用SLF4J的示例实现:

    import org.jsoup.Connection;
    import org.jsoup.helper.HttpConnection;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.IOException;
    
    public class DiagnosticConnection extends HttpConnection {
        static final Logger LOG = LoggerFactory.getLogger(DiagnosticConnection.class);
    
        @Override
        public Connection.Response execute() throws IOException {
            log(this.request());
            Connection.Response response = super.execute();
            log(response);
    
            return response;
        }
    
        public static Connection connect(String url) {
            Connection connection = new DiagnosticConnection();
            connection.url(url);
            return connection;
        }
    
        private static void log(Connection.Request request) {
            LOG.info("========================================");
            LOG.info("[url] {}", request.url());
            LOG.info("== REQUEST ==");
            logBase(request);
            LOG.info("[method] {}", request.method());
            LOG.info("[data] {}", request.data());
            LOG.info("[request body] {}", request.requestBody());
        }
    
        private static void log(Connection.Response response) {
            LOG.info("== RESPONSE ==");
            logBase(response);
            LOG.info("[code] {}", response.statusCode());
            LOG.info("[status msg] {}", response.statusMessage());
            LOG.info("[body] {}", response.body());
            LOG.info("========================================");
        }
    
        private static void logBase(Connection.Base<?> base) {
            LOG.info("[headers] {}", base.headers());
            LOG.info("[cookies] {}", base.cookies());
        }
    
    }
    

    当使用装饰器时,你应该使用DiagnosticConnection.connect(&lt;URL&gt;)而不是DiagnosticConnection.connect(&lt;URL&gt;)

    【讨论】:

    • 我遇到了错误,因为HttpConnection 没有公共构造函数。错误:There is not default constructor available in 'org.jsoup.helper.HttpConnection'
    【解决方案3】:

    根据Gergely Toth 的响应,我创建了自己的LoggerHttpConnection,我正在努力解决这个问题。

    import android.util.Log
    import org.jsoup.Connection
    import org.jsoup.helper.HttpConnection
    import org.jsoup.nodes.Document
    import org.jsoup.parser.Parser
    import java.io.InputStream
    import java.net.Proxy
    import java.net.URL
    import javax.net.ssl.SSLSocketFactory
    
    class LoggerHttpConnection private constructor(
        private val delegate: HttpConnection,
        private val saveFile: Boolean
    ) : Connection {
    
        private val tag = "LoggerHttpConnection"
    
        companion object {
            fun connect(url: String, saveFile: Boolean = false): LoggerHttpConnection {
                return LoggerHttpConnection(
                    HttpConnection.connect(url) as HttpConnection,
                    saveFile
                )
            }
        }
    
        private fun log(request: Connection.Request): String {
            Log.i(tag, "========================================")
            var line = "[url] ${request.url()}"
            var log = "$line\n\n== REQUEST ==\n"
            Log.i(tag, line)
    
            Log.i(tag, "== REQUEST ==")
            log += logBase(request)
    
            line = "[method] ${request.method()}"
            log += "$line\n"
            Log.i(tag, line)
    
            for (data in request.data()) {
                line = "[data] ${data.key()}=${data.value()}"
                log += "$line\n"
                Log.i(tag, line)
            }
    
            line = "[request body] ${request.requestBody()}"
            log += "$line\n"
            Log.i(tag, line)
    
            return log
        }
    
        private fun log(response: Connection.Response): String {
            var line = ""
            var log = "\n== RESPONSE ==\n"
    
            Log.i(tag, "== RESPONSE ==")
            log += logBase(response)
    
            line = "[code] ${response.statusCode()}"
            log += "$line\n"
            Log.i(tag, line)
    
            line = "[status msg] ${response.statusMessage()}"
            log += "$line\n"
            Log.i(tag, line)
    
            line = "[body] ${response.body()}"
            log += "$line\n"
            Log.i(tag, line)
    
            Log.i(tag, "========================================")
    
            return log
        }
    
        private fun logBase(base: Connection.Base<*>): String {
            var line = ""
            var log = ""
            for (header in base.headers()) {
                line = "[header] ${header.key}=${header.value}"
                log += "$line\n"
                Log.i(tag, line)
            }
            for (cookie in base.cookies()) {
                line = "[cookie] ${cookie.key}: ${cookie.value}"
                log += "$line\n"
                Log.i(tag, line)
            }
            return log
        }
    
        override fun execute(): Connection.Response {
            var logs = log(request())
            val response = delegate.execute()
            logs += log(response)
            if (saveFile)
                logs.saveToFile("request_log") //do something to save your log in a file if its necesary
            return response
        }
    
        override fun ignoreContentType(ignoreContentType: Boolean): Connection {
            delegate.ignoreContentType(ignoreContentType)
            return this
        }
    
        override fun postDataCharset(charset: String?): Connection {
            delegate.postDataCharset(charset)
            return this
        }
    
        override fun get(): Document {
            return delegate.get()
        }
    
        override fun post(): Document {
            return delegate.post()
        }
    
        /** Continue implementing necessary methods for Connection */
    
    }
    

    现在只需使用LoggerHttpConnection 而不是Jsoup 声明您的请求,一切都会正常进行

    Connection.Response res = LoggerHttpConnection.connect("LOGIN_URL_HERE")
            .data("user", "USER", "pass", "PASS")
            .method(Connection.Method.POST)
            .execute();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-23
      • 1970-01-01
      • 2011-06-17
      相关资源
      最近更新 更多