Android アプリケーションからクラッシュ データ (少なくともスタック トレース) を取得するにはどうすればよいですか? 少なくとも自分のデバイスで作業してケーブルで取得するときは、理想的には、野生で実行されているアプリケーションの任意のインスタンスから、それを改善してより堅牢にすることができます。
30 に答える
ACRA (Application Crash Report for Android)ライブラリを試すことができます。
ACRA は、Android アプリケーションがクラッシュ レポートを GoogleDoc フォームに自動的に投稿できるようにするライブラリです。Android アプリケーションの開発者を対象としており、アプリケーションがクラッシュしたり、誤った動作をしたときにアプリケーションからデータを取得できるようにします。
アプリに簡単にインストールでき、高度な設定が可能で、サーバー スクリプトをどこにでもホストする必要はありません... レポートは Google Doc スプレッドシートに送信されます。
サンプル アプリケーションとデバッグの目的で、スタック トレースをデバイスの SD カードに書き込んだり、サーバーにアップロードしたりできるシンプルなソリューションを使用します。このソリューションは、Project android-remote-stacktrace (具体的には、デバイスへの保存部分とサーバーへのアップロード部分) に触発されており、Soonil が言及した問題を解決すると思います。最適ではありませんが、動作するので、本番アプリケーションで使用したい場合は改善できます。スタック トレースをサーバーにアップロードする場合は、php スクリプト ( index.php
) を使用してそれらを表示できます。興味がある場合は、以下のすべてのソースを見つけることができます。アプリケーション用の 1 つの Java クラスと、アップロードされたスタック トレースをホストするサーバー用の 2 つのオプションの 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");
?>
[BugSense] Reason: Spam Redirect to another url を試すこともできます。BugSense は、すべてのクラッシュ レポートを収集して分析し、意味のある視覚的なレポートを提供します。無料で、統合するためのコードはわずか 1 行です。
免責事項:私は共同創設者です
Android 2.2 では、Android マーケット アプリケーションからクラッシュ レポートを自動的に取得できるようになりました。
Android マーケット アプリの新しいバグ レポート機能により、開発者はユーザーからクラッシュやフリーズに関するレポートを受け取ることができます。レポートは、発行者アカウントにログインすると利用できます。
http://developer.android.com/sdk/android-2.2-highlights.html
でこれらの例外を処理することは可能ですが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 がシャットダウンできるようにしながら、例外をログに記録することができなかったため、しばらくしてあきらめました。
質問が古すぎることがわかりました。私の答えが同じ問題を抱えている他の人に役立つことを願っています...
Crashlyticsを試してみてください。アプリケーションがインストールされているすべてのデバイスのすべてのクラッシュについて詳細な洞察を提供し、電子メールで通知を送信します..そして最良の部分は、完全に無料で使用できることです..
さて、rrainn と Soonil から提供されたサンプルを調べたところ、エラー処理を台無しにしない解決策が見つかりました。
CustomExceptionHandler を変更して、新しいものを関連付ける Thread から元の UncaughtExceptionHandler を格納するようにしました。新しい「uncaughtException」メソッドの最後で、保存されている UncaughtExceptionHandler を使用して古い関数を呼び出すだけです。
DefaultExceptionHandler クラスでは、sth が必要です。このような:
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 カードにログインするための適切な作業ベースが得られます。
Google Play Developers Console は実際に、クラッシュしてレポートを送信したアプリからのスタック トレースを提供します。また、情報を確認するのに役立つ非常に優れたグラフもあります。以下の例を参照してください。
私はAndroid と iOS のアプリにCrittercismを使っています。これまでのところ、彼らにかなり満足しています!
ここで自分のバージョンを作成しました: http://androidblogger.blogspot.com/2009/12/how-to-improve-your-application-crash.html
基本的には同じですが、http 接続ではなくメールを使用してレポートを送信しています。さらに重要なことに、アプリケーションのバージョン、OS のバージョン、電話のモデル、使用可能なメモリなどの情報をレポートに追加しました。 .
これを使用して、例外の詳細をキャッチします。
String stackTrace = Log.getStackTraceString(exception);
これをデータベースに保存し、ログを維持します。
ライブラリだけでなく、(単純な) サービス全体を使用することもできます。当社はちょうどそのためのサービスをリリースしました: http://apphance.com .
シンプルな .jar ライブラリ (Android 用) があり、追加して 5 分で統合できます。ライブラリはクラッシュ情報だけでなく、実行中のアプリケーションからのログも収集します。また、テスターがデバイスから直接問題を報告できるようにします。コンテキスト全体 (デバイスのローテーション、wifi に接続されているかどうかなど)。アプリケーションのセッション、クラッシュ、ログ、統計などを追跡できる、非常に便利な Web パネルを使用してログを確認できます。このサービスは現在、クローズド ベータ テスト段階にありますが、アクセスをリクエストすると、すぐに提供されます。
免責事項: 私は Polidea の CTO であり、サービスの共同作成者です。
Stackoverflow
この回答を見つけるのに役立つリソースに感謝します。
リモートで Android のクラッシュ レポートをメールで直接見つけることができます。CustomExceptionHandler class 内に電子メールを配置する必要があることを忘れないでください。
public static String sendErrorLogsTo = "tushar.pandey@virtualxcellence.com" ;
必要な手順:
1st) アクティビティの onCreate で、コードのこのセクションを使用します。
if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(this));
}
2番目)私のphpscriptによると、(rrainn)のCustomExceptionHandlerクラスのこのオーバーライドされたバージョンを使用してください。
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();
}
}
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()));
}
Sherlockという Android ライブラリがあります。デバイスとアプリケーションの情報とともに、クラッシュの完全なレポートが提供されます。クラッシュが発生するたびに、通知バーに通知が表示され、通知をクリックすると、クラッシュの詳細が開きます。メールやその他の共有オプションを使用して、クラッシュの詳細を他のユーザーと共有することもできます。
インストール
android {
dataBinding {
enabled = true
}
}
compile('com.github.ajitsing:sherlock:1.0.0@aar') {
transitive = true
}
デモ
fabric と呼ばれるツールがあります。これはクラッシュ分析ツールで、アプリケーションがライブでデプロイされたときや開発中にクラッシュ レポートを取得できます。このツールをアプリケーションに追加するのも簡単でした。アプリケーションがクラッシュすると、そのレポートは fabric.io ダッシュボードから表示できます。レポートは自動的にキャッチされました。ユーザーに許可を求めることはありません。バグ/クラッシュ レポートを送信するかどうか。そして、これは完全に無料です... https://get.fabric.io/
これは非常に野蛮ですが、どこでも logcat を実行することが可能です。getRuntime().exec("logcat >> /sdcard/logcat.log");
自社開発のシステムを社内で使用しており、非常に役に立っています。これは、クラッシュ レポートをサーバーに送信する Android ライブラリと、レポートを受信して分析を行うサーバーです。サーバーは、例外名、スタックトレース、メッセージごとに例外をグループ化します。修正が必要な最も重大な問題を特定するのに役立ちます。私たちのサービスは現在パブリック ベータ版なので、誰でも試すことができます。http://watchcat.coでアカウントを作成するか、デモ アクセスhttp://watchcat.co/reports/index.php?demoを使用して動作を確認することができます。
エラー レポートを追跡するための優れた Web アプリケーションをもう 1 つ見つけました。
設定するステップ数が少ない。
- ログインまたはサインアップして、上記のリンクを使用して構成します。アプリケーションの作成が完了すると、以下のように構成する行が提供されます。
Mint.initAndStartSession(YourActivity.this, "api_key");
- アプリケーションの build.gradl に以下を追加します。
android { ... repositories { maven { url "https://mint.splunk.com/gradle/"} } ... } dependencies { ... compile "com.splunk.mint:mint:4.4.0" ... }
- 上でコピーしたコードを追加し、すべてのアクティビティに追加します。
Mint.initAndStartSession(YourActivity.this, "api_key");
それでおしまい。ログインしてアプリケーション ダッシュボードに移動すると、すべてのエラー レポートが表示されます。
それが誰かを助けることを願っています。
すぐに回答が必要な場合は、logcatを使用できます
$adb shell logcat -f /sdcard/logoutput.txt *:E
現在、ログにジャンクが多すぎる場合は、最初にそれをクリアしてみてください。
$adb shell logcat -c
次に、アプリを実行してから、再度 logcat を実行してみてください。
Google フォームをバックエンドとして使用して ACRA https://github.com/ACRA/acraの使用を開始したばかりで、セットアップと使用が非常に簡単で、デフォルトです。
ただし、Google フォームへのレポートの送信は廃止予定です (その後削除されます) : https://plus.google.com/118444843928759726538/posts/GTTgsrEQdN6 -フォームスプレッドシートの使用法
とにかく、独自の送信者 https://github.com/ACRA/acra/wiki/AdvancedUsage#wiki-Implementing_your_own_senderを定義する ことは可能です。たとえば、送信者にメールを送信してみてください。
最小限の労力でバグセンスにレポートを送信できます: http://www.bugsense.com/docs/android#acra
NB Bugsense の無料アカウントは、1 か月あたり 500 件のレポートに制限されています
Flurry analyticsは、クラッシュ情報、ハードウェアモデル、Androidバージョン、ライブアプリの使用統計を提供します。新しいSDKでは、より詳細なクラッシュ情報http://www.flurry.com/flurry-crash-analytics.htmlを提供しているようです。
アプリが他の人によってダウンロードされ、リモートデバイスでクラッシュしている場合は、Androidエラーレポートライブラリ(このSO投稿で参照)を調べることをお勧めします。自分のローカルデバイスだけにある場合は、LogCatを使用できます。クラッシュが発生したときにデバイスがホストマシンに接続されていなかった場合でも、デバイスを接続してadb logcatコマンドを発行すると、logcat履歴全体がダウンロードされます(少なくとも、バッファリングされる範囲で、通常は大量のログデータになります)。 、それは無限ではありません)。これらのオプションのいずれかがあなたの質問に答えますか?そうでない場合は、もう少し探しているものを明確にすることを試みることができますか?
これは、Android Studio で直接行うことができます。スマートフォンを接続してアプリを実行し、クラッシュさせるだけで、スタック トレースを Android Studio で直接表示できます。