C++ log:快速記錄程序日誌並提高調試效率

一、簡介

在軟件開發過程中,開發人員經常需要在程序不同階段或者不同模塊中輸出一些調試信息,以方便查找問題以及調試程序。而程序的打印日誌是一種非常便捷、常用的方法。

C++是一門強大的編程語言,擁有良好的性能和靈活的特性,在日誌記錄功能方面可以藉助多種不同的庫實現,而利用C++11標準庫提供的特性,開發C++日誌記錄庫是一種簡單、輕量、靈活的方式。

二、如何實現日誌記錄

可以採用printf()函數輸出日誌信息,但這種方法有以下缺點:

  • 無法動態控制日誌級別
  • 無法更改日誌格式
  • 無法在不同平台上實現

因此我們需要使用C++日誌庫來完成這些功能,例如log4cpp、log4cplus、glog等。但這些庫存在以下缺點:

  • 依賴性強,容易出現編譯鏈接錯誤
  • 使用方法複雜,需要較長的學習周期
  • 庫文件較大,佔用磁盤空間

因此,我們可以自己開發一個輕量、簡單的C++日誌記錄庫。

三、實現

下面是利用C++11標準庫實現的一個簡單的日誌記錄庫:

#include <iostream>
#include <fstream>
#include <chrono>
using namespace std;
//日誌類型
enum log_type {
  DEBUG = 0,
  INFO = 1,
  WARN = 2,
  ERROR = 3,
};
//獲取日誌級別字符串
string get_log_level(log_type level) {
  static const string levels[] = {"DEBUG", "INFO", "WARN", "ERROR"};
  return levels[level];
}
//獲取當前時間並返回字符串格式
string get_time() {
  time_t now_time = chrono::system_clock::to_time_t(chrono::system_clock::now());
  string time_str = ctime(&now_time);
  //去掉末尾的\n
  time_str.pop_back();
  return time_str;
}
//日誌記錄函數
void log(log_type level, const string& message) {
  static const string file_name = "log.txt";
  //打開文件
  ofstream ofs(file_name, ios_base::app);
  if (!ofs) {
    cerr << "Failed to open file: " << file_name << endl;
    return;
  }
  //獲取時間和日誌級別
  string time_str = get_time();
  string level_str = get_log_level(level);
  //寫入日誌
  ofs << "[" << time_str << "][" << level_str << "] " << message << endl;
}

以上的代碼中,我們定義了日誌類型log_type和相應的字符串表示。然後定義了一個函數get_time(),用於獲取當前時間並返回字符串格式的時間。

接下來是最重要的函數log(log_type level, const string& message),該函數用於記錄日誌信息。在其中,我們首先指定了日誌存儲的文件名log.txt,並在函數的開頭打開文件並檢查是否打開成功。如果出錯,函數將不執行任何操作。然後,在函數的主體中,我們獲取當前時間和日誌級別,並將這些信息連同日誌信息一起寫入到文件中。

最後是一個簡單的示例,該示例記錄了三個不同級別的日誌:

int main() {
  log(DEBUG, "This is a debug message.");
  log(INFO, "This is an info message.");
  log(WARN, "This is a warning message.");
  log(ERROR, "This is an error message.");
  return 0;
}

該示例記錄了四條日誌,每條日誌都包括了日誌級別、時間戳和日誌信息,將會被寫入log.txt中:

[Sat Nov 21 14:10:57 2020][DEBUG] This is a debug message.
[Sat Nov 21 14:10:57 2020][INFO] This is an info message.
[Sat Nov 21 14:10:57 2020][WARN] This is a warning message.
[Sat Nov 21 14:10:57 2020][ERROR] This is an error message.

四、擴展

上面的C++日誌庫只具有基本的功能,如果想要更好地適應不同需求,我們可能需要對其進行一些擴展。

以下是對C++日誌庫擴展的一些常見需求:

1. 動態設置日誌級別

在實際開發中,我們可能希望可以在運行時動態更改日誌級別,例如實現一個命令行參數–log-level,使得程序可以接受不同的日誌級別。

class Logger {
public:
  static Logger& instance() {
    static Logger logger;
    return logger;
  }

  void set_level(log_type level) {
    level_ = level;
  }

  void log(log_type level, const string& message) {
    if (level >= level_) {
      //do log
    }
  }

private:
  log_type level_ = INFO;
};

以上代碼中,我們實現了一個Logger類,並將其設計為單例模式,使用instance()方法獲取唯一實例。然後,我們添加了一個set_level()方法,用於動態設置日誌級別,並在log()方法中增加了一個級別判斷。在代碼中,我們定義了一個全局日誌級別變量level_,其默認值為INFO。

2. 日誌異步寫入

如果我們想要在日誌記錄和程序運行之間取得更好的平衡,我們可能會選擇讓程序異步寫入日誌文件。這樣可以提高程序的響應性並提高程序的整體效率。

class AsyncLogger {
public:
  AsyncLogger() {
    //start logger thread
  }

