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

news2024/11/20 8:29:24

文章目录

  • 日志系统
  • 通信程序
  • 运行效果

日志系统

// 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

运行效果

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1954863.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软设之数据库关系代数

数据库关系代数 基本概念 元祖行&#xff1a;水平方向上每一行为一条记录&#xff0c;这个记录对应1个实体。一般称为元祖&#xff0c;元祖行或者记录 属性列&#xff1a;垂直方向上每一列为一个属性&#xff0c;一般称为属性列&#xff0c;字段等。关系表达式中可以用列序号…

又要起飞,浏览器居然都可以本地 OCR 啦

前言 PaddleOCR&#xff0c;这是一个由百度开发的开源 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;工具&#xff0c;它可以用于从图像中识别文本。 PaddleOCR支持多种语言的文本识别&#xff0c;并且能够处理多种场景下的图像。 现在…

【Web开发手礼】探索Web开发的魅力(十二)-Vue(2)用户动态页面

前言 主要介绍了用vue框架创建用户动态页面的具体过程&#xff0c;可以帮助学习vue框架的基本知识&#xff01;&#xff01;&#xff01;&#xff01; 用户动态页面 用户信息 用户头像 通过 Bootstrap 所提供的 .img-fluid 类让图片支持响应式布局。其原理是将 max-width: 10…

Java面试八股之Spring boot的自动配置原理

Spring boot的自动配置原理 Spring Boot 的自动配置原理是其最吸引人的特性之一&#xff0c;它大大简化了基于 Spring 框架的应用程序开发。以下是 Spring Boot 自动配置的基本原理和工作流程&#xff1a; 1. 启动类上的注解 Spring Boot 应用通常会在主类上使用 SpringBoot…

ZBrush入门使用介绍——4、笔刷选项说明

大家好&#xff0c;我是阿赵。   这次来看看ZBrush的笔刷的选项用法。 一、选择笔刷 点击笔刷&#xff0c;可以打开笔刷选择面板。 在最上面的Quick Pick&#xff0c;有最近使用过的笔刷&#xff0c;可以快速的选择。下面有很多可以选择的笔刷。但由于笔刷太多&#xff0c;…

AJAX之基础知识

目录 AJAX入门及axios使用什么是AJAX怎么用AJAX 认识URL协议域名资源路径URL查询参数 查询参数URL查询参数axios查询参数 常用请求方法axios请求配置 axios错误处理HTTP协议请求报文请求报文-错误排查响应报文HTTP响应状态码 form-serialize插件 AJAX入门及axios使用 什么是AJ…

【Python机器学习】决策树的构造——信息增益

决策树是最经常使用的数据挖掘算法。它之所以如此流行&#xff0c;一个很重要的原因就是不需要了解机器学习的知识&#xff0c;就能搞明白决策树是如何工作的。 决策树的优缺点&#xff1a; 优点&#xff1a;计算复杂度不高&#xff0c;输出结果易于理解&#xff0c;对中间值的…

RabbitMq手动ack的超简单案例+Confirm和Return机制的配置和使用

最简单的例子 先简单介绍一下这三个方法 basicAck 表示确认成功&#xff0c;使用此方法后&#xff0c;消息会被rabbitmq broker删除 basicNack 表示失败确认&#xff0c;一般在消费消息业务异常时用到此方法&#xff0c;可以将消息重新投递入队列 basicReject 拒绝消息&am…

Chainlit一个快速构建成式AI应用的Python框架,无缝集成与多平台部署

概述 Chainlit 是一个开源 Python 包&#xff0c;用于构建和部署生成式 AI 应用的开源框架。它提供了一种简单的方法来创建交互式的用户界面&#xff0c;这些界面可以与 LLM&#xff08;大型语言模型&#xff09;驱动的应用程序进行通信。Chainlit 旨在帮助开发者快速构建基于…

全网最适合入门的面向对象编程教程:25 类和对象的 Python 实现-Python 判断输入数据类型

全网最适合入门的面向对象编程教程&#xff1a;25 类和对象的 Python 实现-Python 判断输入数据类型 摘要&#xff1a; 本文主要介绍了在使用 Python 面向对象编程时&#xff0c;如何使用 type 函数、isinstance 函数和正则表达式三种方法判断用户输入数据类型&#xff0c;并对…

PWA(渐进式网页应用)方式实现TodoList桌面应用

参考&#xff1a; https://cloud.tencent.com/developer/article/2322236 todlist网页参考&#xff1a; https://blog.csdn.net/weixin_42357472/article/details/140657576 实现在线网页当成app应用&#xff1a; 一个 PWA 应用首先是一个网页, 是通过 Web 技术编写出的一个网…

如何全面提升架构设计的质量?

当我们从可扩展、高可用、高性能等角度设计出来架构的时候&#xff0c;我们如何优化架构呢&#xff1f;就需要从成本、安全、测试等角度进行优化。 如何设计更好的架构 - 步骤 成本 低成本复杂度本质 低成本手段和应用 低成本的主要应用场景 安全 安全性复杂度本质 架构安全…

大语言模型系列-Transformer:深入探索与未来展望

大家好&#xff0c;我是一名测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家联系我&#xff0c;一起【分享测试知识&#xff0c;交流测试技术】 Transformer模型自其问世以来&#xff0c;便迅速在自然语言处理领域崭露头角&a…

2024年【危险化学品生产单位安全生产管理人员】最新解析及危险化学品生产单位安全生产管理人员考试总结

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品生产单位安全生产管理人员最新解析参考答案及危险化学品生产单位安全生产管理人员考试试题解析是安全生产模拟考试一点通题库老师及危险化学品生产单位安全生产管理人员操作证已考过的学员汇总&#xff0c;…

mysql基本数据类型(整型)

一、 常见面试题 整型都有哪些基础类型&#xff0c;各占几个字节 tinyint, smallint, mediumint, int, bigint: 1 2 3 4 8 int(n) 是什么意思&#xff0c;什么时候用到 指定显示位宽&#xff0c;需配合 zerofill 使用&#xff08;不够位宽则在前面补0&#xff09;&#xff0c;…

Could not find a version that satisfies the requirement

Could not find a version that satisfies the requirement 目录 Could not find a version that satisfies the requirement 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;2…

MATLAB被360误杀的解决方案

前面被误杀&#xff0c;今天又被误杀。 前面误杀结果是缺少文件&#xff0c;重装MATLAB也不行。 结果重装了操作系统。 这次&#xff0c;看到了提示额外小心。 当时备份了“病毒”文件&#xff0c;结果备份的也被杀了。 解铃还须系铃人 在360安全卫士里面恢复&#xff0c;步骤…

线性代数|机器学习-P27用于深度学习的神经网络结构

文章目录 1. 概述2. 参数定义3. CNN 网络 1. 概述 – 1. 卷积神经网络 CNNs – 2. 连续型线性分段函数 F – 3. 损失函数 – 4. 链式法则计算反向传播算法梯度 ∇ F g r a d F \nabla F \mathrm{grad}\; F ∇FgradF 2. 参数定义 我们定义每个样本有m维度特征&#xff0c;有…

java找不到符号解决办法

一、java找不到符号 如果你的代码里没有报错&#xff0c;明明是存在的。但是java报错找不到符号。如下所示&#xff0c; 二、解决步骤 1.清除编码工具缓存 本人用的idea&#xff0c; eclipse清除缓存方式有需要的可以百度一下&#xff01; 2.如果是mavne项目的 先clean 再…