【ns-3】Logging系统

article/2025/10/11 17:25:56

文章目录

  • 前言
  • 1. 概述
  • 2. 基本概念
    • 2.1 日志组件
    • 2.2 日志详细程度(严重程度)选项
    • 2.3 日志前缀选项
  • 3. 控制日志输出
  • 参考文献

前言

本篇介绍Logging系统。


1. 概述

许多大型系统都会提供一种基于控制台的消息记录功能,用来向用户即时地反馈程序执行情况。ns-3也不例外。ns-3提供了一种可选的、多等级的消息记录功能—Logging系统。Logging功能可以被完全禁用,可以逐个组件启用,也可以全局启用。Logging系统还可以选择所记录消息的详细程度(verbosity level)或严重程度(severity level),以及为消息添加不同的前缀(prefix)。因此,Logging系统提供了一种非常灵活且功能强大,但使用相对简单的消息记录功能 [1] [2]。

需要说明的是,Logging系统在ns-3中主要用于:一是通过输出网络组件内部模块执行过程方便用户理解;二是通过输出简单的调试信息方便用户调试,而非收集仿真数据 [3]。ns-3提供了一种收集仿真数据的通用机制—Tracing系统,这是我们收集仿真数据的首选 [2]。


2. 基本概念

上面提到,Logging系统提供了一种非常灵活的消息记录功能,不仅可以逐个组件地启用Logging功能,还可以选择所记录消息的详细程度或严重程度,并为消息添加不同的前缀。

因此,在介绍如何使用Logging系统之前,在这一节,我们先介绍日志组件(Log Component)、控制日志输出的详细程度或严重程度选项以及前缀选项等基本概念。

2.1 日志组件

日志组件是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宏注册的,如下图所示:

在这里插入图片描述
在这里插入图片描述
 

2.2 日志详细程度(严重程度)选项

上面说过,通过日志组件,Logging系统可以把信息输出控制精确到某一个C++文件。而通过日志详细程度(或严重程度)选项,Logging系统可以进一步把信息输出控制精确到某一个C++文件的某一层级。

ns-3中,Logging系统通常提供了7个日志详细程度(或严重程度)选项,输出信息的详细程度由低到高(即输出信息由少到多)或者说信息的严重程度由高到低依次为 [1]:

  • LOG_ERROR:记录错误信息(关联的宏为:NS_LOG_ERROR)
  • LOG_WARN:记录警告信息(关联的宏为:NS_LOG_WARN)
  • LOG_DEBUG:记录相对罕见、特别的调试消息(关联的宏为:NS_LOG_DEBUG)
  • LOG_INFO:记录程序进程相关的信息(关联的宏为:NS_LOG_INFO)
  • LOG_FUNCTION:记录描述每个调用函数的消息(关联的宏为:NS_LOG_FUNCTION和NS_LOG_FUNCTION_NOARGS)
  • LOG_LOGIC:记录函数内部逻辑流信息(关联的宏为:NS_LOG_LOGIC)
  • LOG_ALL:记录上述所有信息(无关联的宏)

注释

实际上,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宏中的信息被输出。


注释:

  • 如果按照上述步骤操作没有信息被输出,可以检查一下配置选项(./ns3 show profile):

在这里插入图片描述
如果配置选项为optimized,则不会有信息被输出。因为日志声明在optimized配置下不会被编译 [1]。

  • 实际上,上述日志等级并没有一个非常严格的界限划分(依赖于关联的NS_LOG_TYPE宏的使用),因此有时候阅读C++源码是在所难免的 [4]。

如果我们希望同时输出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_LEVEL_WARN
  • LOG_LEVEL_DEBUG
  • LOG_LEVEL_INFO
  • LOG_LEVEL_FUNCTION
  • LOG_LEVEL_LOGIC
  • LOG_LEVEL_ALL / LOG_ALL

注释:

实际上,LOG_LEVEL_ERROR和LOG_ERROR是一样的,因为他们都只显示自己层级的日志;LOG_LEVEL_ALL和LOG_ALL也是是一致的,因为他们都显示所有层级的日志。


我们还是以log-level-demo.cc示例脚本为例,将LogComponentEnable函数中的第二个参数做如下修改:

在这里插入图片描述

再次使用./ns3编译运行该脚本,结果如下:

在这里插入图片描述
可以看到LOG_INFO及更低详细程度(或更高严重程度)的信息都被输出了。

 

2.3 日志前缀选项

