Android - Tetapkan panjang maksimal pesan logcat

101

Secara default, tampaknya logcat akan memotong pesan log yang dianggap "terlalu panjang". Ini terjadi baik di dalam Eclipse maupun saat menjalankan logcat pada baris perintah menggunakan adb -d logcat, dan memotong beberapa pesan debug penting.

Apakah ada cara untuk menambah panjang string maksimum yang didukung oleh logcat untuk menghentikan pemotongan informasi debug? The dokumentasi resmi menunjukkan bahwa mungkin tidak ada, tapi mungkin logcat mendukung beberapa opsi tambahan yang tidak disebutkan di sana?

aroth
sumber
Mirip: stackoverflow.com/questions/6321555/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Apakah ini menjawab pertanyaan Anda? Berapa batas ukuran untuk Logcat dan bagaimana cara mengubah kapasitasnya?
Josh Correia
1
@JoshCorreia Saya rasa itu bukan duplikat yang bagus, karena itu mengacu pada ukuran buffer total, dan ini per pesan log.
Ryan M
1
@RyanM Ah salahku, aku salah paham dengan pertanyaan lain. Terima kasih, menghapus tanda sebagai penipu.
Josh Correia

Jawaban:

45

Ada buffer ukuran tetap di logcat untuk log biner ( /dev/log/events) dan batasnya adalah 1.024 byte. Untuk log non-biner juga ada batasan:

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

Jadi ukuran pesan sebenarnya untuk log biner dan non-biner adalah ~ 4076 byte. Antarmuka logger kernel memberlakukan LOGGER_ENTRY_MAX_PAYLOADbatas ini .

Sumber liblog (digunakan oleh logcat) juga menyatakan:

  • Pesan tersebut mungkin telah dipotong oleh driver log kernel.

Saya akan merekomendasikan Anda alat nxlog yang tidak menggunakan biner logcat, tetapi karena keterbatasan di kernel saya ragu itu akan menyelesaikan masalah Anda. Namun demikian, ini mungkin patut dicoba. (penafian: Saya penulisnya.)

b0ti
sumber
6
Di mana saya menemukan ini? Apakah ada dalam kode "logcat"? Jadi, apakah saya harus mengkompilasi logcat saya yang telah dimodifikasi?
d4Rk
2
Apa itu log biner / non-biner?
fobbymaster
2
Karena bidang metadata ditambahkan, LOGGER_ENTRY_MAX_PAYLOADtelah dikurangi dari 4076 menjadi 4068 di versi Android yang lebih baru (lihat di sini ).
mhsmith
87

Oke, menarik. Saya kecewa melihat jawabannya adalah "Anda tidak dapat benar-benar mengembangkannya". Pikiran awal saya adalah memecahnya sehingga saya dapat melihat semuanya, jadi di sini saya berbagi dengan Anda bagaimana saya melakukan hal itu (bukan sesuatu yang mewah atau hampir efisien, tetapi itu menyelesaikan pekerjaan dalam keadaan darurat):

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

Diedit untuk menampilkan string terakhir!

Travis
sumber
Tidak masalah! Semoga ini bisa membantu Anda
Travis
Saya cukup yakin ada kesalahan satu per satu di sini. Saya harus menggunakan "i <chunkCount + 1" untuk mendapatkan potongan terakhir
Dan
2
Anda kehilangan string terakhir di: int chunkCount = sb.length() / 4000;Use int chunkCount = sb.length() / 4000; if (chunkCount * 4000 < sb.length()) chunkCount++;
Timur Gilfanov
2
tambahkan else { Log.v(TAG, sb); }juga untuk mencetak log ketika pesan <= 4000 karakter
Bojan Radivojevic Bomber
4
Jawaban ini salah untuk karakter non-ASCII. logcat mendukung UTF8 dan batasnya adalah 4k byte , bukan karakter.
miguel
59

