-
-
Notifications
You must be signed in to change notification settings - Fork 174
Home
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.
Using the official logback-android
XML schema definition file is recommended for XML validation, autocompletion, and hints in the configuration XML.
Add the xmlns
, xmlns:xsi
, and xsi:schemaLocation
attributes to <configuration>
as shown below:
<configuration
xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd"
>
...
</configuration>
When viewing logback.xml
from Android Studio (or other JetBrains IDEs), hover the xsi:schemaLocation
's URI (which appears in red) to reveal the red light-bulb hint, and select Fetch remote resource
.
When viewing logback.xml
in VS Code, use RedHat's XML extension, which automatically fetches the remote XML schema definition.
Example 1: Basic configuration (single destination)
<configuration
xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd"
>
<!-- 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
xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd"
>
<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>
Note these special properties are initialized when the first logger is instantiated (i.e., via LoggerFactory.getLogger()
), but that must be done when the application context is available (e.g., in the onCreate
method of your Application
class or at any point thereafter). Otherwise, the special properties will resolve to empty strings.
-
DATA_DIR
- The current user's internal files directory. Note this is not the application data directory despite the name. This variable will be renamed in a future release for clarity. -
EXT_DIR
- The external storage directory if mounted (e.g., if the SD card is inserted). If not mounted, this isnull
. Usage:${EXT_DIR:-${DATA_DIR}}
, which specifies the external storage directory if mounted. Otherwise, the internal files directory. -
PACKAGE_NAME
- The application package name (fromAndroidManifest.xml
) -
VERSION_NAME
- The application version name (fromAndroidManifest.xml
) -
VERSION_CODE
- The application version code (fromAndroidManifest.xml
)
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
xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd"
>
<includes>
<include file="/sdcard/foo.xml"/>
<include resource="assets/config/test.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
xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd"
>
<includes>
<include file="/sdcard/logback/logback-test.xml"/>
<include file="/sdcard/logback/logback.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
to redirect to logcat
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 {
static {
BasicLogcatConfigurator.configureDefaultContext();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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.stop();
// 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.stop();
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.stop();
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):
# Issue #229
-keepclassmembers class ch.qos.logback.classic.pattern.* { <init>(); }
# The following rules should only be used if you plan to keep
# the logging calls in your released app.
-keepclassmembers ch.qos.logback.** { *; }
-keepclassmembers org.slf4j.impl.** { *; }
-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.*