Android - Legen Sie die maximale Länge der Logcat-Nachrichten fest

101

Standardmäßig scheint logcat jede Protokollnachricht abzuschneiden, die als "zu lang" eingestuft wird. Dies geschieht sowohl innerhalb von Eclipse als auch beim Ausführen von logcat in der Befehlszeile mit adb -d logcatund schneidet einige wichtige Debugging-Meldungen ab.

Gibt es eine Möglichkeit, die von logcat unterstützte maximale Zeichenfolgenlänge zu erhöhen, damit die Debug-Informationen nicht mehr abgeschnitten werden? Die offizielle Dokumentation impliziert, dass dies möglicherweise nicht der Fall ist, aber möglicherweise unterstützt logcat einige zusätzliche Optionen, die dort nicht erwähnt werden.

Aroth
quelle
Ähnlich: stackoverflow.com/questions/6321555/…
Ciro Santilli 1 冠状 病 六四 六四 法轮功
1
@JoshCorreia Ich denke nicht, dass dies ein gutes Duplikat ist, da sich dies auf die Gesamtpuffergröße bezieht und dies pro Protokollnachricht erfolgt.
Ryan M
1
@ RyanM Ah mein schlechtes, ich habe die andere Frage falsch verstanden. Danke, Mark als Betrüger entfernen.
Josh Correia

Antworten:

45

In logcat gibt es einen Puffer mit fester Größe für binäre Protokolle ( /dev/log/events), und diese Grenze liegt bei 1024 Byte. Für die nicht-binären Protokolle gibt es auch eine Grenze:

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

Die tatsächliche Nachrichtengröße für binäre und nicht-binäre Protokolle beträgt also ~ 4076 Byte. Die Kernel-Logger-Schnittstelle legt diese LOGGER_ENTRY_MAX_PAYLOADGrenze fest.

Die Liblog-Quellen (von logcat verwendet) sagen auch:

  • Die Nachricht wurde möglicherweise vom Kernel-Protokolltreiber abgeschnitten.

Ich würde Ihnen das nxlog- Tool empfehlen , das nicht die logcat-Binärdatei verwendet, aber aufgrund der Einschränkungen im Kernel bezweifle ich, dass es Ihr Problem lösen wird. Trotzdem könnte es einen Versuch wert sein. (Haftungsausschluss: Ich bin der Autor.)

b0ti
quelle
6
Wo finde ich das? Ist es im "Logcat" -Code? Muss ich also meinen eigenen modifizierten Logcat kompilieren?
d4Rk
2
Was ist ein binäres / nicht-binäres Protokoll?
Fobbymaster
2
Aufgrund des Hinzufügens von Metadatenfeldern wurde diese LOGGER_ENTRY_MAX_PAYLOADin neueren Android-Versionen von 4076 auf 4068 reduziert (siehe hier ).
Mhsmith
87

Ok, interessant. Ich war enttäuscht zu sehen, dass die Antwort "Sie können es nicht wirklich erweitern" war. Mein erster Gedanke war, es aufzubrechen, damit ich das Ganze sehen kann. Hier teile ich Ihnen mit, wie ich genau das mache (nicht, dass es etwas Besonderes ist oder nahezu effizient, aber es erledigt die Arbeit zur Not):

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

Bearbeitet, um die letzte Zeichenfolge anzuzeigen!

Travis
quelle
Kein Problem! Hoffe, es hat dir geholfen
Travis
Ich bin mir ziemlich sicher, dass hier ein Fehler auftritt. Ich musste "i <chunkCount + 1" verwenden, um den letzten Chunk zu erhalten
Dan
2
Sie haben die letzte Saite verloren in: int chunkCount = sb.length() / 4000;Use int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Timur Gilfanov
2
Hinzufügen, else { Log.v(TAG, sb); }um auch das Protokoll zu drucken, wenn die Nachricht <= 4000 Zeichen lang ist
Bojan Radivojevic Bomber
4
Diese Antwort ist für Nicht-ASCII-Zeichen falsch. logcat unterstützt UTF8 und die Grenze ist 4K Bytes , keine Zeichen.
Miguel
58

Brechen Sie es rekursiv in mehrere Teile auf.

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
quelle
3
Dies ist bei weitem die sauberste Lösung und das erste Mal, dass ich Rekursion im Produktionscode verwendet habe.
Aggressor
2
@Aggressor, warum müssen Sie mehr als 4000 Nachrichten in der Produktion protokollieren?
TWiStErRob
1
Mein Anwendungsfall ist die Ausgabe eines großen JSON-Materials. Dateien sind einfach ein Schmerz.
Marcel Falliere
1
Sehr nützlich, danke. Ich habe eine Antwort gepostet, die die Zeichenfolge an den Zeilenenden durchbricht.
benommen
1
Awesome Simpler Cleaner Cool und schön. Klatschen
Muhammad Ashfaq
11
for( String line : logMesg.split("\n") ) {
    Log.d( TAG, line );
}
user2085092
quelle
5

