1.概述
在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件。在日志文件中存放程序流程中的一些重要信息,
包括:变量名称及其值、消息结构定义、函数返回值及其执行情况、脚本执行及调用情况等。通过阅读日志文件,我们能够较快地跟踪程序流程,并发现程序问题。
因此,熟练掌握日志系统的编写方法并快速地阅读日志文件,是对一个软件开发工程师的基本要求。
本文详细地介绍了Linux下一个简单的日志系统的设计方法,并给出了其C代码实现。本文为相关开发项目Linux下软件日志系统的编写提供了有益的参考。

2.日志系统的框架结构
一个完整的日志系统包括三大部分:配置文件、软件程序和日志文件,它们之间的关系如图1所示。

图1 一个完整的日志系统的框架结构

从图1可以看出,软件程序处于主导地位,它会从配置文件中读取相关的配置信息(这些配置信息用于控制每条日志信息的生成样式),经过处理之后将相关信息输出到日志文件中。

3.生成日志文件的程序流程
基于日志系统的框架结构,生成日志文件的程序流程如图2所示。

图2 生成日志文件的程序流程
在实际的软件程序中,为了在程序的不同地方打印不同的日志,要将生成日志的代码封装为函数,作为API供程序调用。
如果软件没有成功生成日志,那么就不要让其继续执行后续流程,而是要查找问题的原因,直到日志生成正常为止。

4.日志文件命名及日志信息格式
对于日志文件的命名,不同的软件开发项目有不同的规定。一般说来,日志文件都是以log作为后缀,如本文中的日志文件命名为:WriteLog.log。
对于每条日志信息的格式,对于不同的软件来说,也会有所不同。在本文中,日志信息的格式有以下两种(具体使用哪一种通过配置项决定):
第一种:[日志生成时间][文件名][函数名][代码行][日志等级]日志具体信息
第二种:[日志生成时间][日志等级]日志具体信息

5.配置文件说明
本文中使用的配置文件为Config.ini,它包括了两部分信息,如下所示:

[EMPLOYEEINFO]
;the name of employee
EmployeeName=
;the age of employee
EmployeeAge=

[LOG]
;LogLevel, 0-Fatal 1-Error 2-Warn 3-Info 4-Trace 5-Debug 6-All
LogLevel=
;If output position info(filename/functionname/linenum), 1-Yes 0-No
LogPosition=
;Log dir
LogDir=
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其中,“EMPLOYEEINFO”段是指员工信息,包含员工姓名和员工年龄两个配置项。程序会将员工姓名和员工年龄读入,并输出到日志文件中。
“LOG”段是指日志信息,包含日志等级、日志代码位置标识和输出日志文件的目录三个配置项。对于“LogLevel”配置项,只有代码中日志等级不低于
配置值的日志信息才会被输出到日志文件中(例如,LogLevel=4,那么只有Fatal、Error、Warn、Info和Trace等级的日志会被
输出到日志文件中)。“LogPosition”配置项的值用于控制是否在日志文件中显示“文件名/函数名/代码行数”信息,1则显示,0则不显示。
“LogDir”配置项的值表示生成的日志文件存放的目录。

