如何在Android上启用Apache Commons HttpClient的日志记录

33

为了在普通的Java应用程序中启用apache commons HttpClient的日志记录,我使用了:

System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire", "debug");
System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug");

但是在安卓上,我在LogCat中看不到日志。

我有什么遗漏了吗?

5个回答

65

请忽略我之前的评论。我在org.apache.http日志页面上找到了解决方案。你原来的答案是指httpclient-3.x 日志,而最新版本的工作代码来自于http-components 日志

java.util.logging.Logger.getLogger("org.apache.http.wire").setLevel(java.util.logging.Level.FINEST);
java.util.logging.Logger.getLogger("org.apache.http.headers").setLevel(java.util.logging.Level.FINEST);

System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire", "debug");
System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http", "debug");
System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http.headers", "debug");

以及属性:

adb shell setprop log.tag.org.apache.http VERBOSE
adb shell setprop log.tag.org.apache.http.wire VERBOSE
adb shell setprop log.tag.org.apache.http.headers VERBOSE

区别在于日志标记名称不同。


1
你能够让上下文日志正常工作吗?我尝试使用类似的代码行扩展你的解决方案,针对org.apache.http.impl.client记录器,但是我没有看到该包或子包中的类的任何输出。不过,我确实可以获取到线路和头部的日志。 - gnuf
真的。谢谢你这样做。 - Lo-Tan
@joe - 为什么你需要设置adb shell属性? - AlikElzin-kilaka
此外,只需使用以下代码即可查看头文件:java.util.logging.Logger.getLogger("org.apache.http.headers").setLevel(java.util.logging.Level.FINEST);。我不需要设置任何Java系统属性。 - AlikElzin-kilaka
这些行只是展示了如果你想要记录的部分,可以设置哪些内容。如果你只需要其中一个日志标签,那么你只需要设置那个标签即可。setprop 用于确保记录 "httpclient.wire.header" 标签。如果你想要更改日志行为(或完全禁用日志),你可以使用该命令来更改 logcat 显示这些日志的级别。 - Joe
你是在建议我们将此内容硬编码到应用程序中吗?毕竟,将其设置在应用程序之外的整个目的是为了使日志记录可配置!请您详细说明一下您正在做什么,或者最好在Github上提供一个示例。谢谢! - Alex Worden

9
这里提供一种解决方案(不涉及细节)。 控制台:
adb shell setprop log.tag.httpclient.wire.header VERBOSE
adb shell setprop log.tag.httpclient.wire.content VERBOSE

代码:

java.util.logging.Logger.getLogger("httpclient.wire.header").setLevel(java.util.logging.Level.FINEST);
java.util.logging.Logger.getLogger("httpclient.wire.content").setLevel(java.util.logging.Level.FINEST);

测试:

java.util.logging.Logger.getLogger("httpclient.wire.content").log(java.util.logging.Level.CONFIG, "hola");

1
我在我的应用程序中尝试了这个,但是没有看到任何来自HttpClient的日志消息。使用当前SDK中的HttpClient是否有效?也就是说,org.apache.http与org.apache.commons.http相比。我确实看到测试日志消息(例如您的示例中的“hola”),但没有看到来自HttpClient调用的任何内容。 - Joe
1
最近的SDK需要使用第一次响应中使用的前缀“org.apache.http”,而不是“httpclient”,至少在2.1设备上对我有效。 - HefferWolf
@alex2k8 - 为什么需要设置adb shell属性? - AlikElzin-kilaka
你是否知道是否有一种方法可以查看比启用调试模式时获取的更多细节?例如低级TCP信息? - Kevin M

3

细节决定成败。我正在运行2.3.3模拟器,并通过以下方式使其工作:

java.util.logging.Logger.getLogger("org.apache.http.wire").setLevel(java.util.logging.Level.FINEST);
java.util.logging.Logger.getLogger("org.apache.http.headers").setLevel(java.util.logging.Level.FINEST);

在adb shell中执行以下操作:
# setprop log.tag.org.apache.http.wire VERBOSE
# setprop log.tag.org.apache.http.headers VERBOSE

因此,似乎日志说明符不同。

3
您只需要使用 标签。
java.util.logging.Logger.getLogger(yourFullClassName).setLevel(java.util.logging.Level.All);

