ns-3Logging system

Article directory

  • foreword
  • 1 Overview
  • 2. Basic concepts
    • 2.1 Log component
    • 2.2 Log verbosity (severity) options
    • 2.3 Log Prefix Options
  • 3. Control log output
  • references

Foreword

This article introduces the Logging system.

1. Overview

Many large systems will provide a console-based message recording function to give users real-time feedback on program execution. ns-3 is no exception. ns-3 provides an optional, multi-level message logging function-the Logging system. Logging can be disabled completely, enabled on a component-by-component basis, or enabled globally. The Logging system can also choose the verbosity level or severity level of the logged messages, and add different prefixes to the messages. Therefore, the Logging system provides a very flexible and powerful, but relatively simple message logging function [1] [2].

It should be noted that the Logging system is mainly used in ns-3: first, to facilitate user understanding by outputting the execution process of the internal modules of the network component; second, to facilitate user debugging by outputting simple debugging information, rather than collecting simulation data[3] . ns-3 provides a general mechanism for collecting simulation data-the Tracing system, which is our first choice for collecting simulation data [2].

2. Basic concepts

As mentioned above, the Logging system provides a very flexible message recording function. Not only can the Logging function be enabled component by component, but also the level of detail or severity of the recorded messages can be selected, and different prefixes can be added to the messages.

Therefore, before introducing how to use the Logging system, in this section, we first introduce basic concepts such as Log Component, options to control the verbosity or severity of log output, and prefix options.

2.1 Log component

The log component is the smallest management unit of the Logging system. In layman’s terms, a log component refers to a C++ source code file. Through the log component, the Logging system can precisely control the information output to a certain C + + file [1] [4].

It is also very easy to add a C++ file as a log component, just use the NS_LOG_COMPONENT_DEFINE macro registration in the ns3 name space at the beginning of the C++ file. Logging component names must be globally unique (usually based on the file name or the name of a class defined in the file).

For example, the first parameter in LogComponentEnable() in first.cc is the log component name (we will introduce the LogComponentEnable function later), as shown in the following figure:


The UdpEchoClinetApplication log component and the UdpEchoServerApplication log component represent the udp-echo-client.cc file and the udp-echo-server.cc file respectively, which are registered using the NS_LOG_COMPONENT_DEFINE macro within the ns3 name space at the beginning of the respective files, as shown in the following figure :


2.2 Log verbosity (severity) options

As mentioned above, through the log component, the Logging system can precisely control the information output to a certain C++ file. And through the log detail (or severity) option, the Logging system can further control the information output to a certain level of a certain C++ file.

In ns-3, the Logging system usually provides 7 log verbosity (or severity) options, the verbosity of the output information is from low to high (that is, the output information is from less to more) or the severity of the information is from high to low In order [1]:

  • LOG_ERROR: Log error information (the associated macro is: NS_LOG_ERROR)
  • LOG_WARN: Log warning information (the associated macro is: NS_LOG_WARN)
  • LOG_DEBUG: Record relatively rare and special debugging messages (the associated macro is: NS_LOG_DEBUG)
  • LOG_INFO: Record information related to the program process (the associated macro is: NS_LOG_INFO)
  • LOG_FUNCTION: Log messages describing each function call (associated macros are: NS_LOG_FUNCTION and NS_LOG_FUNCTION_NOARGS)
  • LOG_LOGIC: Record the internal logic flow information of the function (the associated macro is: NS_LOG_LOGIC)
  • LOG_ALL: Log all the above information (no associated macro)

Notes:

In fact, ns-3 also defines a log level LOG_NONE in src/core/model/log.h, which will not record any information.

Let’s still take first.cc as an example. The second parameter in LogComponentEnable() is the log detail (severity) option, as shown in the following figure:

It should be noted that, except for LOG_ALL, the remaining six log verbosity (or severity) options can only control the output of the information output in the associated macro. For example, the LOG_INFO level can only control the output of information output by all NS_LOG_INFO macros in the specified logging component.

