日志的编写与线程池的结合

news2024/12/26 21:10:27

目录

一、认识日志 

二、时间的等级划分 

三、日志的输出端 

3.1 保存至文件

 四、日志的部分信息

4.1 日志等级

4.2 日志时间 

五、加载日志

六、日志的宏编写 

七、ThreadPool + Log


一、认识日志 

  1. 记录事件: 日志用于记录系统运行过程中发生的各种事件,包括错误、警告、信息和调试信息等。通过这些记录,可以了解系统在某一时刻的具体状态和行为。

  2. 问题诊断: 当系统出现故障或异常时,通过查看日志可以追踪问题的发生原因、定位错误源头,从而快速解决问题。详细的日志信息能够帮助开发人员和运维人员理解问题的背景和上下文。

  3. 性能监控: 日志可以记录系统的性能数据,如处理时间、响应时间和资源使用情况等。这些信息对于监控系统性能和优化系统效率非常重要。

  4. 安全审计: 日志记录用户操作、系统访问和安全事件等信息,可以用于安全审计和合规性检查,确保系统的安全性和合规性。

  5. 运行分析: 通过分析日志数据,可以发现系统运行中的趋势、模式和潜在问题,从而做出更好的决策和改进系统设计。

  6. 调试和开发: 在开发和测试过程中,日志信息可以帮助开发人员了解代码执行流程和程序状态,便于调试和优化代码。

总之,日志在系统运行、维护、开发和安全等各个方面都起着非常重要的作用,是确保系统稳定性、可靠性和安全性的重要手段。

二、时间的等级划分 

后续通过日志的等级,就可以选择后续操作,例如直接退出、继续执行等。 

enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

三、日志的输出端 

日志可以向显示器中输入,也可以向某文件中输入。

3.1 保存至文件

C++根据文件内容的数据格式分为二进制文件和文本文件。
采用文件流对象操作文件的一般步骤:
1. 定义一个文件流对象
        ifstream ifile(只输入用)
        ofstream ofile(只输出用)
        fstream iofile(既输入又输出用)
2. 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
3. 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
4. 关闭文件 

bool gIsSave = false;//后续通过IsSave选择是否保存到文件
const std::string logname = "log.txt";
void SaveFile(const std::string &filename, const std::string &message)
{
    std::ofstream out(filename, std::ios::app);
    if (!out.is_open())
        return;
    out << message;
    out.close();
}

 四、日志的部分信息

4.1 日志等级

首先要明确一点,日志是要输出给读者看的,我们枚举的日志等级毕竟还是整型,输出后也会导致读者看不懂的问题,所以就需要有一个函数将对应的枚举类型转化成字符串,以便于后续的输出:

std::string LevelToString(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 "Unknown";
    }
}

4.2 日志时间 

日志的名称也能看出其带有很强的时间属性,所以在日志中获取操作的时间也是必不可少的操作。

这里介绍几个C++中与时间有关的概念,包括数据类型、结构体、函数等:

time_t :表述时间的数据类型

struct tm : 包含时间的各个单位的结构体,如下图,第一列为结构体成员,第二列为成员的数据类型,第三列为成员的意义,第四列为成员的取值范围。
其中,特别说明的是 tm_year 与 tm_mon ,它们分别表示的是从1900年后到现在的年数与此时的月份数,但是是从零开始的,所以在输出时,tm_year一般+1900,tm_mon一般+1。

time : 输出型参数,获取当前时间赋值给传入的 time_t 类型的变量

localtime : 返回一个struct tm 的结构体,可以将传入的 time_t 类型变量的值,依次赋值给结构体成员。

下面附上我粗鄙的理解:定义了一个 time_t 类型的变量 curr_time ,使用 time 可以将当前时间赋值给 curr_time ,假设当前时间为2024/7/16 21:48,那么curr_time中可能是一系列连续的数字标识当前时间,例如是202407162148,这样比较粗鄙,猜想系统中的机制肯定更复杂,是很不适合读者去读的,localtime 就可以将该串转化成年=2024,月=7,日=16(事实肯定不是这样因为年是从1900开始,月从0开始,但是这样说比较好理解)......然后就比较方便读者阅读了。

