Android - Đặt độ dài tối đa của tin nhắn logcat


101

Theo mặc định, có vẻ như logcat sẽ cắt bớt bất kỳ thông báo nhật ký nào mà nó cho là "quá dài". Điều này xảy ra cả bên trong Eclipse và khi chạy logcat trên dòng lệnh bằng cách sử dụng adb -d logcatvà đang cắt bớt một số thông báo gỡ lỗi quan trọng.

Có cách nào để tăng độ dài chuỗi tối đa được logcat hỗ trợ để làm cho nó ngừng cắt ngắn thông tin gỡ lỗi không? Các tài liệu chính thức ngụ ý rằng có thể không có, nhưng có lẽ logcat hỗ trợ một số tùy chọn bổ sung không được đề cập đó?





1
@JoshCorreia Tôi không nghĩ đó là một bản sao tốt, vì điều đó đề cập đến tổng kích thước bộ đệm và đây là trên mỗi thông báo nhật ký.
Ryan M

1
@RyanM Ah, tệ thật, tôi hiểu nhầm câu hỏi kia. Cảm ơn, xóa đánh dấu là dupe.
Josh Correia

Câu trả lời:


45

Có một bộ đệm kích thước cố định trong logcat cho nhật ký nhị phân ( /dev/log/events) và giới hạn này là 1024 byte. Đối với các bản ghi không phải nhị phân, cũng có một giới hạn:

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

Vì vậy, kích thước thư thực cho cả bản ghi nhị phân và không nhị phân là ~ 4076 byte. Giao diện ghi nhật ký hạt nhân áp đặt LOGGER_ENTRY_MAX_PAYLOADgiới hạn này .

Các nguồn liblog (được logcat sử dụng) cũng cho biết:

  • Thông báo có thể đã bị cắt ngắn bởi trình điều khiển nhật ký hạt nhân.

Tôi muốn giới thiệu cho bạn công cụ nxlog không sử dụng nhị phân logcat, nhưng do những hạn chế trong hạt nhân, tôi nghi ngờ rằng nó sẽ giải quyết được vấn đề của bạn. Tuy nhiên, nó có thể đáng để thử. (tuyên bố từ chối trách nhiệm: Tôi là tác giả.)


6
Tôi tìm cái này ở đâu? Nó có trong mã "logcat" không? Vì vậy, tôi có phải biên dịch logcat đã sửa đổi của riêng mình không?
d4Rk

2
Nhật ký nhị phân / không nhị phân là gì?
fobbymaster

2
Do các trường siêu dữ liệu được thêm vào, LOGGER_ENTRY_MAX_PAYLOADđã bị giảm từ 4076 xuống 4068 trong các phiên bản Android mới hơn (xem tại đây ).
mhsmith

87

Ok, thú vị. Tôi rất thất vọng khi thấy câu trả lời là "bạn thực sự không thể mở rộng nó". Suy nghĩ ban đầu của tôi là chia nhỏ nó để tôi có thể xem toàn bộ sự việc, vì vậy ở đây tôi chia sẻ với bạn cách tôi làm điều đó (không phải là nó bất cứ thứ gì lạ mắt cũng không phải là gần hiệu quả, nhưng nó hoàn thành công việc một cách nhanh chóng):

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

Đã chỉnh sửa để hiển thị chuỗi cuối cùng!


Không vấn đề gì! Hy vọng nó sẽ giúp bạn ra ngoài
Travis

Tôi khá chắc rằng có một lỗi nào đó ở đây. Tôi đã phải sử dụng "i <chunkCount + 1" để lấy đoạn cuối cùng
Dan

2
Bạn bị mất chuỗi cuối cùng trong: int chunkCount = sb.length() / 4000;Sử dụng int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Timur Gilfanov

2
thêm else { Log.v(TAG, sb); }cũng để in các bản ghi khi được thông báo là <= 4000 ký tự dài
Bojan Radivojević Bomber

4
Câu trả lời này sai đối với các ký tự không phải ASCII. logcat hỗ trợ UTF8 và giới hạn là 4k byte , không phải ký tự.
miguel

58

Chia nó thành nhiều phần một cách đệ quy.

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
Đây là giải pháp sạch nhất cho đến nay và là lần đầu tiên tôi thực sự sử dụng đệ quy trong mã sản xuất.
Aggressor

2
@Aggressor tại sao bạn cần ghi lại hơn 4000 thư dài trong quá trình sản xuất?
TWiStErRob

