如何從我的Android應用程序獲取崩潰數據?


Answers

對於示例應用程序和調試目的,我使用了一個簡單的解決方案,它允許我將堆棧跟踪寫入設備的SD卡並/或上傳到服務器。 這個解決方案受到了項目android-remote-stacktrace啟發(具體來說,保存到設備和上傳到服務器的部分),我認為它解決了Soonil提到的問題。 這不是最優的,但它可以工作,如果你想在生產應用中使用它,你可以改進它。 如果您決定將堆棧跟踪上傳到服務器,則可以使用php腳本( index.php )來查看它們。 如果你有興趣,你可以在下面找到所有的源代碼 - 為你的應用程序提供一個java類,為託管上傳的stacktraces的服務器提供兩個可選的php腳本。

在上下文中(例如主Activity),調用

if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
    Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(
            "/sdcard/<desired_local_path>", "http://<desired_url>/upload.php"));
}

CustomExceptionHandler

public class CustomExceptionHandler implements UncaughtExceptionHandler {

    private UncaughtExceptionHandler defaultUEH;

    private String localPath;

    private String url;

    /* 
     * if any of the parameters is null, the respective functionality 
     * will not be used 
     */
    public CustomExceptionHandler(String localPath, String url) {
        this.localPath = localPath;
        this.url = url;
        this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
    }

    public void uncaughtException(Thread t, Throwable e) {
        String timestamp = TimestampFormatter.getInstance().getTimestamp();
        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        e.printStackTrace(printWriter);
        String stacktrace = result.toString();
        printWriter.close();
        String filename = timestamp + ".stacktrace";

        if (localPath != null) {
            writeToFile(stacktrace, filename);
        }
        if (url != null) {
            sendToServer(stacktrace, filename);
        }

        defaultUEH.uncaughtException(t, e);
    }