Pecah menjadi beberapa bagian secara rekursif.

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);
   }
}
Mark Buikema
sumber
3
Sejauh ini, ini adalah solusi terbersih dan pertama kalinya saya benar-benar menggunakan rekursi dalam kode produksi.
Penyerang
2
@Aggressor mengapa Anda perlu mencatat 4000+ pesan panjang dalam produksi?
TWiStErRob
1
Kasus penggunaan saya adalah untuk mengeluarkan barang json besar. File hanya merepotkan.
Marcel Falliere
1
Terima kasih yang sangat berguna. Saya telah memposting jawaban yang memutus string pada akhir baris.
bingung
1
Pembersih Sederhana yang Luar Biasa, Keren dan Indah. Tepuk
Muhammad Ashfaq
11
for( String line : logMesg.split("\n") ) {
    Log.d( TAG, line );
}
pengguna2085092
sumber
5

Berikut adalah kode yang saya gunakan - memotong garis pada batas 4000 sementara juga memutus garis pada baris baru daripada di tengah-tengah baris. Membuat file log lebih mudah dibaca.

Pemakaian:

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

Penerapan:

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;
    }
}
perbesar sekarang
sumber
4

Kode di bawah ini merupakan penyempurnaan dari apa yang telah diposting oleh Mark Buikema. Ini mematahkan string di baris baru. Berguna untuk mencatat string JSON yang panjang.

  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);
    }
  }
linglung
sumber
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);
Rizki Sunaryo
sumber
2

kami logika paging ini

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

}
neeraj t
sumber
1

memberikan pendapat saya sendiri tentang solusi 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;
    }
}

memanfaatkan fakta yang Log.println()mengembalikan jumlah byte yang ditulis untuk menghindari hardcode "4000". kemudian, secara rekursif menyebut diri Anda pada bagian pesan yang tidak dapat dicatat hingga tidak ada yang tersisa.

Jeffrey Blattman
sumber
Sayangnya, println mengembalikan # byte tertulis, dan karakter! = Byte.
gnuf
1
baik, itu berhasil. saya berasumsi karena saya hanya mencatat teks ascii.
Jeffrey Blattman
1

Jika log Anda sangat panjang (mis. Logging seluruh dump database Anda untuk alasan debugging dll.) Mungkin terjadi bahwa logcat mencegah logging yang berlebihan. Untuk menyiasatinya, Anda dapat menambahkan waktu tunggu evry x milidetik.

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

Hati-hati, gunakan ini hanya untuk tujuan debugging karena dapat menghentikan blokir utas utama.

Patrick Favre
sumber
1

Seperti yang disebutkan @mhsmith, LOGGER_ENTRY_MAX_PAYLOADadalah 4068 di versi Android terbaru. Namun, jika Anda menggunakan 4068 sebagai panjang pesan maksimal dalam potongan kode yang ditawarkan di jawaban lain, pesan tersebut akan dipotong. Ini karena Android menambahkan lebih banyak karakter ke awal dan akhir pesan Anda, yang juga dihitung. Jawaban lain menggunakan batas 4000 sebagai solusi. Namun, dimungkinkan untuk benar-benar menggunakan seluruh batas dengan kode ini (kode menghasilkan tag dari pelacakan tumpukan untuk menunjukkan nama kelas dan nomor baris yang disebut log, silakan mengubahnya):

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);
}
Miloš Černilovský
sumber
0

Saya tidak tahu opsi apa pun untuk menambah panjang logcat, tetapi kami dapat menemukan log yang berbeda seperti log utama, log peristiwa, dll. Log utama biasanya berisi semua yang panjangnya mencapai 4 MB .. Jadi, Anda mungkin bisa mendapatkan apa yang hilang di terminal log. Jalurnya adalah: \ data \ logger.

Vins
sumber
0

Meskipun solusi lain yang disediakan sangat membantu, saya tidak puas karena mereka tidak mencakup kasus ketika log lebih panjang dari dua kali lebih panjang dari LOGGER_ENTRY_MAX_LEN yang disebutkan oleh @ b0ti. Lebih lanjut, bahkan solusi saya berikut ini tidak sempurna karena LOGGER_ENTRY_MAX_LEN tidak diambil secara dinamis. Jika seseorang tahu cara untuk melakukan ini, saya akan senang mendengarnya di komentar! Bagaimanapun, ini adalah solusi yang saya gunakan dalam kode saya sekarang:

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)
        )
);
Nico Feulner
sumber