Статьи

Создание логов в приложениях Android

Для приложений Android ведение журналов осуществляется классом android.util.Log , который является базовым классом журналирования, в котором журналы хранятся в кольцевом буфере для всего устройства. Все журналы для устройства можно увидеть на вкладке LogCat в Eclipse или прочитать с помощью команды logcat . Вот стандартный вывод журнала для вкладки LogCat в Eclipse:

образ

Существует пять уровней журналов, которые вы можете использовать в своем приложении, от самого подробного до наименее подробного:

  • Подробно: для дополнительных информационных сообщений, которые скомпилированы только в приложении отладки, но никогда не включаются в приложение выпуска.
  • Отладка: для сообщений журнала отладки, которые всегда компилируются, но удаляются во время выполнения в выпуске приложения.
  • Информация: Для информации в журналах, которая будет записана в отладке и выпуске.
  • Предупреждение: Для предупреждения в журналах, которые будут записаны в отладке и выпуске.
  • Ошибка: Для ошибки в журналах, которые будут записаны в отладке и выпуске.

Сообщение журнала включает в себя тег, идентифицирующий группу сообщений и сообщение. По умолчанию уровень журнала всех тегов равен Info , что означает, что сообщения уровня Debug и Verbose никогда не должны отображаться, если только команда setprop не используется для изменения уровня журнала. Итак, чтобы записать подробное сообщение в журнал, вы должны вызвать метод isLoggable, чтобы проверить, можно ли записать сообщение, и вызвать метод ведения журнала:

1
2
if (!Log.isLoggable(logMessageTag, Log.Verbose))
   Log.v("MyApplicationTag", logMessage);

А чтобы показать сообщение журнала Debug и Verbose для определенного тега, выполните команду setprop, когда ваше устройство подключено к сети. Если вы перезагрузите устройство, вам придется снова запустить команду.

1
adb shell setprop log.tag.MyApplicationTag VERBOSE