6.重要程序流程
(1) 从配置文件中读取各个配置项的值
该操作的流程如图3所示,具体请参见《Linux下配置文件读取操作流程及其C代码实现》(http://blog.csdn.net/zhouzhaoxiong1227/article/details/45563263)一文。

图3 配置文件读取操作程序流程

(2) 向日志文件中写入日志信息
该操作的流程如图4所示。

图4 向日志文件中写入日志信息程序流程

该流程的具体代码请参考本文附录中的完整程序代码中的WriteLogFile函数。

7.程序测试设计及文件上传
为了测试本日志系统的功能是否正确,在main函数中设计了以下三类日志信息:
第一类:打印程序的版本号及编译时间。
第二类:打印Fatal、Error、Warn、Info、Trace、Debug、All这七个等级的日志各一条。
第三类:调用GetEmployeeInfo函数打印读取到的员工姓名和年龄。

将本程序“WriteLog.c”上传到Linux的“/home/zhou/zhouzx/test”目录下,并在该目录下建立“etc”和“log”目录,将配置文件“Config.ini”上传到“etc”下。文件及目录布局如图5所示。

图5 文件及目录布局

8.代码编译及运行
在Linux下使用“gcc -g -o WriteLog WriteLog.c”命令对程序进行编译,生成“WriteLog”文件。
下面来运行程序。
(1) 将配置文件中的各个配置项的值设置如下:

[EMPLOYEEINFO]
;the name of employee
EmployeeName=wang
;the age of employee
EmployeeAge=25

[LOG]
;LogLevel, 0-Fatal 1-Error 2-Warn 3-Info 4-Trace 5-Debug 6-All
LogLevel=5
;If output position info(filename/linenum), 1-Yes 0-No
LogPosition=1
;Log dir
LogDir=/home/zhou/zhouzx/test/log
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

则生成的日志文件“WriteLog.log”的内容为:

[2015.05.22 11:12:29.402][WriteLog.c][main][0077][INFO]Version [1.0], Build time[May 22 2015 11:11:51].
[2015.05.22 11:12:29.402][WriteLog.c][main][0081][FATAL]The Fatal log info!
[2015.05.22 11:12:29.402][WriteLog.c][main][0085][ERROR]The Error log info!
[2015.05.22 11:12:29.403][WriteLog.c][main][0089][WARN]The Warn log info!
[2015.05.22 11:12:29.403][WriteLog.c][main][0093][INFO]The Info log info!
[2015.05.22 11:12:29.403][WriteLog.c][main][0097][TRACE]The Trace log info!
[2015.05.22 11:12:29.403][WriteLog.c][main][0101][DEBUG]The Debug log info!
[2015.05.22 11:12:29.403][WriteLog.c][GetEmployeeInfo][0144][INFO]EmployeeName is wang, EmployeeAge is 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对照配置文件和日志文件,我们可以看到,“LogLevel”设置的是为5,因此只有日志等级不低于5的日志被输出到了日志文件中;“LogPosition”设置的是为1,因此在日志文件中显示了“文件名/函数名/代码行数”的信息。

(2) 将配置文件中的各个配置项的值设置如下:

[EMPLOYEEINFO]
;the name of employee
EmployeeName=Li
;the age of employee
EmployeeAge=28

[LOG]
;LogLevel, 0-Fatal 1-Error 2-Warn 3-Info 4-Trace 5-Debug 6-All
LogLevel=4
;If output position info(filename/functionname/linenum), 1-Yes 0-No
LogPosition=0
;Log dir
LogDir=/home/zhou/zhouzx/test/log
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

则生成的日志文件“WriteLog.log”的内容为:

[2015.05.22 11:16:53.104][INFO]Version [1.0], Build time[May 22 2015 11:11:51].
[2015.05.22 11:16:53.104][FATAL]The Fatal log info!
[2015.05.22 11:16:53.104][ERROR]The Error log info!
[2015.05.22 11:16:53.104][WARN]The Warn log info!
[2015.05.22 11:16:53.104][INFO]The Info log info!
[2015.05.22 11:16:53.104][TRACE]The Trace log info!
[2015.05.22 11:16:53.104][INFO]EmployeeName is Li, EmployeeAge is 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

对照配置文件和日志文件,我们可以看到,“LogLevel”设置的是为4,因此只有日志等级不低于4的日志被输出到了日志文件中;“LogPosition”设置的是为0,因此在日志文件中不显示“文件名/函数名/代码行数”的信息。

为了验证本日志系统功能的正常与否,要对程序进行多组测试,

9.总结
本文对Linux下一个简单的日志系统的设计方法进行了详细的介绍(其C代码实现请见附录),代码中的写日志相关函数可作为API供其它需要进行类似操作的程序调用。在使用本日志系统的过程中,有以下注意事项:
第一,配置文件中“LOG”段只包括了日志等级、日志代码位置标识和输出日志文件的目录三个配置项。在实际的软件开发项目中,还会有更多的配置参数,像存
放的日志文件的最大个数、每个日志文件的大小阈值、每个已写入完成的日志文件的命名等。可以在本程序的基础上进一步扩展来实现复杂的日志功能。
第二,本文中对日志信息的写入采用的是直接在日志文件后面追加的方式,因此每次测试之前,要在“log”目录下删除上一次产生的“WriteLog.log”文件,否则新的日志信息会写入旧的日志文件中。
第三,由于写日志函数WriteLogFile的入参较多,每次调用的时候编写代码较为繁琐,因此使用一个宏WRITELOGFILE来代替,且只需要带上日志等级和日志消息两个参数即可,其它的如代码文件名、函数名和代码行数直接使用系统自定义的宏即可。

附录:完整的程序代码

/**********************************************************************
* 版权所有 (C)2015, Zhou Zhaoxiong。
*
* 文件名称:WriteLog.c
* 文件标识:无
* 内容摘要:演示日志信息的打印方法
* 其它说明:无
* 当前版本:V1.0
* 作   者:Zhou Zhaoxiong
* 完成日期:20150522
*
**********************************************************************/
#include <stdio.h>
#include <time.h>
#include <sys/time.h>

// 重定义数据类型
typedef signed   int    INT32;
typedef unsigned int    UINT32;
typedef unsigned char   UINT8;

// 函数宏定义
#define WRITELOGFILE(level, msg)  WriteLogFile(__FILE__, __FUNCTION__, __LINE__, level, msg)

// 全局变量
UINT32  g_iLogLevel      = 0;    // 日志等级
UINT32  g_iLogPosition   = 0;    // 日志位置
UINT8   g_szLogFile[100] = {0};  // 带路径的日志文件名

// 日志级别定义
#define LOG_FATAL       0     // 严重错误
#define LOG_ERROR       1     // 一般错误
#define LOG_WARN        2     // 警告
#define LOG_INFO        3     // 一般信息
#define LOG_TRACE       4     // 跟踪信息
#define LOG_DEBUG       5     // 调试信息
#define LOG_ALL         6     // 所有信息

// 员工信息结构体定义
typedef struct
{
    UINT8  szEmployeeName[128];    // 员工姓名
    INT32  iEmployeeAge;           // 员工年龄
} T_EmployeeInfo;

// 函数声明
void WriteLogFile(UINT8 *pszFileName, UINT8 *pszFunctionName, UINT32 iCodeLine, UINT32 iLogLevel, UINT8 *pszContent);
UINT8 *LogLevel(UINT32 iLogLevel);
void GetTime(UINT8 *pszTimeStr);
void GetConfigValue();
void GetStringContentValue(FILE *fp, UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen);
void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8 *pszConfigFileName);
INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName);
void GetEmployeeInfo();
INT32 main();

