1、log4cpp概述
Log4cpp是一個(gè)開源的C++類庫,它提供了C++程序中使用日志和跟蹤調(diào)試的功能,它的優(yōu)點(diǎn)如下:
- 提供應(yīng)用程序運(yùn)行上下文,方便跟蹤調(diào)試;
- 可擴(kuò)展的、多種方式記錄日志,包括命令行、文件、回卷文件、內(nèi)存、syslog服務(wù)器、Win事件日志等;
- 可以動(dòng)態(tài)控制日志記錄級(jí)別,在效率和功能中進(jìn)行調(diào)整;
- 所有配置可以通過配置文件進(jìn)行動(dòng)態(tài)調(diào)整;
- 多語言支持,包括Java(log4j),C++(log4cpp、log4cplus),C(log4c),python(log4p)等;
類似日志工具:glog、boost.log、spdlog
2、原理
Log4cpp有三個(gè)主要的組件:日志類別(Category)、輸出源(Appender)和布局(Layout)。這三種類型的組件一起工作使得系統(tǒng)可以根據(jù)信息的類型和級(jí)別記錄它們,并且在運(yùn)行時(shí)控制這些信息的輸出格式和位置。
三個(gè)組件的介紹:
1)日志類別(Category)含義是:如果配置文件中設(shè)置的級(jí)別是DEBUG,則任意的log都能打印出來;但如果配置的級(jí)別是ERROR,則只有高于ERROR優(yōu)先級(jí)的日志才可以打印出來。
日志的常用優(yōu)先級(jí):DEBUG < INFO < WARN < ERROR < FATAL
2)輸出源(Appender)用來輸出日志(被layout格式化后)到一些設(shè)備上,比如文件、命令行、內(nèi)存等。也可以定義自己的appender輸出日志信息到別的設(shè)備上。log4cpp提供的appender如下:FileAppender 輸出到文件 RollingFileAppender 輸出到回卷文件,即當(dāng)文件到達(dá)某個(gè)大小后回卷 ConsoleAppender 輸出到控制臺(tái)
3)布局(Layout):顯示樣式PatternLayout表示讓用戶根據(jù)類似于C語言printf函數(shù)的轉(zhuǎn)換模式來指定輸出格式
三個(gè)組件之間的關(guān)系:
- Category和Appender的關(guān)系是:多個(gè)Appender可以附加到一個(gè)Category上,這樣一個(gè)日志消息可以同時(shí)輸出到多個(gè)設(shè)備上。
- Appender和Layout的關(guān)系是:Layout附加在Appender上,appender調(diào)用layout處理完日志消息后,記錄到某個(gè)設(shè)備上。
3 log4cplus的安裝
log4cplus是開源的,源代碼可在這里找到。下載源代碼壓縮包后解壓,進(jìn)入主目錄。和大多數(shù)autotools工程一樣,順序執(zhí)行以下命令即可完成安裝。
make
make install
安裝文件將默認(rèn)安裝到/usr/local,庫文件置于/usr/local/lib,頭文件置于/usr/local/include。
是的,這里介紹的安裝及下面介紹的應(yīng)用都是基于linux系統(tǒng)。
4 log4cplus的使用
以下是官方提供的“hello, world”的示例程序:
#include
#include
#include
using namespace log4cplus;
int main()
{
BasicConfigurator config;
config.configure();
Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("main"));
LOG4CPLUS_WARN(logger, LOG4CPLUS_TEXT("Hello, World!"));
return 0;
}
程序包含了一些必要的頭文件,編譯時(shí)需要鏈接log4cplus庫,將這段代碼保存為 test.cpp,執(zhí)行以下命令編譯:
編譯后生成 test 可執(zhí)行文件,運(yùn)行./test,得到如下輸出:
這個(gè)程序使用的是log4cplus內(nèi)置的默認(rèn)配置選項(xiàng),實(shí)際使用中一般要自己配置選項(xiàng),接下來你會(huì)看到。
5 log4cplus配置
log4cplus配置就是定義appender, 定義輸出的格式即 layout。以下列出兩種常用配置,以供參考。
配置輸出到控制臺(tái)(通常用于前臺(tái)程序):
log4cplus.appender.console = log4cplus::ConsoleAppender
log4cplus.appender.console.layout = log4cplus::PatternLayout
log4cplus.appender.console.layout.ConversionPattern = [%D{%m/%d/%y %H:%M:%S,%q} %-5p] - %m%n
配置輸出到文件(通常用于后臺(tái)程序):
log4cplus.appender.file = log4cplus::FileAppender
log4cplus.appender.file.File = /var/log/myapp.log
log4cplus.appender.file.MaxFileSize = 10M
log4cplus.appender.file.Append = true
log4cplus.appender.file.layout = log4cplus::PatternLayout
log4cplus.appender.file.layout.ConversionPattern = [%D{%m/%d/%y %H:%M:%S,%q} %-5p] - %m%n
簡單說明一下,配置文件中l(wèi)og4cplus.logger.logmain即定義一個(gè)logmain對(duì)象,后面跟的兩個(gè)字段前一個(gè)表示log級(jí)別,后一個(gè)指定使用的appender,即日志輸出對(duì)象。log級(jí)別按嚴(yán)重程度從低到高依次為TRACE、DEBUG、INFO、WARN、ERROR、FATAL。log4cplus.appender.xxx定義具體的appender屬性,如是控制臺(tái)還是文件,進(jìn)一步配置文件名、文件大小等。
將配置保存到一個(gè)配置文件中(如log4cplus.conf),以下你將看到如何使用配置文件。有關(guān)更詳細(xì)的配置,讀者可自行摸索。
6 log4cplus運(yùn)用于項(xiàng)目
以上“hello, world”程序只是大概演示log4cplus的用法,實(shí)際項(xiàng)目使用要有系統(tǒng)觀念,就是怎樣用才更方便,我們可以再做點(diǎn)封裝。我們可以定義一個(gè)全局logger對(duì)象,將log4cplus初始化配置放到一個(gè)源文件中,重新定義一些簡化的宏置于頭文件,比如筆者就定義了Log.h/Log.cpp兩個(gè)文件,代碼如下:
Log.h文件:
#include
#include
using namespace log4cplus;
using namespace log4cplus::helpers;
// global object
extern Logger logger;
// define some macros for simplicity
#define LOG_TRACE(logEvent) LOG4CPLUS_TRACE(logger, logEvent)
#define LOG_DEBUG(logEvent) LOG4CPLUS_DEBUG(logger, logEvent)
#define LOG_INFO(logEvent) LOG4CPLUS_INFO(logger, logEvent)
#define LOG_WARN(logEvent) LOG4CPLUS_WARN(logger, logEvent)
#define LOG_ERROR(logEvent) LOG4CPLUS_ERROR(logger, logEvent)
#define LOG_FATAL(logEvent) LOG4CPLUS_FATAL(logger, logEvent)
extern void InitLogger(bool daemonized);
Log.cpp文件:
#include
#include
#include
#include
#include "Log.h"
Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("logmain"));
void InitLogger(bool daemonized)
{
if (daemonized)
PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("/your/path/log4cplusd.conf"));
else
PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("/your/path/log4cplus.conf"));
}
將這兩個(gè)文件置于你的項(xiàng)目中,然后在 main 函數(shù)中調(diào)用 InitLogger() 初始化 log4cplus,再在需要加log的文件中包含Log.h即可。注意InitLogger函數(shù)的參數(shù)daemonized,該參數(shù)表示應(yīng)用程序是否是守護(hù)進(jìn)程(后臺(tái)運(yùn)行),一般我們開發(fā)的應(yīng)用程序大多是守護(hù)進(jìn)程(linux后臺(tái)服務(wù)大多是守護(hù)進(jìn)程),但調(diào)試的時(shí)候會(huì)前臺(tái)運(yùn)行,對(duì)于守護(hù)進(jìn)程,我們只需要把日志記錄到某個(gè)文件中就行了,而對(duì)于前臺(tái)調(diào)試運(yùn)行,我們就只需要將日志輸出到控制臺(tái),所以這里是一點(diǎn)使用技巧。做到這點(diǎn)我們只需分別提供兩個(gè)配置文件即可,"/your/path"就是你放置配置文件的地方,一般可以設(shè)為你應(yīng)用程序部署的目錄下的etc目錄。
至此我們可以使用log4cplus了!以下是實(shí)際的日志輸出效果:
[11/05/12 10:28:36,002 INFO ] - TCPDomain - Connect server success!
[11/05/12 10:28:36,002 TRACE] - Session - Thread run.
[11/05/12 10:28:46,006 ERROR] - TCPDomain - SelectRead time out!
[11/05/12 10:28:56,016 ERROR] - TCPDomain - SelectRead time out!
7 log4cplus交叉編譯
對(duì)于嵌入式應(yīng)用 ,有交叉編譯這么一說。以上的介紹是基于PC的,如果你的平臺(tái)是嵌入式平臺(tái)如arm,則只需編譯鏈接arm平臺(tái)的log4cplus庫即可,其它都一樣。對(duì)于大多數(shù)autotools工程,其交叉編譯方法大致如下:
make
make install
其中--prefix即指定你的安裝目錄,如/opt/log4cplus,--host指定目標(biāo)平臺(tái),CXX指定你的交叉編譯工具(確保shell環(huán)境能找到該工具)。編譯安裝完后可在安裝目錄找到arm版本的庫文件。
8 總結(jié)
日志固然好,但也不建議隨意使用,用多了會(huì)導(dǎo)致程序性能有所下降,且代碼size增加不少。以上是筆者運(yùn)用log4cplus的些許經(jīng)驗(yàn),更深一步的原理機(jī)制有待進(jìn)一步探究。
配置文件
2
3 #指定rootCategory的log優(yōu)先級(jí)是ERROR,其Appenders有兩個(gè),分別是console,TESTAppender
4 log4cpp.rootCategory=ERROR, console,TESTAppender
5
6 #-------定義console屬性-------
7
8 #consoleAppender類型:控制臺(tái)輸出
9 #下面這三條語句表示控制臺(tái)輸出的log輸出的布局按照指定的格式;輸出格式是:[%p] %d{%H:%M:%S.%l} (%c): %m%n
10 log4cpp.appender.console=ConsoleAppender
11 log4cpp.appender.console.layout=PatternLayout
12 log4cpp.appender.console.layout.ConversionPattern=[%p] %d{%H:%M:%S.%l} (%c): %m%n
13
14 #-------定義TESTAppender的屬性-------
15
16 #RollingFileAppender類型:輸出到回卷文件,即文件到達(dá)某個(gè)大小的時(shí)候產(chǎn)生一個(gè)新的文件
17 #下面的語句表示文件輸出到指定的log文件,輸出的布局按照指定的格式,輸出的格式是:[%d{%Y-%m-%d %H:%M:%S.%l} - %p] (%c): %m%n
18 log4cpp.appender.TESTAppender=RollingFileAppender
19
20 #當(dāng)日志文件到達(dá)maxFileSize大小時(shí),將會(huì)自動(dòng)滾動(dòng)
21 log4cpp.appender.TESTAppender.maxFileSize=400000
22
23 #maxBackupIndex指定可以產(chǎn)生的滾動(dòng)文件的最大數(shù)
24 log4cpp.appender.TESTAppender.maxBackupIndex=3
25
26 #fileName指定信息輸出到logs/TESTAppender.txt文件
27 log4cpp.appender.TESTAppender.fileName=logs/TESTAppender.txt
28
29 #PatternLayout 表示可以靈活指定布局模式
30 log4cpp.appender.TESTAppender.layout=PatternLayout
31
32 #append=true 信息追加到上面指定的日志文件中,false表示將信息覆蓋指定文件內(nèi)容
33 log4cpp.appender.TESTAppender.append=true
34 log4cpp.appender.TESTAppender.layout.ConversionPattern=[%d{%Y-%m-%d %H:%M:%S.%l} - %p] (%c): %m%n
ConversionPattern的參數(shù)含義:
%d 輸出日志時(shí)間點(diǎn)的日期或時(shí)間,可以在其后指定格式,如上%d{%Y-%m-%d %H:%M:%S.%l},輸出類似:2017-02-14 09:25:00.953
%p 優(yōu)先級(jí),即DEBUG,INFO,WARN,ERROR,FATAL
%c 輸出日志信息所屬的類目,通常就是所在類的全名
%m 輸出log的具體信息
%n 回車換行
自定義封裝
將上述過程封裝,即可得到自己的日志類
class MyLog
{
private:
MyLog(bool b)
{
outToScreen = b;
}
~MyLog(){}
static MyLog * log;
bool outToScreen;//是否輸出日志信息到屏幕
static std::string _screenInfo;//屏幕日志信息
static std::string _logName;//文件日志名稱
static log4cpp::Category& logCat;
static log4cpp::Category& coutCat;
static log4cpp::FileAppender* logFile;//文件日志輸入
static log4cpp::OstreamAppender* logScreen;//屏幕日志輸入
static log4cpp::Priority::PriorityLevel logPri;//文件日志優(yōu)先級(jí)
static log4cpp::Priority::PriorityLevel coutPri;//屏幕日志優(yōu)先級(jí)
static log4cpp::PatternLayout* logLayout;//日志布局
static log4cpp::PatternLayout* screenLayout;//屏幕布局
public:
//獲取日志函數(shù),默認(rèn)參數(shù)選擇是否輸出到屏幕
static MyLog* getLog(bool toScreen = true,std::string coutName ="screenInfo",std::string logName = "log"){
if(MyLog::log == NULL)
{
MyLog::log = new MyLog(toScreen);
MyLog::_logName = logName;
MyLog::_screenInfo = coutName;
logScreen = new log4cpp::OstreamAppender("logScreen",&std::cout);
logFile = new log4cpp::FileAppender("logFile",MyLog::_logName);
//設(shè)置布局
MyLog::logLayout = new log4cpp::PatternLayout();
MyLog::screenLayout = new log4cpp::PatternLayout();
logLayout->setConversionPattern("%d{%Y/%m/%d,%H:%M:%S} -- [%p] %c: %m%n");
screenLayout->setConversionPattern("%d{%Y/%m/%d %H:%M:%S} -- [%p] %c: %m%n");
MyLog::logScreen->setLayout(screenLayout);
MyLog::logFile->setLayout(logLayout);
//追加到目錄
MyLog::logCat.addAppender(MyLog::logFile);
MyLog::coutCat.addAppender(MyLog::logScreen);
//設(shè)置優(yōu)先級(jí)
MyLog::logCat.setPriority(MyLog::logPri);
MyLog::coutCat.setPriority(MyLog::coutPri);
}
MyLog::log->outToScreen = toScreen;
return MyLog::log;
}
//銷毀日志對(duì)象
static void destoryLog()
{
log4cpp::Category::shutdown();
delete MyLog::log;
}
//設(shè)置日志記錄優(yōu)先級(jí)
static void setPri(log4cpp::Priority::PriorityLevel coutLevel,log4cpp::Priority::PriorityLevel logLevel)
{
MyLog::logPri = logLevel;
MyLog::coutPri = coutLevel;
MyLog::logCat.setPriority(MyLog::logPri);
MyLog::coutCat.setPriority(MyLog::coutPri);
}
//記錄日志,調(diào)用參數(shù)__FILE__, __LINE__ ,__FUNCTION__
void warn(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "warn")
{
char info[4096] = {0};
sprintf(info,"nIn file %s,line %d,function %s:%s",filename,line,function,msg);
if(this->outToScreen)
{
logCat.warn(info);
coutCat.warn(info);
}
else
{
logCat.warn(info);
}
}
void error(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "error")
{
char info[4096] = {0};
sprintf(info,"nIn file %s,line %d,function %s:%s",filename,line,function,msg);
if(this->outToScreen)
{
logCat.error(info);
coutCat.error(info);
}
else
{
logCat.error(info);
}
}
void debug(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "debug")
{
char info[4096] = {0};
sprintf(info,"nIn file %s,line %d,function %s:%s",filename,line,function,msg);
if(this->outToScreen)
{
logCat.debug(info);
coutCat.debug(info);
}
else
{
logCat.debug(info);
}
}
void info(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "info")
{
char info[4096] = {0};
sprintf(info,"nIn file %s,line %d,function %s:%s",filename,line,function,msg);
if(this->outToScreen)
{
logCat.info(info);
coutCat.info(info);
}
else
{
logCat.info(info);
}
}
};
MyLog* MyLog::log = NULL;
std::string MyLog::_screenInfo = "screenInfo";
std::string MyLog::_logName = "log";
log4cpp::Category& root = log4cpp::Category::getRoot();
log4cpp::Category& MyLog::logCat = root.getInstance(MyLog::_logName);
log4cpp::Category& MyLog::coutCat = root.getInstance(MyLog::_screenInfo);
log4cpp::Priority::PriorityLevel MyLog::coutPri = log4cpp::Priority::INFO;
log4cpp::Priority::PriorityLevel MyLog::logPri = log4cpp::Priority::NOTSET;
log4cpp::PatternLayout* MyLog::logLayout = NULL;
log4cpp::PatternLayout* MyLog::screenLayout = NULL;
log4cpp::FileAppender* MyLog::logFile = NULL;//文件日志輸入
log4cpp::OstreamAppender* MyLog::logScreen = NULL;//屏幕日志輸入
//為避免每次調(diào)用都要填寫參數(shù)__FILE__,__LINE__和__FUNCTION__,可以使用帶參數(shù)的宏定義
#define MyLogWARN(msg) MyLog::getLog()->warn(msg,__FILE__,__LINE__,__FUNCTION__);
#define MyLogINFO(msg) MyLog::getLog()->info(msg,__FILE__,__LINE__,__FUNCTION__);
#define MyLogERROR(msg) MyLog::getLog()->error(msg,__FILE__,__LINE__,__FUNCTION__);
#define MyLogDEBUG(msg) MyLog::getLog()->debug(msg,__FILE__,__LINE__,__FUNCTION__);
發(fā)送到遠(yuǎn)程服務(wù)器
應(yīng)用程序中SocketAppender配置
前面啟動(dòng)了LoggingServer,下面說一下需要收集其日志的各個(gè)應(yīng)用程序中配置。
說白了,就是在原來的基礎(chǔ)上加一個(gè)SocketAppender,SocketAppender會(huì)自動(dòng)將日志發(fā)送給loggingserver。這樣直接在loggingserver上就可以查看所有服務(wù)器上的日志信息啦。哥終于不用再擔(dān)心你們的運(yùn)行狀態(tài)啦!
配置文件
log4cplus.logger.rollfile=TRACE,R2
log4cplus.appender.STDOUT=log4cplus::ConsoleAppender
log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout
log4cplus.appender.STDOUT.layout.ConversionPattern=@@@[%d %d{%Z %Q}][pthread_id:%t][%l][%-5p][%c{2}] - %m%n
log4cplus.appender.R2=log4cplus::RollingFileAppender
log4cplus.appender.R2.File=./mytest.log
log4cplus.appender.R2.MaxFileSize=4MB
log4cplus.appender.R2.MaxBackupIndex=5
log4cplus.appender.R2.layout=log4cplus::PatternLayout
log4cplus.appender.R2.layout.ConversionPattern=@@@[%d %d{%Z %Q}][pthread_id:%t][%l][%-5p][%c{2}] - %m%n
log4cplus.appender.RemoteServer=log4cplus::SocketAppender
log4cplus.appender.RemoteServer.host=192.168.2.130
log4cplus.appender.RemoteServer.port=9000
還可以輸出到多個(gè)server,配置如下:
log4cplus.logger.rollfile=TRACE,R2
log4cplus.appender.STDOUT=log4cplus::ConsoleAppender
log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout
log4cplus.appender.STDOUT.layout.ConversionPattern=@@@[%d %d{%Z %Q}][pthread_id:%t][%l][%-5p][%c{2}] - %m%n
#log4cplus.appender.STDOUT.layout.ConversionPattern=@@@[%l][%-5p] - %m%n
log4cplus.appender.R2=log4cplus::RollingFileAppender
log4cplus.appender.R2.File=/opt/apps/3k/i1client/i1client.log
log4cplus.appender.R2.MaxFileSize=10000KB
log4cplus.appender.R2.MaxBackupIndex=100
log4cplus.appender.R2.layout=log4cplus::PatternLayout
log4cplus.appender.R2.layout.ConversionPattern=@@@[%d %d{%Z %Q}][pthread_id:%t][%l][%-5p][%c{2}] - %m%n
#log4cplus.appender.R2.layout.ConversionPattern=@@@[%l][%-5p] - %m%n
log4cplus.appender.RemoteServer=log4cplus::SocketAppender
log4cplus.appender.RemoteServer.host=192.168.2.130
log4cplus.appender.RemoteServer.port=9000
log4cplus.appender.RemoteServer2=log4cplus::SocketAppender
log4cplus.appender.RemoteServer2.host=192.168.2.131
log4cplus.appender.RemoteServer2.port=9001
測試:
在Log4cplus的源碼包中,有一個(gè)loggingServer目錄,該目錄中實(shí)現(xiàn)了一個(gè)LoggingServer。
在編譯Log4cplus時(shí),會(huì)自動(dòng)編譯該目錄,在目錄中生成loggingServer可執(zhí)行文件,當(dāng)然可以自己make(需要依賴log4cplus庫)。
loggingServer使用方式如下:
./loggingserver 9000 log4cplus.properties
9000表示監(jiān)聽的端口號(hào)(不需要地址,默認(rèn)監(jiān)聽本機(jī)地址)
log4cplus.properties是一個(gè)log4cplus的配置文件,和普通的log4cplus配置文件相同,loggingserver收到各個(gè)socket發(fā)來的日志后,根據(jù)配置文件信息,將其寫入文件。
服務(wù)端配置文件如下
log4cplus.rootLogger=TRACE, STDOUT, ALL_MSGS
#Appender輸出位置類型:控制臺(tái)輸出
log4cplus.appender.STDOUT=log4cplus::ConsoleAppender
#日志輸出格式 有詞法分析功能的模式布局器
log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout
log4cplus.appender.STDOUT.layout.ConversionPattern=@@@[%d %d{%Z %Q}][pthread_id:%t][%l][%-5p][%c{2}] - %m%n
#Appender輸出位置類型有:文件輸出 設(shè)置日志追加到文件尾
log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender
#設(shè)置輸出日志路徑
log4cplus.appender.ALL_MSGS.File=log/i1client.log
#設(shè)置日志文件大小
log4cplus.appender.ALL_MSGS.MaxFileSize=1024KB
#設(shè)置生成日志最大個(gè)數(shù)
log4cplus.appender.ALL_MSGS.MaxBackupIndex=20
#日志輸出格式 有詞法分析功能的模式布局器
log4cplus.appender.ALL_MSGS.layout=log4cplus::PatternLayout
log4cplus.appender.ALL_MSGS.layout.ConversionPattern=@@@[%d %d{%Z %Q}][pthread_id:%t][%l][%-5p][%c{2}] - %m%n
-
服務(wù)器
+關(guān)注
關(guān)注
12文章
9335瀏覽量
86133 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
3064瀏覽量
74382 -
程序
+關(guān)注
關(guān)注
117文章
3798瀏覽量
81457 -
C++
+關(guān)注
關(guān)注
22文章
2114瀏覽量
73890 -
日志
+關(guān)注
關(guān)注
0文章
139瀏覽量
10684
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
【龍芯2K0300蜂鳥板試用】tinnu-試用報(bào)告-基于以太網(wǎng)的CAN數(shù)據(jù)中繼模塊
請(qǐng)問STM32F4與XMC4500各有什么優(yōu)點(diǎn)優(yōu)勢(shì)?
LOG101,pdf(Precision Logarithm
LOG102,pdf(Precision Logarithm
LOG112,LOG2112,pdf(Precision Logarithmic and Log Ratio Ampli
LOG101/LOG104偏流調(diào)零電路
![<b class='flag-5'>LOG</b>101/<b class='flag-5'>LOG</b>104偏流調(diào)零電路](https://file1.elecfans.com//web2/M00/A4/E6/wKgZomUMNiOAfouvAAB-g2urIBs861.jpg)
使用Keysight免費(fèi)評(píng)估Log4j/Log4Shell零日漏洞
3個(gè)開源庫讓單片機(jī)開發(fā)更簡單
推薦3個(gè)非常不錯(cuò)的開源日志庫
log4j日志框架分析
![<b class='flag-5'>log4</b>j日志框架分析](https://file.elecfans.com/web2/M00/94/E9/pYYBAGP9n36ARCiXAAGH2W3m17o808.png)
C#上位機(jī)開發(fā)(十三)之使用Log4net添加日志記錄功能
![C#上位機(jī)開發(fā)(十三)之使用<b class='flag-5'>Log4</b>net添加日志記錄功能](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評(píng)論