К сожалению, начиная с Android 4.0, приложение может читать только свои логи. Для отладки было полезно читать журналы из другого приложения, но в некоторых случаях в эти журналы записывалась конфиденциальная информация и создавались вредоносные приложения для их извлечения. Поэтому, если вам нужно отправить файлы журналов для отладки, вам нужно создать собственный класс журнала, используя методы из класса android.util.Log . Помните, что только журналы Info , Warning и Error должны отображаться, когда приложение не запущено в режиме отладки. Вот пример простого регистратора, который упаковывает вызов isLoggable и сохраняет сообщения журналов в основном хранилище устройства (требуется разрешение WRITE_EXTERNAL_STORAGE) и в стандартный буфер:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
  * A logger that uses the standard Android Log class to log exceptions, and also logs them to a
  * file on the device. Requires permission WRITE_EXTERNAL_STORAGE in AndroidManifest.xml.
  * @author Cindy Potvin
  */
 public class Logger
 {
 /**
   * Sends an error message to LogCat and to a log file.
   * @param context The context of the application.
   * @param logMessageTag A tag identifying a group of log messages. Should be a constant in the
   *                      class calling the logger.
   * @param logMessage The message to add to the log.
   */
 public static void e(Context context, String logMessageTag, String logMessage)
    {
    if (!Log.isLoggable(logMessageTag, Log.ERROR))
       return;
 
    int logResult = Log.e(logMessageTag, logMessage);
    if (logResult > 0)
       logToFile(context, logMessageTag, logMessage);
    }
 
/**
   * Sends an error message and the exception to LogCat and to a log file.
   * @param context The context of the application.
   * @param logMessageTag A tag identifying a group of log messages. Should be a constant in the
   *                      class calling the logger.
   * @param logMessage The message to add to the log.
   * @param throwableException An exception to log
   */
 public static void e(Context context, String logMessageTag, String logMessage, Throwable throwableException)
    {
    if (!Log.isLoggable(logMessageTag, Log.ERROR))
       return;
 
    int logResult = Log.e(logMessageTag, logMessage, throwableException);
    if (logResult > 0)
       logToFile(context, logMessageTag, logMessage + "\r\n" + Log.getStackTraceString(throwableException));
    }
 
// The i and w method for info and warning logs should be implemented in the same way as the e method for error logs.
 
/**
   * Sends a message to LogCat and to a log file.
   * @param context The context of the application.
   * @param logMessageTag A tag identifying a group of log messages. Should be a constant in the
   *                      class calling the logger.
   * @param logMessage The message to add to the log.
   */
 public static void v(Context context, String logMessageTag, String logMessage)
    {
                              // If the build is not debug, do not try to log, the logcat be
                              // stripped at compilation.
    if (!BuildConfig.DEBUG || !Log.isLoggable(logMessageTag, Log.VERBOSE))
        return;
 
    int logResult = Log.v(logMessageTag, logMessage);
    if (logResult > 0)
       logToFile(context, logMessageTag, logMessage);
    }
 
/**
   * Sends a message and the exception to LogCat and to a log file.
   * @param logMessageTag A tag identifying a group of log messages. Should be a constant in the
   *                      class calling the logger.
   * @param logMessage The message to add to the log.
   * @param throwableException An exception to log
   */
 public static void v(Context context,String logMessageTag, String logMessage, Throwable throwableException)
    {
                              // If the build is not debug, do not try to log, the logcat be
                              // stripped at compilation.
    if (!BuildConfig.DEBUG || !Log.isLoggable(logMessageTag, Log.VERBOSE))
        return;
 
    int logResult = Log.v(logMessageTag, logMessage, throwableException);
    if (logResult > 0)
       logToFile(context, logMessageTag,  logMessage + "\r\n" + Log.getStackTraceString(throwableException));
    }
 
// The d method for debug logs should be implemented in the same way as the v method for verbose logs.
 
/**
  * Gets a stamp containing the current date and time to write to the log.
  * @return The stamp for the current date and time.
  */
 private static String getDateTimeStamp()
    {
    Date dateNow = Calendar.getInstance().getTime();
                              // My locale, so all the log files have the same date and time format
    return (DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.CANADA_FRENCH).format(dateNow));
    }
 
/**
  * Writes a message to the log file on the device.
  * @param logMessageTag A tag identifying a group of log messages.
  * @param logMessage The message to add to the log.
  */
 private static void logToFile(Context context, String logMessageTag, String logMessage)
    {
    try
       {
                              // Gets the log file from the root of the primary storage. If it does
                              // not exist, the file is created.
       File logFile = new File(Environment.getExternalStorageDirectory(), "TestApplicationLog.txt");
       if (!logFile.exists())
          logFile.createNewFile();
                              // Write the message to the log with a timestamp
       BufferedWriter writer = new BufferedWriter(new FileWriter(logFile, true));
       writer.write(String.format("%1s [%2s]:%3s\r\n", getDateTimeStamp(), logMessageTag, logMessage));
       writer.close();
                              // Refresh the data so it can seen when the device is plugged in a
                              // computer. You may have to unplug and replug to see the latest
                              // changes
      MediaScannerConnection.scanFile(context,
                                      new String[] { logFile.toString() },
                                      null,
                                      null);
 
       }
    catch (IOException e)
       {
       Log.e("com.cindypotvin.Logger", "Unable to log exception to file.");
       }
    }
 }

Если вы выпускаете приложение в магазин приложений или на клиент с этим видом регистратора, вам следует отключить ведение журнала по умолчанию и добавить переключатель в настройках, чтобы включить ведение журнала по требованию. Если регистратор всегда включен, ваше приложение будет часто выполнять запись в основное хранилище и в logcat, что является ненужной нагрузкой, когда все работает правильно. Кроме того, размер файла журнала должен быть ограничен, чтобы избежать заполнения основного хранилища.