文章目录
- 前言
- 一、 目录实现(log.hpp)
- 二、目录的具体使用
- 1.comm.hpp(管道初始化)
- 2.sever.cpp(为读端且令其创建命名管道)
- 3.client.cpp(为写端)
 
前言

我们这个设计的日志可以自定以输出的方向,可以向显示器打印错误信息,也可以向指定目录写入,或者是分类写入
一、 目录实现(log.hpp)
#pragma once
#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#define SIZE 1024
//对错误等级进行分级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
//将错误信息打入到什么位置
#define Screen 1
#define Onefile 2
#define Classfile 3
//存储错误信息的文件默认名字
#define LogFile "log.txt"
using namespace std;
class Log{
public:
Log(){
    //默认打印方式往显示屏
    //规定好默认路径
    printMethod=Screen;
    path="./log/";
}
//自己改变默认答应方式
void Enable(int method){
    printMethod=method;
}
//将错误等级转换为字符串的形式
string levelTostring(int level){
    switch(level){
        case Info:
            return "Info";
        case Debug:
            return "Debuf";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
    }
}
//根据传出的信息,生成字符串logtxt,这个字符串中就是错误信息
void operator()(int level, const char *format, ...)
    {
        //报错信息 格式:默认部分+自定义部分
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        char leftbuffer[SIZE];
        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);
        va_list s;
        va_start(s, format);
        //leftbuffer为获取的报错时间字符串,为默认部分
        //rightbuffer为我们的自定义部分
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);
         
         //将leftbuffer 与rightbuffer合并成字符串logtxt
         //这就是我们的报错的错误信息
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);
        
        //将错误信息按指定方式打印到指定地方
        printLog(level, logtxt);
    }
//将错误信息按指定方式打印到指定地方
void printLog(int level,const string &logtxt){
    switch(printMethod){
        case Screen://打印到屏幕
            cout<<logtxt<<endl;
        case Onefile://打印到一个文件
            printOnefile(LogFile,logtxt);
            break;
        case Classfile://分错误信息打印到不同文件
            printClassfile(level,logtxt);
            break;
        default :
            break;
    }
}
void printOnefile(const string logname,const string logtxt){
    // printOnefile(LogFile,logtxt);
    string _logname=path+logname;  // ./log/log.txt
    int fd=open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
    if(fd<0)
        return;
    write(fd,logtxt.c_str(),logtxt.size());
    close(fd);
}
void printClassfile(int level,const string logtxt){
    // printClassfile(level,logtxt);
    string filename=LogFile;
    filename+=".";
    filename+=levelTostring(level);
    //  log.txt. (string)level
    //设置好文件名字后,再用单文件的打印方式写入文件
    printOnefile(filename,logtxt);
}
private:
int printMethod;//打印方法
string path;//打印路径
};
二、目录的具体使用
这里我们以命名管道之间两个进程传递信息为场景使用这个日志功能
1.comm.hpp(管道初始化)
 #pragma once
 #include<iostream>
 #include<string>
 #include<sys/types.h>
 #include<sys/stat.h>
#include<unistd.h>
#include<cerrno>
#include<string.h>
#include<fcntl.h>
//命名管道的名字与路径
 #define FIFO_FILE "./myfifo"
 #define MODE 0664
//返回的错误码
 enum{
    FIFO_CREAT_ERR=1,
    FIFO_DELETE_ERR,
    FIFO_OPEN_ERR
 };
//采用Init类,类似智能指针,我们在析构函数中调用删除命名管道的函数
//程序结束会自动调用析构函数
class Init{
public:
    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_DELETE_ERR);
        }
    }
};
2.sever.cpp(为读端且令其创建命名管道)
#include "comm.hpp"
#include "log.hpp"
using namespace std;
// 管理管道文件
int main()
{
    Init init;//创建命名管道
    Log log;
    
    log.Enable(Onefile);//重定向错误信息打入位置
    // 打开管道
    int fd = open(FIFO_FILE, O_RDONLY);  
    if (fd < 0)
    {
        //如果有错就打印错误信息
        log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);
        exit(FIFO_OPEN_ERR);
    }
    log(Info, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
    log(Warning, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
    log(Fatal, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
    log(Debug, "server open file done, error string: %s, error code: %d", strerror(errno), errno);
    // 开始通信
    while (true)
    {
        char buffer[1024] = {0};
        int x = read(fd, buffer, sizeof(buffer));
        if (x > 0)
        {
            buffer[x] = 0;
            cout << "client say# " << buffer << endl;
        }
        else if (x == 0)
        {
            log(Debug, "client quit, me too!, error string: %s, error code: %d", strerror(errno), errno);
            break;
        }
        else
            break;
    }
    close(fd);
    return 0;
}
3.client.cpp(为写端)
 #include <iostream>
#include "comm.hpp"
using namespace std;
int main()
{
    int fd = open(FIFO_FILE, O_WRONLY);//打开管道
    if(fd < 0)
    {
        perror("open");
        exit(FIFO_OPEN_ERR);
    }
    cout << "client open file done" << endl;
    string line;
    while(true)
    {
        cout << "Please Enter@ ";
        getline(cin, line);//因为可能有空格,cin不读入空格
        write(fd, line.c_str(), line.size());
    }
    close(fd);
    return 0;
}



