除了日志详细程度(严重程度)选项外,Logging系统还提供了日志前缀选项。这些前缀可以帮助识别消息何时来源于何处,以及严重等级 [1]:

  • LOG_PREFIX_FUNC:加上调用函数名前缀
  • LOG_PREFIX_TIME:加上仿真时间前缀
  • LOG_PREFIX_NODE:加上节点id前缀
  • LOG_PREFIX_LEVEL:加上详细程度(或严重程度)前缀
  • LOG_PREFIX_ALL:加上所有前缀

我们还是以log-level-demo.cc示例脚本为例,对LogComponentEnable函数做如下修改:

在这里插入图片描述
再次使用./ns3编译运行该脚本,结果如下:

在这里插入图片描述
输出信息前面加上了日志详细程度(或严重程度)前缀。

我们再对LogComponentEnable函数做如下修改:

在这里插入图片描述
再次使用./ns3编译运行该脚本,结果如下:

在这里插入图片描述
输出信息前面加上了日志详细程度(或严重程度)前缀以及调用函数名前缀(需要注意的是函数名前缀main()前面的是日志组件名而非文件名或类名)。

 

3. 控制日志输出

ns-3提供了两种常用的控制日志输出的方法 [1]:

  • 通过LogComponentEnable函数控制日志输出:在main()主函数中通过LogComponentEnable函数显式声明

    在这里插入图片描述

    LogComponentEnable函数的第一个参数是日志组件名(字符串),第二个参数是LogLevel选项,包括日志详细程度(严重程度)选项和日志前缀选项,其语法如下:

    LogComponentEnable(<log-component>, <option>) // 单个日志选项
    LogComponentEnable(<log-component>, LogLevel(<option> | <option> | ...)) // 多个日志选项放在LogLevel()中并用|分开
    

    关于如何通过LogComponentEnable函数控制日志输出,我们不再赘述,具体可参考上面的内容。

  • 通过NS_LOG环境变量控制日志输出

    通过上面log-level-demo.cc示例脚本的大量演示我们其实不难发现,通过LogComponentEnable函数控制日志输出我们需要一遍遍修改源代码,比较繁琐。所以ns-3还提供了通过NS_LOG环境变量的方式控制日志输出,其语法如下:

    /*
    每个日志组件的选项罗列在日志组件的后面,用“|”隔开,日志组件和其所对应的选项之间用“=”连接
    不同日志组件之间用“:”隔开
    */
    NS_LOG="<log-component>=<option>|<option>... : <log-component>=<option>|<option>..." ./ns3 run <script>
    

    但是需要注意的是,在NS_LOG环境变量中,日志详细程度(严重程度)选项和日志前缀选项是由以下token给出的,和LogComponentEnable函数中的相关语法不一样 [1]:

    在这里插入图片描述
    在这里插入图片描述
    我们还是以log-level-demo.cc示例脚本为例:
    在这里插入图片描述
    在终端中通过NS_LOG环境变量控制日志输出:
    在这里插入图片描述
    而如果我们和LogComponentEnable函数一样,在NS_LOG环境变量中使用LOG_INFO和LOG_PREFIX_LEVEL控制日志输出的话,那么将不起作用,如下所示:
    在这里插入图片描述


注释:

  • 无论是通过LogComponentEnable函数控制日志输出,还是通过NS_LOG环境变量控制日志输出,都需要使用NS_LOG_COMPONENT_DEFINE宏先注册日志组件。

  • 当通过NS_LOG环境变量控制日志输出时,无需在main()主函数中通过LogComponentEnable函数显式声明开启日志功能,如上个示例所示。

  • NS_LOG=“<log-component>:…”(即在NS_LOG环境变量中不显示指定日志选项),默认的日志详细程度(严重程度)选项为level_all,默认的日志前缀选项为all [1]。

  • 如果在main()主函数中通过LogComponentEnable函数显式声明开启日志功能,那么再通过NS_LOG环境变量控制日志输出时,两种方式会一并作用。
    例如:
    我们在main()主函数中通过LogComponentEnable函数显式声明开启日志功能的同时,使用NS_LOG环境变量控制日志输出:
    在这里插入图片描述
    结果如下:
    在这里插入图片描述
    LogComponentEnable函数和NS_LOG环境变量同时作用了!

  • 如果想启用特定详细程度(严重程度)的所有组件,并添加特定前缀,那么可以使用通配符“*”表示所有日志组件 [1]:

