使用命名管道的通信程序, 加入了日志系统

发布于:2024-07-28 ⋅ 阅读:(134) ⋅ 点赞:(0)

日志系统

// log.hpp
#pragma once
#include <time.h>
#include <iostream>
#include <stdio.h>
#include <string>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdlib>

using std::cout;
using std::endl;

const int MAX_LOG_SIZE = 1024;
// 打开的文件名
const char* LOG_FILE_NAME = "log.txt";

// 日志等级
enum LEVEL
{
    INFO = 1,
    WARNING,
    ERROR,
    FATAL,
    DEBUG
};

// 打印方式
enum PRINT_METHOD
{
    SCREEN = 1, 
    ONE_FILE,
    MULTIPLE_FILE
};

class Log
{
public:
    Log(PRINT_METHOD printMethod = SCREEN) 
    : _printMethod(printMethod), _path("./log/"){}

    std::string LevelToString(LEVEL level)
    {
        switch (level)
        {
        case INFO:
            return "INFO";
            break;
        case WARNING:
            return "WARNING";
            break;
        case ERROR:
            return "ERROR";
            break;
        case FATAL:
            return "FATAL";
            break;
        case DEBUG:
            return "DEBUG";
            break;
        default:
            return "NONE";
            break;
        }
    }

    // 更改打印方式
    void ChangePrintMethod(PRINT_METHOD printMethod)
    {
        _printMethod = printMethod;
    }

    /*需要将字符串处理为: 默认部分+自定义部分*/
    void LoadMessage(LEVEL level, const char *format, ...)
    {
        // 默认部分, 时间
        char leftBuffer[MAX_LOG_SIZE] = {0};
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&(t));
        snprintf(leftBuffer, sizeof(leftBuffer), "[%s][%d-%d-%d %d:%d:%d]",
                 LevelToString(level).c_str(), ctime->tm_year + 1900,
                 ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour,
                 ctime->tm_min, ctime->tm_sec, LevelToString(level).c_str());

        // 自定义部分, 用户输入
        char rightBuffer[MAX_LOG_SIZE] = {0};
        // 因为定义了可变参数, 所以需要处理一下
        va_list args;
        va_start(args, format);
        vsnprintf(rightBuffer, sizeof(rightBuffer), format, args);
        va_end(args);

        // 将两者加到一起
        char logTxt[MAX_LOG_SIZE * 2] = {0};
        snprintf(logTxt, sizeof(logTxt), "%s %s\n", leftBuffer, rightBuffer);

        // cout << logTxt;
        PrintLog(level, logTxt);
    }

    // 通过不同的输出方式, 将logTxt打印到不同的地方
    void PrintLog(LEVEL level, const std::string& logTxt)
    {
        switch (_printMethod)
        {
        case SCREEN:
            cout << logTxt; 
            break;
        case ONE_FILE:
            PrintOneFile(LOG_FILE_NAME, logTxt);
            break;
        case MULTIPLE_FILE:
            PrintMultipleFile(level, logTxt);
            break;
        default:
            break;
        }
    }

    // 向一个文件中写
    void PrintOneFile(const std::string& fileName, const std::string& logTxt)
    {
        std::string logName = _path + fileName;     // 将日志文件写到log文件夹下
        int fd = open(logName.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0666);
        if(fd == -1)    return;
        write(fd, logTxt.c_str(), logTxt.size());
        close(fd);
    }

    // 向多个文件中写
    void PrintMultipleFile(LEVEL level, const std::string& logTxt)
    {
        // 新的文件名: level+LOG_FILE_NAME
        std::string fileName = LevelToString(level) + LOG_FILE_NAME;
        PrintOneFile(fileName, logTxt);
    }

    /* 
    重载(), 调用更方便一点, 可变参数不允许二次传参, 所以代码与前面的LoadMessage()重复 
    现在若想要打日志, 可以有两种方法
    首先先定义对象 Log log
    1. log.LoadMessage(level, const char *format, ...)
    2. log(level, const char *format, ...)
    */
    void operator()(LEVEL level, const char *format, ...)
    {
        // 默认部分, 时间
        char leftBuffer[MAX_LOG_SIZE] = {0};
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&(t));
        snprintf(leftBuffer, sizeof(leftBuffer), "[%s][%d-%d-%d %d:%d:%d]",
                 LevelToString(level).c_str(), ctime->tm_year + 1900,
                 ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour,
                 ctime->tm_min, ctime->tm_sec, LevelToString(level).c_str());

        // 自定义部分, 用户输入
        char rightBuffer[MAX_LOG_SIZE] = {0};
        // 因为定义了可变参数, 所以需要处理一下
        va_list args;
        va_start(args, format);
        vsnprintf(rightBuffer, sizeof(rightBuffer), format, args);
        va_end(args);

        // 将两者加到一起
        char logTxt[MAX_LOG_SIZE * 2] = {0};
        snprintf(logTxt, sizeof(logTxt), "%s %s\n", leftBuffer, rightBuffer);

        // cout << logTxt;
        PrintLog(level, logTxt);
    }
