-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python logger enhancement to support setting log level at real time #1522
Python logger enhancement to support setting log level at real time #1522
Conversation
@prgeor appreciate your feedback on the HLD |
@liat-grozovik ack |
@Junchao-Mellanox There is one flaw with existing implementation of Logging in We need log level which is local to that daemon which is logging. The existing Logger implementation pollutes the log identifier of other daemons as well. |
Hi @liat-grozovik, This PR is pending on PR sonic-net/sonic-buildimage#17171 which is handled by @xincunli-sonic . For PR 17171, we have one open item:
For this item, I can always reproduce on my side, but Xincun cannot reproduce on his side. So, I requested a debug session with him this week. I am waiting for his response. |
51e668a
to
072111b
Compare
thanks @Junchao-Mellanox for keeping handling it. now that the pending PR is merged, i assume you need new PR on top of it. once you have please update the PR description with the new code PR instead of the closed one. @prgeor can we get your approval for the HLD? |
@liat-grozovik reviewing |
SONiC also provides a C/C++ logger implementation in `sonic-platform-common.common.logger.cpp`. This C/C++ logger implementation is also a wrapper of Linux standard `syslog` which is widely used by swss/syncd. It provides the ability to set log level on fly by starting a thread to listen to CONFIG DB LOGGER table change. SONiC infrastructure also provides the Python wrapper for `sonic-platform-common.common.logger.cpp` which is `swsscommon.Logger`. However, this logger implementation also has some drawbacks: | ||
|
||
|
||
1. `swsscommon.Logger` assumes redis DB is ready to connect. This is a valid assumption for swss/syncd. But it is not good for a Python logger implementation because some Python script may be called before redis server starting. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Junchao-Mellanox How are you overcoming this limitation? I see that you are still writing to the DB which means redis server needs to be running
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To overcome it, the implementation will:
- At the beginning of the thread function, it will wait until redis is up. If the connect to redis fails, it will silently ignore the connection error and retry in 5 seconds.
- In the while loop of thread function, catch connection error while reading from redis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Junchao-Mellanox to keep things simple, we an make use of config_db logger table for user to configure the log level for debugging purpose like below:-
"LOGGER": {
"xcvrd": {
"LOGLEVEL": "DEBUG",
"LOGOUTPUT": "SYSLOG"
},
Use the Identifier specified in syslogger to fetch the current log level from config DB
For eg. The identfier could be Xcvrd, CmisMgr, StateUpdateTask etc.
The advantage is we don't need to create extra threads for handling this
Reading redis DB is not a costly affair
This would limit the change to syslogger
Please note, logger.py will be deprecated over time as its usage causes confusion and because of which we introduced syslogger
@Junchao-Mellanox one main advantage of the above approach is enabling logging at module level. For eg. Xcvrd common or main thread can have one SysLogger("Xcvrd") instance for eg. CMIS mgr may have another SysLogger("CmisMgr") instance. The logging can be enabled per module within the same thread/process |
Hi @prgeor , this is exactly the implementation will do. |
Totally agree. And this is exactly my implementation plan. |
@Junchao-Mellanox why do we need a thread? |
a54c8e4
to
5d4285d
Compare
5d4285d
to
776759f
Compare
- "-l": log level. One of "DEBUG", "INFO", "NOTICE", "WARN", "ERROR" | ||
- "--service": container name. Optional. The signal will be sent to that container if present. | ||
- "--program": program name. Optional, only applicable when --service is present. The signal will be sent to that program running in the service. | ||
- "--pid": process ID. Optional. If --service is present, the signal will be sent to the PID running in the service; otherwise, the signal will be send to the PID running in host side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Junchao-Mellanox if user does not specify the optional argument --service, then also this will work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If --service
is not present, the command will try to send the signal to host side process which has the pid equals to --pid
# Update the log level without refresh the configuration | ||
config syslog level -c xcvrd -l DEBUG | ||
|
||
# Update the log level and send SIGHUP to xcvrd running in PMON | ||
config syslog level -c xcvrd -l DEBUG --service pmon --program xcvrd | ||
|
||
# Update the log level and send SIGHUP to PID 20 running in PMON | ||
config syslog level -c xcvrd -l DEBUG --service pmon --pid 20 | ||
|
||
# Update the log level and send SIGHUP to PID 20 running in host | ||
config syslog level -c xcvrd -l DEBUG --pid 20 | ||
|
||
# Send SIGHUP to xcvrd running in PMON without update log level | ||
config syslog level --service pmon --program xcvrd | ||
|
||
# Send SIGHUP to PID 20 running in PMON without update log level | ||
config syslog level --service pmon --pid 20 | ||
|
||
# Send SIGHUP to PID 20 running in host without update log level | ||
config syslog level --pid 20 | ||
|
||
# Invalid, --program must be used together with --service | ||
config syslog level --program xcvrd | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Junchao-Mellanox can we have option signal
instead of level
to send signal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @prgeor , the requirement is to have one single command which update log level and make it effect, so, I have to add these two function together.
#### SysLogger class change | ||
|
||
- A new class level variable `log_registry` shall be added to `SysLogger`class. The type is `dict`. | ||
- A new argument `enable_runtime_config` shall be added to `SysLogger.__init__`. Default to False. | ||
- SysLogger instance shall register itself to `log_registry` if `enable_runtime_config` is True. | ||
- SysLogger instance shall read log level configuration from CONFIG DB if `enable_runtime_config` is True and DB entry is available. | ||
- SysLogger instance shall update its log level configuration to CONFIG DB if `enable_runtime_config` is True and DB entry is not available. | ||
- A new class level method `refresh_config` shall be added to `SysLogger` class. The method shall be called while receiving SIGHUP to update log configuration for each logger instance registered to `log_registry`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Junchao-Mellanox can we maintain the SysLogger Objects inside src/sonic-py-common/sonic_py_common/daemon_base.py so that the main thread will create the list of SysLogger objects required in DaemonBase.logger_instances[]
? This way the main daemon process will be able to do the signal handling based upon the identifier
assigned to each Syslogger debug level configured in the CONFIG_DB
@prgeor could you please help to review following the recent changes based on your feedback? |
@Junchao-Mellanox is there code PR aligned now with the updated HLD? if so, can you please remove the closed PR and update the description with the relevant ones? |
Hi @liat-grozovik , I will remove the closed one. I will add the code PR once it is ready for review. |
|
||
- `SysLogger` class shall be changed to a singleton. | ||
- `SysLogger` instance shall load log level configuration from DB during initialization stage if DB configuration is available. | ||
- `SysLogger` instance shall save log level configuration to DB during initialization stage if DB configuration is not available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Junchao-Mellanox Why syslogger need to save log level to DB? The user will configure the log level which will be updated to DB and the main process would be woken up via signal to fetch the user set log level from the DB. There is no need to save log level within SysLogger class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to register logger itself to DB so that swssloglevel -p
will print all the available log identifiers. Like this:
swssloglevel -p
SAI_API_ACL SAI_LOG_LEVEL_NOTICE
SAI_API_ARS SAI_LOG_LEVEL_NOTICE
SAI_API_ARS_PROFILE SAI_LOG_LEVEL_NOTICE
SAI_API_BFD SAI_LOG_LEVEL_NOTICE
SAI_API_BMTOR SAI_LOG_LEVEL_NOTICE
SAI_API_BRIDGE SAI_LOG_LEVEL_NOTICE
SAI_API_BUFFER SAI_LOG_LEVEL_NOTICE
SAI_API_COUNTER SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_ACL SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_DIRECTION_LOOKUP SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_ENI SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_INBOUND_ROUTING SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_METER SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_OUTBOUND_CA_TO_PASAI_LOG_LEVEL_NOTICE
SAI_API_DASH_OUTBOUND_ROUTING SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_PA_VALIDATION SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_VIP SAI_LOG_LEVEL_NOTICE
SAI_API_DASH_VNET SAI_LOG_LEVEL_NOTICE
SAI_API_DEBUG_COUNTER SAI_LOG_LEVEL_NOTICE
SAI_API_DTEL SAI_LOG_LEVEL_NOTICE
SAI_API_FDB SAI_LOG_LEVEL_NOTICE
SAI_API_GENERIC_PROGRAMMABLE SAI_LOG_LEVEL_NOTICE
SAI_API_HASH SAI_LOG_LEVEL_NOTICE
SAI_API_HOSTIF SAI_LOG_LEVEL_NOTICE
SAI_API_IPMC SAI_LOG_LEVEL_NOTICE
SAI_API_IPMC_GROUP SAI_LOG_LEVEL_NOTICE
SAI_API_IPSEC SAI_LOG_LEVEL_NOTICE
SAI_API_ISOLATION_GROUP SAI_LOG_LEVEL_NOTICE
SAI_API_L2MC SAI_LOG_LEVEL_NOTICE
SAI_API_L2MC_GROUP SAI_LOG_LEVEL_NOTICE
SAI_API_LAG SAI_LOG_LEVEL_NOTICE
SAI_API_MACSEC SAI_LOG_LEVEL_NOTICE
SAI_API_MCAST_FDB SAI_LOG_LEVEL_NOTICE
SAI_API_MIRROR SAI_LOG_LEVEL_NOTICE
SAI_API_MPLS SAI_LOG_LEVEL_NOTICE
SAI_API_MY_MAC SAI_LOG_LEVEL_NOTICE
SAI_API_NAT SAI_LOG_LEVEL_NOTICE
SAI_API_NEIGHBOR SAI_LOG_LEVEL_NOTICE
SAI_API_NEXT_HOP SAI_LOG_LEVEL_NOTICE
SAI_API_NEXT_HOP_GROUP SAI_LOG_LEVEL_NOTICE
SAI_API_POLICER SAI_LOG_LEVEL_NOTICE
SAI_API_PORT SAI_LOG_LEVEL_NOTICE
SAI_API_QOS_MAP SAI_LOG_LEVEL_NOTICE
SAI_API_QUEUE SAI_LOG_LEVEL_NOTICE
SAI_API_ROUTE SAI_LOG_LEVEL_NOTICE
SAI_API_ROUTER_INTERFACE SAI_LOG_LEVEL_NOTICE
SAI_API_RPF_GROUP SAI_LOG_LEVEL_NOTICE
SAI_API_SAMPLEPACKET SAI_LOG_LEVEL_NOTICE
SAI_API_SCHEDULER SAI_LOG_LEVEL_NOTICE
SAI_API_SCHEDULER_GROUP SAI_LOG_LEVEL_NOTICE
SAI_API_SRV6 SAI_LOG_LEVEL_NOTICE
SAI_API_STP SAI_LOG_LEVEL_NOTICE
SAI_API_SWITCH SAI_LOG_LEVEL_NOTICE
SAI_API_SYSTEM_PORT SAI_LOG_LEVEL_NOTICE
SAI_API_TAM SAI_LOG_LEVEL_NOTICE
SAI_API_TUNNEL SAI_LOG_LEVEL_NOTICE
SAI_API_TWAMP SAI_LOG_LEVEL_NOTICE
SAI_API_UDF SAI_LOG_LEVEL_NOTICE
SAI_API_VIRTUAL_ROUTER SAI_LOG_LEVEL_NOTICE
SAI_API_VLAN SAI_LOG_LEVEL_NOTICE
SAI_API_WRED SAI_LOG_LEVEL_NOTICE
buffermgrd NOTICE
coppmgrd NOTICE
fdbsyncd NOTICE
fpmsyncd NOTICE
gearsyncd NOTICE
intfmgrd NOTICE
nbrmgrd NOTICE
neighsyncd NOTICE
orchagent NOTICE
portmgrd NOTICE
portsyncd NOTICE
sflowmgrd NOTICE
syncd NOTICE
teammgrd NOTICE
teamsyncd NOTICE
tlm_teamd NOTICE
tunnelmgrd NOTICE
vlanmgrd NOTICE
vrfmgrd NOTICE
vxlanmgrd NOTICE
wjhcli NOTICE
wjhd NOTICE
Also, if we don't register logger instance to db, swssloglevel tool will reject when we want to update the log. Like this:
root@sonic:/# swssloglevel -c invalid -l NOTICE
Component not present in DB
|
||
- `SysLogger` class shall be changed to a singleton | ||
- A new argument `enable_runtime_config` shall be added to `SysLogger.__init__`. Default to False. | ||
- `SysLogger` instance shall load log level configuration from DB during initialization stage if `enable_runtime_config` is True and DB config is available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Junchao-Mellanox why enable_runtime_config
default is kept False? What is the intention?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two reason here:
- We want to keep backward compatible. Before the feature, there is no runtime config support for SysLogger.
- Enabling runtime config will slow down the logger creation for a litter bit, we want each SysLogger instance to only enable it when need.
HLD link: sonic-net/SONiC#1522 - Why I did it SONiC provides two Python logger implementations: sonic_py_common.logger.Logger and sonic_py_common.syslogger.SysLogger. Both of them do not provide the ability to change log level at real time. Sometimes, in order to get more debug information, developer has to manually change the log level in code on a running switch and restart the Python daemon. This is not convenient. SONiC also provides a C/C++ logger implementation in sonic-platform-common.common.logger.cpp. This C/C++ logger implementation is also a wrapper of Linux standard syslog which is widely used by swss/syncd. It provides the ability to set log level on fly by starting a thread to listen to CONFIG DB LOGGER table change. SONiC infrastructure also provides the Python wrapper for sonic-platform-common.common.logger.cpp which is swsscommon.Logger. However, this logger implementation also has some drawbacks: swsscommon.Logger assumes redis DB is ready to connect. This is a valid assumption for swss/syncd. But it is not good for a Python logger implementation because some Python script may be called before redis server starting. swsscommon.Logger wraps Linux syslog which only support single log identifier for a daemon. So, swsscommon.Logger is not an option too. This PR is a Python logger enhancement which allows user setting log level at run time. - How I did it swsscommon.Logger depends on a thread to listen to CONFIG DB LOGGER table change. It refreshes log level for each logger instances once the thread detects a DB entry change. A thread is considered heavy in a python script, especially that there are many short and simple python scripts which also use logger. To keep python logger light weight, it uses a different design than swsscommon.Logger: A class level logger registry shall be added to SysLoggerclass Each logger instance shall register itself to logger register if enables runtime configuration Logger configuration shall be refreshed by CLI which send a SIGHUP signal to the daemon - How to verify it Manual test New unit test cases
HLD link: sonic-net/SONiC#1522 - Why I did it SONiC provides two Python logger implementations: sonic_py_common.logger.Logger and sonic_py_common.syslogger.SysLogger. Both of them do not provide the ability to change log level at real time. Sometimes, in order to get more debug information, developer has to manually change the log level in code on a running switch and restart the Python daemon. This is not convenient. SONiC also provides a C/C++ logger implementation in sonic-platform-common.common.logger.cpp. This C/C++ logger implementation is also a wrapper of Linux standard syslog which is widely used by swss/syncd. It provides the ability to set log level on fly by starting a thread to listen to CONFIG DB LOGGER table change. SONiC infrastructure also provides the Python wrapper for sonic-platform-common.common.logger.cpp which is swsscommon.Logger. However, this logger implementation also has some drawbacks: swsscommon.Logger assumes redis DB is ready to connect. This is a valid assumption for swss/syncd. But it is not good for a Python logger implementation because some Python script may be called before redis server starting. swsscommon.Logger wraps Linux syslog which only support single log identifier for a daemon. So, swsscommon.Logger is not an option too. This PR is a Python logger enhancement which allows user setting log level at run time. - How I did it swsscommon.Logger depends on a thread to listen to CONFIG DB LOGGER table change. It refreshes log level for each logger instances once the thread detects a DB entry change. A thread is considered heavy in a python script, especially that there are many short and simple python scripts which also use logger. To keep python logger light weight, it uses a different design than swsscommon.Logger: A class level logger registry shall be added to SysLoggerclass Each logger instance shall register itself to logger register if enables runtime configuration Logger configuration shall be refreshed by CLI which send a SIGHUP signal to the daemon - How to verify it Manual test New unit test cases
HLD link: sonic-net/SONiC#1522 - Why I did it SONiC provides two Python logger implementations: sonic_py_common.logger.Logger and sonic_py_common.syslogger.SysLogger. Both of them do not provide the ability to change log level at real time. Sometimes, in order to get more debug information, developer has to manually change the log level in code on a running switch and restart the Python daemon. This is not convenient. SONiC also provides a C/C++ logger implementation in sonic-platform-common.common.logger.cpp. This C/C++ logger implementation is also a wrapper of Linux standard syslog which is widely used by swss/syncd. It provides the ability to set log level on fly by starting a thread to listen to CONFIG DB LOGGER table change. SONiC infrastructure also provides the Python wrapper for sonic-platform-common.common.logger.cpp which is swsscommon.Logger. However, this logger implementation also has some drawbacks: swsscommon.Logger assumes redis DB is ready to connect. This is a valid assumption for swss/syncd. But it is not good for a Python logger implementation because some Python script may be called before redis server starting. swsscommon.Logger wraps Linux syslog which only support single log identifier for a daemon. So, swsscommon.Logger is not an option too. This PR is a Python logger enhancement which allows user setting log level at run time. - How I did it swsscommon.Logger depends on a thread to listen to CONFIG DB LOGGER table change. It refreshes log level for each logger instances once the thread detects a DB entry change. A thread is considered heavy in a python script, especially that there are many short and simple python scripts which also use logger. To keep python logger light weight, it uses a different design than swsscommon.Logger: A class level logger registry shall be added to SysLoggerclass Each logger instance shall register itself to logger register if enables runtime configuration Logger configuration shall be refreshed by CLI which send a SIGHUP signal to the daemon - How to verify it Manual test New unit test cases
…onic-net#1522) * Python logger update * Fix review comment * Change SysLogger class to be a singleton * Update python-logger-enhancement.md * Update python-logger-enhancement.md * Update python-logger-enhancement.md
Overview
SONiC provides a Python logger implementation in
sonic_py_common.logger.Logger
. This implementation is a simple wrapper of Python standard packagesyslog
. It does not provide the ability to change log level at real time. Sometimes, in order to get more debug information, developer has to manually change the log level in code on a running switch and restart the Python daemon. This PR introduces an enhancement to allow user setting log level on fly.PR