std::string GetTimeString()
{
    time_t curr_time = time(nullptr);
    struct tm *format_time = localtime(&curr_time);
    if (format_time == nullptr)
        return "None";
    char time_buffer[1024];
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
             format_time->tm_year + 1900, format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return time_buffer;
}

五、加载日志

这里采用的类似C语言 printf 的传入参数。

va_listva_startva_endvsnprintf:

  • 用于处理可变参数列表的 C 标准库函数。
  • va_list arg:声明一个可变参数列表变量。
  • va_start(arg, format):初始化可变参数列表,从 format 开始。
  • vsnprintf(buffer, sizeof(buffer), format, arg):将可变参数格式化为字符串并存储到 buffer 中。
  • va_end(arg):结束可变参数列表的处理。
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{
    std::string levelstr = LevelToString(level);
    std::string timestr = GetTimeString();
    pid_t selfid = getpid();

    char buffer[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg);

    std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +
                          "[" + std::to_string(selfid) + "]" +
                          "[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer;
    LockGuard lockguard(&lock);
    if (!issave)
        std::cout << message;
    else
        SaveFile(logname, message);
}

六、日志的宏编写 

do while(0)常用于宏定义中代码块的定义,__FILE__, __LINE__都是系统提供的,可以输出当前文件与当前行,此时再使用宏调用日志就简单多了。

#define LOG(level, format, ...)                                                \
    do                                                                         \
    {                                                                          \
        LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \
    } while (0)

#define EnableFile()    \
    do                  \
    {                   \
        gIsSave = true; \
    } while (0)
#define EnableScreen()   \
    do                   \
    {                    \
        gIsSave = false; \
    } while (0)
LOG(INFO, "%s is quit...\n", thread.name().c_str());

七、ThreadPool + Log

注意,这里日志打印形式纯属个人意愿,而且需要打印出线程名的话,ThreadPool 的相关函数的传参也会进行修改,请注意甄别。

#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include "Thread.hpp"
#include "Log.hpp"
using namespace ThreadModule;
const static int gdefaultthreadnum = 3;

template <typename T>
class ThreadPool
{
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadWakeupAll()
    {
        pthread_cond_broadcast(&_cond);
    }
public:
    ThreadPool(int threadnum = gdefaultthreadnum) : _threadnum(threadnum), _waitnum(0), _isrunning(false)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        LOG(INFO, "ThreadPool Construct()\n");
    }
    void HandlerTask(std::string name)
    {
        LOG(INFO, "%s is running...\n", name.c_str());
        while (true)
        {
            LockQueue();
            while (_task_queue.empty() && _isrunning)
            {
                _waitnum++;
                ThreadSleep();
                _waitnum--;
            }
            if (_task_queue.empty() && !_isrunning)
            {
                UnlockQueue();
                break;
            }
            T task = _task_queue.front();
            _task_queue.pop();
            UnlockQueue();
            LOG(DEBUG, "%s get a task", name.c_str());
            task();
            LOG(DEBUG, "%s handler a task, result is: %s\n", name.c_str(), t.ResultToString().c_str());
        }
    }
    void InitThreadPool()
    {
        for (int num = 0; num < _threadnum; num++)
        {
            std::string name = "thread- " + std::to_string(num + 1);
            _threads.emplace_back(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1), name);
            LOG(INFO, "init thread %s done\n", name.c_str());
        }
        _isrunning = true;
    }
    void Start()
    {
        for (auto &thread : _threads)
        {
            thread.Start();
        }
    }
    void Stop()
    {
        LockQueue();
        _isrunning = false; // 线程池退出
        ThreadWakeupAll();
        UnlockQueue();
    }
    void Wait()
    {
        for (auto &thread : _threads)
        {
            thread.Join();
            LOG(INFO, "%s is quit...\n", thread.name().c_str());
        }
    }
    bool Enqueue(const T &task)
    {
        bool ret = false;
        LockQueue();
        if (_isrunning)
        {
            _task_queue.push(task);
            if (_waitnum > 0)
            {
                ThreadWakeup();
            }
            LOG(DEBUG, "enqueue task success\n");
            ret = true;
        }
        UnlockQueue();
        return ret;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    int _threadnum;
    std::vector<Thread> _threads;
    std::queue<T> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    int _waitnum;
    bool _isrunning;
};

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

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

