How to enable logging for apache commons HttpClient on Android

Viewed 29054

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

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

Am I missing some thing?

5 Answers

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