Android - встановлення максимальної довжини повідомлень logcat


101

За замовчуванням здається, що logcat вріже будь-яке повідомлення журналу, яке вважає "занадто довгим". Це відбувається як всередині Eclipse, так і під час запуску logcat в командному рядку, використовуючи adb -d logcat, і обрізає деякі важливі повідомлення про налагодження.

Чи є спосіб збільшити максимальну довжину рядка, що підтримується logcat, щоб змусити її припинити обробляти інформацію про налагодження? З офіційної документації випливає, що їх може бути, але, можливо, logcat підтримує деякі додаткові параметри, про які не йдеться?





1
@JoshCorreia Я не думаю, що це хороший дублікат, оскільки це стосується загального розміру буфера, і це на повідомлення журналу.
Райан М

1
@RyanM Ах моє погано, я неправильно зрозумів інше питання. Дякуємо, знімаючи позначку як дуп.
Джош Коррея

Відповіді:


45

У logcat для двійкових журналів ( /dev/log/events) є буфер фіксованого розміру ( ), і це обмеження становить 1024 байти. Для небінарних журналів також існує обмеження:

#define LOGGER_ENTRY_MAX_LEN        (4*1024)
#define LOGGER_ENTRY_MAX_PAYLOAD (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

Отже, реальний розмір повідомлення як для двійкових, так і не бінарних журналів становить ~ 4076 байт. Інтерфейс реєстратора ядра нав'язує це LOGGER_ENTRY_MAX_PAYLOADобмеження.

Джерела ліблогів (використовувані logcat) також кажуть:

  • Можливо, повідомлення було скорочено драйвером журналу ядра.

Я б рекомендував вам інструмент nxlog , який не використовує двійковий код logcat, але через обмеження в ядрі я сумніваюся, що він вирішить вашу проблему. Тим не менш, варто спробувати. (відмова від відповідальності: я автор.)


6
Де я можу це знайти? Це в коді "logcat"? Отже, чи повинен я скласти свій власний модифікований logcat?
d4Rk

2
Що таке бінарний / небінарний журнал?
fobbymaster

2
Через додані поля метаданих, LOGGER_ENTRY_MAX_PAYLOADв останніх версіях Android було скорочено з 4076 до 4068 (див. Тут ).
mhsmith

87

Гаразд, цікаво. Я був розчарований, побачивши, що відповідь була "ти не можеш її реально розширити". Моя первісна думка полягала в тому, щоб розірвати це, щоб я міг переглянути всю справу, тому тут я поділюся з вами, як я це роблю (не те, що це щось фантазійне, і воно не є ефективним, але роботу він виконує за дрібну частину):

if (sb.length() > 4000) {
    Log.v(TAG, "sb.length = " + sb.length());
    int chunkCount = sb.length() / 4000;     // integer division
    for (int i = 0; i <= chunkCount; i++) {
        int max = 4000 * (i + 1);
        if (max >= sb.length()) {
            Log.v(TAG, "chunk " + i + " of " + chunkCount + ":" + sb.substring(4000 * i));
        } else {
            Log.v(TAG, "chunk " + i + " of " + chunkCount + ":" + sb.substring(4000 * i, max));
        }
    }
} else {
    Log.v(TAG, sb.toString());
}

Відредаговано, щоб показати останню рядок!


Нема проблем! Сподіваюся, це допомогло вам допомогти
Тревіс

Я впевнений, що тут помилка однієї помилки. Я повинен був використовувати «я <chunkCount + 1» , щоб отримати останній фрагмент
Dan

2
Останній рядок ви втратили в: int chunkCount = sb.length() / 4000;Використання int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Тимур Гілфанов,

2
додати else { Log.v(TAG, sb); }також, щоб надрукувати журнал, коли повідомлення дорівнює <= 4000 символів
Боян Радіевович Бомбер

4
Ця відповідь неправильна для символів, що не належать до ASCII. logcat підтримує UTF8, а обмеження становить 4 к байт , а не символи.
Мігель

58

Розбийте його на кілька частин рекурсивно.

public static void largeLog(String tag, String content) {
   if (content.length() > 4000) {
       Log.d(tag, content.substring(0, 4000));
       largeLog(tag, content.substring(4000));
   } else {
       Log.d(tag, content);
   }
}

3
Це, безумовно, найчистіше рішення, і я вперше застосував рекурсію у виробничому коді.
Агресор

2
@Aggressor навіщо вам вносити 4000+ довгих повідомлень у виробництво?
TWiStErRob

1
Мій випадок використання - виводити великий матеріал json. Файли - це просто біль.
Марсель Фальєр

1
Дуже корисна подяка Я опублікував відповідь, яка розриває рядок у кінцях рядка.
запаморочив

1
Дивовижний простіший очищувач Прохолодний і красивий. Клапси
Мухаммед Ашфак


5

Ось код, який я використовую - він обрізає лінії на межі 4000, а також перериває лінію на нових лініях, а не на середніх рядках. Полегшує для читання файл журналу.

Використання:

Logger.debugEntire("....");

Впровадження:

package ...;

import android.util.Log;

import java.util.Arrays;

public class Logger {

