Java / Android - จะพิมพ์การติดตามสแต็กแบบเต็มได้อย่างไร


125

ใน Android (Java) ฉันจะพิมพ์สแต็กเทรซแบบเต็มได้อย่างไร หากแอปพลิเคชันของฉันขัดข้องจาก nullPointerException หรือบางสิ่งบางอย่างแอปพลิเคชันจะพิมพ์การติดตามสแต็กเต็ม (เกือบ) แบบเต็มดังนี้:

java.io.IOException: Attempted read from closed stream.
com.android.music.sync.common.SoftSyncException: java.io.IOException: Attempted read from closed stream.
    at com.android.music.sync.google.MusicSyncAdapter.getChangesFromServerAsDom(MusicSyncAdapter.java:545)
    at com.android.music.sync.google.MusicSyncAdapter.fetchDataFromServer(MusicSyncAdapter.java:488)
    at com.android.music.sync.common.AbstractSyncAdapter.download(AbstractSyncAdapter.java:417)
    at com.android.music.sync.common.AbstractSyncAdapter.innerPerformSync(AbstractSyncAdapter.java:313)
    at com.android.music.sync.common.AbstractSyncAdapter.onPerformLoggedSync(AbstractSyncAdapter.java:243)
    at com.google.android.common.LoggingThreadedSyncAdapter.onPerformSync(LoggingThreadedSyncAdapter.java:33)
    at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:164)
Caused by: java.io.IOException: Attempted read from closed stream.
    at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:148)
    at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:159)
    at java.util.zip.GZIPInputStream.readFully(GZIPInputStream.java:212)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:81)
    at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:64)
    at android.net.http.AndroidHttpClient.getUngzippedContent(AndroidHttpClient.java:218)
    at com.android.music.sync.api.MusicApiClientImpl.createAndExecuteMethod(MusicApiClientImpl.java:312)
    at com.android.music.sync.api.MusicApiClientImpl.getItems(MusicApiClientImpl.java:588)
    at com.android.music.sync.api.MusicApiClientImpl.getTracks(MusicApiClientImpl.java:638)
    at com.android.music.sync.google.MusicSyncAdapter.getChangesFromServerAsDom(MusicSyncAdapter.java:512)
    ... 6 more

อย่างไรก็ตามในบางครั้งเพื่อวัตถุประสงค์ในการดีบักฉันต้องการบันทึกการติดตามสแต็กแบบเต็มจากจุดที่ฉันอยู่ในโค้ด ฉันคิดว่าฉันทำได้แค่นี้:

StackTraceElement trace = new Exception().getStackTrace();
Log.d("myapp", trace.toString());

แต่นี่เป็นการพิมพ์ตัวชี้ไปที่วัตถุ ... ฉันต้องวนซ้ำองค์ประกอบการติดตามสแต็กทั้งหมดเพื่อพิมพ์ออกมาหรือไม่? หรือมีวิธีง่ายๆในการพิมพ์ออกมาทั้งหมด?


1
คุณสามารถใช้วิธีนี้ Thread.currentThread (). getStackTrace () stackoverflow.com/questions/1069066/…
user2024270

คำตอบ:


120

มีการลบล้างวิธีการบันทึกทั้งหมดด้วย(String tag, String msg, Throwable tr)ลายเซ็น

การส่งผ่านข้อยกเว้นเป็นพารามิเตอร์ที่สามควรทำให้คุณมี stacktrace แบบเต็มใน logcat


5
FYI วิธีบันทึกสามอาร์กิวเมนต์ใช้getStackTraceString()วิธีการที่ @Thomas กล่าวถึงเบื้องหลัง
Philipp Reichart

6
ฉันพัฒนา Android มาสองปีแล้วและไม่เคยสังเกตเห็นสิ่งนี้มาก่อน ขอบคุณมาก.
Anh Tuan

มองไปที่ซอร์สโค้ดใช่คุณพูดถูก @PhilippReichart นี่คือรหัสสำหรับLog.dบน AOSP 4.2.2_r1
Ehtesh Choudhury

152

ต่อไปนี้ควรทำเคล็ดลับ:

Log.d("myapp", Log.getStackTraceString(new Exception()));

โปรดทราบว่า...x moreในตอนท้ายไม่ได้ตัดข้อมูลใด ๆ ออกจากการติดตามสแต็ก:

(สิ่งนี้บ่งชี้)ว่าส่วนที่เหลือของการติดตามสแต็กสำหรับข้อยกเว้นนี้ตรงกับจำนวนเฟรมที่ระบุจากด้านล่างของการติดตามสแต็กของข้อยกเว้นที่เกิดจากข้อยกเว้นนี้ (ข้อยกเว้น "ปิดล้อม")

... หรืออีกนัยหนึ่งแทนที่x moreด้วยxบรรทัดสุดท้ายจากข้อยกเว้นแรก


1
อยู่ไหนgetStackTraceString()จากไหน? ไม่ปรากฏใน Eclipse? ไม่ใช่ส่วนหนึ่งของThrowableหรือException....
Jake Wilson

9
"... อีก 6" ตอนท้ายยังไม่ตัดข้อมูลใด ๆ ออกไป มันกำลังบอกคุณว่าสแต็กแทร็กที่เหลือเหมือนกันกับข้อยกเว้นด้านบน เพียงแค่ดู 6 บรรทัดสุดท้ายจากข้อยกเว้นแรก
Tomasz

คุณอาจต้องนำเข้าไลบรารีการบันทึก (โดยใช้import android.util.Log;)
แดน

10