/**********************************************************************
* 功能描述:主函数
* 输入参数:无
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期        版本号       修改人            修改内容
* -------------------------------------------------------------------
* 20150522        V1.0     Zhou Zhaoxiong        创建
***********************************************************************/
INT32 main()
{
    UINT8  szLogContent[1024] = {0};

     // 获取配置文件中各个配置项的值
    GetConfigValue();

    // 先打印版本相关信息
    snprintf(szLogContent, sizeof(szLogContent)-1, "Version [1.0], Build time[%s %s].", __DATE__, __TIME__);
    WRITELOGFILE(LOG_INFO, szLogContent);

    // 打印第一条日志
    snprintf(szLogContent, sizeof(szLogContent)-1, "The Fatal log info!");
    WRITELOGFILE(LOG_FATAL, szLogContent);

    // 打印第二条日志
    snprintf(szLogContent, sizeof(szLogContent)-1, "The Error log info!");
    WRITELOGFILE(LOG_ERROR, szLogContent);

    // 打印第三条日志
    snprintf(szLogContent, sizeof(szLogContent)-1, "The Warn log info!");
    WRITELOGFILE(LOG_WARN, szLogContent);

    // 打印第四条日志
    snprintf(szLogContent, sizeof(szLogContent)-1, "The Info log info!");
    WRITELOGFILE(LOG_INFO, szLogContent);

    // 打印第五条日志
    snprintf(szLogContent, sizeof(szLogContent)-1, "The Trace log info!");
    WRITELOGFILE(LOG_TRACE, szLogContent);

    // 打印第六条日志
    snprintf(szLogContent, sizeof(szLogContent)-1, "The Debug log info!");
    WRITELOGFILE(LOG_DEBUG, szLogContent);

    // 打印第七条日志
    snprintf(szLogContent, sizeof(szLogContent)-1, "The All log info!");
    WRITELOGFILE(LOG_ALL, szLogContent);

    GetEmployeeInfo();   // 获取并打印员工信息

    return 0;
}