  void log(log_type level, const string& message) {
    lock_guard lock(mutex_);
    queue_.emplace(level, message);
  }

  ~AsyncLogger() {
    //stop logger thread
  }

private:
  void logger_thread() {
    while (true) {
      unique_lock lock(mutex_);
      condition_.wait(lock, [this]() {
        return !queue_.empty() || stop_;
      });
      if (stop_ && queue_.empty()) {
        break;
      }
      auto log = queue_.front();
      queue_.pop();
      lock.unlock();
      //write log to file
    }
  }

  mutex mutex_;
  condition_variable condition_;
  queue<pair<log_type, string>> queue_;
  bool stop_ = false;
  thread logger_thread_;
};

以上代碼中,我們實現了一個AsyncLogger類,該類具有一個內部隊列和一個工作線程。用戶通過調用log()方法將日誌消息寫入隊列,而工作線程負責將這些消息異步寫入到日誌文件中。在類的析構函數中,停止工作線程。具有停止標誌stop_,當設置為True時,工作線程會退出。

3. 預處理的日誌消息

預處理的日誌消息允許我們將日誌消息轉換為其它字符序列或格式。這樣,我們就可以更靈活地控制日誌消息的格式和記錄方式。

#define LOG(level, message) \
  do {\
    stringstream ss;\
    ss << message;\
    log(level, ss.str()); \
  } while(0)

以上代碼中,我們使用了一個C++的預處理器宏LOG(),該宏接受兩個參數:一個日誌級別和一個待記錄的信息。當調用該宏時,宏展開為一個logg()函數調用,該函數將日誌級別和記錄的信息進行了連接。

五、總結

在這篇文章中,我們介紹了如何使用C++11標準庫實現一個簡單的日誌記錄庫,該庫可以直接使用,也可以基於需求進行擴展。實現一個簡單的日誌庫並不難,我們只需要一些C++語言基礎和文件流操作。但一旦你要進行擴展,例如添加異步寫入、日誌輪替等功能,那麼你就需要更深入地了解C++11標準庫的使用以及多線程編程,這是日誌實現中難點之一。

原創文章,作者:EZJW,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/145149.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
EZJW的頭像EZJW
上一篇 2024-10-26 11:55
下一篇 2024-10-26 11:55

相關推薦

  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • python強行終止程序快捷鍵

    本文將從多個方面對python強行終止程序快捷鍵進行詳細闡述,並提供相應代碼示例。 一、Ctrl+C快捷鍵 Ctrl+C快捷鍵是在終端中經常用來強行終止運行的程序。當你在終端中運行…

    編程 2025-04-29
  • Python程序需要編譯才能執行

    Python 被廣泛應用於數據分析、人工智能、科學計算等領域,它的靈活性和簡單易學的性質使得越來越多的人喜歡使用 Python 進行編程。然而,在 Python 中程序執行的方式不…

    編程 2025-04-29
  • Ojlat:一款快速開發Web應用程序的框架

    Ojlat是一款用於快速開發Web應用程序的框架。它的主要特點是高效、易用、可擴展且功能齊全。通過Ojlat,開發人員可以輕鬆地構建出高質量的Web應用程序。本文將從多個方面對Oj…

    編程 2025-04-29
  • Python程序文件的拓展

    Python是一門功能豐富、易於學習、可讀性高的編程語言。Python程序文件通常以.py為文件拓展名,被廣泛應用於各種領域,包括Web開發、機器學習、科學計算等。為了更好地發揮P…

    編程 2025-04-29
  • Python購物車程序

    Python購物車程序是一款基於Python編程語言開發的程序,可以實現購物車的相關功能,包括商品的添加、購買、刪除、統計等。 一、添加商品 添加商品是購物車程序的基礎功能之一,用…

    編程 2025-04-29
  • Cron執行日誌用法介紹

    本文將從多個方面對cron執行日誌進行詳細闡述,包括cron執行日誌的定義、cron執行日誌的產生原因、cron執行日誌的格式以及如何解讀cron執行日誌。 一、定義 Cron是一…

    編程 2025-04-29
  • 爬蟲是一種程序

    爬蟲是一種程序,用於自動獲取互聯網上的信息。本文將從如下多個方面對爬蟲的意義、運行方式、應用場景和技術要點等進行詳細的闡述。 一、爬蟲的意義 1、獲取信息:爬蟲可以自動獲取互聯網上…

    編程 2025-04-29
  • Vb運行程序的三種方法

    VB是一種非常實用的編程工具,它可以被用於開發各種不同的應用程序,從簡單的計算器到更複雜的商業軟件。在VB中,有許多不同的方法可以運行程序,包括編譯器、發布程序以及命令行。在本文中…

    編程 2025-04-29
  • Hibernate日誌打印sql參數

    本文將從多個方面介紹如何在Hibernate中打印SQL參數。Hibernate作為一種ORM框架,可以通過打印SQL參數方便開發者調試和優化Hibernate應用。 一、通過配置…

    編程 2025-04-29

發表回復

登錄後才能評論