Android - ตั้งค่าความยาวสูงสุดของข้อความ Logcat


101

ตามค่าเริ่มต้นดูเหมือนว่า logcat จะตัดข้อความบันทึกที่พิจารณาว่า "ยาวเกินไป" สิ่งนี้เกิดขึ้นทั้งภายใน Eclipse และเมื่อรัน logcat บนบรรทัดคำสั่งโดยใช้adb -d logcatและกำลังตัดข้อความการดีบักที่สำคัญบางข้อความ

มีวิธีใดบ้างในการเพิ่มความยาวสตริงสูงสุดที่ logcat รองรับเพื่อหยุดการตัดทอนข้อมูลการดีบัก เอกสารอย่างเป็นทางการแสดงให้เห็นว่าอาจจะไม่มี แต่บางที logcat สนับสนุนบางตัวเลือกเพิ่มเติมไม่ได้กล่าวถึงที่นั่น?





1
@JoshCorreia ฉันไม่คิดว่าจะเป็นการทำซ้ำที่ดีเนื่องจากหมายถึงขนาดบัฟเฟอร์ทั้งหมดและนี่คือต่อข้อความบันทึก
Ryan M

1
@RyanM อาไม่ดีฉันเข้าใจผิดคำถามอื่น ขอบคุณลบเครื่องหมายว่าหลอกลวง
Josh Correia

คำตอบ:


46

มีบัฟเฟอร์ขนาดคงที่ใน 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 แต่เนื่องจากข้อ จำกัด ในเคอร์เนลฉันสงสัยว่ามันจะแก้ปัญหาของคุณได้ อย่างไรก็ตามมันอาจคุ้มค่าที่จะลอง (ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน)


6
ฉันจะหาสิ่งนี้ได้ที่ไหน? อยู่ในรหัส "logcat" หรือไม่? ดังนั้นฉันจะต้องรวบรวม logcat ของฉันเองหรือไม่?
d4Rk

2
บันทึกไบนารี / ไม่ใช่ไบนารีคืออะไร?
fobbymaster

2
เนื่องจากมีการเพิ่มช่องข้อมูลเมตาLOGGER_ENTRY_MAX_PAYLOADจึงลดลงจาก 4076 เป็น 4068 ใน Android เวอร์ชันล่าสุด (ดูที่นี่ )
mhsmith

89

โอเคน่าสนใจ ฉันรู้สึกผิดหวังที่เห็นว่าคำตอบคือ "คุณไม่สามารถขยายได้จริงๆ" ความคิดเริ่มต้นของฉันคือการแยกมันออกเพื่อที่ฉันจะได้เห็นสิ่งทั้งหมดดังนั้นที่นี่ฉันจะแบ่งปันกับคุณว่าฉันทำอย่างนั้นได้อย่างไร (ไม่ใช่ว่ามันเป็นอะไรที่แปลกประหลาดหรือมันใกล้จะมีประสิทธิภาพ แต่มันทำให้งานเสร็จในพริบตา):

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" เพื่อรับชิ้นสุดท้าย
Dan

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

4
คำตอบนี้ไม่ถูกต้องสำหรับอักขระที่ไม่ใช่ ASCII logcat รองรับ UTF8 และขีด จำกัด คือ 4k ไบต์ไม่ใช่อักขระ
มิเกล

61

แบ่งออกเป็นหลาย ๆ ชิ้นซ้ำ ๆ

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
@ ผู้รุกรานทำไมคุณต้องบันทึกข้อความยาวกว่า 4000+ ข้อความในการผลิต?
TWiStErRob

1
กรณีการใช้งานของฉันคือการส่งออกสิ่งของ json ขนาดใหญ่ ไฟล์เป็นเพียงความเจ็บปวด
Marcel Falliere

1
มีประโยชน์มากขอบคุณ ฉันได้โพสต์คำตอบซึ่งแบ่งสตริงที่ส่วนท้ายบรรทัด
มึนงง

1
ตัวทำความสะอาดที่เรียบง่ายน่ากลัวและสวยงาม Claps
Muhammad Ashfaq


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


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 ส่งคืน # ไบต์ที่เขียนและอักขระ! = ไบต์
ทราบ

1
มันใช้งานได้ดี ฉันคิดว่าเป็นเพราะฉันกำลังบันทึกข้อความ ascii เท่านั้น
Jeffrey Blattman

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คือ 4068 ในเวอร์ชัน Android ล่าสุด อย่างไรก็ตามหากคุณใช้ 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

แม้ว่าโซลูชันอื่น ๆ ที่ให้มาจะมีประโยชน์ แต่ฉันไม่พอใจเพราะพวกเขาไม่ได้ครอบคลุมกรณีที่บันทึกยาวกว่าสองเท่าตามที่ LOGGER_ENTRY_MAX_LEN กล่าวถึงโดย @ b0ti นอกจากนี้แม้โซลูชันต่อไปนี้ของฉันจะไม่สมบูรณ์แบบเนื่องจาก 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)
        )
);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.