日志库EasyLogging++学习系列(3)—— 配置功能

在前面的文章 《日志库Easylogging++学习系列(1) —— 简要介绍 》中,我们已经初步见识到了 Easylogging++ 日志库强大的配置功能。那么配置文件中各个字段的意义是什么呢?我们应该如何编写自己的配置文件呢?又或者说,除了配置文件之外,我们还有没有别的方法可以完成日志的配置功能呢?希望各位有疑惑的小伙伴在看了本文的内容之后,都能够找到自己满意的答案!

要完成 Easylogging++ 日志的配置功能,可以通过三种方法去实现,而且每一种方法都非常简单。第一种方法就是使用配置文件,这种方法的好处就是只要修改配置文件即可实现日志格式的重新配置,而不需要修改源程序代码,缺点就是发布程序时必须打包配置文件一起发布,否则程序无法正常运行。在一些小项目中,特别是在只有一个EXE运行文件的程序中,如果我们不希望程序带着一个多余的日志配置文件,那么我们可以使用另外一种方法,就是使用 el::Configurations 类提供的成员函数,这种方法和第一种方法的优缺点刚好相反。最后一种方法就是使用 Easylogging++ 的内联配置功能,但是并不推荐使用这种方法,因为它会显得配置十分凌乱。

方法一:使用配置文件

在程序运行时,可以通过使用 el::Configurations 类加载配置文件来完成 Easylogging++ 的配置功能,配置文件必须遵循下面的语法:

* LEVEL:  
  CONFIGURATION NAME  = "VALUE" ## Comment  
  CONFIGURATION NAME  = "VALUE" 

语法简要说明如下:

  • *LEVEL,指的是日志级别,以星号符“*”开始,并以英文冒号“:”结束。
  • CONFIGURATION NAME ,指的是配置项的名称,全部名称详见下面的表格。
  • "VALUE",指的是配置项的值,各个配置项对应的值类型详见下面的表格。
  • ## Comment,指的是注释,其中两个连续井号"##"表示注释,Comment是注释的内容。注释是可有可无的,但是千万不要在注释中使用双引号,否则可能会出现意想不到的错误。

编写配置文件时,强烈建议先写 Global 级别的配置,这样的好处是可以使其他任何未在配置文件中明确指出的级别都将会自动地继承并使用 Global 级别的配置。比如,如果你想把所有级别的日志都保存在同一个文件里,你只需要在 Global 级别中设置 Filename 就可以了,其他级别的日志将会默认的使用 Global 级别中的 Filename 。下面的表格列举了GitHub上给出的Easylogging++在配置文件中支持的配置项:

 

Configuration NameTypeDescription
EnabledboolDetermines whether or not corresponding level for logger is enabled. You may disable all logs by usingel::Level::Global
To_FileboolWhether or not to write corresponding log to log file
To_Standard_OutputboolWhether or not to write logs to standard output e.g, terminal or command prompt
Formatchar*Determines format/pattern of logging for corresponding level and logger.
Filenamechar*Determines log file (full path) to write logs to for corresponding level and logger
Milliseconds_WidthuintSpecifies milliseconds width. Width can be within range (1-6)
Performance_TrackingboolDetermines whether or not performance tracking is enabled. This does not depend on logger or level. Performance tracking always uses 'performance' logger unless specified
Max_Log_File_Sizesize_tIf log file size of corresponding level is >= specified size, log file will be truncated.
Log_Flush_Thresholdsize_tSpecifies number of log entries to hold until we flush pending log data

 

下面再回顾一下《日志库Easylogging++学习系列(1) —— 简要介绍 》这篇文章中给出的名称为 my_log.conf 的配置文件:

* GLOBAL:  
    ENABLED                 =   true  
    TO_FILE                 =   true  
    TO_STANDARD_OUTPUT      =   true  
    FORMAT                  =   "[%level | %datetime] | %msg"  
    FILENAME                =   "log\\log_%datetime{%Y%M%d}.log"  
    MILLISECONDS_WIDTH      =   3  
    PERFORMANCE_TRACKING    =   false  
    MAX_LOG_FILE_SIZE       =   1048576  
    LOG_FLUSH_THRESHOLD     =   0  
      
* TRACE:  
    FILENAME                =   "log\\trace_log_%datetime{%Y%M%d}.log"  
      
