如何在嵌入式Linux产品中做立体、覆盖产品生命期的调试 ( 3 )

2019-07-13 06:24发布

上接:如何在嵌入式Linux产品中做立体、覆盖产品生命期的调试 ( 2)   这篇谈谈log的做法: 上面一篇谈了print的用法,一般print是把结果输出到stdout/stderr上面去了,也就是我们常见的terminal上面去了;这有个弊端,就是我们的程序是在debug状态时,我们才能看到这些调试信息;        如果程序不在debug状态下运行,这些打印信息是看不着的,而且程序一直在debug状态运行,也不能反映实际情况,尤其对于嵌入式产品来讲;        不在debug状态运行时的一些异常,如何能看到呢?这就用到了Log了。所谓的log就是把运行信息写到文件中去,保存下来;        Linux 有完善的Log系统,比如core的产生,就是Log的一种形式,只不过这时的Log是记录程序“病入膏肓”的状态,我这里所说的Log一般是记录程序中可以预见的一些异常信息,供事后分析使用;         我在这里提供两种Log供大家使用,一种是利用Linux syslog, 一种是我们自己写log;并且把两种log结合到一块,融合到上一篇博文中,结合Log and print:    使用Linux syslog的步骤:   1 头文件   #include   2 几个接口函数   openlog ( … ); vsyslog (…) syslog (....)   Linux syslog一般会把我们的信息写到/etc/log/messages中,当然你也可以配置syslog.conf文件去修改保存的路径、文件名等,至于如何修改,参考附录1. 这里就不冲淡我们的主题了:)   一个问题:保存到/etc/log/messages中好不好? 不好,为什么呢?一个产品,尤其手机中跑的程序至少有30多个,甚至更多,如果每个程序都把log信息写到/etc/log/mssages中,而且和系统的log信息混到了一块;这样的Log会很难看,不同程序的调试信息混到一块,事后查找非常麻烦! 吃大锅饭,责任不明呀,呵呵。   怎么办?分灶吃! Linux syslog有优点,但是缺点更突出!我们只要利用syslog的思想就行了:记录异常、重要的信息到文件中;   好,现在我们准备自己写异常信息到文件中,我们想给不同的程序维护一个自己的log日志,这很好办:给不同的文件名就行了; 另外一个值得考虑的问题:空间的问题!嵌入式产品的Flash空间很宝贵,不能滥用!如果一个程序的异常问题很多,记录的日志很多,日积月累,可能会把Flash的空间给吃完,这样会导致严重的问题,所有的程序都不能正常跑了!所以要限制你的log文件大小,比如说1M大小,看你的产品的Flash大小了;        有了上面的准备,下面我们开始做syslog和自己的log的结合体:   1 准备   static FILE *logfile; static FILE syslog_dummy; static int loglevel; static gboolean first_opened = FALSE; #define LOG_FILE_MAX_SIZE (1024*1024) /* 1M */   static int userlog2syslog[] = {       [USER_LOG_DEBUG]     = LOG_DEBUG,       [USER_LOG_INFO]  = LOG_INFO,       [USER_LOG_NOTICE]    = LOG_NOTICE,       [USER_LOG_ERR]   = LOG_ERR,       [USER_LOG_CRIT]  = LOG_CRIT, };   #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif   static inline int user2syslog_level(int level) {       if (level >= ARRAY_SIZE(userlog2syslog))             return LOG_ERR;         return userlog2syslog[level]; }   static inline char* get_syslog_level(int level) {       if (level >= ARRAY_SIZE(userlog2syslog))             return NULL;         switch(level)       {             case USER_LOG_DEBUG:                   return "LOG_DEBUG";                   break;               case USER_LOG_INFO:                   return "LOG_INFO";                   break;               case USER_LOG_NOTICE:                   return "LOG_NOTICE";                   break;               case USER_LOG_ERR:                   return "LOG_ERR";                   break;               case USER_LOG_CRIT:                   return "LOG_CRIT";                   break;                               default:                   return NULL;                   break;       } }   2 初始化我们的Log int userlog_init(const char *path) {        if (path == NULL)               return -1;        gboolean b_exist = FALSE;        guint log_size = 0;        char *file_name;        file_name = malloc (strlen (path) + 1);        strcpy (file_name, path);               if (!strcmp(file_name, "syslog"))        {               logfile = &syslog_dummy;               openlog("Customized syslog: ", 0, LOG_DAEMON);               goto OUT;        }        else        {               struct stat buf;               if (stat (file_name, &buf) != 0)                      b_exist = FALSE;               else               {                      b_exist = TRUE;                      log_size = buf.st_size;               }        }        free (file_name);               if (b_exist == TRUE)        {                 //当一个log文件过大时,保存一个备份,然后重新开始记录;     //时间长了,你的Log目录下面可能有两个log: 比如my_log,  
    //my_log.old    //这样做的目的,是想尽可能的回溯信息;               if (log_size > LOG_FILE_MAX_SIZE)                {                      char cmd[128] = {0};                      sprintf(cmd, "cp -rf %s %s.old", path, path);                      system(cmd);                        memset(cmd, 0, sizeof(cmd));                      sprintf(cmd, "rm -rf %s ", path);                      system(cmd);                                           logfile = fopen(path, "a+");               }               else                      logfile = fopen(path, "a+");          }        else               logfile = fopen(path, "a+");   OUT:             if (logfile == NULL)               return -1;          first_opened = TRUE;        user_log(USER_LOG_INFO, "logfile successfully opened.");          return 0; }   3 log     void user_log(int level, const char *file, int line, const char *function, const char *format, ...) {        char *timestr;        va_list ap;        time_t tm;        FILE *outfd;          if (level < loglevel)               return;        // 把我们的信息写到syslog中,即/etc/log/messages        if (logfile == &syslog_dummy)         {               va_start(ap, format);               vsyslog(user2syslog_level(level), format, ap);               va_end(ap);        }        else // 把我们的信息写到自己设置的一个文件中        {               if (logfile)                      outfd = logfile;               else                      outfd = stderr;                 tm = time(NULL);               timestr = ctime(&tm);               timestr[strlen(timestr)-1] = '/0';                 if (first_opened)               {                      fprintf(outfd, "/n%s <%s> File:%s, #Line:%d, Func:%s(), Message:",                             timestr, get_syslog_level(level), file, line, function);                      first_opened = FALSE;               }               else                      fprintf(outfd, "%s <%s> File:%s, #Line:%d, Func:%s(), Message:",                             timestr, get_syslog_level(level), file, line, function);                                      va_start(ap, format);               vfprintf(outfd, format, ap);               va_end(ap);                 fprintf(outfd, "/n");               fflush(outfd);        } }       4 如何使用这样的Log   void test_func2(void) {        if (exception) //有需要严重关注的异常,我就写Log               user_log(USER_LOG_INFO, "write the log to syslog or my own log file.");        return; }   //一般在主程序中初始化一下我们的log void main(int argc, char *argv[]) {     // 如果传入的参数是syslog,则我们的log将写入/etc/log/messages        userlog_init("syslog");   // 如果传入的是你自己的log文件名,则写入你自己的Log中, // 建议大家不要写到syslog中,吃大锅饭,到时候,所有的信息都搅在一块,难找! // 还是包干到户的好!最好把你的Log放在程序的当前目录,随便你了。 // userlog_init("./my_log");   test_func2();     ……          return; }         如果,大家有其它的log方法,欢迎一块探讨。     附录1: 这是我以前在网上看到的一个如何配置syslog.conf方法,放在这里,供大家参考。 配置文件/etc/syslog.conf的实例解析 蓝森林 http://www.lslnet.com 200741 18:37  

//
info或更高级别的消息送到/var/log/messages,除了mail以外。 //其中*是通配符,代表任何设备;none表示不对任何级别的信息进行记录。 *.info;mail.none;authpriv.none /var/log/messages //authpirv设备的任何级别的信息记录到/var/log/secure文件中,这主要是一些和认、权限使用相关的信息。 authpriv.* /var/log/secure   //mail设备中的任何级别的信息记录到/var/log/maillog文件中,这主要是和电子邮件相关的信息。 mail.* /var/log/maillog //cron设备中的任何级别的信息记录到/var/log/cron文件中,这主要是和系统中定期执行的任务相关的信息。 cron.* /var/log/cron   //将任何设备的emerg级别的信息发送给所有正在系统上的用户。 *.emerg *   //uucpnews设备的crit级别的信息记录到/var/log/spooler文件中。 uucp,news.crit /var/log/spooler   //将和系统启动相关的信息记录到/var/log/boot.log文件中。 local7.* /var/log/boot.log