非常艰巨的任务,但希望这会有所帮助......
(做了很多复制/粘贴,所以如果我遗漏了一些重要的代码,请告诉我!我还没有测试最大 1MB 设置 - 将它放在 MainActivity.onCreate() 中也可能有意义,所以我们不要用任何日志消息来调用它,但这有效......)
其中一些来自 LogCollector,因此在应得的情况下给予信用:(https://code.google.com/p/android-log-collector/)
我的 LogCollector 类中的相关方法:(采用明显的输入并通过电子邮件发送多个日志附件,如果两者都存在 - 我可以选择启用日志文件和 logcat。顺便说一句,logcat 的内存约为 64KB,所以你不会得到很多日志记录,因此我需要记录到文件)
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();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
我的实用程序类中的重要方法:
// 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("ArnoldSwarteneger@gmail.com", "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("ArnoldSwarteneger@gmail.com", "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();
}