记录安卓系统日志保存成txt文件

记录安卓系统日志

背景:项目组里面的APP开发人员要求能够记录APP允许的系统日志。你要是主板连了串口调试线就简单了,终端里面一个logcat命令就行了,该过滤的过滤下。
使用 logcat 获取安卓的系统日志,当串口连上主板调试线的时候,在中断里面直接输入linux命令。
在这里插入图片描述
就是一个效果。
在这里插入图片描述

这里所谓的去日志就是把这些要的日志,保存成文本文件,然后拿出来分析。
思路:写一个程序,能够自动的运行 logcat ,并将截取到的信息输出 文本文件!!
当APP开发人员需要的时候,使用adb pull 去把日志文件下载下来。

设计自动化程序

这里晚点再来补充啊!

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "gg_log.h"

#define		MAXBUF			1024
#define		LOGFILE			"/sdcard/gg/log/dlog.txt"
#define		PFILE			"/sdcard/gg/log/p.txt"
#define		MAXFILE			(50*1024*1024)

int main(int argc, char *argv[])
{
	int				n;
	char			buf[MAXBUF];
	int				len;
	int				fd[2], fdlog, fdp;
	int				i;
	long			location;
	//在linux内核中查找,其实就是 int类型
	pid_t			pid;

	//创建管道
	//当进程创建管道时,每次都需要提供两个文件描述符来操作管道。
	//其中一个对管道进行写操作,另一个对管道进行读操作。
	//参数数组包含pipe使用的两个文件的描述符。
	//fd[0]:读管道,fd[1]:写管道。
	if (pipe(fd) < 0) 
	{
		printf("pipe error\n");
		return -1;
	}
	//是创建一个新进程的唯一方式
	//当程序调用fork()函数并返回成功之后,程序就将变成两个进程,调用fork()者为父进程,后来生成者为子进程。
	if ((pid = fork()) < 0) 
	{	
		printf("fork error\n");
	} 
	else if (pid == 0) 
	{		//child
		write(fd[1], "hello world\n", 12);
		close(fd[0]);		//close read
		//用来复制一个现存的文件描述符,使两个文件描述符指向同一个file结构体
		//从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2)
		//0与进程的标准输入相关联,
		//1与进程的标准输出相关联,
		//2与进程的标准错误输出相关联
		dup2(fd[1], 0);
		dup2(fd[1], 1);
		dup2(fd[1], 2);
		if (fd[1] > 2)
			close(fd[1]);
/*		
		if (execlp("/system/bin/logcat", "-s", "ZHEGT", (char *)0) < 0) 
		{
			printf("child exec error!\n");
			exit(1);
		} 
*/
		//相当于在终端里面输入命令
		if (system("logcat -s ZHEGT") < 0) 
		{
			printf("system error!\n");
			exit(1);
		}
	}
	//parent
	close(fd[1]);		//close write

	//打开文件
	//fdlog 和 fdp,是两个文件描述符
	if (check_open_file(&fdlog, &fdp) < 0)
	{
		printf("check_open_file error!\n");
		return -1;
	}
	//printf("flag1\n");
	//获取的 location 是个什么东西呢?感觉是个地址,字符串数组地址
	//想明白了,这是获取文件的最后位置
	if ((location = get_location(&fdp)) < 0) 
	{
		if (location == -2) 
		{
			printf("init location\n");
			location = 0;
			//没有文件最后位置,表明是第一次,就设置文件位置
			if (set_location(&fdp, 0) < 0) 
			{
				printf("set location error\n");
				return -1;
			}
		}
		else 
		{
			printf("get_location error!\n");
			return -1;
		}
	}

	//定位到文件最后的位置
	if (lseek(fdlog, location, SEEK_SET) < 0) 
	{
		printf("lseek error!\n");
		return -1;
	}
	
	//进入死循环
	while(1)
	{
		if ((len = read(fd[0], buf, MAXBUF)) < 0) 
		{
			printf("read error!\n");
			return -1;
		}
		else 
		{
			if (len > 0) 
			{
				location += len;
				printf("len=%d, location=%ld\n", len, location);
				if (location >= MAXFILE) 
				{
					location -= len;
					for (i = 0;i <= (MAXFILE - location); i++ )
					{
						if (write(fdlog, "*", 1) < 0) 
						{
							printf("write error1!\n");
							return -1;
						}
					}
					location = len;
					if (lseek(fdlog, 0, SEEK_SET) < 0) 
					{
						printf("lseek error!\n");
						return -1;
					}
				}
				if (write(fdlog, buf, len) < 0) 
				{
					printf("write error2!\n");
					return -1;
				}
				for (i = 0; i < 5; i++) 
				{
					if (write(fdlog, "\n", 1) < 0) 
					{
						printf("write error3");
						return -1;
					}
				}
				if (lseek(fdlog, -5, SEEK_CUR) < 0) 
				{
					printf("lseek error!");
					return -1;
				}
				if (set_location(&fdp, location) < 0) 
				{
					printf("set location error\n");
					return -1;
				}
			}
			else 
			{
				printf("sleep 5s");
				sleep(5);
			}
		}
	}

	exit(0);
}