/**********************************************************************
 * 功能描述: 获取并打印员工信息
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 * 修改日期        版本号         修改人         修改内容
 * -------------------------------------------------------------------
 * 20150522        V1.0      Zhou Zhaoxiong      创建
 ********************************************************************/
void GetEmployeeInfo()
{
    UINT8  szLogContent[1024] = {0};

    T_EmployeeInfo tEmployeeInfo = {0};

    // 获取并打印员工信息
    // 获取员工姓名
    GetConfigFileStringValue("EMPLOYEEINFO", "EmployeeName", "", tEmployeeInfo.szEmployeeName, sizeof(tEmployeeInfo.szEmployeeName), "Config.ini");

    // 获取员工年龄
    tEmployeeInfo.iEmployeeAge = GetConfigFileIntValue("EMPLOYEEINFO", "EmployeeAge", 0, "Config.ini");
    if (tEmployeeInfo.iEmployeeAge == -1)  // 判断获取到的年龄是否正确
    {
        snprintf(szLogContent, sizeof(szLogContent)-1, "GetEmployeeInfo: Get EmployeeAge failed!");
        WRITELOGFILE(LOG_ERROR, szLogContent);
        return;
    }

    // 打印读取到的员工姓名和年龄
    snprintf(szLogContent, sizeof(szLogContent)-1, "EmployeeName is %s, EmployeeAge is %d", tEmployeeInfo.szEmployeeName, tEmployeeInfo.iEmployeeAge);
    WRITELOGFILE(LOG_INFO, szLogContent);
}

/**********************************************************************
 * 功能描述: 将内容写到日志文件中
 * 输入参数: pszFileName-代码文件名
             pszFunctionName-代码所在函数名
             iCodeLine-代码行
             iLogLevel-日志等级
             pszContent-每条日志的具体内容
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 * 修改日期        版本号         修改人          修改内容
 * -------------------------------------------------------------------
 * 20150522        V1.0      Zhou Zhaoxiong      创建
 ********************************************************************/
void WriteLogFile(UINT8 *pszFileName, UINT8 *pszFunctionName, UINT32 iCodeLine, UINT32 iLogLevel, UINT8 *pszContent)
{
    FILE  *fp                 = NULL;
    UINT8  szLogContent[2048] = {0};
    UINT8  szTimeStr[128]     = {0};

    if (pszFileName == NULL || pszContent == NULL)
    {
        return;
    }

    // 过滤日志等级
    if (iLogLevel > g_iLogLevel)
    {
        return;
    }

    fp = fopen(g_szLogFile, "at+");      // 打开文件, 每次写入的时候在后面追加
    if (fp == NULL)
    {
        return;
    }

    // 写入日志时间
    GetTime(szTimeStr);
    fputs(szTimeStr, fp);

    // 写入日志内容
    if (g_iLogPosition == 1)    // 在日志信息中显示"文件名/函数名/代码行数"信息
    {
        snprintf(szLogContent, sizeof(szLogContent)-1, "[%s][%s][%04d][%s]%s\n", pszFileName, pszFunctionName, iCodeLine, LogLevel(iLogLevel), pszContent);
    }
    else    // 不用在日志信息中显示"文件名/代码行数"信息
    {
        snprintf(szLogContent, sizeof(szLogContent)-1, "[%s]%s\n", LogLevel(iLogLevel), pszContent);
    }
    fputs(szLogContent, fp);

    fflush(fp);     // 刷新文件
    fclose(fp);     // 关闭文件
    fp = NULL;      // 将文件指针置为空

    return;
}

/**********************************************************************
 * 功能描述: 获取对应的日志等级
 * 输入参数: iLogLevel-日志等级
 * 输出参数: 无
 * 返 回 值: 日志等级信息字符串
 * 其它说明: 无
 * 修改日期        版本号         修改人          修改内容
 * -------------------------------------------------------------------
 * 20150522        V1.0      Zhou Zhaoxiong      创建
 ********************************************************************/