    private static final String LOG_TAG = "MyRockingApp";

    /** @see <a href="http://stackoverflow.com/a/8899735" /> */
    private static final int ENTRY_MAX_LEN = 4000;

    /**
     * @param args If the last argument is an exception than it prints out the stack trace, and there should be no {}
     *             or %s placeholder for it.
     */
    public static void d(String message, Object... args) {
        log(Log.DEBUG, false, message, args);
    }

    /**
     * Display the entire message, showing multiple lines if there are over 4000 characters rather than truncating it.
     */
    public static void debugEntire(String message, Object... args) {
        log(Log.DEBUG, true, message, args);
    }

    public static void i(String message, Object... args) {
        log(Log.INFO, false, message, args);
    }

    public static void w(String message, Object... args) {
        log(Log.WARN, false, message, args);
    }

    public static void e(String message, Object... args) {
        log(Log.ERROR, false, message, args);
    }

    private static void log(int priority, boolean ignoreLimit, String message, Object... args) {
        String print;
        if (args != null && args.length > 0 && args[args.length-1] instanceof Throwable) {
            Object[] truncated = Arrays.copyOf(args, args.length -1);
            Throwable ex = (Throwable) args[args.length-1];
            print = formatMessage(message, truncated) + '\n' + android.util.Log.getStackTraceString(ex);
        } else {
            print = formatMessage(message, args);
        }
        if (ignoreLimit) {
            while (!print.isEmpty()) {
                int lastNewLine = print.lastIndexOf('\n', ENTRY_MAX_LEN);
                int nextEnd = lastNewLine != -1 ? lastNewLine : Math.min(ENTRY_MAX_LEN, print.length());
                String next = print.substring(0, nextEnd /*exclusive*/);
                android.util.Log.println(priority, LOG_TAG, next);
                if (lastNewLine != -1) {
                    // Don't print out the \n twice.
                    print = print.substring(nextEnd+1);
                } else {
                    print = print.substring(nextEnd);
                }
            }
        } else {
            android.util.Log.println(priority, LOG_TAG, print);
        }
    }

    private static String formatMessage(String message, Object... args) {
        String formatted;
        try {
            /*
             * {} is used by SLF4J so keep it compatible with that as it's easy to forget to use %s when you are
             * switching back and forth between server and client code.
             */
            formatted = String.format(message.replaceAll("\\{\\}", "%s"), args);
        } catch (Exception ex) {
            formatted = message + Arrays.toString(args);
        }
        return formatted;
    }
}

4

Код нижче - це уточнення того, що було розміщено Марком Буйкемою. Він розриває рядок у нових рядках. Корисно для реєстрації довгих рядків JSON.

  public static void dLong(String theMsg)
  {
    final int MAX_INDEX = 4000;
    final int MIN_INDEX = 3000;

    // String to be logged is longer than the max...
    if (theMsg.length() > MAX_INDEX)
    {
      String theSubstring = theMsg.substring(0, MAX_INDEX);
      int    theIndex = MAX_INDEX;

      // Try to find a substring break at a line end.
      theIndex = theSubstring.lastIndexOf('\n');
      if (theIndex >= MIN_INDEX)
      {
        theSubstring = theSubstring.substring(0, theIndex);
      }
      else
      {
        theIndex = MAX_INDEX;
      }

      // Log the substring.
      Log.d(APP_LOG_TAG, theSubstring);

      // Recursively log the remainder.
      dLong(theMsg.substring(theIndex));
    }

    // String to be logged is shorter than the max...
    else
    {
      Log.d(APP_LOG_TAG, theMsg);
    }
  }


2

нам ця логіка підкачки

    /*
     * StringBuffer sb - long text which want to show in multiple lines 
     * int lenth - lenth of line need
     */

public static void showInPage(StringBuffer sb, int lenth) {
    System.out.println("sb.length = " + sb.length());
    if (sb.length() > lenth) {

        int chunkCount = sb.length() / lenth; // integer division
        if ((chunkCount % lenth) > 1)
            chunkCount++;
        for (int i = 0; i < chunkCount; i++) {
            int max = lenth * (i + 1);
            if (max >= sb.length()) {
                System.out.println("");
                System.out.println("chunk " + i + " of " + chunkCount + ":"
                        + sb.substring(lenth * i));
            } else {
                System.out.println("");
                System.out.println("chunk " + i + " of " + chunkCount + ":"
                        + sb.substring(lenth * i, max));
            }
        }
    }

}

1

забезпечивши власне рішення рішення Травіса,

void d(String msg) {
  println(Log.DEBUG, msg);
}

private void println(int priority, String msg) {
    int l = msg.length();
    int c = Log.println(priority, TAG, msg);
    if (c < l) {
        return c + println(priority, TAG, msg.substring(c+1));
    } else {
        return c;
    }
}

скористайтеся тим, що Log.println()повертає кількість записаних байтів, щоб уникнути жорсткого кодування "4000". потім, рекурсивно називайте себе частиною повідомлення, яке не вдалося зареєструвати, поки не залишилося нічого.


На жаль, println повертає кількість написаних байтів, і символів! = Байтів.
gnuf

