Imported CrashReporter.

This commit is contained in:
Pierre 2018-04-13 17:11:23 +02:00
parent 104ddfb8cb
commit d665be6138
3 changed files with 412 additions and 0 deletions

View File

@ -18,6 +18,10 @@ android {
buildConfigField "String", "api_url", "\"http://devweb.local/comunic/api/\""
buildConfigField "String", "api_service_name", "\"ComunicAndroid\""
buildConfigField "String", "api_service_token", "\"5eQ8WGAeDTTyY\""
//Connexion to Crash Reporter
buildConfigField "String", "crash_reporter_url", "\"http://devweb.local/CrashReporterWeb/project/api/v1/push\""
buildConfigField "String", "crash_reporter_key", "\"KSyqOzkfJasDTxE0wrXYnUPl8dV1veBc\""
}
release {
@ -28,6 +32,10 @@ android {
buildConfigField "String", "api_url", "\"https://api.communiquons.org/\""
buildConfigField "String", "api_service_name", "\"ComunicAndroid\""
buildConfigField "String", "api_service_token", "\"cWHlmMS5A1\""
//Connexion to Crash Reporter
buildConfigField "String", "crash_reporter_url", "\"https://crashreporter.communiquons.org/api/v1/push\""
buildConfigField "String", "crash_reporter_key", "\"z38jtULRuKHb2BOyvG4VTXDhn9gwelZ1\""
}
}
}

View File

