Android-设置logcat消息的最大长度


101

默认情况下,logcat似乎会截断它认为“太长”的任何日志消息。这在Eclipse内部以及使用在命令行上运行logcat时都会发生adb -d logcat,并且会截断一些重要的调试消息。

有什么方法可以增加logcat支持的最大字符串长度,以使其停止截断调试信息?在官方文件意味着可能没有,但也许logcat的支持一些额外的选项不是有提及?





1
@JoshCorreia我不认为这是一个很好的副本,因为它指的是总缓冲区大小,而这是每个日志消息。
Ryan M

1
@RyanM啊,我不好,我误解了另一个问题。谢谢,删除标记为欺骗。
Josh Correia

Answers:


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限制。

liblog源(由logcat使用)还表示:

  • 该消息可能已被内核日志驱动程序截断。

我建议您使用不使用logcat二进制文件的nxlog工具,但是由于内核的限制,我怀疑它能否解决您的问题。但是,可能值得尝试。(免责声明:我是作者。)


6
我在哪里找到这个?它在“ logcat”代码中吗?因此,我必须编译自己的修改后的logcat吗?
d4Rk

2
什么是二进制/非二进制日志?
fobbymaster '16

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

编辑以显示最后一个字符串!


没问题!希望它能对您有所帮助
Travis

我很确定这里有一个错误。我必须使用“ i <chunkCount + 1”来获取最后一个块

2
您在以下位置丢失了最后一个字符串:int chunkCount = sb.length() / 4000;使用 int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Timur Gilfanov

2
添加else { Log.v(TAG, sb); }还可以在消息<= 4000字符长时打印日志
Bojan Radivojevic Bomber 2014年

4
对于非ASCII字符,此答案是错误的。logcat支持UTF8,限制为4k 字节,而不是字符。
miguel 2015年

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 '16

1
我的用例是输出一个很大的json内容。文件简直是痛苦。
Marcel Falliere'3

1
非常有用的感谢。我已经发布了一个答案,该答案在行尾打断了字符串。
呆呆的

1
令人敬畏的更简单的清洁器酷而美丽。拍手
穆罕默德·阿什法克

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

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

下面的代码是Mark Buikema发布的内容的完善。它将字符串换行。对于记录长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

我们这个分页逻辑

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

提供我自己对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;
    }
}

利用Log.println()返回返回的字节数以避免对“ 4000”进行硬编码的事实。然后,递归地称呼自己,直到消息一无所有为止。


不幸的是,println返回写入的字节数和字符!=字节。
gnuf

1
好吧,它有效。我想是因为我只记录ascii文本。
杰弗里·布拉特曼

1

如果您的日志很长(例如,出于调试原因而记录整个数据库转储等),则logcat可能会阻止过多的日志记录。要解决此问题,您可以添加一个超时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长度的任何选择,但是我们可以找到不同的日志,例如主日志,事件日志等。主日志通常包含长度高达4Mb的所有内容。因此,您可能会丢失在日志终端。路径是:\ data \ logger。


0

尽管提供的其他解决方案很有帮助,但我对它们不满意,因为它们没有涵盖日志的长度大于@ b0ti提到的LOGGER_ENTRY_MAX_LEN的情况。此外,由于无法动态获取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)
        )
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.