Расположение файла Android LogCat и отправка его копии по электронной почте

Я использую LogCat, чтобы получить отзыв о выполнении моего приложения Android, когда оно работает через мой эмулятор eclipse.

У меня другое поведение, когда apk работает на одном из моих реальных телефонов. Мне любопытно, создает ли LogCat файл где-нибудь на телефоне, к которому я тоже могу получить доступ.

В конечном счете, я хотел бы получить результаты LogCat и отправить их себе по электронной почте через адрес электронной почты службы поддержки. Цель состоит в том, чтобы позволить пользователям, у которых есть проблемы с программой, присылать мне копию своих LogCat результатов, когда у них возникают проблемы.

Создает ли LogCat файл, к которому я могу получить доступ, и могу ли я отправить его себе по электронной почте? Если LogCat этого не делает, есть ли альтернативный способ сделать это?


person James Oravec    schedule 18.07.2013    source источник
comment
Чтобы уточнить, я вижу ссылку на то, как это сделать с помощью adb, в stackoverflow.com/questions/4424544/, но есть ли способ включить это в производстве и получить информацию обратно?   -  person James Oravec    schedule 19.07.2013
comment
Если LogCat этого не делает, то любые предложения по альтернативному способу сделать это будут оценены. -- используйте ACRA: acra.ch, возможно, в сочетании с вашей собственной регистрацией вне LogCat.   -  person CommonsWare    schedule 19.07.2013


Ответы (1)


Очень сложная задача, но, надеюсь, это поможет...

(Выполняется много копирования/вставки, поэтому, пожалуйста, дайте мне знать, если я пропустил какой-то важный код! Я еще не проверял максимальную настройку 1 МБ - также может иметь смысл поместить ее в MainActivity.onCreate() вместо этого, поэтому мы не называйте это сообщением журнала, но это работает...)

Часть из этого взята из LogCollector, поэтому отдаем должное там, где это необходимо:(https://code.google.com/p/android-log-collector/)

Соответствующий метод из моего класса LogCollector: (принимает очевидный ввод и отправляет по электронной почте несколько вложений журналов, если оба существуют - у меня есть возможность включить файл журнала, а также logcat. Кстати, logcat имеет ~ 64 КБ памяти, поэтому вы НЕ получите много журналов из этого, поэтому мне нужно войти в файл)

public boolean sendLog(String email, String subject, String body) {
    Logger.v("LogCollector - sendLog()");
    ArrayList<String> lines = mLastLogs;
    if (lines.size() > 0) {
        Uri emailUri = Uri.parse("mailto:" + email);
        ///////////////////////////////////////////////////////////////////////////////////////     
        // Create and open folder for output file
        Logger.d("LogCollector - Creating folder & file...");
        final String filename = "AppName_logCat.txt";
        File folder = new File(Environment.getExternalStorageDirectory()+"/temp/");
        // Create directory structure if needed
        if(folder.mkdirs()){
            Logger.v("Created temp folder.");  
        }else{
            Logger.v("Did NOT create temp folder - perhaps it already exists");  
        }
        //Create log file
        File logFile = new File(Environment.getExternalStorageDirectory()+"/temp/", filename);
        Logger.v("Log File Path: "+logFile.getAbsolutePath());  
        FileWriter fileWriter;
        //String phoneInfo = collectPhoneInfo();
        //String appInfo = collectAppInfo();
        //Put contents into log file, including phone info
        try {
            Logger.d("LogCollector - Putting info into log file...");
            fileWriter = new FileWriter(logFile, false);        //dont append, clear first
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            //bufferedWriter.write(phoneInfo);
            //bufferedWriter.write("\n\n");
            //bufferedWriter.write(appInfo);
            bufferedWriter.write("\n\n");
            for (String line : lines) {
                    bufferedWriter.write(line);
                    bufferedWriter.newLine();
            }
            bufferedWriter.close();
        } catch (IOException e1) {
            Logger.w("LogCollector - Error putting log info into file: "+e1);
            if(!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
                Logger.w("SD card not present or not accessible");
            }
            return false;
        }
        // Check if log can be read for debugging
        if(!logFile.canRead()){
            Logger.e("Can't read file!");
            return false;
        }
        // Create appLogFile objects
        appLogFile = new File(Environment.getExternalStorageDirectory()+"/temp/", appFilename);

        //Send log file via email
        Logger.d("LogCollector - Emailing Logs...");
        // Need to assemble body this way due to Android bug
        //emailIntent.putExtra(Intent.EXTRA_TEXT, body);                        //Regular method - Causes warning
        //ArrayList<String> extra_text = new ArrayList<String>();               //workaround
        //extra_text.add("See attached CSV files.");                            //workaround
        //emailIntent.putStringArrayListExtra(Intent.EXTRA_TEXT, extra_text);   //causes no error but missing body/text - not a big deal, but pointless to have if doesnt issue a body
        // Put info in email
        Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
        emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{emailUri.toString()});
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
        emailIntent.setType("text/plain");
        ArrayList<Uri> uris = new ArrayList<Uri>();     
        String[] filePaths;             
        // If appLogFile exists & is valid, attach to email
        if(appLogFile.exists() && appLogFile.isFile() && appLogFile.canRead()) {
            Logger.i("appLogFile exists; attaching to email");
            filePaths = new String[] {logFile.toString(),appLogFile.toString()};
        }else{
            Logger.w("Error finding or reading logfile. Debug disabled?!");
            filePaths = new String[] {logFile.toString()};
        }
        for (String file : filePaths) {
            File fileIn = new File(file);
            Uri u = Uri.fromFile(fileIn);
            uris.add(u);
        }
        emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
        mContext.startActivity(Intent.createChooser(emailIntent, "Email Logs to Developer"));
    }
    return true;
}