@ -0,0 +1,396 @@
package org.communiquons.android.comunic.client.crashreporter;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.util.Log;
import org.communiquons.android.comunic.client.BuildConfig;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
/**
* Crash Reporter library
*
* This library intends to report fatal error to a remote server
*
* Licence : the MIT Licence
*
* @author Pierre HUBERT
* Created by pierre on 4/12/18.
*/
public class CrashReporter implements Thread.UncaughtExceptionHandler {
/**
* Debug tab
*/
private static final String TAG = "CrashReporter";
/**
* Connexion timeout
*/
private static final int API_CONNEXION_TIMEOUT = 3000;
/**
* Method used to connect to the api
*/
private static final String API_CONNEXION_METHOD = "POST";
/**
* Report file name
*/
private static final String REPORT_FILENAME = "crash_report.txt";
/**
* Application context
*/
private Context mContext;
/**
* Default UncaughtExceptionHandler
*/
private Thread.UncaughtExceptionHandler defaultUEH;
/**
* API URL
*/
private String mApiURL;
/**
* Application key
*/
private String mAppKey;
/**
* Construct the library
*
* @param context A valid context (the application context will be stored)
* @param url The URL where the reports have to be uploaded
* @param key The application key
*/
public CrashReporter(Context context, String url, String key){
//Set application context and activity references
mContext = context.getApplicationContext();
//Save api information
mApiURL = url;
mAppKey = key;
//Save default thread uncaught exception handler
this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
}
/**
* Update the API URL
*
* @param ApiURL The new API URL
*/
public void setApiURL(String ApiURL) {
this.mApiURL = ApiURL;
}
/**
* Get the current API URL
*
* @return The current API URL
*/
public String getApiURL() {
return mApiURL;
}
/**
* Update the application key
*
* @param appKey The application key
*/
public void setAppKey(String appKey) {
this.mAppKey = appKey;
}
/**
* Get the current application key
*
* @return The application key
*/
public String getAppKey() {
return mAppKey;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
//Generate the report
String report = generateReport(t, e);
Log.e(TAG, "Generated report: " + report);
//Try to upload the report
if(!save_report(report))
Log.e(TAG, "Could not save the report!");
else
Log.v(TAG, "Report successfully saved for later upload.");
//Call default exception handler
this.defaultUEH.uncaughtException(t, e);
}
/**
* Generate the crash report
*
* @param t The thread where the exception occurred
* @param e The exception
* @return The report as a string
*/
private String generateReport(Thread t, Throwable e){
//Begin report
String report = "Exception: " + e.toString() + "\n\n";
//Generic information
report += "Thread name: " + t.getName() + "\n";
report += "Application ID: " + BuildConfig.APPLICATION_ID +"\n";
report += "Application version: " + BuildConfig.VERSION_NAME +"\n";
report += "Code version: " + BuildConfig.VERSION_CODE + "\n\n";
report += "\n";
//Process stack trace
report += "---------- Stack trace ----------\n";
report += stackTraceToString(e.getStackTrace());
report += "---------------------------------\n\n\n";
//Process error cause
report += "------------ Cause --------------\n";
Throwable cause = e.getCause();
if(cause != null){
report += cause.getMessage() + "\n";
report += stackTraceToString(cause.getStackTrace());
}
else
report += "No data available.\n";
report += "---------------------------------\n";
return report;
}
/**
* Turn a stack trace array into a string
*
* @param array The array to convert
* @return Generated string
*/
private String stackTraceToString(StackTraceElement[] array){
String string = "";
for(StackTraceElement el : array){
string += el.toString() + "\n";
}
return string;
}
/**
* Asynchronously upload a report to the server
*/
@UiThread
public void uploadAwaitingReport(){
new AsyncTask<Void, Void, Void>(){
@Override
protected Void doInBackground(Void... params) {
async_upload();
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/**
* Push online any awaiting report
*/
private void async_upload(){
//Get the report file
File file = get_report_file(false);
//Check if the file exists or not
if(file == null){
Log.v(TAG, "Report file seems not to exists.");
return;
}
//Get report content
String report;
try {
report = readIs(new FileInputStream(file));
} catch (IOException e) {
e.printStackTrace();
return;
}
//Launch report upload
if(!upload(report))
Log.e(TAG, "An error occurred while trying to upload report!");
else
Log.v(TAG, "The report has been successfully uploaded:");
//Delete the awaiting report
if(!file.delete()){
Log.e(TAG, "An error occurred while trying to delete report file !");
}
}
/**
* Intend to upload the report online
*
* @param report The report to upload
* @return TRUE in case of success / FALSE else
*/
private boolean upload(String report){
try {
//Prepare the request body
String requestBody = "app_key=" + URLEncoder.encode(mAppKey, "UTF-8")
+ "&report=" + report;
//Prepare the connexion
URL url = new URL(mApiURL);
//Open URL connection
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//Setup a few settings
conn.setRequestMethod(API_CONNEXION_METHOD);
conn.setDoOutput(true);
conn.setDoInput(false);
conn.setConnectTimeout(API_CONNEXION_TIMEOUT);
conn.setChunkedStreamingMode(0);
//Connect to the server
conn.connect();
//Write report
OutputStream os = new BufferedOutputStream(conn.getOutputStream());
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
writer.write(requestBody);
writer.flush();
writer.close();
os.close();
conn.disconnect();
//Success
return true;
} catch (java.io.IOException e) {
e.printStackTrace();
return false;
}
}
/**
* Save the report locally for ulterior upload
*
* @param report The report to save
* @return TRUE for a success / FALSE else
*/
private boolean save_report(String report){
//Get the file
File file = get_report_file(true);
//Check for error
if(file == null){
Log.e(TAG, "Could not create report file!");
return false;
}
try {
//Open the file for writing
OutputStream os = new BufferedOutputStream(new FileOutputStream(file, false));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
writer.write(URLEncoder.encode(report, "UTF-8"));
writer.flush();
writer.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
//Success
return true;
}
/**
* Get the saved report file
*
* @param create Create the file if does not exists
* @return The report file (null in case of failure)
*/
@Nullable
private File get_report_file(boolean create){
File file = new File(mContext.getCacheDir(), REPORT_FILENAME);
//Check file existence
if(!file.exists()){
//Check if the file can be created
if(create) {
try {
//Intend to create the file
if (!file.createNewFile())
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
else
return null;
}
return file;
}
/**
* Read an input stream into a string
*
* @param is The input stream to read
* @return Read string
*/
private String readIs(InputStream is) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
StringBuilder out = new StringBuilder();
String line;
while((line = bufferedReader.readLine()) != null)
out.append(line);
bufferedReader.close();
return out.toString();
}
}

View File

@ -15,7 +15,9 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import org.communiquons.android.comunic.client.BuildConfig;
import org.communiquons.android.comunic.client.R;
import org.communiquons.android.comunic.client.crashreporter.CrashReporter;
import org.communiquons.android.comunic.client.data.helpers.APIRequestHelper;
import org.communiquons.android.comunic.client.data.helpers.AccountHelper;
import org.communiquons.android.comunic.client.data.utils.AccountUtils;
@ -87,6 +89,12 @@ public class MainActivity extends AppCompatActivity implements openConversationL
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Initialize crash reporter
CrashReporter reporter = new CrashReporter(this, BuildConfig.crash_reporter_url,
BuildConfig.crash_reporter_key);
reporter.uploadAwaitingReport();
Thread.setDefaultUncaughtExceptionHandler(reporter);
//Initialize account objects
accountHelper = new AccountHelper(this);