1
Trường hợp sử dụng của tôi là xuất một thứ json lớn. Các tập tin chỉ đơn giản là một nỗi đau.
Marcel Falliere

1
Rất hữu ích, cảm ơn. Tôi đã đăng một câu trả lời làm đứt chuỗi ở cuối dòng.
đờ đẫn

1
Trình dọn dẹp đơn giản hơn tuyệt vời Tuyệt vời và đẹp. Vỗ tay
Muhammad Ashfaq


5

Đây là mã tôi sử dụng - nó cắt bớt các dòng ở giới hạn 4000 đồng thời ngắt dòng ở các dòng mới chứ không phải ở giữa dòng. Giúp cho việc đọc tệp nhật ký dễ dàng hơn.

Sử dụng:

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

Thực hiện:

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

Đoạn mã dưới đây là sự chắt lọc những gì được đăng bởi Mark Buikema. Nó phá vỡ chuỗi ở các dòng mới. Hữu ích để ghi các chuỗi JSON dài.

  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

chúng tôi logic phân trang này

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

cung cấp giải pháp của riêng tôi về giải pháp của 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;
    }
}

tận dụng thực tế là Log.println()trả về số byte được ghi để tránh mã hóa cứng "4000". sau đó, tự gọi đệ quy cho phần tin nhắn không thể ghi được cho đến khi không còn gì.


Thật không may, println trả về # byte được viết và ký tự! = Byte.
gnuf

1
tốt, nó hoạt động. tôi giả sử vì tôi chỉ ghi nhật ký văn bản ascii.
Jeffrey Blattman

1

Nếu nhật ký của bạn rất dài (ví dụ: ghi toàn bộ kết xuất cơ sở dữ liệu của bạn vì lý do gỡ lỗi, v.v.) thì có thể xảy ra trường hợp logcat ngăn ghi nhật ký quá mức. Để giải quyết vấn đề này, bạn có thể thêm thời gian chờ x mili giây.

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

Hãy lưu ý, chỉ sử dụng nó cho mục đích gỡ lỗi vì nó có thể tạm dừng chuỗi chính.


1

Như @mhsmith đã đề cập, LOGGER_ENTRY_MAX_PAYLOAD4068 trong các phiên bản Android gần đây. Tuy nhiên, nếu bạn sử dụng 4068 làm độ dài tin nhắn tối đa trong các đoạn mã được cung cấp trong các câu trả lời khác, thì các tin nhắn sẽ bị cắt bớt. Điều này là do Android thêm nhiều ký tự hơn vào đầu và cuối tin nhắn của bạn, các ký tự này cũng được tính. Các câu trả lời khác sử dụng giới hạn 4000 như một giải pháp thay thế. Tuy nhiên, có thể thực sự sử dụng toàn bộ giới hạn với mã này (mã tạo thẻ từ dấu vết ngăn xếp để hiển thị tên lớp và số dòng được gọi là nhật ký, vui lòng sửa đổi điều đó):

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

Tôi không biết bất kỳ tùy chọn nào để tăng độ dài của logcat, nhưng chúng tôi có thể tìm thấy các nhật ký khác nhau như nhật ký chính, nhật ký sự kiện, v.v. Nhật ký chính thường chứa mọi thứ, độ dài của nó lên đến 4Mb .. Vì vậy, bạn có thể lấy lại những gì mình đã mất trong thiết bị đầu cuối nhật ký. Đường dẫn là: \ data \ logger.


0

Mặc dù các giải pháp được cung cấp khác hữu ích nhưng tôi không hài lòng với chúng vì chúng không đề cập đến các trường hợp khi nhật ký dài hơn gấp đôi LOGGER_ENTRY_MAX_LEN mà @ b0ti đề cập. Hơn nữa, ngay cả giải pháp sau đây của tôi cũng không hoàn hảo vì LOGGER_ENTRY_MAX_LEN không được tìm nạp động. Nếu ai đó biết cách để làm điều này, tôi rất muốn nghe về nó trong các bình luận! Dù sao, đây là giải pháp tôi sử dụng trong mã của mình ngay bây giờ:

final int loggerEntryMaxLength = 4096;
int i = 0;
while (output.length() / loggerEntryMaxLength > i) {
    int startIndex = i++ * loggerEntryMaxLength;
    int endIndex = i * loggerEntryMaxLength;
    Log.d(TAG, output.substring(startIndex, endIndex));
}
int startIndex = i * loggerEntryMaxLength;
Log.d(
        TAG,
        output.substring(
                startIndex,
                startIndex + (output.length() % loggerEntryMaxLength)
        )
);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.