原文地址:https://github.com/tony19/logback-android/issues/54
Please provide an example of how to configure the AsyncAppender with a FileAppender to write to the file in an async way. I am getting many StrictMode policy violations (StrictModeDiskWriteViolation) on every log write to my log file.
Thanks.
I verified the following config works in Android 4.2.2 without any exceptions.
<configuration debug="true"> <property name="LOG_DIR" value="/data/data/com.example/files" /> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>${LOG_DIR}/log.txt</file> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="FILE" /> </appender> <root level="DEBUG"> <appender-ref ref="ASYNC" /> </root> </configuration>
Example: Configure by in-memory XML string
package com.example; import java.io.ByteArrayInputStream; import java.io.InputStream; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException; import android.app.Activity; import android.os.Bundle; import android.view.Menu; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); configureLogbackByString(); org.slf4j.Logger log = LoggerFactory.getLogger(MainActivity.class); log.info("hello world!!"); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override protected void onDestroy() { super.onDestroy(); // Assume SLF4J is bound to logback-classic in the current environment. // This must be called to properly shutdown AsyncAppender and flush logs // upon application exit. LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); loggerContext.stop(); } String LOGBACK_XML = "<configuration debug='true'>" + " <property name='LOG_DIR' value='/data/data/com.example/files' />" + " <appender name='FILE' class='ch.qos.logback.core.FileAppender'>" + " <file>${LOG_DIR}/log.txt</file>" + " <encoder>" + " <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>" + " </encoder>" + " </appender>" + " <appender name='ASYNC' class='ch.qos.logback.classic.AsyncAppender'>" + " <appender-ref ref='FILE' />" + " </appender>" + " <root level='DEBUG'>" + " <appender-ref ref='ASYNC' />" + " </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(); } } }
Example: Configure with direct calls into logback
package com.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import ch.qos.logback.classic.AsyncAppender; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.util.StatusPrinter; 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); log.info("hello world!!"); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override protected void onDestroy() { super.onDestroy(); // assume SLF4J is bound to logback-classic in the current environment LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); loggerContext.stop(); } 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{35} - %msg%n"); encoder1.start(); FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>(); fileAppender.setContext(lc); fileAppender.setName("FILE"); fileAppender.setFile(this.getFileStreamPath("log.txt").getAbsolutePath()); fileAppender.setEncoder(encoder1); fileAppender.start(); AsyncAppender asyncAppender = new AsyncAppender(); asyncAppender.setContext(lc); asyncAppender.setName("ASYNC"); // UNCOMMENT TO TWEAK OPTIONAL SETTINGS // // excluding caller data (used for stack traces) improves appender's performance // asyncAppender.setIncludeCallerData(false); // // set threshold to 0 to disable discarding and keep all events // asyncAppender.setDiscardingThreshold(0); // asyncAppender.setQueueSize(256); asyncAppender.addAppender(fileAppender); asyncAppender.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(asyncAppender); StatusPrinter.print(lc); } }