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

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

331910 次浏览

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

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关闭它发生的组件,所以我在一段时间后放弃了它。

对于示例应用程序和调试目的,我使用了一个简单的解决方案,它允许我将堆栈跟踪写入设备的sd卡和/或将其上传到服务器。这个解决方案受到了Projectandroid-远程-堆栈跟踪(特别是保存到设备和上传到服务器部分)的启发,我认为它解决了Soonil提到的问题。它不是最佳的,但它可以工作,如果你想在生产应用程序中使用它,你可以改进它。如果你决定将堆栈跟踪上传到服务器,你可以使用php脚本(index.php)来查看它们。如果你有兴趣,你可以在下面找到所有的源代码——一个java类用于你的应用程序,两个可选的php脚本用于托管上传的堆栈跟踪的服务器。

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

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");
?>

好的,我查看了rran和Soonil提供的样本,找到了解决方案 这不会搞砸错误处理。

我修改了CustomExceptionHandler,以便它存储来自我们关联新线程的原始UncaughtExceptionHandler。在新的“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 您有一个良好的工作基础,用于登录到您的网络服务器或 SD卡。

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

这基本上是一样的,但我使用邮件而不是超文本传输协议连接发送报告,而且,更重要的是,我添加了一些信息,如应用程序版本,操作系统版本,手机型号,或可用内存到我的报告。

您可以尝试适用于Android的应用程序崩溃报告库:

ACRA是一个库,使Android应用程序能够自动将其崩溃报告发布到GoogleDoc表单。它面向Android应用程序开发人员,以帮助他们在应用程序崩溃或行为错误时从应用程序中获取数据。

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

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

新的Androidbug报告功能 市场应用程序使开发人员能够 接收崩溃和冻结报告 他们的用户。报告将 当他们登录到他们的 发行账号

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

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

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

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

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

免责声明:我是宝德的CTO,也是该服务的共同创建者。

您还可以尝试[BugSense]原因:垃圾邮件重定向到另一个URL。BugSense收集和分析所有崩溃报告,并为您提供有意义的可视化报告。它是免费的,只需1行代码即可集成。

声明:我是联合创始人

我一直在为我的Android和iOS应用程序使用Crittercism-在TechCrunch上听说过它们。到目前为止,对它们非常满意!

如果您的应用程序正在被其他人下载并在远程设备上崩溃,您可能需要查看Android错误报告库(在这个so帖子中引用)。如果它只在您自己的本地设备上,您可以使用LogCat。即使崩溃发生时设备没有连接到主机,连接设备并发出adb logcat命令也会下载整个logcat历史记录(至少在它被缓冲的程度上,通常是大量日志数据,它不是无限的)。这两个选项中的任何一个都回答了你的问题吗?如果不是,你能尝试更清楚地说明你在寻找什么吗?

使用它来捕获异常详细信息:

String stackTrace = Log.getStackTraceString(exception);

将其存储在数据库中并维护日志。

刚刚开始使用ACRAhttps://github.com/ACRA/acra,使用Google窗体作为后端,非常容易设置和使用,这是默认设置。

但向Google窗体发送报告将被弃用(然后删除): https://github.com/ACRA/acra/wiki/Notice-on-Google-Form-Spreadsheet-usage

无论如何,可以定义自己的发件人 例如,您可以尝试发送电子邮件发件人。

只需最小的努力,就可以将报告发送到bugense: http://www.bugsense.com/docs/android#acra

NB bugSense免费帐户限制为500报告/月

我们在公司内部使用我们自制的系统,它为我们提供了很好的服务。这是一个Android库,可以向服务器发送崩溃报告,服务器接收报告并进行一些分析。服务器按异常名称、堆栈跟踪、消息对异常进行分组。它有助于识别需要修复的最关键问题。 我们的服务现在处于公开测试阶段,所以每个人都可以尝试。您可以在http://watchcat.co创建帐户,或者您可以使用演示访问http://watchcat.co/reports/index.php?demo看看它是如何工作的。

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

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

