Android - Ustaw maksymalną długość komunikatów logcat

101

Domyślnie wygląda na to, że logcat obcina każdy komunikat dziennika, który uzna za „za długi”. Dzieje się tak zarówno w środowisku Eclipse, jak i podczas uruchamiania programu logcat w wierszu poleceń przy użyciu adb -d logcati powoduje obcięcie niektórych ważnych komunikatów debugowania.

Czy istnieje sposób na zwiększenie maksymalnej długości ciągu obsługiwanego przez program logcat, aby przestał obcinać informacje debugowania? Z oficjalnej dokumentacji wynika, że ​​może nie być, ale może logcat obsługuje dodatkowe opcje, o których tam nie wspomniano?

aroth
źródło
Podobne: stackoverflow.com/questions/6321555/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@JoshCorreia Nie sądzę, żeby to był dobry duplikat, ponieważ odnosi się to do całkowitego rozmiaru bufora, a to jest na komunikat dziennika.
Ryan M
1
@RyanM Ach mój zły, źle zrozumiałem drugie pytanie. Dzięki, usuwam znak jako dupe.
Josh Correia

Odpowiedzi:

45

W logcat istnieje bufor o stałym rozmiarze dla dzienników binarnych ( /dev/log/events), a ten limit wynosi 1024 bajty. W przypadku dzienników niebinarnych istnieje również ograniczenie:

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

Zatem rzeczywisty rozmiar wiadomości dla dzienników binarnych i niebinarnych wynosi ~ 4076 bajtów. Interfejs programu rejestrującego jądro narzuca ten LOGGER_ENTRY_MAX_PAYLOADlimit.

Źródła liblog (używane przez logcat) również mówią:

  • Wiadomość mogła zostać obcięta przez sterownik dziennika jądra.

Polecam Ci nxlog narzędzie, które nie korzysta z pliku binarnego logcat, ale ze względu na ograniczenia w jądrze wątpię, że będzie rozwiązać problem. Niemniej jednak warto spróbować. (zastrzeżenie: jestem autorem).

b0ti
źródło
6
Gdzie to znajdę? Czy znajduje się w kodzie „logcat”? Czy musiałbym więc skompilować własny zmodyfikowany logcat?
d4Rk
2
Co to jest dziennik binarny / niebinarny?
fobbymaster
2
Ze względu na dodawanie pól metadanych LOGGER_ENTRY_MAX_PAYLOADzostała zmniejszona z 4076 do 4068 w nowszych wersjach Androida (patrz tutaj ).
mhsmith,
88

Ok, ciekawe. Byłem rozczarowany, widząc, że odpowiedź brzmiała: „naprawdę nie da się jej rozwinąć”. Moją początkową myślą było to, aby to rozbić, aby móc zobaczyć całość, więc tutaj podzielę się z wami, jak to robię (nie to, że to nic wymyślnego ani nie jest prawie wydajne, ale robi to w mgnieniu oka):

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());
}

Edytowano, aby pokazać ostatni ciąg!

Travis
źródło
Nie ma problemu! Mam nadzieję, że ci pomogło
Travis,
Jestem prawie pewien, że tutaj jest jeden błąd. Musiałem użyć „i <chunkCount + 1”, aby pobrać ostatni fragment
Dan,
2
Przegrałeś ostatnią strunę w: int chunkCount = sb.length() / 4000;Wykorzystanie int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Timur Gilfanov
2
dodaj, else { Log.v(TAG, sb); }aby również wydrukować dziennik, gdy wiadomość jest <= 4000 znaków
Bojan Radivojevic Bomber
4
Ta odpowiedź jest nieprawidłowa w przypadku znaków spoza zestawu ASCII. logcat obsługuje UTF8, a limit wynosi 4 kB , a nie znaków.
miguel
59

Podziel go rekurencyjnie na kilka części.

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);
   }
}
Mark Buikema
źródło
3
Jest to zdecydowanie najczystsze rozwiązanie i po raz pierwszy użyłem rekurencji w kodzie produkcyjnym.
Agresor,
2
@Aggressor, dlaczego musisz rejestrować ponad 4000 długich komunikatów w produkcji?
TWiStErRob
1
Moim przypadkiem użycia jest wyprowadzenie dużego pliku json. Pliki są po prostu uciążliwe.
Marcel Falliere
1
Bardzo przydatne dzięki. Wysłałem odpowiedź, która zrywa ciąg na końcach linii.
oszołomiony
1
Niesamowity prostszy środek czyszczący Chłodny i piękny. Claps
Muhammad Ashfaq
11
for( String line : logMesg.split("\n") ) {
    Log.d( TAG, line );
}
user2085092
źródło
5

