Android: imposta la lunghezza massima dei messaggi logcat


101

Per impostazione predefinita, sembra che logcat troncerà qualsiasi messaggio di log che considera "troppo lungo". Ciò accade sia all'interno di Eclipse che durante l'esecuzione di logcat sulla riga di comando utilizzando adb -d logcat, e sta troncando alcuni importanti messaggi di debug.

C'è un modo per aumentare la lunghezza massima della stringa supportata da logcat per far sì che smetta di troncare le informazioni di debug? La documentazione ufficiale implica che potrebbe non esserci, ma forse logcat supporta alcune opzioni aggiuntive non menzionate lì?





1
@ JoshCorreia Non penso che sia un buon duplicato, in quanto si riferisce alla dimensione totale del buffer, e questo è per messaggio di log.
Ryan M

1
@ RyanM Ah, colpa mia, ho frainteso l'altra domanda. Grazie, rimuovendo mark as dupe.
Josh Correia

Risposte:


45

C'è un buffer di dimensione fissa in logcat per i log binari ( /dev/log/events) e questo limite è di 1024 byte. Per i log non binari c'è anche un limite:

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

Quindi la dimensione reale del messaggio per i registri binari e non binari è di ~ 4076 byte. L'interfaccia del logger del kernel impone questo LOGGER_ENTRY_MAX_PAYLOADlimite.

I sorgenti di liblog (usati da logcat) dicono anche:

  • Il messaggio potrebbe essere stato troncato dal driver di registro del kernel.

Ti consiglierei lo strumento nxlog che non usa il binario logcat, ma a causa delle limitazioni nel kernel dubito che risolverà il tuo problema. Tuttavia, potrebbe valere la pena provare. (disclaimer: sono l'autore.)


6
Dove lo trovo? È nel codice "logcat"? Quindi, dovrei compilare il mio logcat modificato?
d4Rk

2
Cos'è il log binario / non binario?
fobbymaster

2
A causa dell'aggiunta di campi di metadati, LOGGER_ENTRY_MAX_PAYLOADè stato ridotto da 4076 a 4068 nelle versioni più recenti di Android (vedere qui ).
mhsmith

87

Ok, interessante. Sono rimasto deluso nel vedere che la risposta è stata "non puoi davvero espanderla". Il mio pensiero iniziale era di romperlo in modo da poter vedere l'intera cosa, quindi qui condivido con te come faccio proprio questo (non che sia qualcosa di stravagante né sia ​​quasi efficiente, ma fa il lavoro in un pizzico):

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

Modificato per mostrare l'ultima stringa!


Nessun problema! Spero ti abbia aiutato
Travis

Sono abbastanza sicuro che qui ci sia un errore di disattivazione. Ho dovuto usare "i <chunkCount + 1" per ottenere l'ultimo pezzo
Dan

2
Hai perso l'ultima stringa in: int chunkCount = sb.length() / 4000;Usa int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Timur Gilfanov

2
aggiungi else { Log.v(TAG, sb); }per stampare anche il registro quando il messaggio è lungo <= 4000 caratteri
Bojan Radivojevic Bomber

4
Questa risposta è sbagliata per i caratteri non ASCII. logcat supporta UTF8 e il limite è di 4k byte , non caratteri.
miguel

59

Suddividi in più parti in modo ricorsivo.

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
Questa è di gran lunga la soluzione più pulita e la prima volta che ho effettivamente utilizzato la ricorsione nel codice di produzione.
Aggressore

2
@Aggressor perché è necessario registrare più di 4000 messaggi lunghi in produzione?
TWiStErRob

1
Il mio caso d'uso è produrre una grande roba json. I file sono semplicemente un dolore.
Marcel Falliere

1
Grazie molto utili. Ho pubblicato una risposta che interrompe la stringa alle terminazioni di riga.
stordito il

1
Fantastico pulitore più semplice, bello e bello. Claps
Muhammad Ashfaq

11
for( String line : logMesg.split("\n") ) {
    Log.d( TAG, line );
}

5

Ecco il codice che uso: tronca le righe al limite di 4000 interrompendo anche la riga su nuove righe piuttosto che nel mezzo della riga. Rende un file di registro più facile da leggere.

Utilizzo:

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

Implementazione:

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

Il codice seguente è un perfezionamento di quanto pubblicato da Mark Buikema. Spezza la stringa su nuove righe. Utile per la registrazione di stringhe JSON lunghe.

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

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

2

noi questa logica di paginazione

    /*
     * 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

fornendo la mia opinione sulla soluzione di Travis,

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

approfitta del fatto che Log.println()restituisce il numero di byte scritti per evitare l'hardcoding "4000". quindi, chiamare ricorsivamente te stesso dalla parte del messaggio che non può essere registrata finché non è rimasto nulla.


Sfortunatamente, println restituisce # di byte scritti e caratteri! = Byte.
gnuf

1
bene, funziona. presumo perché sto solo registrando testo ASCII.
Jeffrey Blattman

1

Se il log è molto lungo (es. Registrazione dell'intero dump del database per motivi di debug, ecc.), Può accadere che logcat impedisca un log eccessivo. Per aggirare questo problema puoi aggiungere un timeout ogni x millisecondi.

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

Attenzione, usalo solo a scopo di debug in quanto potrebbe bloccare i blocchi del thread principale.


1

Come menzionato da @mhsmith, LOGGER_ENTRY_MAX_PAYLOADè 4068 nelle recenti versioni di Android. Tuttavia, se utilizzi 4068 come lunghezza massima del messaggio negli snippet di codice offerti in altre risposte, i messaggi verranno troncati. Questo perché Android aggiunge più caratteri all'inizio e alla fine del tuo messaggio, che contano anche. Altre risposte utilizzano il limite di 4000 come soluzione alternativa. Tuttavia, è possibile utilizzare davvero l'intero limite con questo codice (il codice genera un tag dalla traccia dello stack per mostrare il nome della classe e il numero di riga che ha chiamato il registro, sentiti libero di modificarlo):

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

Non conosco alcuna opzione per aumentare la lunghezza di logcat, ma possiamo trovare i diversi registri come il registro principale, il registro degli eventi ecc. Il registro principale di solito contiene tutto ciò che la sua lunghezza va fino a 4 Mb .. Quindi potresti essere in grado di ottenere ciò che hai perso nel terminale di registro. Il percorso è: \ data \ logger.


0

Sebbene le altre soluzioni fornite fossero utili, non ne ero soddisfatto perché non coprivano i casi in cui il registro è più lungo del doppio del LOGGER_ENTRY_MAX_LEN menzionato da @ b0ti. Inoltre, anche la mia seguente soluzione non è perfetta in quanto LOGGER_ENTRY_MAX_LEN non viene recuperato dinamicamente. Se qualcuno conosce un modo per farlo, mi piacerebbe saperlo nei commenti! Ad ogni modo, questa è la soluzione che uso nel mio codice in questo momento:

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)
        )
);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.