UINT8 *LogLevel(UINT32 iLogLevel)
{
    switch (iLogLevel)
    {
        case LOG_FATAL:
        {
            return "FATAL";
        }

        case LOG_ERROR:
        {
            return "ERROR";
        }

        case LOG_WARN :
        {
            return "WARN";
        }

        case LOG_INFO :
        {
            return "INFO";
        }

        case LOG_TRACE:
        {
            return "TRACE";
        }

        case LOG_DEBUG:
        {
            return "DEBUG";
        }

        case LOG_ALL:
        {
            return "ALL";
        }

        default:
        {
            return "OTHER";
        }
    }
}

/**********************************************************************
 * 功能描述: 获取时间串
 * 输入参数: pszTimeStr-时间串
 * 输出参数: pszTimeStr-时间串
 * 返 回 值: 无
 * 其它说明: 时间串样式: YYYY.MM.DD HH:MIN:SS.Usec
 * 修改日期        版本号         修改人         修改内容
 * -------------------------------------------------------------------
 * 20150522        V1.0      Zhou Zhaoxiong      创建
 ********************************************************************/
void GetTime(UINT8 *pszTimeStr)
{
    struct tm      tSysTime     = {0};
    struct timeval tTimeVal     = {0};
    time_t         tCurrentTime = {0};

    UINT8  szUsec[20] = {0};    // 微秒
    UINT8  szMsec[20] = {0};    // 毫秒

    if (pszTimeStr == NULL)
    {
        return;
    }

    tCurrentTime = time(NULL);
    localtime_r(&tCurrentTime, &tSysTime);   // localtime_r是线程安全的

    gettimeofday(&tTimeVal, NULL);
    sprintf(szUsec, "%06d", tTimeVal.tv_usec);  // 获取微秒
    strncpy(szMsec, szUsec, 3);                // 微秒的前3位为毫秒(1毫秒=1000微秒)

    sprintf(pszTimeStr, "[%04d.%02d.%02d %02d:%02d:%02d.%3.3s]",
            tSysTime.tm_year+1900, tSysTime.tm_mon+1, tSysTime.tm_mday,
            tSysTime.tm_hour, tSysTime.tm_min, tSysTime.tm_sec, szMsec);
}

/**********************************************************************
* 功能描述: 获取配置文件完整路径(包含文件名)
* 输入参数: pszConfigFileName-配置文件名
            pszWholePath-配置文件完整路径(包含文件名)
* 输出参数: 无
* 返 回 值: 无
* 其它说明: 无
* 修改日期       版本号        修改人        修改内容
* ------------------------------------------------------------------
* 20150522       V1.0     Zhou Zhaoxiong     创建
********************************************************************/
void GetCompletePath(UINT8 *pszConfigFileName, UINT8 *pszWholePath)
{
    UINT8 *pszHomePath      = NULL;
    UINT8  szWholePath[256] = {0};

    // 先对输入参数进行异常判断
    if (pszConfigFileName == NULL || pszWholePath == NULL)
    {
        return;
    }

    pszHomePath = (UINT8 *)getenv("HOME");     // 获取当前用户所在的路径
    if (pszHomePath == NULL)
    {
        return;
    }

    // 拼装配置文件路径
    snprintf(szWholePath, sizeof(szWholePath)-1, "%s/zhouzx/test/etc/%s", pszHomePath, pszConfigFileName);

    strncpy(pszWholePath, szWholePath, strlen(szWholePath));
}

/**********************************************************************
 * 功能描述: 获取日志配置项的值
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 * 修改日期        版本号         修改人         修改内容
 * -------------------------------------------------------------------
 * 20150522        V1.0      Zhou Zhaoxiong      创建
 ********************************************************************/