Oto kod, którego używam - obcina wiersze na granicy 4000, jednocześnie przerywając wiersz w nowych wierszach, a nie w środku wiersza. Ułatwia odczytanie pliku dziennika.

Stosowanie:

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

Realizacja:

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;
    }
}
Enl8enmentnow
źródło
4

Poniższy kod jest udoskonaleniem tego, co opublikował Mark Buikema. Zrywa ciąg w nowych wierszach. Przydatne do rejestrowania długich ciągów 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);
    }
  }
oszołomiony
źródło
3
int i = 3000;
while (sb.length() > i) {
    Log.e(TAG, "Substring: "+ sb.substring(0, i));
    sb = sb.substring(i);
}
Log.e(TAG, "Substring: "+ sb);
Rizki Sunaryo
źródło
2

nam tę logikę stronicowania

    /*
     * 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));
            }
        }
    }

}
neeraj t
źródło
1

przedstawię własne podejście do rozwiązania Travisa,

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;
    }
}

skorzystać z faktu, że Log.println()zwraca liczbę zapisanych bajtów, aby uniknąć zakodowania na sztywno „4000”. następnie rekurencyjnie wywołaj swoją część wiadomości, której nie można było zarejestrować, dopóki nic nie zostanie.

Jeffrey Blattman
źródło
Niestety println zwraca # zapisanych bajtów i znaków! = Bajtów.
gnuf
1
dobrze, to działa. Zakładam, ponieważ rejestruję tylko tekst ascii.
Jeffrey Blattman
1

Jeśli Twój dziennik jest bardzo długi (np. Rejestrowanie całego zrzutu bazy danych w celu debugowania itp.), Może się zdarzyć, że logcat zapobiega nadmiernemu rejestrowaniu. Aby obejść ten problem, możesz dodać limit czasu co x milisekund.

/**
 * 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);
}

Uważaj, używaj tego tylko do debugowania, ponieważ może to zatrzymać blokowanie głównego wątku.

Patrick Favre
źródło
1

Jak wspomniał @mhsmith, LOGGER_ENTRY_MAX_PAYLOADw najnowszych wersjach Androida jest to 4068. Jeśli jednak użyjesz 4068 jako maksymalnej długości wiadomości we fragmentach kodu oferowanych w innych odpowiedziach, wiadomości zostaną obcięte. Dzieje się tak, ponieważ Android dodaje więcej znaków na początku i na końcu wiadomości, które również się liczą. Inne odpowiedzi wykorzystują limit 4000 jako obejście. Jednak w tym kodzie można naprawdę wykorzystać cały limit (kod generuje tag ze śladu stosu, aby pokazać nazwę klasy i numer linii, która wywołała dziennik, możesz to zmodyfikować):

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);
}
Miloš Černilovský
źródło
0

Nie znam żadnej opcji, aby zwiększyć długość logcat, ale możemy znaleźć różne dzienniki, takie jak dziennik główny, dziennik zdarzeń itp. Dziennik główny zwykle zawiera wszystko, co jego długość dochodzi do 4 MB .. Więc możesz odzyskać utracone dane w terminalu dziennika. Ścieżka to: \ data \ logger.

Vins
źródło
0

Chociaż inne dostarczone rozwiązania były pomocne, nie byłem z nich zadowolony, ponieważ nie obejmowały przypadków, w których dziennik jest dłuższy niż dwa razy dłuższy niż LOGGER_ENTRY_MAX_LEN wspomniany przez @ b0ti. Co więcej, nawet moje poniższe rozwiązanie nie jest idealne, ponieważ LOGGER_ENTRY_MAX_LEN nie jest pobierany dynamicznie. Jeśli ktoś zna sposób na to, chciałbym usłyszeć o tym w komentarzach! W każdym razie jest to rozwiązanie, którego teraz używam w swoim kodzie:

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)
        )
);
Nico Feulner
źródło