In order to illustrate this point more intuitively, let’s take a look at the following log-level-demo.cc sample script written by ourselves, and at the same time review and practice the relevant knowledge of the log component introduced above:


First, we register the file as a logging component called “LogLevelDemo” using the NS_LOG_COMPONENT_DEFINE macro in the ns3 namespace scope at the beginning of the file.

Next, we use the LogComponentEnable function in the main function to enable the LogLevelDemo log component, and set the log verbosity (or severity) option to LOG_INFO (about the use of the LogComponentEnable function, we will introduce it in detail later).

Then, we use ./ns3 to compile and run the script in the terminal in the ns-3.37 directory (the log-level-demo.cc file needs to be placed in the ns-3.37/scratch directory):


When compiling and running the log-level-demo.cc file for the first time, the default configuration will be automatically performed first.

The result is as follows:

We can see that only the information in the NS_LOG_INFO macro is output.

We can modify the log verbosity (or severity) option of LogComponentEnable to LOG_DEBUG, compile and run the script again using ./ns3, the results are as follows:

We can see that only the information in the NS_LOG_DEBUG macro is output.

Comments:

  • If no information is output according to the above steps, you can check the configuration options (./ns3 show profile):


If the configuration option is optimized, no information will be output. Because the log statement will not be compiled under the optimized configuration [1].

  • In fact, the above log levels do not have a very strict boundary division (depending on the use of the associated NS_LOG_TYPE macro), so sometimes reading C++ source code is unavoidable[4 ].

If we want to output the information in the NS_LOG_INFO macro and the NS_LOG_DEBUG macro at the same time, then we can do this:


Use ./ns3 to compile and run the script again, the result is as follows:

Both the information in the NS_LOG_INFO macro and the NS_LOG_DEBUG macro are output!

Sometimes, we want to output information with a certain level of detail (or severity) and a lower level of detail (or higher level of severity). For example, we want to output both LOG_INFO and lower verbosity (or higher severity) information. It would be cumbersome to specify explicitly as above, so the Logging system also defines a corresponding LOG_LEVEL_TYPE for each LOG_TYPE (except LOG_NONE), which can output LOG_TYPE and lower verbosity (or higher severity) information [1 ]:

  • LOG_LEVEL_ERROR
  • LOG_LEVEL_WARN
  • LOG_LEVEL_DEBUG
  • LOG_LEVEL_INFO
  • LOG_LEVEL_FUNCTION
  • LOG_LEVEL_LOGIC
  • LOG_LEVEL_ALL / LOG_ALL

Comments:

In fact, LOG_LEVEL_ERROR and LOG_ERROR are the same, because they only display logs of their own level; LOG_LEVEL_ALL and LOG_ALL are also consistent, because they both display logs of all levels.

Let’s still take the log-level-demo.cc sample script as an example, and modify the second parameter in the LogComponentEnable function as follows:

Use ./ns3 to compile and run the script again, the result is as follows:


You can see that LOG_INFO and lower verbosity (or higher severity) information are output.

2.3 Log Prefix Options

In addition to the log verbosity (severity) option, the Logging system also provides a log prefix option. These prefixes help identify when and where a message originated, and the severity level [1]:

  • LOG_PREFIX_FUNC: Add the prefix of the calling function name
  • LOG_PREFIX_TIME: Add simulation time prefix
  • LOG_PREFIX_NODE: Add node id prefix
  • LOG_PREFIX_LEVEL: Prefix the verbosity (or severity)
  • LOG_PREFIX_ALL: add all prefixes

Let’s still take the log-level-demo.cc sample script as an example, and modify the LogComponentEnable function as follows:


Use ./ns3 to compile and run the script again, the result is as follows:


The output is prefixed with the log verbosity (or severity) level.

Let’s modify the LogComponentEnable function as follows:


Use ./ns3 to compile and run the script again, the result is as follows:


