日志与可变参数

 在c语言中,我们常用的printf()函数就使用到了可变参数。那他是什么呢?

 #include

       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
       int sprintf(char *str, const char *format, ...);
       int snprintf(char *str, size_t size, const char *format, ...);

 可以看到printf函数家族的最后一个参数都是“...”。那这个“...”参数是什么意思呢?

看一下我们平时使用printf函数的格式

const char* str="hello world";
int a=1;
printf("%s %d\n", str,a);

const char *format参数就是前面的“%s %d\n”这样的字符串,...参数就是可变参数也就是说你可以指定任意参数任意类型。

这意味着printf函数与其他函数都有所不同,普通函数都指明了参数个数以及参数类型,但是printf函数不同,他会根据你输入的参数个数以及参数类型打印出结果。那么他是如何做到的呢?


1.printf函数会根据format中的%s或者%d,%x%p这样的字符来判断后面的参数是什么类型,从而准确打印。

2. printf函数会使用 va_list p指明一个参数指针p,va_arg(p,int)根据类型提取参数,va_start(p,format)让p指向可变参数部分的起始地址,va_end(p)这样的函数来把参数指针置空,这样就保证了可变参数的完全打印,不会漏掉某个参数; 
 

简单日志demo

主要是为了理解日志的思想,利用了可变参数,使得程序的测试结果标准化,可视化,简易化,减轻了程序员负担。

下面的日志格式: 日志等级 时间 进程pid 消息体。

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 日志是有日志等级的

const std::string filename = "log/tcpserver.log";

enum
{
    Debug = 0,
    Info,
    Warning,
    Error,
    Fatal,
    Uknown
};

static std::string toLevelString(int level)
{
    switch (level)
    {
    case Debug:
        return "Debug";
    case Info:
        return "Info";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    case Fatal:
        return "Fatal";
    default:
        return "Uknown";
    }
}

static std::string getTime()
{
    time_t curr = time(nullptr);
    struct tm *tmp = localtime(&curr);
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d", tmp->tm_year + 1900, tmp->tm_mon+1, tmp->tm_mday,
             tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
    return buffer;
}

// 日志格式: 日志等级 时间 pid 消息体
// logMessage(DEBUG, "hello: %d, %s", 12, s.c_str()); // DEBUG hello:12, world
void logMessage(int level, const char *format, ...)
{
    char logLeft[1024];
    std::string level_string = toLevelString(level);
    std::string curr_time = getTime();
    snprintf(logLeft, sizeof(logLeft), "[%s] [%s] [%d] ", level_string.c_str(), curr_time.c_str(), getpid());

    char logRight[1024];
    va_list p;
    va_start(p, format);
    vsnprintf(logRight, sizeof(logRight), format, p);
    va_end(p);

    // 打印
    // printf("%s%s\n", logLeft, logRight);

    // 保存到文件中
    FILE *fp = fopen(filename.c_str(), "a");
    if(fp == nullptr)return;
    fprintf(fp,"%s%s\n", logLeft, logRight);
    fflush(fp); //可写也可以不写
    fclose(fp);


    // 预备
    //  va_list p; // char *
    //  int a = va_arg(p, int);  // 根据类型提取参数
    //  va_start(p, format); //p指向可变参数部分的起始地址
    //  va_end(p); // p = NULL;
}

你可能感兴趣的:(windows,服务器,linux)