Android - Définir la longueur maximale des messages logcat


101

Par défaut, il semble que logcat tronquera tout message de journal qu'il considère comme "trop ​​long". Cela se produit à la fois à l'intérieur d'Eclipse et lors de l'exécution de logcat sur la ligne de commande à l'aide de adb -d logcat, et tronque certains messages de débogage importants.

Existe-t-il un moyen d'augmenter la longueur de chaîne maximale prise en charge par logcat pour qu'il arrête de tronquer les informations de débogage? La documentation officielle laisse entendre qu'il n'y en a peut-être pas, mais peut-être que logcat prend en charge certaines options supplémentaires non mentionnées ici?





1
@JoshCorreia Je ne pense pas que ce soit un bon doublon, car cela fait référence à la taille totale du tampon, et c'est par message de journal.
Ryan M

1
@RyanM Ah mon mauvais, j'ai mal compris l'autre question. Merci, suppression de la marque comme dupe.
Josh Correia

Réponses:


45

Il existe un tampon de taille fixe dans logcat pour les journaux binaires ( /dev/log/events) et cette limite est de 1024 octets. Pour les journaux non binaires, il existe également une limite:

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

Ainsi, la taille réelle des messages pour les journaux binaires et non binaires est d'environ 4076 octets. L'interface de journalisation du noyau impose cette LOGGER_ENTRY_MAX_PAYLOADlimite.

Les sources liblog (utilisées par logcat) disent également:

  • Le message peut avoir été tronqué par le pilote de journal du noyau.

Je vous recommanderais l' outil nxlog qui n'utilise pas le binaire logcat, mais en raison des limitations du noyau, je doute qu'il résoudra votre problème. Néanmoins, cela vaut peut-être la peine d'essayer. (avertissement: je suis l'auteur.)


6
Où puis-je trouver cela? Est-ce dans le code "logcat"? Alors, devrais-je compiler mon propre logcat modifié?
d4Rk

2
Qu'est-ce que le journal binaire / non binaire?
fobbymaster

2
En raison de l'ajout de champs de métadonnées, LOGGER_ENTRY_MAX_PAYLOADa été réduit de 4076 à 4068 dans les versions plus récentes d'Android (voir ici ).
mhsmith

87

Ok, intéressant. J'ai été déçu de voir que la réponse était "vous ne pouvez pas vraiment l'élargir". Ma première pensée était de le diviser pour que je puisse voir le tout, alors je partage ici avec vous comment je fais exactement cela (non pas que ce soit quelque chose d'extraordinaire ni presque efficace, mais cela fait le travail à la rigueur):

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

Modifié pour afficher la dernière chaîne!


Aucun problème! J'espère que cela vous a aidé
Travis

Je suis presque sûr qu'il y a une erreur ici. J'ai dû utiliser "i <chunkCount + 1" pour obtenir le dernier morceau
Dan

2
Vous avez perdu la dernière chaîne de: int chunkCount = sb.length() / 4000;Utilisation int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Timur Gilfanov

2
ajouter else { Log.v(TAG, sb); }pour imprimer également le journal lorsque le message est <= 4000 caractères
Bojan Radivojevic Bomber

4
Cette réponse est erronée pour les caractères non ASCII. logcat soutient UTF8 et la limite est 4k octets et non des caractères.
miguel

59

Divisez-le en plusieurs morceaux de manière récursive.

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
C'est de loin la solution la plus propre et la première fois que j'utilise la récursivité dans le code de production.
Aggressor

2
@Aggressor pourquoi avez-vous besoin d'enregistrer plus de 4000 messages longs en production?
TWiStErRob

1
Mon cas d'utilisation est de sortir un gros truc json. Les fichiers sont tout simplement pénibles.
Marcel Falliere

1
Merci très utile. J'ai publié une réponse qui brise la chaîne aux fins de ligne.
étourdi le

1
Nettoyant génial plus simple cool et beau. Claps
Muhammad Ashfaq


5

Voici le code que j'utilise - il tronque les lignes à la limite de 4000 tout en coupant la ligne au niveau des nouvelles lignes plutôt qu'au milieu de la ligne. Rend un fichier journal plus facile à lire.

Usage:

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

La mise en oeuvre:

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

Le code ci-dessous est un raffinement de ce qui a été publié par Mark Buikema. Il rompt la chaîne aux nouvelles lignes. Utile pour la journalisation de longues chaînes 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);
    }
  }

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

nous cette logique de pagination

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

fournir ma propre vision de la solution de 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;
    }
}

profiter du fait qui Log.println()renvoie le nombre d'octets écrits pour éviter le codage en dur "4000". puis, appelez-vous de manière récursive sur la partie du message qui n'a pas pu être enregistrée jusqu'à ce qu'il ne reste plus rien.


Malheureusement, println renvoie le nombre d'octets écrits et les caractères! = Octets.
gnuf

1
eh bien, ça marche. Je suppose parce que je ne consigne que du texte ascii.
Jeffrey Blattman

1

Si votre journal est très long (par exemple, la journalisation du vidage complet de votre base de données pour des raisons de débogage, etc.), il peut arriver que logcat empêche une journalisation excessive. Pour contourner ce problème, vous pouvez ajouter un délai d'expiration toutes les x millisecondes.

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

Attention, ne l'utilisez qu'à des fins de débogage car cela peut arrêter le thread principal des blocs.


1

Comme @mhsmith l'a mentionné, le LOGGER_ENTRY_MAX_PAYLOADest 4068 dans les versions récentes d'Android. Toutefois, si vous utilisez 4068 comme longueur maximale de message dans les extraits de code proposés dans d'autres réponses, les messages seront tronqués. En effet, Android ajoute plus de caractères au début et à la fin de votre message, qui comptent également. D'autres réponses utilisent la limite de 4000 comme solution de contournement. Cependant, il est possible d'utiliser vraiment toute la limite avec ce code (le code génère une balise à partir de la trace de la pile pour afficher le nom de la classe et le numéro de ligne qui a appelé le journal, n'hésitez pas à le modifier):

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

Je ne connais aucune option pour augmenter la longueur de logcat, mais nous pouvons trouver les différents journaux comme le journal principal, le journal des événements, etc. Le journal principal contient généralement tout ce que sa longueur peut atteindre jusqu'à 4 Mo. dans le terminal de journal. Le chemin est: \ data \ logger.


0

Bien que les autres solutions fournies aient été utiles, elles ne m'ont pas satisfait car elles ne couvraient pas les cas où le journal est plus long que deux fois plus long que le LOGGER_ENTRY_MAX_LEN mentionné par @ b0ti. De plus, même ma solution suivante n'est pas parfaite car le LOGGER_ENTRY_MAX_LEN n'est pas récupéré dynamiquement. Si quelqu'un connaît un moyen de le faire, j'aimerais en entendre parler dans les commentaires! Quoi qu'il en soit, c'est la solution que j'utilise dans mon code en ce moment:

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)
        )
);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.