void GetConfigValue()
{
    UINT8  szLogDir[256] = {0};

    // 日志等级
    g_iLogLevel = GetConfigFileIntValue("LOG", "LogLevel", 5, "Config.ini");

    // 日志位置
    g_iLogPosition = GetConfigFileIntValue("LOG", "LogPosition", 0, "Config.ini");

    // 日志文件存放目录
    GetConfigFileStringValue("LOG", "LogDir", "", szLogDir, sizeof(szLogDir), "Config.ini");
    snprintf(g_szLogFile, sizeof(g_szLogFile)-1, "%s/WriteLog.log", szLogDir);
}

/**********************************************************************
* 功能描述: 获取具体的字符串值
* 输入参数: fp-配置文件指针
            pszSectionName-段名, 如: GENERAL
            pszKeyName-配置项名, 如: EmployeeName
            iOutputLen-输出缓存长度
* 输出参数: pszOutput-输出缓存
* 返 回 值: 无
* 其它说明: 无
* 修改日期       版本号        修改人         修改内容
* ------------------------------------------------------------------
* 20150522       V1.0     Zhou Zhaoxiong     创建
********************************************************************/
void GetStringContentValue(FILE *fp, UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen)
{
    UINT8  szSectionName[100]    = {0};
    UINT8  szKeyName[100]        = {0};
    UINT8  szContentLine[256]    = {0};
    UINT8  szContentLineBak[256] = {0};
    UINT32 iContentLineLen       = 0;
    UINT32 iPositionFlag         = 0;

    // 先对输入参数进行异常判断
    if (fp == NULL || pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL)
    {
        printf("GetStringContentValue: input parameter(s) is NULL!\n");
        return;
    }

    sprintf(szSectionName, "[%s]", pszSectionName);
    strcpy(szKeyName, pszKeyName);

    while (feof(fp) == 0)
    {
        memset(szContentLine, 0x00, sizeof(szContentLine));
        fgets(szContentLine, sizeof(szContentLine), fp);      // 获取段名

        // 判断是否是注释行(以;开头的行就是注释行)或以其他特殊字符开头的行
        if (szContentLine[0] == ';' || szContentLine[0] == '\r' || szContentLine[0] == '\n' || szContentLine[0] == '\0')
        {
            continue;
        }

        // 匹配段名
        if (strncasecmp(szSectionName, szContentLine, strlen(szSectionName)) == 0)
        {
            while (feof(fp) == 0)
            {
                memset(szContentLine,    0x00, sizeof(szContentLine));
                memset(szContentLineBak, 0x00, sizeof(szContentLineBak));
                fgets(szContentLine, sizeof(szContentLine), fp);     // 获取字段值

                // 判断是否是注释行(以;开头的行就是注释行)
                if (szContentLine[0] == ';')
                {
                    continue;
                }

                memcpy(szContentLineBak, szContentLine, strlen(szContentLine));

                // 匹配配置项名
                if (strncasecmp(szKeyName, szContentLineBak, strlen(szKeyName)) == 0)
                {
                    iContentLineLen = strlen(szContentLine);
                    for (iPositionFlag = strlen(szKeyName); iPositionFlag <= iContentLineLen; iPositionFlag ++)
                    {
                        if (szContentLine[iPositionFlag] == ' ')
                        {
                            continue;
                        }
                        if (szContentLine[iPositionFlag] == '=')
                        {
                            break;
                        }

                        iPositionFlag = iContentLineLen + 1;
                        break;
                    }

                    iPositionFlag = iPositionFlag + 1;    // 跳过=的位置

                    if (iPositionFlag > iContentLineLen)
                    {
                        continue;
                    }

                    memset(szContentLine, 0x00, sizeof(szContentLine));
                    strcpy(szContentLine, szContentLineBak + iPositionFlag);

                    // 去掉内容中的无关字符
                    for (iPositionFlag = 0; iPositionFlag < strlen(szContentLine); iPositionFlag ++)
                    {
                        if (szContentLine[iPositionFlag] == '\r' || szContentLine[iPositionFlag] == '\n' || szContentLine[iPositionFlag] == '\0')
                        {
                            szContentLine[iPositionFlag] = '\0';
                            break;
                        }
                    }

                    // 将配置项内容拷贝到输出缓存中
                    strncpy(pszOutput, szContentLine, iOutputLen-1);
                    break;
                }
                else if (szContentLine[0] == '[')
                {
                    break;
                }
            }
            break;
        }
    }
}

/**********************************************************************
* 功能描述: 从配置文件中获取字符串
* 输入参数: pszSectionName-段名, 如: GENERAL
            pszKeyName-配置项名, 如: EmployeeName
            pDefaultVal-默认值
            iOutputLen-输出缓存长度
            pszConfigFileName-配置文件名
* 输出参数: pszOutput-输出缓存
* 返 回 值: 无
* 其它说明: 无
* 修改日期       版本号        修改人         修改内容
* ------------------------------------------------------------------
* 20150522       V1.0     Zhou Zhaoxiong     创建
********************************************************************/
void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8 *pszConfigFileName)
{
    FILE  *fp                    = NULL;
    UINT8  szWholePath[256]      = {0};

    // 先对输入参数进行异常判断
    if (pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL || pszConfigFileName == NULL)
    {
        printf("GetConfigFileStringValue: input parameter(s) is NULL!\n");
        return;
    }

    // 获取默认值
    if (pDefaultVal == NULL)
    {
        strcpy(pszOutput, "");
    }
    else
    {
        strcpy(pszOutput, pDefaultVal);
    }

    // 打开配置文件
    GetCompletePath(pszConfigFileName, szWholePath);
    fp = fopen(szWholePath, "r");
    if (fp == NULL)
    {
        printf("GetConfigFileStringValue: open %s failed!\n", szWholePath);
        return;
    }

    // 调用函数用于获取具体配置项的值
    GetStringContentValue(fp, pszSectionName, pszKeyName, pszOutput, iOutputLen);

    // 关闭文件
    fclose(fp);
    fp = NULL;
}

/**********************************************************************
* 功能描述: 从配置文件中获取整型变量
* 输入参数: pszSectionName-段名, 如: GENERAL
            pszKeyName-配置项名, 如: EmployeeName
            iDefaultVal-默认值
            pszConfigFileName-配置文件名
* 输出参数: 无
* 返 回 值: iGetValue-获取到的整数值   -1-获取失败
* 其它说明: 无
* 修改日期       版本号       修改人         修改内容
* ------------------------------------------------------------------
* 20150522       V1.0     Zhou Zhaoxiong     创建
********************************************************************/
INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName)
{
    UINT8  szGetValue[512] = {0};
    INT32  iGetValue       = 0;

    // 先对输入参数进行异常判断
    if (pszSectionName == NULL || pszKeyName == NULL || pszConfigFileName == NULL)
    {
        printf("GetConfigFileIntValue: input parameter(s) is NULL!\n");
        return -1;
    }

    GetConfigFileStringValue(pszSectionName, pszKeyName, NULL, szGetValue, 512-1, pszConfigFileName);    // 先将获取的值存放在字符型缓存中

    if (szGetValue[0] == '\0' || szGetValue[0] == ';')    // 如果是结束符或分号, 则使用默认值
    {
        iGetValue = iDefaultVal;
    }
    else
    {
        iGetValue = atoi(szGetValue);
    }

    return iGetValue;
}