NS_LOG="*=<option>|<option>“
  • 如果想启用特定日志组件,并打开所有日志详细程度(严重程度),也可以使用通配符“*”代替”all“或者”level_all“。但是日志详细程度(严重程度)的通配符应在”|“符号之前(如果有的话)[1]:
NS_LOG="<log-component>=*|<option>“ // 等价于NS_LOG="<log-component>=all|<option>“// 或者NS_LOG="<log-component>=level_all|<option>“
  • 如果想启用特定日志组件,并添加所有日志前缀,也可以使用通配符“*”代替”all。但是日志前缀的通配符必须在”|“符号之后 [1]:
NS_LOG="<log-component>=<option>|*“ // 等价于NS_LOG="<log-component>=<option>|all“
  • 以下命令等价 [1]:
    在这里插入图片描述

参考文献

[1]: ns-3 Manual
[2]: ns-3 Tutorial
[3]: ns-3网络模拟器基础及应用
[4]: 开源网络模拟器ns-3:架构与实践

http://chatgpt.dhexx.cn/article/jBe28CUb.shtml

相关文章

Nlog详解

Nlog详解 零、文章目录 一、Nlog详解 1、概述 NLog是一个基于.NET平台编写的日志记录类库&#xff0c;我们可以使用NLog在应用程序中添加极为完善的跟踪调试代码。可以在任何一种.NET语言中输出带有上下文的&#xff08;contextual information&#xff09;调试诊断信息&am…

iOS之深入解析NSLog的调试技巧

一、DEBUG 宏区分调试模式和发布模式进行特殊处理 ① 利用 Configuration 配置不同的编译环境 一个应用往往对应多个域名的情况&#xff0c;测试地址、生产地址、后台接口开发者的个人主机地址&#xff1a; 1.开发人员环境&#xff08;Other&#xff09;连接写服务人的电脑&…

TDD(测试驱动开发)死了吗?

01、前言 很早之前&#xff0c;曾在网络上见到过 TDD 这 3 个大写的英文字母&#xff0c;它是 Test Driven Development 这三个单词的缩写&#xff0c;也就是“测试驱动开发”的意思——听起来很不错的一种理念。 其理念主要是确保两件事&#xff1a; 确保所有的需求都能被照…

unitTest+Ddt数据驱动测试

我们设计测试用例时&#xff0c;会出现测试步骤一样&#xff0c;只是其中的测试数据有变化的情况&#xff0c;比如测试登录时的账号密码。这个时候&#xff0c;如果我们依然使用一条case一个方法的话&#xff0c;会出现大量的代码冗余&#xff0c;而且效率也会大大降低。此时&a…

测试驱动开发(TDD)的理论基础

在开始理论介绍之前&#xff0c;先思考一个问题&#xff1a;软件开发中最重要的是什么&#xff1f; 可能有的小伙伴就会说&#xff1a;良好的数据库设计&#xff0c;一个健壮可扩展的架构&#xff0c;规范的编码风格&#xff0c;设计文档等。没错这些在开发中都很重要&#xf…

什么时候应该编写单元测试?什么是TDD?

一、传统方法与TDD方法 1、有人认为软件编码完成后编写单元测试&#xff08;传统方法&#xff09;&#xff0c;流程如下&#xff1a; 缺点&#xff1a;编写完功能代码再写单元测试会导致单元测试“粒度”比较粗。对同样的功能代码&#xff0c;如果采用TDD方案&#xff0c;结果…

前端做自动化测试 —— 用TDD的思想做组件测试

Test-Driven Development(测试驱动开发,以下简述TDD)是一种设计方法论, 原理是在开发功能代码之前, 先编写单元测试用例代码, 通过测试来推动整个开发的进行. 本文详细描述了在创作 react-stillness-component, 组件的过程中, 是如何学习 TDD 的思想来完成功能开发以及测试的 一…

深度解读 - TDD(测试驱动开发)

本文结构&#xff1a; 什么是 TDD为什么要 TDD怎么 TDDFAQ学习路径延伸阅读 什么是 TDD TDD 有广义和狭义之分&#xff0c;常说的是狭义的 TDD&#xff0c;也就是 UTDD&#xff08;Unit Test Driven Development&#xff09;。广义的 TDD 是 ATDD&#xff08;Acceptance Tes…

【TDD】测试驱动开发

欢迎关注微信公众号“Python小灶&#xff0c;和我一起每天学习Python新知识”&#xff0c;还可添加博主Vx&#xff1a;yf03064131&#xff0c;方便一起交流学习。 或者B站搜索 有只小新 原视频地址链接&#xff1a;点击这里 代码地址&#xff1a;点击这里 本文为大致翻译以及…

