安卓如何将日志写入文本文件

147

我正在尝试使用以下代码将日志写入Android文件上的自定义Log.txt文件,但是这个方法只创建了文件却没有写入任何内容。基本上我想读取文件中先前的内容,然后将我的数据附加到现有内容之后。

代码如下:

public static void write(String str) 
    {
        InputStream fileInputStream = null;
        FileOutputStream fileOutpurStream = null;
        try
        { 
            fileInputStream = new FileInputStream(file);
            fileOutpurStream = new FileOutputStream(file);
            if(file.exists())
            {
                int ch = 0;
                int current = 0;
                StringBuffer buffer = new StringBuffer();
                while((ch = fileInputStream.read()) != -1)
                {
                    buffer.append((char) ch);
                    current++;
                }
                byte data[]=new byte[(int)file.length()];
                fileInputStream.read(data);   
                fileOutpurStream.write(data);
                fileOutpurStream.write(str.getBytes(),0,str.getBytes().length);
                fileOutpurStream.flush();
            } 
            else
            {   
                file.createNewFile();
                fileOutpurStream.write(str.getBytes(),0,str.getBytes().length);
                fileOutpurStream.flush();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                fileInputStream.close();
                fileOutpurStream.flush();
                fileOutpurStream.close();
                fileOutpurStream = null;
                fileInputStream = null;
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
16个回答

271

希望这能帮到你...

public void appendLog(String text)
{       
   File logFile = new File("sdcard/log.file");
   if (!logFile.exists())
   {
      try
      {
         logFile.createNewFile();
      } 
      catch (IOException e)
      {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }
   try
   {
      //BufferedWriter for performance, true to set append to file flag
      BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true)); 
      buf.append(text);
      buf.newLine();
      buf.close();
   }
   catch (IOException e)
   {
      // TODO Auto-generated catch block
      e.printStackTrace();
   }
}

88
不要忘记在清单文件中添加写入外部存储权限! - virusss8
5
在buf.close()之前,我需要添加buf.flush();但是这个函数正是我所需要的。谢谢。 - Csabi
3
我该从哪里调用这个函数,以便将所有应用程序的日志写入文件? - uniruddh
10
我建议重构代码,将buf.close()放在finally块内。 - William Price
3
“<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />” 当然是 @virusss8 的意思。 - Bengt
显示剩余9条评论

33

对于那些对Java日志记录和Android日志记录不太熟悉的人

  1. Log4j 是通用的Java日志记录实现,现在是Apache软件基金会的项目。它不是Android特定的,因此与Android存在一些不兼容性。
  2. SLF4J 不是一个日志记录实现,而是一个抽象层。它有助于避免诸如每个第三方库依赖于项目并尝试使用其自己的日志记录实现(例如Log4j)之类的情况。来源

Android中记录日志到txt的一些选项如下

  1. 答案中所述,使用 logcat -f 记录到文件。请注意,从 Android 4.2开始,READ_LOGS权限没有任何影响,每个 应用程序(除非手机已root)只能读取其自己的日志。 这里的缺点是logcat缓冲区是循环的,具有大小 限制。您可能无法获取早期日志。
  2. 像先前的答案中所述,使用适用于移动设备(如Android)的microlog4android。可能有一种方法,但我想不出, 如何使用 microlog4Android 记录应用程序内部存储。 日志路径的唯一选择是外部存储器,例如SD卡,因此我 无法使用它。
  3. 使用Log4jandroid-logging-log4j。android-logging-log4j 是做什么的?它使Log4j在Android中更易于使用 通过提供两个函数。

    • 除了记录文件之外,还将日志发送到logcat的选项
    • 通过提供LogConfigurator类,简单地设置Log4j配置选项,例如文件路径、最大文件大小、备份数等。

    下面是一个简单的例子。请注意,下面示例中的 logger 对象是返回的Log4j对象,而不是 android-logging-log4j 类。因此,android-logging-log4j 仅用于配置Log4j。

  4. 尚未尝试过LogBack。LogBack 是由同一人开发的 提出了Log4J 1.x 和 SLF4J 库。虽然与Log4j 2.x无关。

在Android中使用Log4j的步骤。

  1. log4j-1.2.x.jarandroid-logging-log4j-1.0.3.jar都添加到 libs 文件夹中。

  2. 仅在使用外部存储时添加权限
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

  3. 编写 Log4j

    package com.example.logger;
    
    import android.os.Environment;
    import de.mindpipe.android.logging.log4j.LogConfigurator;
    
    public class Log4jHelper {
        private final static LogConfigurator mLogConfigrator = new LogConfigurator();
    
        static {
            configureLog4j();
        }
    
        private static void configureLog4j() {
            String fileName = Environment.getExternalStorageDirectory() + "/" + "log4j.log";
            String filePattern = "%d - [%c] - %p : %m%n";
            int maxBackupSize = 10;
            long maxFileSize = 1024 * 1024;
    
            configure( fileName, filePattern, maxBackupSize, maxFileSize );
        }
    
        private static void configure( String fileName, String filePattern, int maxBackupSize, long maxFileSize ) {
            mLogConfigrator.setFileName( fileName );
            mLogConfigrator.setMaxFileSize( maxFileSize );
            mLogConfigrator.setFilePattern(filePattern);
            mLogConfigrator.setMaxBackupSize(maxBackupSize);
            mLogConfigrator.setUseLogCatAppender(true);
            mLogConfigrator.configure();
    
        }
    
        public static org.apache.log4j.Logger getLogger( String name ) {
            org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger( name );
            return logger;
        }
    }
    
  4. 在Activity类中

  5. org.apache.log4j.Logger log= Log4jHelper.getLogger( "YourActivity" );
    log.error("Error");
    log.info("Info");
    log.warn("Warn");
    

示例源代码。请注意,log4j 2.x(改进的功能)是从头开始重写的,与log4j 1.x不兼容。因此,您必须在android-logging-log4j jar中使用log4j 1.2.x jar。我能够将日志记录到应用程序内部文件中,稍后通过setReadable(true, false)将该文件发送电子邮件。


26

我使用microlog4android,但是文档非常简陋。他们只需要添加一个“快速入门教程”链接就可以了。

这里有一个我找到的快速入门教程。

  1. Add the following static variable in your main Activity:

    private static final Logger logger = LoggerFactory.getLogger();
    
  2. Add the following to your onCreate() method:

    PropertyConfigurator.getConfigurator(this).configure();
    
  3. Create a file named microlog.properties and store it in assets directory

  4. Edit the microlog.properties file as follows:

    microlog.level=DEBUG
    microlog.appender=LogCatAppender;FileAppender
    microlog.formatter=PatternFormatter
    microlog.formatter.PatternFormatter.pattern=%c [%P] %m %T
    
  5. Add logging statements like this:

    logger.debug("M4A");
    

针对每个类,按照1)中的规定创建一个记录器对象。

6.您可以添加以下权限:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

这里是教程的源码


5
FileAppender 会在您的 sdcard 上创建一个名为 "microlog.txt" 的文件(在我的情况下,完整路径为 "/sdcard/microlog.txt")。该文件记录日志信息。 - Ruslan Yanchyshyn
3
这个快速教程应该是Microlog4Android官方文档的一部分。 - Johan Karlsson
错误:Android Dex:[项目] com.android.dex.DexException:多个dex文件定义了Lcom/google/code/microlog4android/Level;? - user2660852

11

警告:我可能完全误解了你的意思,但是如果你只是想要一个日志文件,为什么要费心呢?

将以下内容放入一个bat文件中(将路径更改为您的工具目录,并将yourappname替换为您的应用程序名称):

cd "C:\devAndroid\Software\android-sdk-windows-1.6_r1\android-sdk-windows-1.6_r1\tools"
adb logcat -v time   ActivityManager:W  yourappname:D  *:W >"C:\devAndroid\log\yourappname.log"

然后在你的代码中只需要做类似这样的事情:

Log.d("yourappname", "Your message");

连接USB电缆并运行您的批处理文件即可创建日志。

祝好


不要忘记导入android.util.Log。 - Spidey
它返回“错误:多个设备和模拟器-等待设备-”,什么都没有。有什么问题吗? - eros
21
如果您在运行此应用程序时不在计算机旁边,该应用程序如何工作? - DDSports
@DDSports 不是这样的。但他没有理解问题的本质。 - Ewoks
@DDSports 你可以使用无线adb。 - Daniel F

8
我用以下的命令行代码解决了这个问题:
File outputFile = new File("pathToFile"); 
Runtime.getRuntime().exec("logcat -c");
Runtime.getRuntime().exec("logcat -v time -f " + outputFile.getAbsolutePath())

"time"选项会添加元数据字段的详细信息,包括日期、调用时间、优先级/标签以及发出消息的进程的PID。

然后在您的代码中,只需执行类似于以下内容的操作(使用android.util.Log):

Log.d("yourappname", "Your message");

4
adb连接时,它能很好地工作,但当设备处于独立模式时则无法正常运行。我不会在生产环境中使用它,但在调试模式下它是一个简单的解决方案。 - Julien Kronegg

8
使用 slf4android 库。
它是使用 android java.util.logging.* 实现的 slf4j api 的简单实现。
特点:
  • 开箱即用的文件记录日志
  • 通过 LoggerConfiguration.configuration().addHandlerToLogger 记录到任何其他位置
  • 摇动您的设备以通过电子邮件发送带有屏幕截图的日志
  • 非常小,只有 ~55kB
slf4android 主要由 @miensol 维护。
在我们的博客上阅读更多关于 slf4android 的内容:

8

经过长时间的调查,我发现:

  • android.util.Log默认使用java.util.logging.Logger
  • LogCat使用名称为""logger,要获取它的实例,请使用LogManager.getLogManager().getLogger("")
  • 在运行调试应用程序后,Android设备将向LogCat添加com.android.internal.logging.AndroidHandlerlogger实例
  • 但是!!!com.android.internal.logging.AndroidHandler仅将消息打印到logcat中,当级别大于java.util.logging.Level.INFO时,例如(Level.INFO, Level.WARNING, Level.SEVERE, Level.OFF

因此,要将日志写入文件,只需简单地向rootLogger ""添加一个java.util.logging.FileHandler即可:

  class App : Application{
    override fun onCreate() {
      super.onCreate()
      Log.d(TAG, printLoggers("before setup"))

      val rootLogger = java.util.logging.LogManager.getLogManager().getLogger("")
      val dirFile = destinationFolder
      val file = File(dirFile,"logFile.txt")
      val handler = java.util.logging.FileHandler(file.absolutePath, 5* 1024 * 1024/*5Mb*/, 1, true)
      handler.formatter = AndroidLogFormatter(filePath = file.absolutePath)

      rootLogger?.addHandler(handler)

      Log.d(TAG, printLoggers("after setup"))
    }
  }

val destinationFolder: File
        get() {
            val parent = 
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absoluteFile
            val destinationFolder = File(parent, "MyApp")
            if (!destinationFolder.exists()) {
                destinationFolder.mkdirs()
                destinationFolder.mkdir()
            }
            return destinationFolder
        }

class AndroidLogFormatter(val filePath: String = "", var tagPrefix: String = "") : Formatter() {

    override fun format(record: LogRecord): String {
        val tag = record.getTag(tagPrefix)
        val date = record.getDate()
        val level = record.getLogCatLevel()
        val message = record.getLogCatMessage()
        return "$date $level$tag: $message\n"
    }
}

fun LogRecord.getTag(tagPrefix: String): String {
    val name = loggerName
    val maxLength = 30
    val tag = tagPrefix + (if (name.length > maxLength) name.substring(name.length - maxLength) else name)
    return tag
}

fun LogRecord.getDate(): String? {
    return Date(millis).formatedBy("yyyy-MM-dd HH:mm:ss.SSS")
}

fun Date?.formatedBy(dateFormat: String): String? {
    val date = this
    date ?: return null
    val writeFormat = SimpleDateFormat(dateFormat, Locale.getDefault()) //MM в HH:mm
    return writeFormat.format(date)
}

fun LogRecord.getLogCatMessage(): String {
    var message = message

    if (thrown != null) {
        message += Log.getStackTraceString(thrown)
    }
    return message
}

fun Int.getAndroidLevel(): Int {
    return when {
        this >= Level.SEVERE.intValue() -> { // SEVERE
            Log.ERROR
        }
        this >= Level.WARNING.intValue() -> { // WARNING
            Log.WARN
        }
        this >= Level.INFO.intValue() -> { // INFO
            Log.INFO
        }
        else -> {
            Log.DEBUG
        }
    }
}

fun LogRecord.getLogCatLevel(): String {
    return when (level.intValue().getAndroidLevel()) {
        Log.ERROR -> { // SEVERE
            "E/"
        }
        Log.WARN -> { // WARNING
            "W/"
        }
        Log.INFO -> { // INFO
            "I/"
        }
        Log.DEBUG -> {
            "D/"
        }
        else -> {
            "D/"
        }
    }
}

fun getLoggerLevel(level: Int): Level {
    return when (level) {
        Log.ERROR -> { // SEVERE
            Level.SEVERE
        }
        Log.WARN -> { // WARNING
            Level.WARNING
        }
        Log.INFO -> { // INFO
            Level.INFO
        }
        Log.DEBUG -> {
            Level.FINE
        }
        else -> {
            Level.FINEST
        }
    }
}

要打印应用程序中的所有记录器,请使用以下命令:

Log.e(TAG, printLoggers("before setup"))

 private fun printLoggers(caller: String, printIfEmpty: Boolean = true): String {
        val builder = StringBuilder()
        val loggerNames = LogManager.getLogManager().loggerNames
        builder.appendln("--------------------------------------------------------------")
        builder.appendln("printLoggers: $caller")
        while (loggerNames.hasMoreElements()) {
            val element = loggerNames.nextElement()
            val logger = LogManager.getLogManager().getLogger(element)
            val parentLogger: Logger? = logger.parent
            val handlers = logger.handlers
            val level = logger?.level
            if (!printIfEmpty && handlers.isEmpty()) {
                continue
            }
            val handlersNames = handlers.map {
                val handlerName = it.javaClass.simpleName
                val formatter: Formatter? = it.formatter
                val formatterName = if (formatter is AndroidLogFormatter) {
                    "${formatter.javaClass.simpleName}(${formatter.filePath})"
                } else {
                    formatter?.javaClass?.simpleName
                }
                "$handlerName($formatterName)"
            }
            builder.appendln("level: $level logger: \"$element\" handlers: $handlersNames parentLogger: ${parentLogger?.name}")
        }
        builder.appendln("--------------------------------------------------------------")
        return builder.toString()
    }

嗨。使用这个“”记录器有什么好处?我看不出与自定义记录器有什么区别。或者是我漏掉了什么吗? - Abins Shaji
我尝试过这个,但是出现了java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.logging.Logger java.util.logging.Logger.getParent()' on a null object reference的错误。 - Selmeny

7
这可能有点晚了,但希望能对您有所帮助... 尝试这个...
public void writefile()
    {
        File externalStorageDir = Environment.getExternalStorageDirectory();
        File myFile = new File(externalStorageDir , "yourfilename.txt");

        if(myFile.exists())
        {
           try
           {

        FileOutputStream fostream = new FileOutputStream(myFile);
        OutputStreamWriter oswriter = new OutputStreamWriter(fostream); 
        BufferedWriter bwriter = new BufferedWriter(oswriter);   
        bwriter.write("Hi welcome ");
        bwriter.newLine();            
        bwriter.close(); 
        oswriter.close(); 
        fostream.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
        else
        {
            try {
                myFile.createNewFile();
            }
            catch (IOException e) 
            {
                e.printStackTrace();
            }
        }

在这里,bfwritter.newline 将你的文本写入文件中。并且添加权限。

 <uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE"/>

务必在您的清单文件中加入此内容。


7

13
这个库看起来不错,但是文档很糟糕。我无法弄清楚如何让它工作,最终只能自己组合一些东西。 - Paul Lammertsma
@PaulLammertsma: 我认为你应该贡献给Microlog4Android。顺便提一下:它与Log4j具有相同的API。抱歉文档有点差。 - Johan Karlsson
@Paul Lammertsma 如果文档太糟糕以至于你无法使用它,那么任何东西都怎么可能是好的呢?如果你无法让它正常工作,那么它就是垃圾。 - The incredible Jan

3
我创建了一个简单、轻量级的类(大约260行代码),它扩展了标准的android.util.Log实现,提供了基于文件的日志记录功能:每个日志消息都通过android.util.Log记录,并写入设备上的文本文件。
你可以在github上找到它:
https://github.com/volkerv/FileLog

1
你觉得我们需要确保文件操作不在UI线程上执行吗?如果我们有大量的文件记录,会怎样呢? - Jayshil Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接