【问题标题】:Android - Set max length of logcat messagesAndroid - 设置 logcat 消息的最大长度
【发布时间】:2012-02-11 21:42:23
【问题描述】:

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

有什么方法可以增加 logcat 支持的最大字符串长度,使其停止截断调试信息? official documentation 暗示可能没有,但也许 logcat 支持其中未提及的一些附加选项?

【问题讨论】:

标签: android logging


【解决方案1】:

好的,很有趣。我很失望地看到答案是“你不能真正扩展它”。我最初的想法是把它拆开,这样我就可以查看整个事情,所以在这里我与你分享我是如何做到这一点的(不是说它有什么花哨的,也不是近乎高效的,但它可以在紧要关头完成工作):

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

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

【讨论】:

  • 没问题!希望对你有所帮助
  • 我很确定这里有一个错误。我不得不使用“i
  • 你丢失了最后一个字符串:int chunkCount = sb.length() / 4000; 使用int chunkCount = sb.length() / 4000; if (chunkCount * 4000 &lt; sb.length()) chunkCount++;
  • 添加 else { Log.v(TAG, sb); } 以在消息长度
  • 这个答案对于非 ASCII 字符是错误的。 logcat 支持 UTF8,限制为 4k 字节,而不是字符。
【解决方案2】:

递归地把它分成几部分。

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

【讨论】:

  • 这是迄今为止最干净的解决方案,也是我第一次在生产代码中实际使用递归。
  • @Aggressor 为什么你需要在生产环境中记录 4000 多条长消息?
  • 我的用例是输出一个大的 json 内容。文件只是一种痛苦。
  • 非常有用,谢谢。我已经发布了一个答案,它在行尾断开了字符串。
  • 真棒更简单的清洁剂酷而美丽。鼓掌
【解决方案3】:

在 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 使用)也说:

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

我会向您推荐nxlog 工具,它不使用 logcat 二进制文件,但由于内核的限制,我怀疑它能否解决您的问题。尽管如此,它可能值得一试。 (免责声明:我是作者。)

【讨论】:

  • 我在哪里可以找到这个?它在“logcat”代码中吗?那么,我需要自己编译修改后的 logcat 吗?
  • 什么是二进制/非二进制日志?
  • 由于添加了元数据字段,LOGGER_ENTRY_MAX_PAYLOAD 在最新版本的 Android 中已从 4076 减少到 4068(请参阅 here)。
【解决方案4】:
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;
        }
    }
    

    【讨论】:

      【解决方案6】:

      下面的代码是对 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);
          }
        }
      

      【讨论】:

        【解决方案7】:
        int i = 3000;
        while (sb.length() > i) {
            Log.e(TAG, "Substring: "+ sb.substring(0, i));
            sb = sb.substring(i);
        }
        Log.e(TAG, "Substring: "+ sb);
        

        【讨论】:

          【解决方案8】:

          我们这个分页逻辑

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

          【讨论】:

            【解决方案9】:

            提供我自己对 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 返回写入的字节数,字符 != 字节。
            • 嗯,它有效。我假设是因为我只记录 ascii 文本。
            【解决方案10】:

            如果您的日志很长(例如,出于调试原因记录整个数据库转储等),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);
            }
            

            请注意,仅将其用于调试目的,因为它可能会阻塞主线程。

            【讨论】:

              【解决方案11】:

              正如@mhsmith 提到的,LOGGER_ENTRY_MAX_PAYLOAD 在最近的 Android 版本中是 4068。但是,如果您在其他答案中提供的代码 sn-ps 中使用 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);
              }
              

              【讨论】:

                【解决方案12】:

                虽然其他提供的解决方案很有帮助,但我对它们并不满意,因为它们没有涵盖日志长度超过 @b0ti 提到的 LOGGER_ENTRY_MAX_LEN 两倍的情况。此外,即使我的以下解决方案也不完美,因为 LOGGER_ENTRY_MAX_LEN 不是动态获取的。如果有人知道这样做的方法,我很想在 cmets 中听到它!无论如何,这是我现在在代码中使用的解决方案:

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

                【讨论】:

                  【解决方案13】:

                  我不知道增加 logcat 长度的任何选项,但我们可以找到不同的日志,如主日志、事件日志等。主日志通常包含长度达到 4Mb 的所有内容。所以你可以得到你在日志终端丢失了什么。路径是:\data\logger。

                  【讨论】:

                    猜你喜欢
                    • 2017-05-07
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-11-07
                    • 1970-01-01
                    相关资源
                    最近更新 更多