相关文章

word 设置多级混合标题自动更新

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 有没有体会过多级标题&#xff0c;怎么设置都不听使唤的情况&#xff1f; 我想要的格式是&#xff1a; 二、原因分析 多级标题中发现&#xff0c;输入编号格式这里有个数字没有底纹,是了&#xff0…

解析 Mira :基于 Web3,让先进的 AI 技术易于访问和使用

“Mira 平台正在以 Web3 的方式解决当前 AI 开发面临的复杂性问题&#xff0c;同时保护 AI 贡献者的权益&#xff0c;让他们可以自主拥有并货币化自己的模型、数据和应用&#xff0c;以使先进的 AI 技术更加易于访问和使用。” AI 代表着一种先进的生产力&#xff0c;它通过深…

nginx代理缓存

在服务器架构中&#xff0c;反向代理服务器除了能够起到反向代理的作用之外&#xff0c;还可以缓存一些资源&#xff0c;加速客户端访问&#xff0c;nginx的ngx_http_proxy_module模块不仅包含了反向代理的功能还包含了缓存功能。 1、定义代理缓存规则 参数详解&#xff1a; p…

万字长文之分库分表里如何优化分页查询?【后端面试题 | 中间件 | 数据库 | MySQL | 分库分表 | 分页查询】

分库分表的一般做法 一般会使用三种算法&#xff1a; 哈希分库分表&#xff1a;根据分库分表键算出一个哈希值&#xff0c;根据这个哈希值选择一个数据库。最常见的就是数字类型的字段作为分库分表键&#xff0c;然后取余。比如在订单表里&#xff0c;可以按照买家的ID除以8的…

开发实战经验分享:互联网医院系统源码与在线问诊APP搭建

作为一名软件开发者&#xff0c;笔者有幸参与了多个互联网医院系统的开发项目&#xff0c;并在此过程中积累了丰富的实战经验。本文将结合我的开发经验&#xff0c;分享互联网医院系统源码的设计与在线问诊APP的搭建过程。 一、需求分析 在开发任何系统之前&#xff0c;首先要…

UPFC统一潮流控制器的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 UPFC统一潮流控制器的simulink建模与仿真。能够在不增加输电线路物理容量的情况下&#xff0c;显著提高电力系统的传输能力和稳定性。UPFC能够同时控制输电线路的有功功率、无…

技术速递|Let’s Learn .NET Aspire – 开始您的云原生之旅!

作者&#xff1a;James Montemagno 排版&#xff1a;Alan Wang Let’s Learn .NET 是我们全球性的直播学习活动。在过去 3 年里&#xff0c;来自世界各地的开发人员与团队成员一起学习最新的 .NET 技术&#xff0c;并参加现场研讨会学习如何使用它&#xff01;最重要的是&#…

微软研究人员为电子表格应用开发了专用人工智能LLM

微软的 Copilot 生成式人工智能助手现已成为该公司许多软件应用程序的一部分。其中包括 Excel 电子表格应用程序&#xff0c;用户可以在其中输入文本提示来帮助处理某些选项。微软的一组研究人员一直在研究一种新的人工智能大型语言模型&#xff0c;这种模型是专门为 Excel、Go…

在设计电气系统时,电气工程师需要考虑哪些关键因素?

在设计电气系统时&#xff0c;电气工程师需要考虑多个关键因素&#xff0c;以确保系统的安全性、可靠性、效率和经济性。我收集归类了一份plc学习包&#xff0c;对于新手而言简直不要太棒&#xff0c;里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕设800套和语言…

【Neural signal processing and analysis zero to hero】- 1