and

adb shell setprop log.tag.correspondingTag VERBOSE

Android使用此函数从类全名中获取对应的标签:

public static String loggerNameToTag(String loggerName)
  {
    if (loggerName == null) {
      return "null";
    }

    int length = loggerName.length();
    if (length <= 23) {
      return loggerName;
    }

    int lastPeriod = loggerName.lastIndexOf(".");
    return length - (lastPeriod + 1) <= 23 ? loggerName.substring(lastPeriod + 1) : loggerName.substring(loggerName.length() - 23);
  }

例如,我想为“org.apache.http.impl.client.DefaultRequestDirector”类启用日志记录,可以执行以下操作:

String clzName = "org.apache.http.impl.client.DefaultRequestDirector";
String newClzName = loggerNameToTag(clzName);
System.out.println("className:" + clzName + " tagName is " + newClzName);    //get tagName from class full name,and then it will be used in setprop
Logger jdkLogger = Logger.getLogger(clzName);
jdkLogger.setLevel(Level.ALL);
if (jdkLogger.isLoggable(Level.FINE))
{
        jdkLogger.log(Level.FINE, "jdk log msg");    
        jdkLogger.log(Level.Fine,"tagName is")
}

在adb shell中执行以下步骤。
setprop log.tag.DefaultRequestDirector VERBOSE

0
对于 Apache Http Components 5.x 版本,您需要使用 slf4j 日志门面,例如 JUL(Jakarta Common Logging)来在编译时绑定日志后端。
implementation 'org.slf4j:slf4j-jdk14:2.0.0-alpha7'

通过以下方式检索root记录器

final Logger rootLogger = java.util.logging.Logger.getLogger("");

或者

final Logger rootLogger = java.util.logging.LogManager.getLogManager().getLogger("")

将日志级别设置为适当的级别,否则消息将被丢弃

rootLogger.setLevel(Level.ALL);

实现自己的java.util.logging.Handler以接收消息

public static class LogHandler extends Handler {

    @Override
    public void close() {
        // do nothing
    }

    @Override
    public void flush() {
        // do nothing
    }

    @Override
    public void publish(LogRecord record) {
        final Level level = record.getLevel();
        if (level == Level.INFO) {
            Log.i(record.getLoggerName(), record.getMessage());
        } else if (level == Level.WARNING) {
            Log.w(record.getLoggerName(), record.getMessage());
        } else if (level == Level.SEVERE) {
            Log.e(record.getLoggerName(), record.getMessage());
        } else if (level == Level.FINE) {
            Log.d(record.getLoggerName(), record.getMessage());
        } else {
            Log.v(record.getLoggerName(), record.getMessage());
        }
    }
}

设置处理程序

final LogHandler logHandler = new LogHandler();
rootLogger.addHandler(logHandler);

并且对于模块,还有一个“setLevel”函数的好处。需要一个容器来保持对未初始化的外部记录器的强引用,否则垃圾收集器将会释放它们,而“level”参数将被设置为默认值。
private static HashMap<String, Logger> sLoggerMap = new HashMap<>();

用于保存模块的类

public static class Module {
    final String mModule;

    public static final Module APACHE_HTTP = new Module("org.apache.hc.client5.http");
    public static final Module APACHE_HTTP_WIRE = new Module("org.apache.hc.client5.http.wire");
    public static final Module APACHE_HTTP_HEADERS = new Module("org.apache.hc.client5.http.headers");

    Module(final String module) {
        mModule = module;
    }
}

使用函数

public static void setLogging(final Module module, final Level level) {
    Logger logger = sLoggerMap.get(module.mModule);
    if (logger == null) {
        logger = Logger.getLogger(module.mModule);
        sLoggerMap.put(module.mModule, logger);
    }
    logger.setLevel(level);
}

然后像这样使用它(我将所有内容封装在Logging类中)

Logging.setLogging(Logging.Module.APACHE_HTTP_WIRE, Level.FINE);

当请求是异步的时候,它也有自己的缺点。切换日志级别将影响所有正在进行的请求。

对于发布版本,请在proguard-rules.pro中添加以下内容。

-keep class org.apache.hc.client5.http.** {
    <fields>;
    <methods>;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接