当前位置:首页 » 《关于电脑》 » 正文

C++日志管理从基础到完善

13 人参与  2024年12月25日 08:02  分类 : 《关于电脑》  评论

点击全文阅读


万古教员有名言,自信人生二百年。
个人主页:oioihoii
喜欢内容的话欢迎关注、点赞、收藏!感谢支持,祝大家祉猷并茂,顺遂无虞

在这里插入图片描述

版本三:基础日志代码

在设计C++日志系统时,我们需要考虑以下几个关键点:

易用性:日志系统应该易于使用,开发者应该能够轻松地添加日志条目。性能:日志系统应该尽可能地减少对应用程序性能的影响。这意味着日志记录的过程应该尽可能地快速,以减少对应用程序的延迟。灵活性:日志系统应该能够支持不同级别的日志(如错误、警告、信息、调试等),并能够在运行时动态地更改日志级别。可配置性:日志系统应该允许开发者配置日志的各种参数,如日志文件的位置、日志的格式等。线程安全:如果应用程序是多线程的,那么日志系统也必须是线程安全的。

下面是一个简单的C++日志系统的设计,它考虑了上述的所有因素:

#include <iostream>#include <fstream>#include <mutex>#include <memory>enum class LogLevel {    ERROR,    WARNING,    INFO,    DEBUG};class Logger {public:    Logger(const std::string& file_name) {        file_stream_.open(file_name, std::ios::out | std::ios::app);    }    ~Logger() {        file_stream_.close();    }    void Log(LogLevel level, const std::string& message) {        std::lock_guard<std::mutex> lock(mutex_);        switch(level) {            case LogLevel::ERROR:                file_stream_ << "[ERROR] ";                break;            case LogLevel::WARNING:                file_stream_ << "[WARNING] ";                break;            case LogLevel::INFO:                file_stream_ << "[INFO] ";                break;            case LogLevel::DEBUG:                file_stream_ << "[DEBUG] ";                break;        }        file_stream_ << message << std::endl;    }private:    std::ofstream file_stream_;    std::mutex mutex_;};int main() {    auto logger = std::make_shared<Logger>("log.txt");    logger->Log(LogLevel::INFO, "This is an info message");    logger->Log(LogLevel::ERROR, "This is an error message");    return 0;}

在这个设计中,我们使用了一个std::ofstream对象来写入日志文件,使用了一个std::mutex对象来确保线程安全,使用了一个枚举类LogLevel来表示不同的日志级别。我们的Log函数接受一个日志级别和一个消息,然后将它们写入日志文件。

这只是一个基础的日志系统设计,实际的日志系统可能会更复杂,例如,它可能会支持将日志发送到多个目的地(如文件、网络、控制台等),支持多种日志格式,支持日志轮转等。

版本二:考虑性能的进阶日志

对于大量日志的生成,确实需要考虑性能问题。频繁地打开和关闭文件会导致性能下降。在这种情况下,我们可以考虑以下优化:

缓冲日志消息:我们可以在内存中缓冲日志消息,然后在缓冲区满时一次性写入文件,而不是每次都打开和关闭文件。这可以大大提高性能,但是需要注意的是,如果程序崩溃,缓冲区中的日志消息可能会丢失。

异步写入:我们可以在一个单独的线程中写入日志文件,这样就不会阻塞主程序的执行。这需要更复杂的线程同步机制,但是可以进一步提高性能。

下面是一个改进的版本,使用了缓冲区和异步写入:

#include <iostream>#include <fstream>#include <mutex>#include <memory>#include <thread>#include <condition_variable>#include <queue>enum class LogLevel {    ERROR,    WARNING,    INFO,    DEBUG};class Logger {public:    Logger(const std::string& file_name) : exit_(false) {        file_stream_.open(file_name, std::ios::out | std::ios::app);        worker_ = std::thread(&Logger::writeLog, this);    }    ~Logger() {        exit_ = true;        cv_.notify_all();        worker_.join();        file_stream_.close();    }    void Log(LogLevel level, const std::string& message) {        std::lock_guard<std::mutex> lock(mutex_);        log_queue_.push(std::make_pair(level, message));        cv_.notify_all();    }private:    void writeLog() {        while (true) {            std::unique_lock<std::mutex> lock(mutex_);            cv_.wait(lock, [this]{ return !log_queue_.empty() || exit_; });            while (!log_queue_.empty()) {                auto log = log_queue_.front();                log_queue_.pop();                switch(log.first) {                    case LogLevel::ERROR:                        file_stream_ << "[ERROR] ";                        break;                    case LogLevel::WARNING:                        file_stream_ << "[WARNING] ";                        break;                    case LogLevel::INFO:                        file_stream_ << "[INFO] ";                        break;                    case LogLevel::DEBUG:                        file_stream_ << "[DEBUG] ";                        break;                }                file_stream_ << log.second << std::endl;            }            if (exit_ && log_queue_.empty()) break;        }    }    std::ofstream file_stream_;    std::mutex mutex_;    std::condition_variable cv_;    std::queue<std::pair<LogLevel, std::string>> log_queue_;    std::thread worker_;    bool exit_;};int main() {    auto logger = std::make_shared<Logger>("log.txt");    logger->Log(LogLevel::INFO, "This is an info message");    logger->Log(LogLevel::ERROR, "This is an error message");    return 0;}