The output information is prefixed with the log verbosity (or severity) prefix and the calling function name prefix (it should be noted that the function name prefix main() is preceded by the log component name rather than the file name or class name).

3. Control log output

ns-3 provides two commonly used methods of controlling log output [1]:

  • Control the log output through the LogComponentEnable function: explicitly declare through the LogComponentEnable function in the main() main function

    The first parameter of the LogComponentEnable function is the log component name (string), and the second parameter is the LogLevel option, including the log detail (severity) option and the log prefix option. The syntax is as follows:

    LogComponentEnable(<log-component>, <option>) // single log option
    LogComponentEnable(<log-component>, LogLevel(<option> | <option> | ...)) // Multiple log options are placed in LogLevel() and separated by |
    

    Regarding how to control the log output through the LogComponentEnable function, we will not go into details. For details, please refer to the above content.

  • Control log output through NS_LOG environment variable

    Through a large number of demonstrations of the log-level-demo.cc sample script above, it is not difficult to find that we need to modify the source code over and over again through the LogComponentEnable function to control the log output, which is cumbersome. So ns-3 also provides the way to control the log output through the NS_LOG environment variable, and its syntax is as follows:

    /*
    The options of each log component are listed behind the log component, separated by "|", and the log component and its corresponding options are connected by "="
    Different log components are separated by ":"
    */
    NS_LOG="<log-component>=<option>|<option>... : <log-component>=<option>|<option>..." ./ns3 run <script>
    

    However, it should be noted that in the NS_LOG environment variable, the log detail (severity) option and log prefix option are given by the following token, which is different from the related syntax in the LogComponentEnable function [1]:



    Let’s take the log-level-demo.cc sample script as an example:

    Control the log output through the NS_LOG environment variable in the terminal:

    And if we use LOG_INFO and LOG_PREFIX_LEVEL to control the log output in the NS_LOG environment variable like the LogComponentEnable function, it will not work, as shown below:

Comments:

  • Whether the log output is controlled through the LogComponentEnable function or through the NS_LOG environment variable, it is necessary to use the NS_LOG_COMPONENT_DEFINE macro to register the log component first.

  • When the log output is controlled through the NS_LOG environment variable, there is no need to explicitly declare the log function through the LogComponentEnable function in the main() main function, as shown in the previous example.

  • NS_LOG=”:…” (that is, the specified log option is not displayed in the NS_LOG environment variable), the default log verbosity (severity) option is level_all, the default The log prefix option is all[1].

  • If the log function is explicitly declared through the LogComponentEnable function in the main() main function, then when the log output is controlled through the NS_LOG environment variable, the two methods will work together.
    For example:
    While explicitly declaring to enable the log function through the LogComponentEnable function in the main() main function, we use the NS_LOG environment variable to control the log output:

    The result is as follows:

    The LogComponentEnable function and the NS_LOG environment variable work at the same time!

  • If you want to enable all components of a specific verbosity (severity), and add a specific prefix, you can use the wildcard “*” to represent all log components [1]:

NS_LOG="*=<option>|<option>"
  • If you want to enable a specific log component and turn on all log verbosity (severity), you can also use the wildcard “*” instead of “all” or “level_all”. But the wildcard for log verbosity (severity) should be before the “|” symbol (if any) [1]:
NS_LOG="<log-component>=*|<option>“ // Equivalent to NS_LOG="<log-component>=all|<option>“
                                    // or NS_LOG="<log-component>=level_all|<option>"
  • If you want to enable a specific log component and add all log prefixes, you can also use the wildcard character “*” instead of “all. But the wildcard character for the log prefix must be after the “|” symbol [1]:
NS_LOG="<log-component>=<option>|*" // Equivalent to NS_LOG="<log-component>=<option>|all"
  • The following commands are equivalent [1]:

References

[1]: ns-3 Manual
[2]: ns-3 Tutorial
[3]: ns-3 network simulator foundation and application
[4]: Open Source Network Simulator ns-3: Architecture and Practice