    private void writeToFile(String stacktrace, String filename) {
        try {
            BufferedWriter bos = new BufferedWriter(new FileWriter(
                    localPath + "/" + filename));
            bos.write(stacktrace);
            bos.flush();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendToServer(String stacktrace, String filename) {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(url);
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("filename", filename));
        nvps.add(new BasicNameValuePair("stacktrace", stacktrace));
        try {
            httpPost.setEntity(
                    new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
            httpClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

upload.php

<?php
    $filename = isset($_POST['filename']) ? $_POST['filename'] : "";
    $message = isset($_POST['stacktrace']) ? $_POST['stacktrace'] : "";
    if (!ereg('^[-a-zA-Z0-9_. ]+$', $filename) || $message == ""){
        die("This script is used to log debug data. Please send the "
                . "logging message and a filename as POST variables.");
    }
    file_put_contents($filename, $message . "\n", FILE_APPEND);
?>

index.php

<?php
    $myDirectory = opendir(".");
    while($entryName = readdir($myDirectory)) {
        $dirArray[] = $entryName;
    }
    closedir($myDirectory);
    $indexCount = count($dirArray);
    sort($dirArray);
    print("<TABLE border=1 cellpadding=5 cellspacing=0 \n");
    print("<TR><TH>Filename</TH><TH>Filetype</th><th>Filesize</TH></TR>\n");
    for($index=0; $index < $indexCount; $index++) {
        if ((substr("$dirArray[$index]", 0, 1) != ".") 
                && (strrpos("$dirArray[$index]", ".stacktrace") != false)){ 
            print("<TR><TD>");
            print("<a href=\"$dirArray[$index]\">$dirArray[$index]</a>");
            print("</TD><TD>");
            print(filetype($dirArray[$index]));
            print("</TD><TD>");
            print(filesize($dirArray[$index]));
            print("</TD></TR>\n");
        }
    }
    print("</TABLE>\n");
?>
Question

我如何從我的Android應用程序獲取崩潰數據(至少是堆棧跟踪)? 至少在通過電纜檢索我自己的設備時,理想情況下是在我的應用程序的任何實例上運行,這樣我可以改進它並使其更加穩定。




Google Firebase是Google最新(2016年)為您提供手機中的崩潰/錯誤數據的方式。 將其包含在build.gradle文件中:

compile 'com.google.firebase:firebase-crash:9.0.0'

致命的崩潰會自動記錄而不需要用戶輸入,您還可以記錄非致命的崩潰或其他事件,如:

try
{

}
catch(Exception ex)
{
    FirebaseCrash.report(new Exception(ex.toString()));
}



感謝中的資源,幫助我找到答案。

您可以直接在您的電子郵件中找到遠程Android崩潰報告 。 注意你必須把你的電子郵件放入CustomExceptionHandler類中

public static String sendErrorLogsTo = "tushar.pandey@virtualxcellence.com" ;

所需步驟:

1)在你的活動的onCreate使用你的代碼的這一部分。

    if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
        Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(this));
    }   

根據我的phpscript,使用這個被覆蓋的版本的CustomExceptionHandler類(rrainn)。

package com.vxmobilecomm.activity;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.util.Log;

public class CustomExceptionHandler implements UncaughtExceptionHandler {

    private UncaughtExceptionHandler defaultUEH;
    public static String sendErrorLogsTo = "tushar.pandey@virtualxcellence.com" ;

    Activity activity;

    public CustomExceptionHandler(Activity activity) {
        this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
        this.activity = activity;
    }

    public void uncaughtException(Thread t, Throwable e) {

        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        e.printStackTrace(printWriter);
        String stacktrace = result.toString();
        printWriter.close();
        String filename = "error" + System.nanoTime() + ".stacktrace";

        Log.e("Hi", "url != null");
        sendToServer(stacktrace, filename);

        StackTraceElement[] arr = e.getStackTrace();
        String report = e.toString() + "\n\n";
        report += "--------- Stack trace ---------\n\n";
        for (int i = 0; i < arr.length; i++) {
            report += "    " + arr[i].toString() + "\n";
        }
        report += "-------------------------------\n\n";

        report += "--------- Cause ---------\n\n";
        Throwable cause = e.getCause();
        if (cause != null) {
            report += cause.toString() + "\n\n";
            arr = cause.getStackTrace();
            for (int i = 0; i < arr.length; i++) {
                report += "    " + arr[i].toString() + "\n";
            }
        }
        report += "-------------------------------\n\n";

        defaultUEH.uncaughtException(t, e);
    }

    private void sendToServer(String stacktrace, String filename) {
        AsyncTaskClass async = new AsyncTaskClass(stacktrace, filename,
                getAppLable(activity));
        async.execute("");
    }

    public String getAppLable(Context pContext) {
        PackageManager lPackageManager = pContext.getPackageManager();
        ApplicationInfo lApplicationInfo = null;
        try {
            lApplicationInfo = lPackageManager.getApplicationInfo(
                    pContext.getApplicationInfo().packageName, 0);
        } catch (final NameNotFoundException e) {
        }
        return (String) (lApplicationInfo != null ? lPackageManager
                .getApplicationLabel(lApplicationInfo) : "Unknown");
    }

    public class AsyncTaskClass extends AsyncTask<String, String, InputStream> {
        InputStream is = null;
        String stacktrace;
        final String filename;
        String applicationName;

        AsyncTaskClass(final String stacktrace, final String filename,
                String applicationName) {
            this.applicationName = applicationName;
            this.stacktrace = stacktrace;
            this.filename = filename;
        }

        @Override
        protected InputStream doInBackground(String... params) 
        { 
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost(
                    "http://suo-yang.com/books/sendErrorLog/sendErrorLogs.php?");

            Log.i("Error", stacktrace);

            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
                        6);

                nameValuePairs.add(new BasicNameValuePair("data", stacktrace));
                nameValuePairs.add(new BasicNameValuePair("to",sendErrorLogsTo));
                nameValuePairs.add(new BasicNameValuePair("subject",applicationName));

                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

                HttpResponse response = httpclient.execute(httppost);

                HttpEntity entity1 = response.getEntity();

                BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(
                        entity1);

                is = bufHttpEntity.getContent();

            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return is;
        }

        @Override
        protected void onPostExecute(InputStream result) {
            super.onPostExecute(result);

            Log.e("Stream Data", getStringFromInputStream(is));
        }
    }

    // convert InputStream to String
    private static String getStringFromInputStream(InputStream is) {

        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();

        String line;
        try {

            br = new BufferedReader(new InputStreamReader(is));
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return sb.toString();

    }
}



在Android 2.2中,現在可以自動從Android電子市場應用程序獲取崩潰報告:

針對Android電子市場應用程序的新bug報告功能使開發人員能夠收到來自用戶的崩潰和凍結報告。 這些報告將在他們登錄到其發布者帳戶時可用。

http://developer.android.com/sdk/android-2.2-highlights.html




好吧,我查看了rrainn和Soonil提供的樣本,並找到了一個不會影響錯誤處理的解決方案。

我修改了CustomExceptionHandler,以便它將原來的UncaughtExceptionHandler從我們關聯新線程的Thread中存儲起來。 在新的“uncaughtException”的末尾 - 方法我只是使用存儲的UncaughtExceptionHandler調用舊函數。

在DefaultExceptionHandler類中你需要某事。 喜歡這個:

public class DefaultExceptionHandler implements UncaughtExceptionHandler{
  private UncaughtExceptionHandler mDefaultExceptionHandler;

  //constructor
  public DefaultExceptionHandler(UncaughtExceptionHandler pDefaultExceptionHandler)
  {
       mDefaultExceptionHandler= pDefaultExceptionHandler;
  }
  public void uncaughtException(Thread t, Throwable e) {       
        //do some action like writing to file or upload somewhere         

        //call original handler  
        mStandardEH.uncaughtException(t, e);        

        // cleanup, don't know if really required
        t.getThreadGroup().destroy();
  }
}

通過對http://code.google.com/p/android-remote-stacktrace上的代碼進行修改,您有一個很好的工作基礎,可以將該字段登錄到Web服務器或sd卡。




剛開始使用ACRA https://github.com/ACRA/acra時,使用Google表單作為後端,並且非常容易設置和使用,這是默認設置。

但將報告發送到Google表單將被棄用(然後刪除): https://plus.google.com/118444843928759726538/posts/GTTgsrEQdN6 https://plus.google.com/118444843928759726538/posts/GTTgsrEQdN6 https://github.com/ACRA/acra/wiki/Notice-on-Google-Form-Spreadsheet-usage

無論如何,可以定義您自己的發件人https://github.com/ACRA/acra/wiki/AdvancedUsage#wiki-Implementing_your_own_sender您可以嘗試以電子郵件發件人為例。

用最少的努力就可以發送報告給bugsense: http://www.bugsense.com/docs/android#acra : http://www.bugsense.com/docs/android#acra

NB Bugsense免費賬戶限於每月500份報告




有這個叫做Sherlock的 android庫。 它為您提供完整的崩潰報告以及設備和應用程序信息。 每當發生崩潰時,它會在通知欄中顯示通知,並在通知單擊時打開崩潰詳細信息。 您也可以通過電子郵件或其他共享選項與他人共享崩潰詳細信息。

安裝

android {
    dataBinding {
      enabled = true
    }
}

compile('com.github.ajitsing:sherlock:1.0.0@aar') {
    transitive = true
}

演示




您也可以使用整個(簡單)服務,而不是僅使用庫。 我們公司剛剛為此發布了一項服務: http//apphance.com

它有一個簡單的.jar庫(用於Android),您可以在5分鐘內添加和集成該庫,然後該庫不僅收集崩潰信息,還收集來自正在運行的應用程序的日誌,還可以讓測試人員直接從設備報告問題 - 包括整個環境(設備旋轉,無論是否連接到無線網絡等)。 您可以使用非常漂亮且有用的Web面板查看日誌,您可以在其中跟踪與應用程序,崩潰,日誌,統計信息等有關的會話。 該服務現在處於內測測試階段,但您可以請求訪問,我們會很快給您。

免責聲明:我是Polidea的首席技術官,也是該服務的共同創始人。




您可以直接在Android Studio中執行此操作。 只需連接手機,運行應用程序,讓它崩潰,並且您可以直接在Android Studio中查看堆棧跟踪。




我發現了一個更好的Web應用程序來跟踪錯誤報告。

https://mint.splunk.com/

少量的配置步驟。

  1. 使用上述鏈接登錄或註冊並配置。 一旦你創建了一個應用程序,他們將會提供一個如下所示的配置。
Mint.initAndStartSession(YourActivity.this, "api_key");
  1. 在應用程序的build.gradl中添加以下內容。
android {
...
    repositories {
        maven { url "https://mint.splunk.com/gradle/"}
    }
...
}

dependencies {
...
    compile "com.splunk.mint:mint:4.4.0"
...
}
  1. 添加我們上面複製的代碼並將其添加到每個活動。

    Mint.initAndStartSession(YourActivity.this,“api_key”);

而已。 您登錄並轉至您的應用程序儀表板,您將獲得所有錯誤報告。

希望它能幫助別人。







如果你想立即得到答案,你可以使用logcat

$adb shell logcat -f /sdcard/logoutput.txt *:E

如果您的日誌中有太多垃圾,請嘗試先清除它。

$adb shell logcat -c

然後嘗試運行你的應用程序,然後再次logcat。




我是我們為此設計的Bugsnag的創始人之一。 Bugsnag會自動捕獲Android應用程序中未處理的異常,並將它們發送到我們的儀表板,您可以在其中優先考慮修補程序並深入診斷信息。

以下是選擇或構建崩潰報告系統時需要考慮的一些重要事項以及一些代碼片段:

  • 自動檢測未處理的異常( 示例代碼
  • 收集診斷數據,如內存使用情況,設備信息等( 示例代碼
  • 根本原因有效地將碰撞組合在一起
  • 允許您跟踪用戶在每次崩潰前採取的操作以幫助重現( 示例代碼

如果您想查看關於Android上崩潰處理/報告的一些最佳實踐,您可以查看Bugsnag完全開源的崩潰報告庫的完整源代碼,隨時將其分開並在您自己的應用程序中使用它!




我發現問題太老了,希望我的回答對於有同樣問題的其他人有幫助。

Crashlytics一個嘗試。 它將深入了解所有設備上的所有崩潰情況,並通過電子郵件向您發送通知。最好的部分是它的完全免費使用..







Related