ใช้ Log.getStackTraceString (Throwable t) คุณสามารถรับสแต็กเทรซที่ยาวขึ้นได้โดยการขุดลึกลงไป ตัวอย่างเช่น:

try {
    ...
} catch(Exception e) {
    Log.d("Some tag", Log.getStackTraceString(e.getCause().getCause()));
}

ดึงมาจากhttp://developer.android.com/reference/android/util/Log.html#getStackTraceString%28java.lang.Throwable%29


Log.getStackTraceString()ไม่บันทึกการติดตามสแต็กเพียงแค่ส่งคืนเป็น String รหัสข้างต้นจะไม่ได้เข้าสู่ระบบอะไรและยังจะล้มเหลวด้วย NPE ถ้าเป็นe.getCause() null
Philipp Reichart

9
private static String buildStackTraceString(final StackTraceElement[] elements) {
    StringBuilder sb = new StringBuilder();
    if (elements != null && elements.length > 0) {
        for (StackTraceElement element : elements) {
            sb.append(element.toString());
        }
    }
    return sb.toString();
}


// call this at your check point
Log.d(TAG, buildStackTraceString(Thread.currentThread().getStackTrace()));

6

คุณสามารถใช้สิ่งนี้:

public static String toString(StackTraceElement[] stackTraceElements) {
    if (stackTraceElements == null)
        return "";
    StringBuilder stringBuilder = new StringBuilder();
    for (StackTraceElement element : stackTraceElements)
        stringBuilder.append(element.toString()).append("\n");
    return stringBuilder.toString();
}


2

คุณต้องใช้ Throwable Object เพื่อรับ stackTrace แบบเต็ม

try{
 // code here
}catch(Exception e){
    String exception = getStackTrace(e);
}

public static String getStackTrace(final Throwable throwable) {
     final StringWriter sw = new StringWriter();
     final PrintWriter pw = new PrintWriter(sw, true);
     throwable.printStackTrace(pw);
     return sw.getBuffer().toString();
}

อ้างอิง: https://stackoverflow.com/a/18546861


0

ตอนนี้ฉันทำฟังก์ชันเรียกซ้ำได้อย่างรวดเร็วซึ่งจะทำซ้ำการโยนและโยนได้ getCause ()

นั่นเป็นเพราะทุก "throwable.getCause ()" ส่งกลับข้อความยกเว้นใหม่ให้คุณโดยมีบางบรรทัดซ้ำและขึ้นบรรทัดใหม่ ดังนั้นแนวคิดคือถ้ามี "สาเหตุ" มีบรรทัดที่มี "n more .. " บน main throwable ดังนั้นฉันจึงได้บรรทัดสุดท้ายก่อนบรรทัดที่มี "n more .. " จากนั้นฉันจะได้รับข้อความแสดงสาเหตุและ ในที่สุดฉันก็ซับข้อความสาเหตุที่ได้รับเฉพาะส่วนหลังจากบรรทัดสุดท้ายที่ซ้ำกัน (บรรทัดสุดท้ายที่ปรากฏบนทั้งสอง: หลักที่โยนได้และสาเหตุที่สามารถโยนได้)

จากนั้นฉันจะใช้การเรียกซ้ำเมื่อฉันได้รับข้อความสาเหตุดังนั้นการเรียกใช้ฟังก์ชันเดิมอีกครั้งเพื่อรับข้อความสาเหตุจากช่องสัญญาณหลักฉันจะได้รับข้อความที่ถูกแทนที่แล้ว หากสาเหตุที่สามารถโยนได้จากการโยนหลักมีสาเหตุอื่นด้วยดังนั้นการโยนหลักจึงมี 3 ระดับ (หลัก -> สาเหตุ -> สาเหตุของสาเหตุ) ที่โยนได้หลักฉันจะได้รับ "ข้อความสาเหตุ" ข้อความที่ถูกแทนที่แล้ว (ใช้แนวคิดเดียวกันกับหลัก

public static <T extends Throwable> String printStackTraceString(T ex) {     // Recursive
    Throwable tr = ex;
    if (tr != null) {
        String st = Log.getStackTraceString(tr);
        if (tr.getCause() != null) {
            // Recursion...
            String cs = printStackTraceString(tr.getCause());

            String r1 = st.subSequence(0x0, st.lastIndexOf("\n", st.lastIndexOf("\n") - "\n".length())).toString();
            String replace = r1.substring(r1.lastIndexOf("\n"));
            if (cs.contains(replace)) {
                return r1.concat(cs.subSequence(cs.indexOf(replace) + replace.length(), cs.length()).toString());
            }
        }
        return st;
    }
    return "";
}

ฉันลองใช้แค่ 2 ระดับ (หลัก -> สาเหตุ) และไม่เพิ่มอีก: / หากมีอะไรผิดพลาดโปรดแก้ไขฟังก์ชันและเขียนความคิดเห็น: D

ขอให้มีความสุขในการเขียนโปรแกรมและขอให้เป็นวันที่ดีเช่นกัน: D

สำคัญ:

บางครั้งรหัสนี้อาจได้รับข้อยกเว้นหาก "st" ไม่มี "\ n" หรือคล้ายกัน (ฉันพบว่ามีข้อยกเว้นบางประเภท stacktraces มีปัญหานี้) ในการแก้ปัญหานี้คุณต้องเพิ่มการตรวจสอบก่อนแถวรหัส: "String r1 = ... "

คุณต้องตรวจสอบ: "st" มี "\ n" และดัชนีเริ่มต้นและสิ้นสุดของ "st.subSequence" นั้นถูกต้องทั้งคู่

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.