The basics of neural signal processing course from youtube: 传送地址 Possible preprocessing steps Signal artifacts (not) to worry about doing visual based artifact rejection so that means that before you start analyzing, you can identify those data epic…

《学会 SpringBoot · 定制 SpringMVC》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;如需交流&#xff…

Pytorch学习笔记day1—— 安装教程

这里写自定义目录标题 Pytorch安装方式 工作需要&#xff0c;最近开始搞一点AI的事情。但是这个国产的AI框架&#xff0c;实话说对初学者不太友好 https://www.mindspore.cn/ 比如说它不支持win下的CUDA&#xff0c;可是我手里只有3070Ti和4060也不太可能自己去买昇腾就有点绷不…

C语言 | Leetcode C语言题解之第239题滑动窗口最大值

题目&#xff1a; 题解&#xff1a; int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {int prefixMax[numsSize], suffixMax[numsSize];for (int i 0; i < numsSize; i) {if (i % k 0) {prefixMax[i] nums[i];} else {prefixMax[i] fmax(pref…

C++深度解析教程笔记9-静态成员变量,静态成员函数,二阶构造,友元,函数重载,操作符重载

C深度解析教程笔记9 第25课 - 类的静态成员变量实验-数对象个数&#xff08;失败&#xff09;实验-静态变量小结 第26课 - 类的静态成员函数实验-修改对象的静态变量数值实验-利用静态成员函数实验-静态变量静态函数实现统计对象个数小结 第27课 - 二阶构造模式实验-初始化是否…

【JavaEE】HTTP(2)

&#x1f921;&#x1f921;&#x1f921;个人主页&#x1f921;&#x1f921;&#x1f921; &#x1f921;&#x1f921;&#x1f921;JavaEE专栏&#x1f921;&#x1f921;&#x1f921; &#x1f921;&#x1f921;&#x1f921;下一篇文章&#xff1a;【JavaEE】HTTP协议(…

C++——类和对象(下)

文章目录 一、再探构造函数——初始化列表二、 类型转换三、static成员静态成员变量静态成员函数 四、 友元友元函数友元类 五、内部类六、匿名对象 一、再探构造函数——初始化列表 之前我们实现构造函数时&#xff0c;初始化成员变量主要使⽤函数体内赋值&#xff0c;构造函…

【读点论文】ASAM: Boosting Segment Anything Model with Adversarial Tuning,对抗学习提升性能

ASAM: Boosting Segment Anything Model with Adversarial Tuning Abstract 在不断发展的计算机视觉领域&#xff0c;基础模型已成为关键工具&#xff0c;对各种任务表现出卓越的适应性。其中&#xff0c;Meta AI 的 Segment Anything Model (SAM) 在图像分割方面表现突出。然…

第十一届MathorCup高校数学建模挑战赛-C题:海底数据中心的散热优化设计(续)(附MATLAB代码实现)

目录 5.3 问题三的求解 5.3.1 数据分析 5.3.2 数据处理 5.3.4 得出结论 5.4 问题四的求解 5.4.1 数据分析 5.4.2 算法分析 5.5 问题五的求解 六、模型评价与推广 6.1 模型的优点 6.2 模型的缺点 6.3 模型的推广 七、参考文献 代码实现 8.1 图 4 的代码 8.2 图 5 的代码 8.3 图…

旗晟巡检机器人的应用场景有哪些?

巡检机器人作为现代科技的杰出成果&#xff0c;已广泛应用于各个关键场景。从危险的工业现场到至关重要的基础设施&#xff0c;它们的身影无处不在。它们以精准、高效、不知疲倦的特性&#xff0c;担当起保障生产、守护安全的重任&#xff0c;为行业发展注入新的活力。那么&…

VMware安装CentOS 7

在虚拟机中安装无论是Windows还是Linux其实都差不多&#xff0c;主要还是需要熟悉VMware的使用&#xff0c;多新增几次就熟悉了&#xff0c;可以反复删除再新增去练习… 如下是安装CentOS 7 安装过程&#xff1a; VMare Workstation 16 PRO 中安装CentOS 7 CentOS 7 下载推荐…