//检测能否成功打开日志文件
int check_open_file(int *fdlog, int *fdp)
{
	//创建文件 "/sdcard/gg/log/dlog.txt" 
	//不存在就创建,存在就打开
	if (access(LOGFILE, F_OK) < 0)
	{
		printf("logfile does not exist! create it!\n");
		if ( (*fdlog = open(LOGFILE, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR)) < 0) 
		{
			printf("create logfile failed!\n");
			return -1;
		}
	}
	else 
	{
		if ((*fdlog = open(LOGFILE, O_WRONLY | O_SYNC)) < 0)
		{
			printf("open logfile error!\n");
			return -1;
		}
	}
	
	//创建文件 "/sdcard/gg/log/p.txt"
	//不存在就创建,存在就打开
	if (access(PFILE, F_OK) < 0)
	{
		printf("pfile does not exist! create it!\n");
		if ( (*fdp = open(PFILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | O_SYNC)) < 0)
		{
			printf("create pfile failed!\n");
			return -1;
		}
	}
	else
	{
		if ((*fdp = open(PFILE, O_RDWR | O_SYNC)) < 0)
		{
			printf("open pfile error!\n");
			return -1;
		}
	}
	return 0;
}

long get_location(int *fdp)
{
	char			buf[20];
	long			location;

	//返回值为实际读取到的字节数
	if (read(*fdp, buf, 10) != 10)
	{
		printf("empty pfile, need to init!\n");
		return -2;
	}
	//字符产最后一位赋值
	buf[10] =0;		//set to null

	return(atol(buf));
}

int set_location(int *fdp, long location)
{
	char			buf[20];
	
	//构造buf字符串
	if (snprintf(buf, 20, "%010ld", location) < 0)
	{
		printf("snprintf error!\n");
		return -1;
	}
	if (strlen(buf) != 10)
	{
		printf("length != 10\n");
		return -1;
	}
	//lseek函数将文件指针移动后,那么再去read/write时就是从移动过后的位置开始的了
	if (lseek(*fdp, 0, SEEK_SET) < 0)
	{
		printf("lseek error!\n");
		return -1;
	}
	if (write(*fdp, buf, 10) != 10)
	{
		printf("write location error!\n");
		return -1;
	}

	return 0;
}

实现效果

这个程序运行之后,会把系统日志全部存储在 /sdcard/gg/log/dlog.txt 中,之后就是各种办法下载下来就是了,可以直接SD卡拷贝,也可以adb 工具下载!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android系统提供了相应的API来获取设备的开关机数据,可以通过BroadcastReceiver接收设备开关机的广播,并在接收到广播时记录相应的数据。以下是一个示例代码: ```java public class BootReceiver extends BroadcastReceiver { private static final String TAG = "BootReceiver"; @Override public void onReceive(Context context, Intent intent) { if (intent == null) { return; } String action = intent.getAction(); if (action == null) { return; } switch (action) { case Intent.ACTION_BOOT_COMPLETED: // 设备开机 Log.i(TAG, "Boot completed"); saveBootData(context, true); break; case Intent.ACTION_SHUTDOWN: // 设备关机 Log.i(TAG, "Shutdown"); saveBootData(context, false); break; } } private void saveBootData(Context context, boolean isBoot) { SharedPreferences sp = context.getSharedPreferences("boot_data", Context.MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); String key = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date()); int bootCount = sp.getInt(key + "_boot_count", 0); int shutdownCount = sp.getInt(key + "_shutdown_count", 0); if (isBoot) { bootCount++; } else { shutdownCount++; } editor.putInt(key + "_boot_count", bootCount); editor.putInt(key + "_shutdown_count", shutdownCount); editor.apply(); } } ``` 可以在AndroidManifest.xml中注册该BroadcastReceiver: ```xml <receiver android:name=".BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.SHUTDOWN" /> </intent-filter> </receiver> ``` 可以使用JobScheduler或AlarmManager定时获取某段时间内的用户开机率,以下是一个示例代码: ```java public class BootJobService extends JobService { private static final String TAG = "BootJobService"; private static final int JOB_ID = 1001; private static final long INTERVAL = 24 * 60 * 60 * 1000; // 一天 @Override public boolean onStartJob(JobParameters params) { Log.i(TAG, "onStartJob"); getBootRate(getApplicationContext()); scheduleNextJob(); return false; } @Override public boolean onStopJob(JobParameters params) { Log.i(TAG, "onStopJob"); return false; } private void scheduleNextJob() { JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); if (jobScheduler == null) { return; } JobInfo jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(this, BootJobService.class)) .setMinimumLatency(INTERVAL) .setOverrideDeadline(INTERVAL + 60 * 1000) // 最多延迟一分钟 .setRequiresCharging(false) .setPersisted(true) .build(); jobScheduler.schedule(jobInfo); } private void getBootRate(Context context) { SharedPreferences sp = context.getSharedPreferences("boot_data", Context.MODE_PRIVATE); String key = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date()); int bootCount = sp.getInt(key + "_boot_count", 0); int shutdownCount = sp.getInt(key + "_shutdown_count", 0); double rate = bootCount * 1.0 / (bootCount + shutdownCount); Log.i(TAG, "Boot rate: " + rate); // TODO: 将数据保存到日志文件中 } } ``` 可以在应用启动时启动定时任务: ```java public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); scheduleJob(); } private void scheduleJob() { JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); if (jobScheduler == null) { return; } JobInfo jobInfo = new JobInfo.Builder(BootJobService.JOB_ID, new ComponentName(this, BootJobService.class)) .setMinimumLatency(BootJobService.INTERVAL) .setOverrideDeadline(BootJobService.INTERVAL + 60 * 1000) // 最多延迟一分钟 .setRequiresCharging(false) .setPersisted(true) .build(); jobScheduler.schedule(jobInfo); Log.i(TAG, "Job scheduled"); } } ``` 最后,可以使用File类将数据保存到日志文件中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值