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

上接:如何在嵌入式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 <syslog.h>

 

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 2007 4 1 18:37

 

<script src="/72890.js" language="JavaScript1.1" type="text/javascript"> </script>

//
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

 

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值