Пользовательский класс Logger: (обрабатывает все мои журналы — также записывает в файл, если включена опция отладки)

public class Logger {
private static final String TAG = "AppName";
private static final int MAX_FILESIZE=1;    //in MB
private static File logFolder;
private static File logFile;
private static String filename = TAG+"_logfile.txt";
private static FileWriter fileWriter;
private static BufferedWriter bufferedWriter;
private static SimpleDateFormat sdf;
private static String dateTime;
private static int PID;
private static int TID;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static void v(String message) {
    // Do normal logging to logcat
    Log.v(TAG,message);
    // Log to file
    if(MainActivity.enable_debug()) {
        PID= android.os.Process.myPid();
        TID=android.os.Process.myTid();
        logToFile(PID,TID,"V",message);
    }
}

public static void d(String message) {
    // Do normal logging to logcat
    Log.d(TAG,message);
    // Log to file
    if(MainActivity.enable_debug()) {
        PID= android.os.Process.myPid();
        TID=android.os.Process.myTid();
        logToFile(PID,TID,"D",message);
    }
}
public static void i(String message) {
    // Do normal logging to logcat
    Log.i(TAG,message);
    // Log to file
    if(MainActivity.enable_debug()) {
        PID= android.os.Process.myPid();
        TID=android.os.Process.myTid();
        logToFile(PID,TID,"I",message);
    }
}
public static void w(String message) {
    // Do normal logging to logcat
    Log.w(TAG,message);
    // Log to file
    if(MainActivity.enable_debug()) {
        PID= android.os.Process.myPid();
        TID=android.os.Process.myTid();
        logToFile(PID,TID,"W",message);
    }
}   
public static void e(String message) {
    // Do normal logging to logcat
    Log.e(TAG,message);
    // Log to file
    if(MainActivity.enable_debug()) {
        PID= android.os.Process.myPid();
        TID=android.os.Process.myTid();
        logToFile(PID,TID,"E",message);
    }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@SuppressLint("SimpleDateFormat")
private static void logToFile(int PID,int TID,String LEVEL,String message) {
    //return if there is no SD card, or it's inaccessible
    if(!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
        return;
    }
    // Date - Time - PID - TID - LEVEL - TAG? - Message
    // Create and initialize temp folder for log file if doesn't already exist      
    if(logFolder == null) {
        logFolder = new File(Environment.getExternalStorageDirectory()+"/temp/");
    }
    // Create temp folder if doesn't exist
    if(!logFolder.exists()) {
        //Logger.i("Creating temp folder on SD card root...");
        logFolder.mkdirs();
    }
    // Create log file if doesn't already exist
    if(logFile == null) {
        logFile = new File(Environment.getExternalStorageDirectory()+"/temp/", filename);
        try {
            logFile.createNewFile();
        } catch (IOException e) {
            Logger.e("Error creating new file: "+e);
        }
    }
    // Check log file validity - Error if there's a problem with the file
    // Not sure if this is a performance hit
    if(!logFile.exists() || !logFile.isFile() || !logFile.canRead()) {
        //Logger.e("Problem with logFile! Doesnt exist, isn't a file, or can't read it");
        return;
    }
    //Get Date/Time
    if(sdf == null) {
        sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");    //determines dateTime format
    }
    dateTime = sdf.format(new Date());  //set to current date/time

    // Write log message to file
    try {
        if(fileWriter == null) {
            //if(size of file is > 1MB or whatever, then set below to false to clear file first?  Or need to do something better so we dont wipe mid incoming text) {
            if(logFile.length() > MAX_FILESIZE*1024*1024) {
                Logger.i("logFile is > "+MAX_FILESIZE+" MB, clearing first...");
                fileWriter = new FileWriter(logFile, false);        // true=dont append, clear first
            }else{
                fileWriter = new FileWriter(logFile, true);         // false=append, clear first
            }
        }
        if(bufferedWriter == null) {
            bufferedWriter = new BufferedWriter(fileWriter);
        }
        bufferedWriter.write(dateTime+" "+PID+" "+TID+" "+LEVEL+" "+TAG+": "+message);  //write line to log file
        bufferedWriter.newLine();
        bufferedWriter.flush();     //forces to write to file?
    } catch (IOException e) {
        Logger.e("Error writing to log: ");
        e.printStackTrace();
    }           
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}

Важные методы из моего класса Utilities:

// EMAIL LOGS
public static void emailLogsDialog(final Context context) {
    final AlertDialog.Builder builder = new AlertDialog.Builder(context); 
    builder.setTitle("Send Logs to Developer");
    builder.setMessage("Do you want to send your system logs to the Developer for troubleshooting?\n\nWarning: The logs may contain personal information; this is beyond the Developer's control.");
    builder.setInverseBackgroundForced(true);
    builder.setPositiveButton("Ok",new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog,int which) {
            dialog.dismiss();
            emailLogs(context);
        }
    });
    builder.setNegativeButton("Cancel",new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog,int which) {
            dialog.dismiss();
        }
    });
    AlertDialog alertConfirm = builder.create();
    alertConfirm.show();
}
public static void emailLogs(final Context context) {
    final LogCollector logCollector = new LogCollector(context);
    final AlertDialog.Builder builder = new AlertDialog.Builder(context);  
    new AsyncTask<Void, Void, Boolean>() {
        AlertDialog alert;
        @Override
        protected Boolean doInBackground(Void... params) {
            return logCollector.collect();
        }
        @Override
        protected void onPreExecute() {
            builder.setTitle("Send Logs to Developer");
            builder.setMessage("Collecting Logs & Emailing now...");
            builder.setInverseBackgroundForced(true);
            builder.setNegativeButton("Cancel",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog,int which) {
                            dialog.dismiss();
                            return;
                        }
                    });
            alert = builder.create();
            alert.show();
        }
        @Override
        protected void onPostExecute(Boolean result) {
            alert.dismiss();
            builder.setTitle("Send Logs to Developer");
            builder.setMessage("Logs successfully sent to Developer\n\n (Make sure your email app successfully sent the email.)");
            builder.setInverseBackgroundForced(true);
            builder.setPositiveButton("Ok",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog,int which) {
                            dialog.dismiss();
                        }
                    });
            if (result) {
                Logger.d("Successfully extracted logs.");
                if(logCollector.sendLog("[email protected]", "OnCallPager Error Log", "Error Log\n")) {
                    Toast.makeText(context,"Logs successfully extracted to your default email application",Toast.LENGTH_LONG).show();
                }else{
                    Toast.makeText(context,"There was a problem extracting the logs.\n\nDo you have an SD card and is it mounted?",Toast.LENGTH_LONG).show();
                }
            }else{
                Logger.e("Failed to extract logs!");
                Toast.makeText(context,"Error acquiring logs!",Toast.LENGTH_LONG).show();
            }
        }    
    }.execute();
}

Журналы вызовов таким образом:

Logger.v("LogCollector - sendLog()");

Ключ для sendEmail (с логами): (как указано выше)

if(logCollector.sendLog("[email protected]", "OnCallPager Error Log", "Error Log\n")) {
                Toast.makeText(context,"Logs successfully extracted to your default email application",Toast.LENGTH_LONG).show();
            }else{
                Toast.makeText(context,"There was a problem extracting the logs.\n\nDo you have an SD card and is it mounted?",Toast.LENGTH_LONG).show();
            }
person Kevin    schedule 11.01.2014