-
-
Notifications
You must be signed in to change notification settings - Fork 173
Home
logback-android
brings the power of logback to Android. logback
is a reliable, generic, fast, and flexible logging library for Java applications.
Runs on Android 2.1 or higher.
The current version is 1.0.10-2.
- Add
logback-android
andslf4j-api
to your project class path. - Configure
logback-android
using an XML file or in-code statements. Otherwise, logging is silently disabled. - Use
slf4j-api
in your application to write logging statements as shown in the example below.
Example:
package com.example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// SLF4J
Logger LOG = LoggerFactory.getLogger(MainActivity.class);
LOG.info("hello world");
}
}
OR Maven users can simply add these dependencies to pom.xml
:
<dependency>
<groupId>com.github.tony19</groupId>
<artifactId>logback-android-core</artifactId>
<version>1.0.10-2</version>
</dependency>
<dependency>
<groupId>com.github.tony19</groupId>
<artifactId>logback-android-classic</artifactId>
<version>1.0.10-2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
logback-android
can be configured simply by creating assets/logback.xml
, containing configuration XML. This file is read automatically upon loading the first logger from your code. Additional code configuration is not necessary.
Example 1: Basic configuration (single destination)
<configuration>
<!-- Create a file appender for a log in the application's data directory -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>/data/data/com.example/files/log/foo.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Write INFO (and higher-level) messages to the log file -->
<root level="INFO">
<appender-ref ref="file" />
</root>
</configuration>
Example 2: Advanced configuration (multiple destinations)
<configuration>
<property name="LOG_DIR" value="/data/data/com.example/files" />
<!-- Create a logcat appender -->
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
<encoder>
<pattern>%msg</pattern>
</encoder>
</appender>
<!-- Create a file appender for TRACE-level messages -->
<appender name="TraceLog" class="ch.qos.logback.core.FileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACE</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<file>${LOG_DIR}/trace.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Create a file appender for DEBUG-level messages -->
<appender name="DebugLog" class="ch.qos.logback.core.FileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<file>${LOG_DIR}/debug.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Write TRACE messages from class A to its own log -->
<logger name="com.example.A" level="TRACE">
<appender-ref ref="TraceLog" />
</logger>
<!-- Write DEBUG messages from class B to its own log -->
<logger name="com.example.B" level="DEBUG">
<appender-ref ref="DebugLog" />
</logger>
<!-- Write INFO (and higher-level) messages to logcat -->
<root level="INFO">
<appender-ref ref="logcat" />
</root>
</configuration>
logback-android
also supports configuration XML within your application's AndroidManifest.xml
(requires setting up your initialization search path). Simply put the configuration XML inside the <manifest>/<logback>
element as shown in the example below.
Example:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<logback>
<configuration>
<appender
name="LOGCAT"
class="ch.qos.logback.classic.android.LogcatAppender" >
<tagEncoder>
<pattern>%logger{0}</pattern>
</tagEncoder>
<encoder>
<pattern>[ %thread ] %msg%n</pattern>
</encoder>
</appender>
<root level="WARN" >
<appender-ref ref="LOGCAT" />
</root>
</configuration>
</logback>
</manifest>
If you enter configuration XML in AndroidManifest.xml
, the Android SDK r21+ emits an error message by default due to the android
namespace prefix missing from the logback-android
configuration elements. To resolve this, change the Severity level for MissingPrefix in your project's Android Lint preferences from Eclipse.
Even though assets/logback.xml
is the first configuration loaded, this file could include other configuration files from within your JAR or from the host filesystem. This is achieved with the <includes>
tag, containing a list of optional <include>
tags (i.e., no error is thrown for nonexistent resources/files). The first <include>
tag that points to an existent resource/file causes the remainder of the list to be ignored.
Example:
<configuration>
<includes>
<include file="/sdcard/foo.xml"/>
<include resource="assets/config/test.xml"/>
<include resource="AndroidManifest.xml"/>
</includes>
</configuration>
Prior to v1.0.8-1
, the initialization search path was hard-coded, and that can be recreated with this configuration:
<configuration>
<includes>
<include file="/sdcard/logback/logback-test.xml"/>
<include file="/sdcard/logback/logback.xml"/>
<include resource="AndroidManifest.xml"/>
<include resource="assets/logback-test.xml"/>
<include resource="assets/logback.xml"/>
</includes>
</configuration>
If you prefer code-based configuration instead of the XML method above, you can use the logback
classes directly to initialize logback-android
as shown in the following examples. Note the direct usage of logback
classes removes the advantage of the facade provided by SLF4J.
Example: Uses BasicLogcatConfigurator
package com.example;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.android.BasicLogcatConfigurator;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BasicLogcatConfigurator.configureDefaultContext();
org.slf4j.Logger log = LoggerFactory.getLogger(MainActivity.class);
for (int i = 0; i < 10; i++) {
log.info("hello world");
}
}
}
Example: Configures appenders directly
package com.example;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.android.LogcatAppender;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
configureLogbackDirectly();
org.slf4j.Logger log = LoggerFactory.getLogger(MainActivity.class);
for (int i = 0; i < 10; i++) {
log.info("hello world");
}
}
private void configureLogbackDirectly() {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
lc.reset();
// setup FileAppender
PatternLayoutEncoder encoder1 = new PatternLayoutEncoder();
encoder1.setContext(lc);
encoder1.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
encoder1.start();
FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
fileAppender.setContext(lc);
fileAppender.setFile(this.getFileStreamPath("app.log").getAbsolutePath());
fileAppender.setEncoder(encoder1);
fileAppender.start();
// setup LogcatAppender
PatternLayoutEncoder encoder2 = new PatternLayoutEncoder();
encoder2.setContext(lc);
encoder2.setPattern("[%thread] %msg%n");
encoder2.start();
LogcatAppender logcatAppender = new LogcatAppender();
logcatAppender.setContext(lc);
logcatAppender.setEncoder(encoder2);
logcatAppender.start();
// add the newly created appenders to the root logger;
// qualify Logger to disambiguate from org.slf4j.Logger
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
root.addAppender(fileAppender);
root.addAppender(logcatAppender);
}
}
Example: Configures by XML file
// snip…
private void configureLogbackByFilePath() {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
lc.reset();
JoranConfigurator config = new JoranConfigurator();
config.setContext(lc);
try {
config.doConfigure("/path/to/config.xml");
} catch (JoranException e) {
e.printStackTrace();
}
}
Example: Configures by in-memory XML string
// snip…
static final String LOGBACK_XML =
"<configuration>" +
"<appender name='FILE' class='ch.qos.logback.core.FileAppender'>" +
"<file>foo.log</file>" +
"<append>false</append>" +
"<encoder>" +
"<pattern>%-4r [%t] %-5p %c{35} - %m%n</pattern>" +
"</encoder>" +
"</appender>" +
"<root level='DEBUG'>" +
"<appender-ref ref='FILE' />" +
"</root>" +
"</configuration>"
;
private void configureLogbackByString() {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
lc.reset();
JoranConfigurator config = new JoranConfigurator();
config.setContext(lc);
InputStream stream = new ByteArrayInputStream(LOGBACK_XML.getBytes());
try {
config.doConfigure(stream);
} catch (JoranException e) {
e.printStackTrace();
}
}
When optimizing your application with ProGuard, include the following rules to prevent logback-android
and SLF4J calls from being removed (unless that's desired):
-keep class ch.qos.** { *; }
-keep class org.slf4j.** { *; }
-keepattributes *Annotation*
If you don't use the mailing features of logback
(i.e., the SMTPAppender
), you might encounter an error while exporting your app with ProGuard. To resolve this, add the following rule:
-dontwarn ch.qos.logback.core.net.*
For help with using logback-android, ask the mailing list: [email protected].
logback-android
is built with Apache Maven 2+. Use these commands to create the uber JAR (with debug symbols).
git clone git://github.com/tony19/logback-android.git
cd logback-android
./makejar.sh
The jar would be in: target/logback-android-<version>.jar
logback-android: the reliable, generic, fast and flexible logging framework for Android.
Copyright © 1999-2013, QOS.ch. All rights reserved.
This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License v1.0 as published by the Eclipse Foundation
or (per the licensee's choosing)
under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation.