Linux下一个简单的日志系统的设计及其C代码实现

时间:2021-06-09 17:01:20

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

2.日志系统的框架结构
一个完整的日志系统包括三大部分:配置文件、软件程序和日志文件,它们之间的关系如图1所示。
Linux下一个简单的日志系统的设计及其C代码实现
图1 一个完整的日志系统的框架结构

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

3.生成日志文件的程序流程
基于日志系统的框架结构,生成日志文件的程序流程如图2所示。
Linux下一个简单的日志系统的设计及其C代码实现
图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)一文。
Linux下一个简单的日志系统的设计及其C代码实现
图3 配置文件读取操作程序流程

(2) 向日志文件中写入日志信息
该操作的流程如图4所示。
Linux下一个简单的日志系统的设计及其C代码实现
图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所示。
Linux下一个简单的日志系统的设计及其C代码实现
图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;
}