1
ну, це працює. я припускаю, тому що я лише реєструю текст ascii.
Джефрі Блатман

1

Якщо ваш журнал дуже довгий (наприклад, реєстрація всього дампа вашої бази даних з причин налагодження тощо), може статися, що logcat запобігає надмірному веденню журналу. Щоб вирішити це, ви можете додати час очікування evry x мілісекунд.

/**
 * Used for very long messages, splits it into equal chunks and logs each individual to
 * work around the logcat max message length. Will log with {@link Log#d(String, String)}.
 *
 * @param tag     used in for logcat
 * @param message long message to log
 */
public static void longLogDebug(final String tag, @NonNull String message) {
    int i = 0;

    final int maxLogLength = 1000;
    while (message.length() > maxLogLength) {
        Log.d(tag, message.substring(0, maxLogLength));
        message = message.substring(maxLogLength);
        i++;

        if (i % 100 == 0) {
            StrictMode.noteSlowCall("wait to flush logcat");
            SystemClock.sleep(32);
        }
    }
    Log.d(tag, message);
}

Остерігайтеся, використовуйте це лише для налагодження, оскільки це може зупинити блоки основного потоку.


1

Як згадував @mhsmith, LOGGER_ENTRY_MAX_PAYLOADв останніх версіях Android це 4068. Однак якщо ви використовуєте 4068 як максимальну довжину повідомлення у фрагментах коду, запропонованих в інших відповідях, повідомлення будуть усічені. Це пояснюється тим, що Android додає більше символів на початок і кінець вашого повідомлення, які також враховуються. Інші відповіді використовують обмеження 4000 як спосіб вирішення. Однак реально використовувати цілий ліміт за допомогою цього коду (код генерує тег із сліду стека, щоб показати ім'я класу та номер рядка, який викликав журнал, сміливо змінюйте це):

private static final int MAX_MESSAGE_LENGTH = 4068;

private enum LogType {
    debug,
    info,
    warning,
    error
}

private static void logMessage(LogType logType, @Nullable String message, @Nullable String tag) {
    logMessage(logType, message, tag, Thread.currentThread().getStackTrace()[4]);
}

private static void logMessage(LogType logType, @Nullable String message, @Nullable String customTag, StackTraceElement stackTraceElement) {
    // don't use expensive String.format
    String tag = "DASHBOARDS(" + stackTraceElement.getFileName() + "." + (!TextUtils.isEmpty(customTag) ? customTag : stackTraceElement.getMethodName()) + ":" + stackTraceElement.getLineNumber() + ")";
    int maxMessageLength = MAX_MESSAGE_LENGTH - (tag.length()) - 4; // minus four because android adds a letter showing the log type before the tag, e. g. "D/" for debug, and a colon and space are added behind it, i. e. ": "
    if (message == null || message.length() <= maxMessageLength) {
        logMessageInternal(logType, message, tag);
    } else {
        maxMessageLength -= 8; // we will add counter to the beginning of the message, e. g. "(12/15) "
        int totalChunks = (int) Math.ceil((float) message.length() / maxMessageLength);
        for (int i = 1; i <= totalChunks; i++) {
            int start = (i - 1) * maxMessageLength;
            logMessageInternal(logType, "(" + i + "/" + totalChunks + ") " + message.substring(start, Math.min(start + maxMessageLength, message.length())), tag);
        }
    }
}

private static void logMessageInternal(LogType logType, String message, String tag) {
    if (message == null) {
        message = "message is null";
    }
    switch (logType) {
        case debug:
            Log.d(tag, message);
            break;
        case info:
            Log.i(tag, message);
            break;
        case warning:
            Log.w(tag, message);
            break;
        case error:
            Log.e(tag, message);
    }
}

public static void d(String debug, String tag) {
    logMessage(LogType.debug, debug, tag);
}

0

Я не знаю жодного варіанту збільшення довжини logcat, але ми можемо знайти різні журнали, такі як головний журнал, журнал подій тощо. Основний журнал зазвичай містить усе, що його довжина йде до 4 Мб. Таким чином, ви можете отримати те, що ви втратили в лог-терміналі. Шлях: \ data \ logger.


0

Хоча інші надані рішення були корисними, я не був задоволений ними, оскільки вони не охоплювали випадки, коли журнал довший удвічі довший, ніж LOGGER_ENTRY_MAX_LEN, згаданий @ b0ti. Крім того, навіть моє наступне рішення не є ідеальним, оскільки LOGGER_ENTRY_MAX_LEN не отримується динамічно. Якщо хтось знає спосіб це зробити, я хотів би почути про це в коментарях! У будь-якому випадку, це рішення, яке я зараз використовую у своєму коді:

final int loggerEntryMaxLength = 4000;
int logLength = loggerEntryMaxLength - 2 - TAG.length();
int i = 0;
while (output.length() / logLength > i) {
    int startIndex = i++ * logLength;
    int endIndex = i * logLength;
    Log.d(TAG, output.substring(startIndex, endIndex));
}
int startIndex = i * logLength;
Log.d(
        TAG,
        output.substring(
                startIndex,
                startIndex + (output.length() % logLength)
        )
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.