Question

I can use ACRA library to manage force close error by handling uncaught exception. The report can sent to google doc., email and custom web service successfully..

But what I want..

  • How can I write the report to file [ex. sdcard/myapp/myLog.txt] ?

why I want this..

  • My app user may not have internet connection while force close occurs.. if so then I will miss the report, if i write the report to file then I can sent to my server when the internet connection available.
Was it helpful?

Solution

I think what you want to achieve is already done by ACRA. Here's what I see in my abd logcat :

01-23 12:15:28.056: D/ACRA(614): Writing crash report file.
01-23 12:15:28.136: D/ACRA(614): Mark all pending reports as approved.
01-23 12:15:28.136: D/ACRA(614): Looking for error files in /data/data/com.ybi/files
01-23 12:15:28.136: V/ACRA(614): About to start ReportSenderWorker from #handleException
01-23 12:15:28.146: D/ACRA(614): Add user comment to null
01-23 12:15:28.146: D/ACRA(614): #checkAndSendReports - start
01-23 12:15:28.146: D/ACRA(614): Looking for error files in /data/data/com.ybi/files

First thing that ACRA does is creating a report on a file on the inner storage of your app. Then if you're online and the errorreporter is correctly initialized, it sends the report. Otherwise, the reports are kept in the data storage (for a later sending).

I didn't look into the data but I'm currently working on a custom logger. So if you want to do the same things than ACRA, it's easy :

    ACRA.init(this);

    // a custom reporter for your very own purposes
    ErrorReporter.getInstance().setReportSender(new LocalReportSender(this));

And then :

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.acra.ACRA;
import org.acra.CrashReportData;
import org.acra.ReportField;
import org.acra.sender.ReportSender;
import org.acra.sender.ReportSenderException;

import android.content.Context;

import de.akquinet.android.androlog.Log;

public class LocalReportSender implements ReportSender {

private final Map<ReportField, String> mMapping = new HashMap<ReportField, String>() ;
private FileOutputStream crashReport = null; 

public LocalReportSender(Context ctx) {
    // the destination
    try {
        crashReport = ctx.openFileOutput("crashReport", Context.MODE_WORLD_READABLE);
    } catch (FileNotFoundException e) {
        Log.e("TAG", "IO ERROR",e);
    }
}

@Override
public void send(CrashReportData report) throws ReportSenderException {

    final Map<String, String> finalReport = remap(report);

    try {
        OutputStreamWriter osw = new OutputStreamWriter(crashReport);

        Set set = finalReport.entrySet();
        Iterator i = set.iterator();

        while (i.hasNext()) {
            Map.Entry<String,String> me = (Map.Entry) i.next();
            osw.write("[" + me.getKey() + "]=" + me.getValue());
        }

        osw.flush();
        osw.close();
    } catch (IOException e) {
        Log.e("TAG", "IO ERROR",e);
    }

}

private static boolean isNull(String aString) {
    return aString == null || ACRA.NULL_VALUE.equals(aString);
}

private Map<String, String> remap(Map<ReportField, String> report) {

    ReportField[] fields = ACRA.getConfig().customReportContent();
    if (fields.length == 0) {
        fields = ACRA.DEFAULT_REPORT_FIELDS;
    }

    final Map<String, String> finalReport = new HashMap<String, String>(
            report.size());
    for (ReportField field : fields) {
        if (mMapping == null || mMapping.get(field) == null) {
            finalReport.put(field.toString(), report.get(field));
        } else {
            finalReport.put(mMapping.get(field), report.get(field));
        }
    }
    return finalReport;
}

}

I didn't fully tested it yet but you get the idea. Hope it helps.

OTHER TIPS

I guess the answer from @Gomoku7 contains some deprecated code so I'll just post the solution that I used:

Call this in onCreate():

ACRA.init(this);
ACRA.getErrorReporter().setReportSender(new LocalReportSender(this));

Here I have basically changed the code to use BufferedWriter so that I could write directly to SD card which was not possible with openFileOutput(). Therefore only method send() and constructor LocalReportSender() are slightly changed.

Note: Be aware that the logfile grows quite quickly so be sure you don't take up MBs of space of your user's SDcard because of a log file :)

private class LocalReportSender implements ReportSender {

    private final Map<ReportField, String> mMapping = new HashMap<ReportField, String>();
    private FileWriter crashReport = null;

    public LocalReportSender(Context ctx) {
        // the destination
        File logFile = new File(Environment.getExternalStorageDirectory(), "log.txt");

        try {
            crashReport = new FileWriter(logFile, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void send(CrashReportData report) throws ReportSenderException {
        final Map<String, String> finalReport = remap(report);

        try {
            BufferedWriter buf = new BufferedWriter(crashReport);

            Set<Entry<String, String>> set = finalReport.entrySet();
            Iterator<Entry<String, String>> i = set.iterator();

            while (i.hasNext()) {
                Map.Entry<String, String> me = (Entry<String, String>) i.next();
                buf.append("[" + me.getKey() + "]=" + me.getValue());
            }

            buf.flush();
            buf.close();
        } catch (IOException e) {
            Log.e("TAG", "IO ERROR", e);
        }
    }

    private boolean isNull(String aString) {
        return aString == null || ACRAConstants.NULL_VALUE.equals(aString);
    }

    private Map<String, String> remap(Map<ReportField, String> report) {

        ReportField[] fields = ACRA.getConfig().customReportContent();
        if (fields.length == 0) {
            fields = ACRAConstants.DEFAULT_REPORT_FIELDS;
        }

        final Map<String, String> finalReport = new HashMap<String, String>(
                report.size());
        for (ReportField field : fields) {
            if (mMapping == null || mMapping.get(field) == null) {
                finalReport.put(field.toString(), report.get(field));
            } else {
                finalReport.put(mMapping.get(field), report.get(field));
            }
        }
        return finalReport;
    }

}

I have used ACRA but not in this form (used to send the logs to my own server), so I am not sure how to do this.
But in such a case cant you acquire the system logs as a whole (that will be a detailed one ofcourse) using the other libs/apis and write it to a file.

OR, what you can do is, use the code of the ACRA zip and modify it a little eg.Using the file "CrashReportData.java" or "CrashReporterDialog.java" in its package and get fetch the content from there and saving it to your file.

I am talking about its version 4.2.3.

the solution above works perfectly. There is probably only one thing, If the file is not visible using file explorer, try to add intent broadcast to Intent.ACTION_MEDIA_SCANNER_SCAN_FILE

check this link

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top