开发者

How to enable logging for apache commons HttpClient on Android

To enable logging for apache commons HttpClient in normal Java application I used:

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");
开发者_JAVA百科

But on android I don't see logs in LogCat.

Am I missing some thing?


Ignore my earlier comment. I found the solution on the org.apache.http logging page. Your original answer was referring to httpclient-3.x logging, and the working code for recent versions comes from http-components logging

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");

and properties:

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

The difference is in the logging tag names.


Here is a solution (without digging into details)

Console:

adb shell setprop log.tag.httpclient.wire.header VERBOSE
adb shell setprop log.tag.httpclient.wire.content VERBOSE

Code:

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);

Test:

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


The devil is in the details. I'm running the 2.3.3 emulator and got it working with:

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);

and in the adb shell

# setprop log.tag.org.apache.http.wire VERBOSE
# setprop log.tag.org.apache.http.headers VERBOSE

Thus it seems the log specifiers are different.


You just need to use

java.util.logging.Logger.getLogger(yourFullClassName).setLevel(java.util.logging.Level.All);

and

adb shell setprop log.tag.correspondingTag VERBOSE

Android use this function to get correspondingTag from class full name:

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);
  }

so for example,I want to enable logging for “org.apache.http.impl.client.DefaultRequestDirector” class,do such things below:

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")
}

And then in adb shell

setprop log.tag.DefaultRequestDirector VERBOSE


For Apache Http Components version 5.x, you need to use slf4j logging Facade e.g. JUL (Jakarta Common Logging) to bind a logging backend at compile time.

implementation 'org.slf4j:slf4j-jdk14:2.0.0-alpha7'

Retrieve the root logger via

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

or

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

Set the logging level to proper level, otherwise messages are discarded

rootLogger.setLevel(Level.ALL);

Implement your own java.util.logging.Handler for receiving messages

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());
        }
    }
}

Set the handler

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

And for goodies a setLevel function for modules. A container is required to keep strong reference to uninitialized external loggers, otherwise garbage collector will deallocate and level parameter will be set to default.

private static HashMap<String, Logger> sLoggerMap = new HashMap<>();

Class to hold modules

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;
    }
}

With function

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);
}

And use it like so (I'm encapsulating everything inside Logging class)

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

It has its own drawbacks when requests are async. Switching log level will affect all the in progress request.

For release build, add following to proguard-rules.pro

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