如何从我的Android应用程序获取崩溃数据?

How do I obtain crash-data from my Android application?

如何从我的Android应用程序获取崩溃数据(至少是堆栈跟踪)?至少在我自己的设备上工作时是通过电缆来检索的,但理想情况下是从我在野外运行的应用程序的任何实例中检索,这样我就可以改进它并使其更坚固。


您可以尝试ACRA(Android应用程序崩溃报告)库:

ACRA is a library enabling Android Application to automatically post their crash reports to a GoogleDoc form. It is targetted to android applications developers to help them get data from their applications when they crash or behave erroneously.

它很容易安装在你的应用程序中,高度可配置,不需要你在任何地方托管服务器脚本…报告发送到谷歌文档电子表格!


为了示例应用程序和调试的目的,我使用了一个简单的解决方案,它允许我将stacktrace写入设备的SD卡和/或将其上载到服务器。这个解决方案的灵感来自于Android远程StackTrace项目(特别是保存到设备和上传到服务器部件),我认为它解决了Soonil提到的问题。它不是最佳的,但是它可以工作,如果您想在生产应用程序中使用它,您可以对它进行改进。如果决定将stacktrace上载到服务器,可以使用php脚本(index.php来查看它们。如果您感兴趣的话,您可以找到以下所有的源代码:一个Java类用于您的应用程序,以及两个可选的PHP脚本,用于承载上传的堆栈跟踪的服务器。

在上下文(例如主活动)中,调用

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

CustomExceptionHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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

1
2
3
4
5
6
7
8
9
10
<?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 ."
", FILE_APPEND);
?>

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
    $myDirectory = opendir(".");
    while($entryName = readdir($myDirectory)) {
        $dirArray[] = $entryName;
    }
    closedir($myDirectory);
    $indexCount = count($dirArray);
    sort($dirArray);
    print("<TABLE border=1 cellpadding=5 cellspacing=0
");
    print("<TR><TH>Filename</TH><TH>Filetype</th><th>Filesize</TH></TR>
");
    for($index=0; $index < $indexCount; $index++) {
        if ((substr("$dirArray[$index]", 0, 1) !=".")
                && (strrpos("$dirArray[$index]",".stacktrace") != false)){
            print("<TR><TD>");
            print("$dirArray[$index]");
            print("</TD><TD>");
            print(filetype($dirArray[$index]));
            print("</TD><TD>");
            print(filesize($dirArray[$index]));
            print("</TD></TR>
");
        }
    }
    print("</TABLE>
");
?>


你也可以尝试Bugsense。Bugsense collects and analyzed all crash reports and gives you meaningful and visual reports.它是自由的,它只是一条完整的代码线。

我是一个共同的创始人


在Android 2.2中,现在可以自动从Android市场应用程序中获取崩溃报告:

New bug reporting feature for Android
Market apps enables developers to
receive crash and freeze reports from
their users. The reports will be
available when they log into their
publisher account.

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


使用Thread.setDefaultUncaughtExceptionHandler()可以处理这些异常,但这似乎与Android处理异常的方法有点混乱。我尝试使用这种性质的处理程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private class ExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread thread, Throwable ex){
        Log.e(Constants.TAG,"uncaught_exception_handler: uncaught exception in thread" + thread.getName(), ex);

        //hack to rethrow unchecked exceptions
        if(ex instanceof RuntimeException)
            throw (RuntimeException)ex;
        if(ex instanceof Error)
            throw (Error)ex;

        //this should really never happen
        Log.e(Constants.TAG,"uncaught_exception handler: unable to rethrow checked exception");
    }
}

但是,即使重新引发异常,我也无法获得所需的行为,即在允许Android关闭发生异常的组件的同时记录异常,因此我在一段时间后放弃了它。


我看到这个问题太老了,希望我的答案能帮助其他人有同样的问题。。。

给我一个尝试。它会在所有设备上提供索引,并通过电子邮件通知你,最好的部分是完全免费使用。


好吧,我看了一下提供的来自rrainn和soonil的样本,发现了一个解决方案这不会破坏错误处理。

我修改了customExceptionHandler,以便它存储我们关联新线程的原始未捕获的ExceptionHandler。在新的"未捕获异常"结束时-方法我只是使用存储的未捕获异常处理程序调用旧函数。

在DefaultExceptionHandler类中,您需要这样的东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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卡。


我一直在用批评我的机器人和IOS Apps--听听他们在技术上的故事。很高兴和他们在一起!


Google Play开发人员控制台实际上为您提供了那些崩溃并发送报告的应用程序的堆栈跟踪,它还提供了一个非常好的图表来帮助您查看信息,请参见下面的示例:

enter image description here


我在这里制作了自己的版本:http://androidblogger.blogspot.com/2009/12/how-to-improve-your-application-crash.html

基本上是一样的,但我使用的是邮件而不是HTTP连接来发送报告,更重要的是,我在报告中添加了一些信息,如应用程序版本、操作系统版本、电话型号或可用内存…


Use this to catch the exception details:

1
String stackTrace = Log.getStackTraceString(exception);

在数据库中保存这个,并保存记录。


您还可以为它使用一个完整的(简单的)服务,而不仅仅是库。我们公司刚刚发布了一项服务:http://apphance.com。

它有一个简单的.jar库(Android版),您可以在5分钟内添加和集成,然后库不仅收集崩溃信息,还收集运行应用程序的日志,还可以让您的测试人员直接从设备报告问题-包括整个上下文(设备旋转,无论它是否连接到WiFi等等)。您可以使用一个非常好和有用的Web面板查看日志,在这里您可以跟踪与应用程序的会话、崩溃、日志、统计信息等。该服务现在处于封闭测试阶段,但您可以请求访问,我们很快就将其提供给您。

免责声明:我是Polidea的首席技术官,也是该服务的共同创建者。


谢谢您的帮助,帮助我找到这个答案。

你可以直接在电子邮件中找到你的远程安卓坠机报告。回忆你必须把你的电子邮件放到客户机上。

ZZU1

Steps required:

在您活动的任何时候使用您的代码的这一节。

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

(二)根据我的PHPSCRIPT,使用这一过滤版本的海关除外处理员类别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
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 ="[email protected]" ;

    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() +"

";
        report +="--------- Stack trace ---------

";
        for (int i = 0; i < arr.length; i++) {
            report +="   " + arr[i].toString() +"
";
        }
        report +="-------------------------------

";

        report +="--------- Cause ---------

";
        Throwable cause = e.getCause();
        if (cause != null) {
            report += cause.toString() +"

";
            arr = cause.getStackTrace();
            for (int i = 0; i < arr.length; i++) {
                report +="   " + arr[i].toString() +"
";
            }
        }
        report +="-------------------------------

";

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

    }
}

有一个叫做Fabric的工具,这是一个崩溃分析工具,它允许您在应用程序实时部署和开发期间获取崩溃报告。向应用程序中添加此工具也很简单。当应用程序崩溃时,可以从fabric.io仪表板查看崩溃报告。THW报告被自动捕获。它不会请求用户许可。他/她是否要发送错误/崩溃报告。这是完全免费的…https://get.fabric.io网站/


google firebase是google最新的(2016年)方法,可以在手机上为您提供崩溃/错误数据。将其包含在build.gradle文件中:

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

致命崩溃会自动记录,而不需要用户输入,您也可以记录非致命崩溃或其他事件,如:

1
2
3
4
5
6
7
8
try
{

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

这是非常残忍的,但是在任何地方都可以运行logcat,所以一个快速而肮脏的黑客会添加到任何捕获块getRuntime().exec("logcat >> /sdcard/logcat.log");中。


我们在公司内部使用我们的家庭成长系统,非常适合我们。It's an Android Library that send crash reports to server and server that receives reports and make some analytics.服务器组例外的名称,堆栈跟踪,消息。It helps to identify most critical issues that need to be fixed.我们的服务现在在公共场所,所以每个人都可以尝试。你可以在http://watchcat.co或只是看看它如何使用Demo Access http://watchcat.co/reports/index.php?Demo


现在,一天的FireBase崩溃报告非常流行,而且更容易使用。有关详细信息,请参阅以下链接:FireBase崩溃报告

希望它能帮助你。


有一个叫夏洛克的Android库。它提供崩溃的完整报告以及设备和应用程序信息。每当发生崩溃时,它会在通知栏中显示一个通知,并在单击通知时打开崩溃详细信息。您还可以通过电子邮件或其他共享选项与其他人共享崩溃详细信息。

安装

1
2
3
4
5
6
7
8
9
android {
    dataBinding {
      enabled = true
    }
}

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

演示

enter image description here


虽然本页上的许多答案都很有用,但它们很容易过时。AppBrain网站汇总统计数据,使您能够找到最流行的故障报告解决方案:

Android崩溃报告库

app brain website

您可以看到,在发布此图片时,Crashlytics用于5.24%的应用程序和12.38%的安装。


如果你想马上回答,可以使用标志

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

如果你现在的记录里有太多的Junk,试着先把它弄清楚。

法国电力公司

然后再尝试运行你的应用程序


我找到了一个更好的Web应用程序来跟踪错误报告。

https://mint.splunk.com网站/

要配置的步骤很少。

  • 使用上述链接登录或注册并配置。一旦您创建了一个应用程序,它们将提供一行配置,如下所示。
  • 1
    Mint.initAndStartSession(YourActivity.this,"api_key");
  • 在应用程序的build.gradl中添加以下内容。
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    android {
    ...
        repositories {
            maven { url"https://mint.splunk.com/gradle/"}
        }
    ...
    }

    dependencies {
    ...
        compile"com.splunk.mint:mint:4.4.0"
    ...
    }
  • 添加我们在上面复制的代码,并将其添加到每个活动中。

    Mint.initAndStartSession(YourActivity.this,"api_key");

  • 就是这样。登录并转到应用程序仪表板,您将获得所有错误报告。

    希望它能帮助别人。


    For an alternate crash reporting/exceptional tracking service check out Raygun.IO-it's got a bunch of nice logic for handling android crashes,including decent user experience when plugging it in your app(two lines of code in your main main activity and a few lines of XML pasted int

    当你的APP崩溃时,它会自动抓住堆栈轨迹,自动抓住堆栈轨迹,为硬件/软件提供环境数据,用户跟踪信息,任何特定的自定义数据等等。它将这些信息传递给API异步,以至于不会阻挡UI thread,如果没有网络可用,则将其封存到磁盘上。

    Disclaimer:I build the Android Provider:


    参加晚会晚了,我支持并相信ACRA是最好的选择。它易于设置和配置。我已经创建了一个详细的指南,其中包含来自各地的输入,使用ACRA获取崩溃报告,并使用mandrilap将其发送到我的电子邮件地址。

    链接到帖子:https://andridician.wordpress.com/2015/03/29/sending-crash-reports-with-acra-over-email-using-mandrill/

    链接到github上的示例项目:https://github.com/ayushgoyal/acrasample


    我是Bugsnag的创始人之一,我们正是为这个目的而设计的。Bugsnag自动捕获Android应用程序中未处理的异常,并将其发送到我们的仪表板,在那里您可以确定修复的优先级并深入诊断信息。

    以下是选择或构建崩溃报告系统时需要考虑的一些重要事项,以及一些代码片段:

    • 自动检测未处理的异常(示例代码)
    • 收集诊断数据,如内存使用、设备信息等(示例代码)
    • 按根本原因有效地将崩溃分组在一起
    • 允许您跟踪用户在每次崩溃前采取的操作,以帮助重现(示例代码)

    如果您想在Android上看到一些关于崩溃处理/报告的最佳实践,您可以查看Bugsnag的崩溃报告库的完整源代码,它是完全开放的源代码,请随意将其拆分并在您自己的应用程序中使用!


    just started to use acra https://github.com/acra/acra using google forms as backed and it's very easy to setup& use,it's the default.

    But sending reports to Google forms are going to be deprecated(then removed):https://plus.google.com/118444843928759726538/posts/gtgsreqdn6HTTPS://github.com/acra/acra/wiki/notice-on-google-form-spreadsheet-use

    无论如何都可以确定你自己的参议员HTTPS://github.com/acra/acra/wiki/advanceducance 355;wiki-implementing uter你可以尝试用电子邮件发送为例。

    With minimum effort s is possible to send reports to Bugsense:http://www.bugsense.com/docs/android 35a

    NB The Bugsense Free Account is Limited to 500 Report/Month


    如果你的应用被其他人下载并在远程设备上崩溃,你可能想看一个安卓错误报告图书馆。如果这是你自己的本地设备,你可以使用Logcat。即使在坠机时,装置没有与主机连接,但连接了装置并产生了一个ADB Logcat命令,这将使整个Logcat历史下载(最起码,它是缓冲的,通常是一个loooot of log data,it's just not infinite).Even if the device wasn't connected to a host machine when the crash ocurred,connected the device and issuing an回答你的问题有什么选择吗?如果你不能试着更清楚你在找什么?


    您可以在Android Studio中直接执行此操作。只需连接您的手机,运行应用程序,让它崩溃,您可以直接在Android Studio中查看stacktrace。


    Flurry Analytics为您提供崩溃信息、硬件模型、Android版本和实时应用程序使用情况统计。在新的SDK中,它们似乎提供了更详细的崩溃信息http://www.flurry.com/flurry-crash-analytics.html。


    谷歌改变了你实际收到的崩溃报告的数量。以前你只有手动报告的错误报告。

    自从上次开发者大会和Android Vitals的推出,您还可以从能够共享诊断数据的用户那里获得崩溃报告。

    You'll see all crashes collected from Android devices whose users have opted in to automatically share usage and diagnostics data. Data is available for the previous two months.

    查看崩溃和应用程序未响应(ANR)错误