在这个版本中,我们使用了一个std::queue来缓冲日志消息,使用了一个std::condition_variable来同步主线程和工作线程,使用了一个std::thread来执行日志写入。我们的Log函数将日志消息添加到队列中,然后通知工作线程。工作线程在收到通知后,会将队列中的所有日志消息写入文件。

版本三:添加定时删除的完整日志管理

要实现日志文件的定时清除功能,我们需要添加一些额外的代码来管理日志文件。以下是一个改进的版本,它会在每天的特定时间(例如午夜)创建一个新的日志文件,并删除30天前的日志文件:

#include <iostream>#include <fstream>#include <mutex>#include <memory>#include <thread>#include <condition_variable>#include <queue>#include <chrono>#include <filesystem>#include <iomanip>enum class LogLevel {    ERROR,    WARNING,    INFO,    DEBUG};class Logger {public:    Logger(const std::string& dir, int days) : exit_(false), dir_(dir), days_(days) {        worker_ = std::thread(&Logger::writeLog, this);    }    ~Logger() {        exit_ = true;        cv_.notify_all();        worker_.join();    }    void Log(LogLevel level, const std::string& message) {        std::lock_guard<std::mutex> lock(mutex_);        log_queue_.push(std::make_pair(level, message));        cv_.notify_all();    }private:    void writeLog() {        while (true) {            std::unique_lock<std::mutex> lock(mutex_);            cv_.wait(lock, [this]{ return !log_queue_.empty() || exit_; });            auto now = std::chrono::system_clock::now();            auto date = std::chrono::system_clock::to_time_t(now);            std::tm tm;            localtime_s(&tm, &date);            std::ostringstream oss;            oss << dir_ << "/" << std::put_time(&tm, "%Y%m%d") << ".log";            if (!file_stream_.is_open() || oss.str() != file_name_) {                file_stream_.close();                file_name_ = oss.str();                file_stream_.open(file_name_, std::ios::out | std::ios::app);                deleteOldLogs();            }            while (!log_queue_.empty()) {                auto log = log_queue_.front();                log_queue_.pop();                switch(log.first) {                    case LogLevel::ERROR:                        file_stream_ << "[ERROR] ";                        break;                    case LogLevel::WARNING:                        file_stream_ << "[WARNING] ";                        break;                    case LogLevel::INFO:                        file_stream_ << "[INFO] ";                        break;                    case LogLevel::DEBUG:                        file_stream_ << "[DEBUG] ";                        break;                }                file_stream_ << log.second << std::endl;            }            if (exit_ && log_queue_.empty()) break;        }    }    void deleteOldLogs() {        auto now = std::chrono::system_clock::now();        auto date = std::chrono::system_clock::to_time_t(now);        std::tm tm;        localtime_s(&tm, &date);        tm.tm_mday -= days_;        mktime(&tm);        std::ostringstream oss;        oss << dir_ << "/" << std::put_time(&tm, "%Y%m%d") << ".log";        std::filesystem::remove(oss.str());    }    std::ofstream file_stream_;    std::mutex mutex_;    std::condition_variable cv_;    std::queue<std::pair<LogLevel, std::string>> log_queue_;    std::thread worker_;    bool exit_;    std::string dir_;    std::string file_name_;    int days_;};int main() {    auto logger = std::make_shared<Logger>("logs", 30);    logger->Log(LogLevel::INFO, "This is an info message");    logger->Log(LogLevel::ERROR, "This is an error message");    return 0;}

在这个版本中,我们在writeLog函数中检查当前的日期,如果日期改变了,我们就创建一个新的日志文件,并删除30天前的日志文件。我们使用了C++17的std::filesystem库来删除文件。注意,这个代码只在支持C++17的编译器上有效。

这个代码的一个限制是,它假设日志文件的名字是按照"YYYYMMDD.log"的格式命名的。如果你的日志文件的命名规则不同,你需要修改writeLogdeleteOldLogs函数中的代码。

进一步探讨交流以及更多惊喜关注公众号联系我!再次欢迎关注、点赞、收藏,系列内容可以点击专栏目录订阅,感谢支持,祝大家祉猷并茂,顺遂无虞
在这里插入图片描述

若将文章用作它处,请一定注明出处,商用请私信联系我!


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/206473.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1