如果你的日志中现在有太多的垃圾,试着先清除它。

$adb shell logcat -c

然后尝试运行您的应用程序,然后再次运行logcat。

我看到这个问题太老了,希望我的回答对其他有同样问题的人有所帮助…

试试Crashlytics。它将深入了解拥有您的应用程序的所有设备上的所有崩溃,并通过电子邮件向您发送通知…最好的部分是它完全免费使用…

对于替代的崩溃报告/异常跟踪服务,请查看Raygun.io-它有一系列处理Android崩溃的好逻辑,包括将其插入应用程序时的良好用户体验(主活动中的两行代码和粘贴到AndroidManifest中的几行XML)。

当您的应用程序崩溃时,它会自动获取堆栈跟踪,硬/软件环境数据,用户跟踪信息,您指定的任何自定义数据等,它将其异步发布到API,因此不会阻塞UI线程,并将其缓存到磁盘,如果没有可用的网络。

免责声明:我构建了Android提供商:)

感谢Stackoverflow中的资源帮助我找到这个答案。

你可以找到您的远程Android崩溃报告直接进入您的电子邮件。记住你必须将您的电子邮件放入CustomExceptionHandler类中

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

所需步骤:

1)在你的活动的onCreate中使用你的代码的这一部分。

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

第二)使用这个覆盖版本的CustomExceptionHandler类(rran),根据我的phpcript。

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


}
}

派对迟到了,我支持并相信ACRA是所有选择中最好的。它易于设置和配置。我创建了一个详细的指南,其中包含来自各地的输入,以使用ACRA获取崩溃报告,并使用MandrillAp将其邮寄到我的电子邮件地址。

链接到文章:https://androidician.wordpress.com/2015/03/29/sending-crash-reports-with-acra-over-email-using-mandrill/

链接到github上的示例项目:https://github.com/ayushhgoyal/AcraSample

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

输入图片描述

我发现了一个很棒的Web应用程序来跟踪错误报告。

要配置的步骤较少。

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

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

希望它能帮助某人。

有一个工具叫结构,这是一个崩溃分析工具,它将允许您获得崩溃报告,当应用程序实时部署和开发过程中。 将此工具添加到您的应用程序也很简单。 当您的应用程序崩溃时,可以从fabric.io仪表板查看崩溃报告。thw报告被捕获automatically.it不会请求用户许可。他/她是否要发送bug /crash报告。 这是完全免费的… https://get.fabric.io/

我是漏洞的创始人之一,我们正是为此目的而设计的。Bugsnag会自动捕获Android应用程序中未处理的异常,并将它们发送到我们的仪表板,您可以在其中优先考虑修复程序并深入研究诊断信息。

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

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

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

谷歌Firebase是Google最新的(2016)方式,可在手机上为您提供崩溃/错误数据。 将其包含在您的build.gradle文件中:

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

致命崩溃会自动记录,无需用户输入,您还可以记录非致命崩溃或其他事件,如下所示:

try
{


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

现在,Firebase崩溃报告非常受欢迎且更易于使用。 请参考以下链接以获取更多信息: Firebase崩溃报告

希望对你有帮助。

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

安装

android {
dataBinding {
enabled = true
}
}


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

Demo

在此处输入图片描述

Google更改了您实际获得的崩溃报告数量。以前您只获得手动报告bug报告。

自从上次开发者大会和androidvitals的介绍以来,您还可以从启用共享诊断数据的用户那里获得崩溃报告。

您将看到从用户选择自动共享使用和诊断数据的Android设备收集的所有崩溃。前两个月的数据可用。

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

虽然此页面上的许多答案都很有用,但它们很容易过时。AppBrain网站汇总了统计数据,可让您找到当前最流行的崩溃报告解决方案:

Android崩溃报告库

应用大脑网站

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

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