Hier ist der Code, den ich verwende - er schneidet die Zeilen an der Grenze von 4000 ab und unterbricht die Zeile auch bei neuen Zeilen und nicht in der Mitte der Zeile. Erleichtert das Lesen der Protokolldatei.

Verwendung:

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

Implementierung:

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
quelle
4

Der folgende Code ist eine Verfeinerung dessen, was von Mark Buikema gepostet wurde. Es bricht die Zeichenfolge bei neuen Zeilen. Nützlich zum Protokollieren langer JSON-Zeichenfolgen.

  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);
    }
  }
benommen
quelle
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
quelle
2

uns diese Paging-Logik

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

Bereitstellung meiner eigenen Sicht auf Travis 'Lösung,

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

Nutzen Sie die Tatsache, dass Log.println()die Anzahl der geschriebenen Bytes zurückgegeben wird, um eine Hardcodierung von "4000" zu vermeiden. Rufen Sie sich dann rekursiv bei dem Teil der Nachricht an, der erst protokolliert werden konnte, wenn nichts mehr übrig ist.

Jeffrey Blattman
quelle
Leider gibt println die Anzahl der geschriebenen Bytes und die Zeichen! = Bytes zurück.
Gnuf
1
Nun, es funktioniert. Ich nehme an, weil ich nur ASCII-Text protokolliere.
Jeffrey Blattman
1

Wenn Ihr Protokoll sehr lang ist (z. B. Protokollierung des gesamten Speicherauszugs Ihrer Datenbank aus Debugging-Gründen usw.), kann es vorkommen, dass logcat eine übermäßige Protokollierung verhindert. Um dies zu umgehen, können Sie alle x Millisekunden eine Zeitüberschreitung hinzufügen.

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

Beachten Sie, dass Sie dies nur zum Debuggen verwenden, da dadurch der Hauptthread des Blocks angehalten werden kann.

Patrick Favre
quelle
1

Wie @mhsmith erwähnte, LOGGER_ENTRY_MAX_PAYLOADist das in den letzten Android-Versionen 4068. Wenn Sie jedoch 4068 als maximale Nachrichtenlänge in den in anderen Antworten angebotenen Codefragmenten verwenden, werden die Nachrichten abgeschnitten. Dies liegt daran, dass Android am Anfang und am Ende Ihrer Nachricht weitere Zeichen hinzufügt, die ebenfalls zählen. Andere Antworten verwenden das Limit von 4000 als Problemumgehung. Es ist jedoch möglich, das gesamte Limit mit diesem Code wirklich zu nutzen (der Code generiert ein Tag aus dem Stack-Trace, um den Klassennamen und die Zeilennummer anzuzeigen, die das Protokoll aufgerufen haben. Sie können dies jederzeit ändern):

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ý
quelle
0

Ich kenne keine Option, um die Länge von logcat zu erhöhen, aber wir können die verschiedenen Protokolle wie Hauptprotokoll, Ereignisprotokoll usw. finden. Das Hauptprotokoll enthält normalerweise alles, was seine Länge bis zu 4 MB beträgt. So können Sie möglicherweise das bekommen, was Sie verloren haben im Protokollterminal. Der Pfad lautet: \ data \ logger.

Vins
quelle
0

Obwohl die anderen bereitgestellten Lösungen hilfreich waren, war ich mit ihnen nicht zufrieden, da sie keine Fälle abdeckten, in denen das Protokoll länger als doppelt so lang ist wie das von @ b0ti erwähnte LOGGER_ENTRY_MAX_LEN. Darüber hinaus ist auch meine folgende Lösung nicht perfekt, da LOGGER_ENTRY_MAX_LEN nicht dynamisch abgerufen wird. Wenn jemand einen Weg kennt, dies zu tun, würde ich gerne in den Kommentaren davon hören! Auf jeden Fall ist dies die Lösung, die ich gerade in meinem Code verwende:

final int loggerEntryMaxLength = 4096;
int i = 0;
while (output.length() / loggerEntryMaxLength > i) {
    int startIndex = i++ * loggerEntryMaxLength;
    int endIndex = (i * loggerEntryMaxLength) - 1;
    Log.d(TAG, output.substring(startIndex, endIndex));
}
int startIndex = i * loggerEntryMaxLength;
Log.d(
        TAG,
        output.substring(
                startIndex,
                startIndex + (output.length() % loggerEntryMaxLength)
        )
);
Nico Feulner
quelle