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

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=

其中,“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

则生成的日志文件“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

对照配置文件和日志文件,我们可以看到,“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

则生成的日志文件“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

对照配置文件和日志文件,我们可以看到,“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;
}


本人微信公众号:zhouzxi,请扫描以下二维码:

时间: 2024-11-01 09:20:00

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

Windows下一个简单的日志系统的C代码实现

       问题描述        实现一个简单的基于Windows的日志系统,要求写入日志文件的内容的样式如下:        [时间]+[文件名]+[日志等级]+日志具体内容        如:        [2015.02.2514:35:13.143][WriteLog.c][INFO]This is a test!        其中,"2015.02.25 14:35:13.143"为当前时间(精确到毫秒),"WriteLog.c"为所打印的日志内容

Linux下一个简单的多线程互斥锁的例子

本篇文章是对Linux下一个简单的多线程互斥锁的例子进行了分析介绍,需要的朋友可以参考下   复制代码 代码如下: #include <stdio.h> #include <pthread.h> pthread_mutex_t Device_mutex ; int count=0; void thread_func1() {    while(1)    {        pthread_mutex_lock(&Device_mutex);        printf(&q

架设linux下最简单的VPN系统

本文是根据我公司的实际应用情况写的, 但是稍加修改即可应用到很多地方,http://www.aliyun.com/zixun/aggregation/32593.html">系统运行的两个月来,证明还是安全可靠 稳定的,呵... 我公司在北京,但是主要设备都在南京电信的一个主要IDC里,那边有我们的两个PIX525UR(做了故障切换),上面做了严格的访问控制,因此,为了方便公司里的移动,出差及在家的员工办公,才有了做VPN系统的想法.好使具有相应权限的使用者从个人PC通过支持MPPE128

浅析Linux下一个简单的多线程互斥锁的例子_unix linux

复制代码 代码如下: #include <stdio.h>#include <pthread.h>pthread_mutex_t Device_mutex ;int count=0;void thread_func1(){   while(1)   {       pthread_mutex_lock(&Device_mutex);       printf("thread1: %d\n",count);       pthread_mutex_unlo

构建一个简单的CaaS系统_docker

在CaaS系统出现前企业应用架构基本被IaaS/SaaS/PaaS等模式垄断,直到Docker的出现为我们打开了另一个扇大门,废话不说了,我们直奔主题. 我们先了解下一个简单的CaaS系统是如何为用户提供服务的: 企业用户上传它的应用代码或其他代码托管方式,我们生成用户应用的镜像,或者用户直接上传镜像,或者用户直接使用我们提供的基础服务镜像 用户部署他的镜像应用,启动它的镜像容器 用户访问他的应用服务 OK,需求确定了,该搬砖了. 用户镜像制作 既然是一个简单的CaaS系统,我们就不让用户上传代

Taskwarrior:Linux下一个很棒的命令行TODO工具

Taskwarrior 是 Ubuntu/Linux 下一个简单而直接的基于命令行的 TODO 工具.这个开源软件是我曾用过的最简单的基于命令行的工具之一.Taskwarrior 可以帮助你更好地组织你自己,而不用安装笨重的新工具--这有时丧失了 TODO 工具的目的. Taskwarrior:一个基于简单的基于命令行帮助完成任务的TODO工具 Taskwarrior是一个开源.跨平台.基于命令行的 TODO 工具,它帮你在终端中管理你的 to-do 列表.这个工具让你可以轻松地添加任务.展示列

如何构建一个简单的CAAS系统

在CAAS系统出现前企业应用架构基本被IAAS/SAAS/PAAS等模式垄断,直到docker的出现为我们打开了另一个扇大门,废话不说了,我们直奔主题 我们先了解下一个简单的CAAS系统是如何为用户提供服务的 企业用户上传它的应用代码或其他代码托管方式,我们生成用户应用的镜像,或者用户直接上传镜像,或者用户直接使用我们提供的基础服务镜像 用户部署他的镜像应用,启动它的镜像容器 用户访问他的应用服务 OK,需求确定了,该搬砖了. 1. 用户镜像制作 既然是一个简单的CAAS系统,我们就不让用户上传

lnav:Linux下一个基于控制台的高级日志文件查看器

服务器日志是一个由服务器创建并经常更新.用于抓取特定服务和应用的所有活动信息的日志文件.当你的应用或者服务出现问题时这个文件就会非常有用.从日志文件中你可以获取所有关于该问题的信息,例如基于警告或者错误信息它什么时候开始表现不正常. LNAV(Log file Navigator)是 Linux 下一个基于控制台的高级日志文件查看器.它和其它文件查看器,例如 cat.more.tail 等,完成相同的任务,但有很多普通文件查看器没有的增强功能(尤其是它自带多种颜色和易于阅读的格式). 它能在解压

lnav:Linux 下一个基于控制台的高级日志文件查看器

服务器日志是一个由服务器创建并经常更新.用于抓取特定服务和应用的所有活动信息的日志文件.当你的应用或者服务出现问题时这个文件就会非常有用.从日志文件中你可以获取所有关于该问题的信息,例如基于警告或者错误信息它什么时候开始表现不正常. LNAV(Log file Navigator)是 Linux 下一个基于控制台的高级日志文件查看器.它和其它文件查看器,例如 cat.more.tail 等,完成相同的任务,但有很多普通文件查看器没有的增强功能(尤其是它自带多种颜色和易于阅读的格式). 它能在解压