private:
    PRINT_METHOD _printMethod;
    std::string _path;
};

通信程序

// server.cc
#include "common.hpp"
#include "log.hpp"

int main()
{
    Init init;
    Log log(MULTIPLE_FILE);
    // 打开信道
    int fd = open(FIFO_FILE, O_RDONLY);
    if(fd == -1) {
        // perror("open");
        log(FATAL, "open file error, error string: %s, error code: %d", strerror(errno), errno);
        exit(FIFO_OPEN_ERR);
    }
    // cout << "server open file done" << endl;
    // 测试日志功能
    log(INFO, "server open file done");
    log(FATAL, "server open file done");
    log(WARNING, "server open file done");
    log(DEBUG, "server open file done");
    // 开始通信, 从管道中读数据
    while(true) {
        char buffer[N] = {0};
        int x = read(fd, buffer, sizeof(buffer));
        if(x > 0) {
            buffer[x] = 0;
            cout << "client say@ " << buffer << endl;
        }
        else if(x == 0) {
            // cout << "client quit, me too!" << endl;
            log(INFO, "client quit, me too!");
            break;
        }
        else {
            // perror("read");
            log(FATAL, "read file error, error string: %s, error code: %d", strerror(errno), errno);
            exit(FIFO_READ_ERR);
        }
    }
    return 0;
}
// client.cc
#include "common.hpp"

int main()
{
    // 打开信道
    int fd = open(FIFO_FILE, O_WRONLY);
    if(fd == -1) {
        perror("open");
        exit(FIFO_OPEN_ERR);
    }
    cout << "client open file done" << endl;
    // 开始通信, 向管道中写数据
    std::string line;
    while(true) {
        cout << "Please enter@ ";
        getline(std::cin, line);
        write(fd, line.c_str(), line.size());
    }
    return 0;
}
// common.hpp
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include <cstring>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <fcntl.h>

using std::cout;
using std::endl;

#define FIFO_FILE "./myfifo"
#define MODE 0666

const int N = 1024;

enum {
    FIFO_CREAT_ERR = 1,
    FIFO_DEL_ERR,
    FIFO_OPEN_ERR,
    FIFO_READ_ERR
};

// 用于处理server.cc部分代码的初始化和析构处理
struct Init
{
    Init()
    {
        // 创建信道
        int n = mkfifo(FIFO_FILE, MODE);
        if (n == -1) {
            perror("mkfifo");
            exit(FIFO_CREAT_ERR);
        }
    }
    ~Init()
    {
        // 删除信道
        int m = unlink(FIFO_FILE);
        if (m == -1) {
            perror("unlink");
            exit(FIFO_DEL_ERR);
        }
    }
};
.PHONY:all
all : server client

server : server.cc
	g++ -o $@ $^ -std=c++11
client : client.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -rf server client

运行效果

多文件
打印到屏幕上
单文件


网站公告

今日签到

点亮在社区的每一天
去签到