测试驱动开发(TDD)的学习使用

测试驱动开发TDD 是一种不同于传统软件开发流程的新型开发方式 特点&#xff1a; 先编写测试代码 – 实现主要逻辑 再写功能代码 – 实现细节 通过测试来推动整个开发的进行。 有助于编写简洁可用和高质量的代码&#xff0c;并加速开发过程呢。 比如写一个Person类&#xff0c;…

从 TDD 到测试策略

“ 前端没法 TDD / 前端不容易做 TDD / 前端 TDD 收益不大 ” 这是进公司后无数人给我判的“死刑”。 事实上好像的确如此&#xff1f; 在这个崇尚敏捷的组织里&#xff0c;我们有毕业生的入职前培训&#xff0c;入职后培训&#xff0c;有 TwU&#xff0c;有无数定期不定期的…

使用ddt执行数据驱动测试

所谓数据驱动测试&#xff0c;简单的理解为数据的改变从而驱动自动化测试的执行&#xff0c;最终引起测试结果的改变。通过使用数据驱动测试的方法&#xff0c;可以在需要验证多组数据测试场景中&#xff0c;使用外部数据源实现对输入输出与期望值的参数化&#xff0c;避免在测…

先测试再开发?TDD测试驱动开发了解一下?

1、什么是TDD 我第一次接触TDD这个概念&#xff0c;是在<<代码整洁之道>>中&#xff0c;作者鲍勃大叔在书中&#xff0c;写了一些关于测试代码的代码规范&#xff0c;其实就提到了有关TDD三定律: - 定律一&#xff1a; 在编写不能通过的单元测试前&#xff0c;不…

测试驱动开发(TDD)前端篇

当你在写生产代码时&#xff0c;你处在高认知的状态&#xff08;obvious&#xff09;&#xff0c;你的研发流程和你的工程实践&#xff0c;有助于你一步一步的提升你的认知能力&#xff0c;把你的问题进行一个降解&#xff08;分解&#xff09;&#xff0c;只要你做到同样的事情…

TDD 开发测试

测试驱动开发(Test-Driven Development)。是敏捷开发中的一项核心实践和技术。 TDD是在开发功能代码之前&#xff0c;先编写单元测试用例代码&#xff0c;测试代码确定需要编写什么产品代码。 变红 ——> 变绿 ——> 重构 在进行 TDD 案例编写的时候&#xff0c;看一…

TDD测试驱动开发

TDD测试驱动开发 什么是测试驱动开发测试驱动开发该怎么做&#xff1f;需求一&#xff1a;输入一个非元音字符&#xff0c;并预期返回字符本身 (输入"h" 返回“h”)需求二&#xff1a;输入一个元音&#xff08;a,e,i,o,u&#xff09;&#xff0c;返回 mommy (输入&qu…

TDD测试驱动学习

gtest 和 gmock 安装 //如果不知道对应库名字可以执行这个命令查找对应库,如果没找到要去跟新对应的源sudo apt update sudo apt-cache search gtest sudo apt-cache search gmock 测试例子 #include <string> using std::string;// 定义 Soundex 类 class Soundex {pub…

TDD (test driver development)测试驱动开发

##为什么需要测试驱动/或者说需要单元测试 我们工作接触的软件项目&#xff0c;不是学生时代&#xff0c;玩一玩就不管了&#xff0c;工作的项目&#xff0c;需要长期维护&#xff0c;并且随着时间的推移需要增加新的需求&#xff0c;进行修改&#xff0c;优化。此时已经距离你…

tdd(测试驱动开发)的概述

最近的工作的项目&#xff0c;使用了tdd&#xff08;test-driven development测试驱动开发&#xff09;的开发模式。 这两几年大概听说了无数种xxx-dd, ddd, tdd, atdd, bdd, fdd, udd各种名词眼花缭乱&#xff0c;当然很多dd其实也有相互借鉴&#xff08;抄袭&#xff09;的部…

测试驱动开发(TDD)实践与技巧

文章目录 引言Google Mock测试用例结构断言经典式断言Hamcrest 断言 测试驱动开发&#xff1a;第一个示例开场白开始吧去掉不干净的代码增量性fixture 设置思索与测试驱动开发测试驱动与测试 测试驱动开发基础与单元测试单元测试的组织结构测试驱动开发周期&#xff1a;红-绿-重…