* DEBUG:  
    FILENAME                =   "log\\debug_log_%datetime{%Y%M%d}.log"  
      
* FATAL:  
    ENABLED                 =   false  
      
* ERROR:  
    FILENAME                =   "log\\error_log_%datetime{%Y%M%d}.log"  
      
* WARNING:  
    FILENAME                =   "log\\warning_log_%datetime{%Y%M%d}.log"  
      
* INFO:  
    FILENAME                =   "log\\info_log_%datetime{%Y%M%d}.log"  
      
* VERBOSE:  
    ENABLED                 =   false  

在上面的文件中,我们以 Global 级别开始,并在 Global 级别中把 Easylogging++ 支持的所有的配置项都设置好了,这样 Global 级别的配置就会被后续的日志级别所继承,一直到后续的日志级别再次明确地设置配置项才会覆盖原有的来自于 Global 级别的配置。比如这里的TRACE、DEBUG、ERROR、WARNING、INFO等级别都显式地定义了 Filename ,所以这几个级别的日志记录会各自保存在相应的文件里面。而FATAL、VERBOSE这两个级别则显式地定义了 ENABLE ,并将其值设置为false,所以这两个级别的日志记录将会被禁用。下面的代码再次演示如何使用该配置文件(注意注释的内容):

#include "easylogging++.h"  
  
INITIALIZE_EASYLOGGINGPP  
  
int main(int argc, char** argv)  
{  
    /*/ 
    必须设置标记 LoggingFlag::StrictLogFileSizeCheck 
    否则,配置文件中MAX_LOG_FILE_SIZE = 1048576不生效 
    /*/  
    el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);  
  
    el::Configurations conf("my_log.conf");  
  
    /// 可以单独设置某一个logger的配置  
    el::Loggers::reconfigureLogger("default", conf);  
  
    /// 也可以设置全部logger的配置  
    el::Loggers::reconfigureAllLoggers(conf);  
  
    LOG(INFO) << "***** info log  *****";  
  
    system("pause");  
    return 0;  
}  

方法二:使用 el::Configurations 类的成员函数

在 Easylogging++ 日志库中,封装了配置类 el::Configurations,该类提供了完成日志配置功能的全部接口,建议参考该类的源码,可以查看和了解更多更详细和更全面的功能接口。下面的代码演示了几个常用接口的用法:

#include "easylogging++.h"  
  
INITIALIZE_EASYLOGGINGPP  
  
int main(int argc, const char** argv)   
{  
    /// 使用默认配置  
    el::Configurations defaultConf;  
    defaultConf.setToDefault();  
    LOG(INFO) << "Using el::Configurations class";  
      
    /// 重新设置INFO级别的配置项FORMAT的值  
    defaultConf.set(el::Level::Info,  
        el::ConfigurationType::Format, "%datetime %level %msg");  
    /// 重新设置配置  
    el::Loggers::reconfigureLogger("default", defaultConf);  
    LOG(INFO) << "Using el::Configurations class";  
  
    // 重新设置GLOBAL级别的配置项FORMAT的值  
    defaultConf.setGlobally(  
        el::ConfigurationType::Format, "%datetime %msg");  
    /// 重新设置配置  
    el::Loggers::reconfigureLogger("default", defaultConf);  
    LOG(INFO) << "Using el::Configurations class";  
  
    system("pause");  
    return 0;  
}  

方法三:使用内联配置功能

所谓的内联配置功能,就是说你可以通过使用 std::string 字符串来完成日志的配置功能,但是要注意在 std::string 字符串中加上换行符保证字符串的格式遵循配置文件中的格式。比如,如果要使用内联配置功能实现和方法二中一样的输出格式,那么实现代码必须像下面这样:

#include "easylogging++.h"  
  
INITIALIZE_EASYLOGGINGPP  
  
int main(int argc, const char** argv)   
{  
    /// 使用默认配置  
    el::Configurations defaultConf;  
    defaultConf.setToDefault();  
    LOG(INFO) << "Using inline configuration";  
      
    /// 重新设置INFO级别的配置项FORMAT的值  
    defaultConf.parseFromText("*INFO:\n FORMAT = %datetime %level %msg");  
    /// 重新设置配置  
    el::Loggers::reconfigureLogger("default", defaultConf);  
    LOG(INFO) << "Using inline configuration";  
  
    // 重新设置GLOBAL级别的配置项FORMAT的值  
    defaultConf.parseFromText("*INFO:\n FORMAT = %datetime %msg");  
    /// 重新设置配置  
    el::Loggers::reconfigureLogger("default", defaultConf);  
    LOG(INFO) << "Using inline configuration";  
  
    system("pause");  
    return 0;  
}  
#include "easylogging++.h"
 