Linux下一个简单的日志系统的设计及其C代码实现的更多相关文章

  1. Linux下一个简单守护进程的实现 (Daemon)

    在Linux/UNIX系统引导的时候会开启很多服务,这些服务称为守护进程(也叫Daemon进程).守护进程是脱离于控制终端并且在后台周期性地执行某种任务或等待处理某些事件的进程,脱离终端是为了避免进程 ...

  2. Linux下一个简单sniffer的实现

    Sniffer(嗅探器)是一种基于被动侦听原理的网络分析方式.将网络接口设置在监听模式,便可以将网上传输的源源不断的信息截获.对于网络监听的基本原理我们不在赘述,我们也不开启网卡的混杂模式,因为现在的 ...

  3. 利用生产者消费者模型和MQ模型写一个自己的日志系统-并发设计里一定会用到的手段

    一:前言 写这个程序主要是用来理解生产者消费者模型,以及通过这个Demo来理解Redis的单线程取原子任务是怎么实现的和巩固一下并发相关的知识:这个虽然是个Demo,但是只要稍加改下Appender部 ...

  4. Linux 下一个很棒的命令行工具

    导读 Taskwarrior 是 Ubuntu/Linux 下一个简单而直接的基于命令行的 TODO 工具.这个开源软件是我曾用过的最简单的基于命令行的工具之一.Taskwarrior 可以帮助你更好 ...

  5. linux下一个oracle11G DG建立(一个):准备环境

    linux下一个oracle11G  DG建立(一个):准备环境 周围环境 名称 主库 备库 主机名 bjsrv shsrv 软件版本号 RedHat Enterprise5.5.Oracle 11g ...

  6. [转帖] Linux 创建一个简单的私有CA、发证、吊销证书

    原创帖子地址:   https://blog.csdn.net/mr_rsq/article/details/71001810 Linux 创建一个简单的私有CA.发证.吊销证书 2017年04月30 ...

  7. 一个简单的CS系统打包过程图文版

    一个简单的CS系统打包过程图文版 1.     打包内容 1.1.  此次打包的要求和特点 主工程是一个CS系统: 此CS系统运行的先决条件是要有.Net Framework 3.5: 主工程安装完成 ...

  8. Memcahce(MC)系列(两)Linux下一个Memcache安装

    Linux下一个memcache安装 memcache是高性能.分布式的内存对象缓存系统,用于在动态应用中降低数据库负载.提升訪问速度.眼下用memcache解决互联网上的大用户读取是很流行的一种使用 ...

  9. 14行脚本配置Linux下一个Java环境变量

    供Java人们刚开始学习.多半Java它需要花费大量的精力在开发环境的配置,于Linux下一个,构造Java环境变量,很可能加入这一努力. 为此,我做了一个bash脚本来配置自己主动Java环境变量. ...

随机推荐

  1. NOI2007 生成树计数

    题目 首先我要吐槽,这题目就是坑,给那么多无用的信息,我还以为要根据提示才能做出来呢! 算法1 暴力,傻傻地跟着提示,纯暴力\(40\)分,高斯消元\(60\)分. 算法2 DP!一个显然的东西是,这 ...

  2. Win32 Windows编程 十二

    一.对话框 1.对话框的分类 2.对话框的基本使用方式 3.对话框资源 4.有模式对话框的使用 5. 无模式对话框的使用 5.1 加入对话框资源 5.2 定义窗体处理函数 BOOL CALLBACK ...

  3. UVA 1524 - Hot or Cold?(数学)

    UVA 1524 - Hot or Cold? 题目链接 题意:给一个一元n次方程,带入x表示时间,f(x)表示温度,如今要求[s, e]的平均温度 思路:平均温度就是 总温度/ (e - s),画出 ...

  4. Microsoft Visual C++运行库合集下载(静默安装)

    Microsoft Visual C++运行库合集下载 CN启示录2013-06-02上传   Microsoft Visual C++运行库合集由国外网友McRip制作,包含了VC2005.VC20 ...

  5. Qt生成灰度图(转载)

    Qt生成灰度图(转载)   项目中用到大量基础图像处理知识,其中灰度图的生成是很重要的一环. 先补充一些基础知识: ------------------------------------------ ...

  6. JAVA 操作 DBF 文件数据库

    1.依赖夹包 javadbf-[].4.1.jar jconn3.jar 2.添加属性文件 jdbc.properties jdbc.driverClassName=com.sybase.jdbc3. ...

  7. javascript 变量转义

    $(this).append('<a href="2-1partner.html"><div><img width="645" h ...

  8. 修改XPMenu让ToolButton在Down=True时正确显示

    XPMenu是一个不错的程序界面效果控件,但它也存在不少不足之处.我最近又对它作了一点修改. 原因是我在程序里有一个ToolButton,其Style=tbsButton,当Down=True时,XP ...

  9. UVA 839 (13.08.20)

     Not so Mobile  Before being an ubiquous communications gadget, a mobile wasjust a structure made of ...

  10. IT忍者神龟之中的一个句sql语句——连接同一字段的全部值

    Oracle能够用SYS_CONNECT_BY_PATH字符串聚合函数: SELECT LTRIM(MAX(SYS_CONNECT_BY_PATH(productname, ', ')), ', ') ...