本篇介绍Logging系统。
许多大型系统都会提供一种基于控制台的消息记录功能,用来向用户即时地反馈程序执行情况。ns-3也不例外。ns-3提供了一种可选的、多等级的消息记录功能—Logging系统。Logging功能可以被完全禁用,可以逐个组件启用,也可以全局启用。Logging系统还可以选择所记录消息的详细程度(verbosity level)或严重程度(severity level),以及为消息添加不同的前缀(prefix)。因此,Logging系统提供了一种非常灵活且功能强大,但使用相对简单的消息记录功能 [1] [2]。
需要说明的是,Logging系统在ns-3中主要用于:一是通过输出网络组件内部模块执行过程方便用户理解;二是通过输出简单的调试信息方便用户调试,而非收集仿真数据 [3]。ns-3提供了一种收集仿真数据的通用机制—Tracing系统,这是我们收集仿真数据的首选 [2]。
上面提到,Logging系统提供了一种非常灵活的消息记录功能,不仅可以逐个组件地启用Logging功能,还可以选择所记录消息的详细程度或严重程度,并为消息添加不同的前缀。
因此,在介绍如何使用Logging系统之前,在这一节,我们先介绍日志组件(Log Component)、控制日志输出的详细程度或严重程度选项以及前缀选项等基本概念。
日志组件是Logging系统的最小管理单位。通俗地说,一个日志组件就是指一个C++源代码文件。通过日志组件,Logging系统就可以把信息输出控制精确到某一个C++文件 [1] [4]。
而要把一个C++文件添加为日志组件也很容易,只要在C++文件开头在ns3名字空间范围内使用NS_LOG_COMPONENT_DEFINE宏注册即可。日志组件名必须是全局唯一的(通常基于文件名或文件中定义的类的名)。
例如,在first.cc中LogComponentEnable()中的第一个参数即是日志组件名(关于LogComponentEnable函数我们会在后面再介绍),如下图所示:
UdpEchoClinetApplication日志组件和UdpEchoServerApplication日志组件代表的分别是udp-echo-client.cc文件和udp-echo-server.cc文件,是在各自文件开头在ns3名字空间范围内使用NS_LOG_COMPONENT_DEFINE宏注册的,如下图所示:
上面说过,通过日志组件,Logging系统可以把信息输出控制精确到某一个C++文件。而通过日志详细程度(或严重程度)选项,Logging系统可以进一步把信息输出控制精确到某一个C++文件的某一层级。
ns-3中,Logging系统通常提供了7个日志详细程度(或严重程度)选项,输出信息的详细程度由低到高(即输出信息由少到多)或者说信息的严重程度由高到低依次为 [1]:
注释:
实际上,ns-3在src/core/model/log.h中还定义了一个日志等级LOG_NONE,其不会记录任何信息。
我们还是以first.cc为例,LogComponentEnable()中的第二个参数是日志详细程度(严重程度)选项,如下图所示:
需要注意的是,除了LOG_ALL之外,其余6个日志详细程度(或严重程度)选项只能控制其所关联的宏中所输出的信息的输出。例如,LOG_INFO等级只能控制指定日志组件中所有NS_LOG_INFO宏所输出的信息的输出。
为了更加直观地说明这一点,我们看一下下面这个我们自己编写的log-level-demo.cc示例脚本,同时顺便回顾和实战上面介绍的日志组件相关知识:
首先,我们在文件开头的ns3名字空间范围内使用NS_LOG_COMPONENT_DEFINE宏将该文件注册为一个名为“LogLevelDemo”的日志组件。
接着,我们在main函数中使用LogComponentEnable函数启用了LogLevelDemo日志组件,并将日志详细程度(或严重程度)选项设置为LOG_INFO(关于LogComponentEnable函数的使用,我们稍后再详细介绍)。
然后,我们在ns-3.37目录下,在终端中使用./ns3编译运行该脚本(log-level-demo.cc文件需放在ns-3.37/scratch目录下):
第一次编译运行log-level-demo.cc文件时会先自动进行默认配置。
结果如下:
我们可以看到,只有NS_LOG_INFO宏中的信息被输出。
我们可以修改LogComponentEnable的日志详细程度(或严重程度)选项为LOG_DEBUG,再次使用./ns3编译运行该脚本,结果如下:
我们可以看到,只有NS_LOG_DEBUG宏中的信息被输出。
注释:
如果配置选项为optimized,则不会有信息被输出。因为日志声明在optimized配置下不会被编译 [1]。
如果我们希望同时输出NS_LOG_INFO宏和NS_LOG_DEBUG宏中的信息,那么我们可以这样:
再次使用./ns3编译运行该脚本,结果如下:
NS_LOG_INFO宏和NS_LOG_DEBUG宏中的信息都被输出了!
有时候,我们想把某一详细程度(或严重程度)及更低详细程度(或更高严重程度)的信息一并输出。例如,我们想同时输出LOG_INFO及更低详细程度(或更高严重程度)的信息。如果像上面一样显式指定的话会比较繁琐,因此Logging系统针对每个LOG_TYPE(LOG_NONE除外)还定义了一个对应的LOG_LEVEL_TYPE,可以输出LOG_TYPE及更低详细程度(或更高严重程度)的信息 [1]:
注释:
实际上,LOG_LEVEL_ERROR和LOG_ERROR是一样的,因为他们都只显示自己层级的日志;LOG_LEVEL_ALL和LOG_ALL也是是一致的,因为他们都显示所有层级的日志。
我们还是以log-level-demo.cc示例脚本为例,将LogComponentEnable函数中的第二个参数做如下修改:
再次使用./ns3编译运行该脚本,结果如下:
可以看到LOG_INFO及更低详细程度(或更高严重程度)的信息都被输出了。
除了日志详细程度(严重程度)选项外,Logging系统还提供了日志前缀选项。这些前缀可以帮助识别消息何时来源于何处,以及严重等级 [1]:
我们还是以log-level-demo.cc示例脚本为例,对LogComponentEnable函数做如下修改:
再次使用./ns3编译运行该脚本,结果如下:
输出信息前面加上了日志详细程度(或严重程度)前缀。
我们再对LogComponentEnable函数做如下修改:
再次使用./ns3编译运行该脚本,结果如下:
输出信息前面加上了日志详细程度(或严重程度)前缀以及调用函数名前缀(需要注意的是函数名前缀main()前面的是日志组件名而非文件名或类名)。
ns-3提供了两种常用的控制日志输出的方法 [1]:
通过LogComponentEnable函数控制日志输出:在main()主函数中通过LogComponentEnable函数显式声明
LogComponentEnable函数的第一个参数是日志组件名(字符串),第二个参数是LogLevel选项,包括日志详细程度(严重程度)选项和日志前缀选项,其语法如下:
LogComponentEnable(,
关于如何通过LogComponentEnable函数控制日志输出,我们不再赘述,具体可参考上面的内容。
通过NS_LOG环境变量控制日志输出
通过上面log-level-demo.cc示例脚本的大量演示我们其实不难发现,通过LogComponentEnable函数控制日志输出我们需要一遍遍修改源代码,比较繁琐。所以ns-3还提供了通过NS_LOG环境变量的方式控制日志输出,其语法如下:
/*
每个日志组件的选项罗列在日志组件的后面,用“|”隔开,日志组件和其所对应的选项之间用“=”连接
不同日志组件之间用“:”隔开
*/
NS_LOG="=| ... : =| ..." ./ns3 run