INITIALIZE_EASYLOGGINGPP
 
int main(int argc, const char** argv) 
{
	/// 使用默认配置
	el::Configurations defaultConf;
	defaultConf.setToDefault();
	LOG(INFO) << "Using inline configuration";
	
	/// 重新设置INFO级别的配置项FORMAT的值
	defaultConf.parseFromText("*INFO:\n FORMAT = %datetime %level %msg");
	/// 重新设置配置
	el::Loggers::reconfigureLogger("default", defaultConf);
	LOG(INFO) << "Using inline configuration";
 
	// 重新设置GLOBAL级别的配置项FORMAT的值
	defaultConf.parseFromText("*INFO:\n FORMAT = %datetime %msg");
	/// 重新设置配置
	el::Loggers::reconfigureLogger("default", defaultConf);
	LOG(INFO) << "Using inline configuration";
 
	system("pause");
	return 0;
}

仔细看上面的代码,注意每一次调用函数parseFromText()的参数中都包含了一个换行符 “\n”。假如没有这个换行符,就不能正确的解析出配置项的内容,从而会导致配置无效。由于这个原因,当需要设置的配置项很多的时候,如果还使用内联配置功能完成日志的配置,就会使得代码变得凌乱复杂,而且很容易造成错误,所以尽量不要使用这种方法或者避免使用这种方法。

默认配置功能

如果你希望为现有的或者将来新建的日志记录器设置一个默认的配置,你可以使用下面的这个函数:

el::Loggers::setDefaultConfigurations(el::Configurations& configurations, bool configureExistingLoggers = false)

当你在编写大型程序或者调用同样使用 Easylogging++ 的第三方库时,默认配置功能显得十分有用。 这个功能使得后续任何新建的日志记录器,都将会使用默认配置;如果你希望现有的日志记录器也使用默认配置,只需把函数的第二个参数设置为 true 即可。下面的代码演示了默认配置功能:

#include "easylogging++.h"
 
INITIALIZE_EASYLOGGINGPP
 
int main(void)
{
	el::Configurations defaultConf;
	defaultConf.setGlobally(el::ConfigurationType::Format, "[%logger] %level: %msg");
 
	/// 只为新建的日志记录器设置默认配置
	el::Loggers::setDefaultConfigurations(defaultConf);
	LOG(INFO) << "Set default configuration but existing loggers not updated yet";
 
	/// 新建日志记录器 testDefaultConf1
	el::Loggers::getLogger("testDefaultConf1");
	CLOG(INFO, "testDefaultConf1") << "Logging using new logger 1";
 
	// 为现有的日志记录器设置默认配置
	el::Loggers::setDefaultConfigurations(defaultConf, true);
	LOG(INFO) << "Existing loggers updated as well";
 
	/// 新建日志记录器 testDefaultConf2
	el::Loggers::getLogger("testDefaultConf2");
	CLOG(INFO, "testDefaultConf2") << "Logging using new logger 2";
 
	system("pause");
	return 0;
}

全局配置功能

全局配置并不是指 Global 级别,而是指利用全局配置文件为全部或部分,甚至是为新建的日志记录器注册配置。全局配置文件必须遵循下面的语法:

-- LOGGER ID ## Case sensitive
  ## Everything else is same as configuration file

-- ANOTHER LOGGER ID
  ## Configuration for this logger

语法简要说明如下:

  • LOGGER ID ,指的是记录器 ID,大小写敏感,并以两个破折号开始。
  • 其余部分的语法规则和本文中方法一的配置文件的语法规则完全一样。

一旦你编写好了全局配置文件,你仅需使用一个函数就可以完成你的日志记录器的配置,甚至是注册一个全新的日志记录器。需要注意的是,你不能使用空白的配置来注册新的日志记录器,也就是说在 LOGGER ID 下面,你至少得定义一个配置项。下面的代码演示了全局配置功能:

