Reputation: 477
I want to use java.util.logging on Android. I want to configure the logging system with logging.properties. But how can I tell Android using the specific configure file? For example, I placed the logging.properties in the classpath root of the application. How Android knows the location of logging.properties.
Thanks
Upvotes: 33
Views: 25887
Reputation: 118
So, the approach that I took works better than the above, at least in my experience anyway.
The Socket.io java client library allows you to specify the WebSocket.Factory, so simply provide it with a factory that produces sockets with logging 🤷
Under the hood it's using OkHttp, and an OkHttpClient implements the WebSocket.Factory interface, so it looks a little like this:
/**
* A SocketFactory configured to log all events that occur over the socket
*/
class SocketIoCallFactory(
private val okHttpClient: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor()).build(),
) : WebSocket.Factory by okHttpClient {
override fun newWebSocket(request: Request, listener: WebSocketListener): WebSocket {
val webSocket = okHttpClient.newWebSocket(request, LoggingWebSocketListener(listener))
return WebSocketWithLogging(webSocket)
}
}
class WebSocketWithLogging(private val webSocket: WebSocket) : WebSocket by webSocket {
override fun send(bytes: ByteString): Boolean {
Timber.i("Sending bytes: $bytes")
return webSocket.send(bytes)
}
override fun send(text: String): Boolean {
Timber.i("Sending text: $text")
return webSocket.send(text)
}
}
// default websocket factory for socket.io with logging <3
io.socket.client.IO.setDefaultOkHttpWebSocketFactory(SocketIoCallFactory())
🚀
Upvotes: -1
Reputation: 12847
Here is modified answer of Christian Bauer
If somewhere in any third party lib you will see something like this:
final public class AnyClass {
private static final Logger logger = Logger.getLogger(AnyClass.class.getName());
...
if (logger.isLoggable(Level.FINE)) {
logger.fine("any log event");
}
...
}
Add to your code this after creating first instance of AnyClass.class
:
SetupLogger.setup(Arrays.asList(
new Pair<>(AnyClass.class.getName(), Level.FINEST),//AnyClass.class.getName() is because instance `logger` has his name
new Pair<>(AnyOther.class.getName(), Level.FINEST)
));
SetupLogger .class
public class SetupLogger {
static public void setup(List<Pair<String, Level>> loggerNames) {
// suppress the logging output to the console
AndroidLoggingHandler rootHandler = new AndroidLoggingHandler();
AndroidLoggingHandler.reset(rootHandler );
for (Pair<String, Level> pair : loggerNames) {
Logger logger = Logger.getLogger(pair.first);
logger.setLevel(pair.second);
}
}
}
Upvotes: 2
Reputation:
This is now an FAQ for one of my projects, hopefully more people will find this here: java.util.logging works fine on Android. Please don't use anything else in your code, logging frameworks are like a pest in the Java world.
What is broken is the default logging handler shipped with Android, it ignores any log messages with level finer than INFO. You don't see DEBUG etc. messages.
The reason is the call to Log.isLoggable() in AndroidHandler.java:
Here is how you fix it:
import android.util.Log;
import java.util.logging.*;
/**
* Make JUL work on Android.
*/
public class AndroidLoggingHandler extends Handler {
public static void reset(Handler rootHandler) {
Logger rootLogger = LogManager.getLogManager().getLogger("");
Handler[] handlers = rootLogger.getHandlers();
for (Handler handler : handlers) {
rootLogger.removeHandler(handler);
}
rootLogger.addHandler(rootHandler);
}
@Override
public void close() {
}
@Override
public void flush() {
}
@Override
public void publish(LogRecord record) {
if (!super.isLoggable(record))
return;
String name = record.getLoggerName();
int maxLength = 30;
String tag = name.length() > maxLength ? name.substring(name.length() - maxLength) : name;
try {
int level = getAndroidLevel(record.getLevel());
Log.println(level, tag, record.getMessage());
if (record.getThrown() != null) {
Log.println(level, tag, Log.getStackTraceString(record.getThrown()));
}
} catch (RuntimeException e) {
Log.e("AndroidLoggingHandler", "Error logging message.", e);
}
}
static int getAndroidLevel(Level level) {
int value = level.intValue();
if (value >= Level.SEVERE.intValue()) {
return Log.ERROR;
} else if (value >= Level.WARNING.intValue()) {
return Log.WARN;
} else if (value >= Level.INFO.intValue()) {
return Log.INFO;
} else {
return Log.DEBUG;
}
}
}
In the main activity/initialization code of your application:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidLoggingHandler.reset(new AndroidLoggingHandler());
java.util.logging.Logger.getLogger("my.category").setLevel(Level.FINEST);
...
TL;DR: Yes, you could use some magic properties, or adb shell command, or even learn how the stupid built-in logging handler's DalvikLogging.loggerNameToTag
converts category names to tags (which you would have to do for those magic properties and shell commands), but why bother? Isn't logging painful enough?
Upvotes: 65
Reputation: 7859
In case one of you only wants to route java.util.logging output from third party libraries, this can achieved pretty easily with SLF4J and its jul-to-slf4j bridge. This works for FINE
and FINEST
log statements, too, BTW.
Here's the maven dependency
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
To bootstrap it, put the following in your Application
class, Roboguice Module or somewhere else where it gets executed before your first log statement. (Configuring this in assets/logging.properties
seems not to work, unfortunately).
/*
* add SLF4JBridgeHandler to j.u.l's root logger, should be done once
* during the initialization phase of your application
*/
SLF4JBridgeHandler.install();
You can then either
configure all your log statements from assets/logback.xml
(using logback-android). See here for a mapping of log levels.
or just use SLF4J-android to forward the log statements to logcat. To do so, just put the following dependency to your classpath, no further config required:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-android</artifactId>
<version>${slf4j.version}</version>
</dependency>
I implemented this successfully using
<properties>
<slf4j.version>1.7.6</slf4j.version>
</properties>
Note:
INFO
) are routed to logcat by android. So make sure to apply appropriate filters that avoid duplicates.Upvotes: 3
Reputation: 443
At least on Android 4.3, with unchanged Handler, if you use
adb shell setprop log.tag.YOURTAG DEBUG
You can see messages logged with up to Level.FINE in logcat. I haven't figured out a way to log higher levels, but that's sufficient for me.
Upvotes: 3
Reputation: 95459
Generally one uses android.util.Log for logging on Android. There are some key advantages to using that logger, such as being able to use adb logcat
to view logging output sent to those logs.
You can try put logging.properties in assets/
or res/raw/
. If Android doesn't pick those up there, then one can use java.util.logging.LogManager.readConfiguration(java.io.InputStream) to force load it. (You can use the Resources and AssetManager classes to get the file as an InputStream).
Upvotes: 5