#include "easylogging++.h"
 
INITIALIZE_EASYLOGGINGPP
 
int main(void) 
{
	LOG(INFO) << "Info log before using global configuration";
 
	/// 只需一个函数即可实现全局配置功能
	el::Loggers::configureFromGlobal("global.conf");
 
	LOG(INFO) << "Info log AFTER using global configuration";
	LOG(ERROR) << "Error log AFTER using global configuration";
 
	/// 用全局配置文件中新建的日志记录器 testGlobalConf 来记录日志
	CLOG(TRACE, "testGlobalConf") << "TRACE Logging using new logger";
	CLOG(DEBUG, "testGlobalConf") << "DEBUG Logging using new logger";
	CLOG(WARNING, "testGlobalConf") << "WARNING Logging using new logger";
	CLOG(ERROR, "testGlobalConf") << "ERROR Logging using new logger";
	CLOG(INFO, "testGlobalConf") << "INFO Logging using new logger";
 
	system("pause");
	return 0;
}

其中全局配置文件 global.conf 的内容如下:

-- default 
   *INFO:
      FORMAT   = "%level %msg"
      FILENAME = "/tmp/logs/wow.log"
   *ERROR:
      FORMAT   = "%levshort %fbase:%line %msg"

	  
-- testGlobalConf
   *GLOBAL:
      FORMAT                  =   "[%level | %datetime] | %msg"
      ENABLED                 =   true
      TO_FILE                 =   true
      TO_STANDARD_OUTPUT      =   true
      MILLISECONDS_WIDTH      =   3

读取配置

在某些情况下,如果我们想要获取某一个日志记录器的当前配置, 可以通过使用 el::Loggers 类的成员函数 typedConfigurations()来实现,建议参考该类的源码,可以查看和了解更多更详细和更全面的功能接口,下面的代码片段演示了该功能:

el::Logger* defaultLogger = el::Loggers::getLogger("default");
/// 记录器default是否被禁用
bool enabled = defaultLogger->typedConfigurations()->enabled(el::Level::Info);
/// 记录器default的INFO级别的日志输出格式
std::string format =  defaultLogger->typedConfigurations()->logFormat(el::Level::Info).format();

学习配置日志是掌握 Easylogging++ 使用的关键,通过灵活地运用配置功能,才能更好地将 Easylogging++ 的强悍之处应用到我们的程序开发当中。在本文列举了几个比较通用的配置方法,然而,在某些应用场景下,我们还需要通过其他的一些配置手段来辅助我们完成某些特殊功能,这些辅助配置手段包括设置命令行参数、设置日志标记、配置宏定义等,限于篇幅,后面的文章再对这些内容的学习过程逐一进行记录。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Easylogging是一个简单易用的日志,它可以帮助开发人员在程序运行过程中记录并追踪各种日志信息。它提供了许多便捷的方法,使开发人员能够根据实际需要对日志进行配置,并将其输出到不同的地方,如控制台、文件等。 多线程是一种在同一个进程中同时执行多个任务的方法。多线程可以提高程序的并发性和效率,但也会带来一些问题。其中一个常见的问题是内存暴涨。 内存暴涨指的是程序在运行过程中占用的内存空间急剧增加。多线程程序中,每个线程都有自己的栈空间,用于存储局部变量等数据。当多个线程同时执行时,可能会导致大量的栈帧被同时创建和销毁,从而占用大量的内存空间。此外,多线程程序还可能存在共享数据的问题,需要使用一些同步机制来保证数据的正确性,这也会增加内存的开销。 为了解决多线程程序中的内存暴涨问题,可以采取一些措施。首先,可以对线程进行优化,尽量减少线程的创建和销毁次数,减少栈空间占用。其次,可以优化共享数据的访问方式,使用一些高效的同步机制,如读写锁、原子操作等,减少内存开销。此外,还可以使用一些内存管理工具来监测和调优程序的内存使用情况,及时发现和解决内存暴涨问题。 总结来说,Easylogging可以帮助我们方便地记录和追踪日志信息,多线程能够提高程序的并发性和效率,但同时也会带来内存暴涨的问题。为了解决内存暴涨,我们可以采取一些优化措施,减少线程的创建和销毁次数,优化共享数据的访问方式,并使用内存管理工具监测和调优程序的内存使用情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值