Skip to content

Commit c10b4e1

Browse files
authored
[logging] introduce log level override feature (openthread#12903)
This commit introduces a mechanism to temporarily override the log level. The `Instance` class now provides `OverrideLogLevel()` and `RestoreLogLevel()` methods. When an override is active, the effective log level is the maximum of the original user-set level and the override level. If `SetLogLevel()` is called while an override is active, it updates the original level and the effective level is recomputed. This ensures that log messages are generated only when needed, without permanently losing the user's original log level configuration. The feature is controlled by the new configuration macro `OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE`. A new Nexus unit test `test_log_override.cpp` is added to validate the behavior of these new feature.
1 parent 5fad120 commit c10b4e1

7 files changed

Lines changed: 226 additions & 2 deletions

File tree

src/core/config/logging.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,20 @@
197197
#define OPENTHREAD_CONFIG_LOG_MAX_SIZE 150
198198
#endif
199199

200+
/**
201+
* @def OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
202+
*
203+
* Define to 1 to enable the log level override feature and its associated APIs.
204+
*
205+
* This feature is used when `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE` is also enabled.
206+
*
207+
* When enabled, new mechanism is added to allow temporary override of the current log level (e.g., to increase the
208+
* level to capture more detailed information) and subsequently restore it to the original user-specified level.
209+
*/
210+
#ifndef OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
211+
#define OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE 0
212+
#endif
213+
200214
/**
201215
* @}
202216
*/

src/core/instance/instance.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ Instance::Instance(void)
320320
#endif
321321
#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
322322
, mLogLevel(static_cast<LogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL_INIT))
323+
#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
324+
, mOriginalLogLevel(kLogLevelNone)
325+
, mOverrideLogLevel(kLogLevelNone)
326+
, mIsLogLevelOverriden(false)
327+
#endif
323328
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
324329
, mIsLogLevelSet(false)
325330
#else
@@ -591,20 +596,66 @@ Error Instance::SetLogLevel(LogLevel aLogLevel)
591596
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && !OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE
592597
ExitNow(error = kErrorNotCapable);
593598
#else
599+
#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
600+
if (mIsLogLevelOverriden)
601+
{
602+
mOriginalLogLevel = aLogLevel;
603+
aLogLevel = Max(aLogLevel, mOverrideLogLevel);
604+
}
605+
#endif
594606
VerifyOrExit(mLogLevel != aLogLevel);
595607
mLogLevel = aLogLevel;
608+
SignalLogLevelChange();
609+
#endif
596610

611+
exit:
612+
return error;
613+
}
614+
615+
void Instance::SignalLogLevelChange(void)
616+
{
597617
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
598618
mIsLogLevelSet = true;
599619
#else
600620
otPlatLogHandleLevelChanged(mLogLevel);
601621
#endif
622+
602623
otPlatLogHandleLogLevelChanged(this, mLogLevel);
603-
#endif
624+
}
625+
626+
#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
627+
void Instance::OverrideLogLevel(LogLevel aLogLevel)
628+
{
629+
LogLevel logLevel;
630+
631+
if (!mIsLogLevelOverriden)
632+
{
633+
mOriginalLogLevel = GetLogLevel();
634+
mIsLogLevelOverriden = true;
635+
}
636+
637+
mOverrideLogLevel = aLogLevel;
638+
639+
logLevel = Max(mOverrideLogLevel, mOriginalLogLevel);
640+
641+
VerifyOrExit(mLogLevel != logLevel);
642+
mLogLevel = logLevel;
643+
SignalLogLevelChange();
604644

605645
exit:
606-
return error;
646+
return;
647+
}
648+
649+
void Instance::RestoreLogLevel(void)
650+
{
651+
VerifyOrExit(mIsLogLevelOverriden);
652+
mIsLogLevelOverriden = false;
653+
IgnoreError(SetLogLevel(mOriginalLogLevel));
654+
655+
exit:
656+
return;
607657
}
658+
#endif // OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
608659

609660
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
610661
Error Instance::SetGlobalLogLevel(LogLevel aLogLevel)

src/core/instance/instance.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,31 @@ class Instance : public otInstance, private NonCopyable
356356
*/
357357
Error SetLogLevel(LogLevel aLogLevel);
358358

359+
#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
360+
/**
361+
* Overrides the current log level with a new one.
362+
*
363+
* The active log level will be the maximum of the original (user-set) log level and the override log level.
364+
* If this is the first time an override is requested, the current log level is saved as the original log level.
365+
*
366+
* Subsequent calls to `SetLogLevel()` will update the original log level, and the active log level will be
367+
* updated accordingly (again as the maximum of the new original and the override level).
368+
*
369+
* This method can be called multiple times to change the override log level.
370+
*
371+
* @param[in] aLogLevel The override log level.
372+
*/
373+
void OverrideLogLevel(LogLevel aLogLevel);
374+
375+
/**
376+
* Restores the log level to its original (user-set) value.
377+
*
378+
* This clears the log level override state. If the log level is not currently overridden, calling this
379+
* method has no effect.
380+
*/
381+
void RestoreLogLevel(void);
382+
#endif // OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
383+
359384
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
360385
/**
361386
* Sets the global log level.
@@ -523,6 +548,9 @@ class Instance : public otInstance, private NonCopyable
523548
Instance(void);
524549
void AfterInit(void);
525550
#endif
551+
#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
552+
void SignalLogLevelChange(void);
553+
#endif
526554

527555
//-----------------------------------------------------------------------------------------------------------------
528556
// `static` variables
@@ -866,6 +894,11 @@ class Instance : public otInstance, private NonCopyable
866894

867895
#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
868896
LogLevel mLogLevel;
897+
#if OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE
898+
LogLevel mOriginalLogLevel;
899+
LogLevel mOverrideLogLevel;
900+
bool mIsLogLevelOverriden;
901+
#endif
869902
#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
870903
bool mIsLogLevelSet;
871904
#endif

tests/nexus/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ ot_nexus_test(1_4_CS_TC_3 "cert;nexus")
382382
ot_nexus_test(border_admitter "core;nexus")
383383
ot_nexus_test(border_agent "core;nexus")
384384
ot_nexus_test(border_agent_tracker "core;nexus")
385+
ot_nexus_test(log_override "core;nexus")
385386
ot_nexus_test(discover_scan "core;nexus")
386387
ot_nexus_test(dtls "core;nexus")
387388
ot_nexus_test(form_join "core;nexus")

tests/nexus/openthread-core-nexus-config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 1
9090
#define OPENTHREAD_CONFIG_LOG_LEVEL_INIT OT_LOG_LEVEL_CRIT
9191
#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
92+
#define OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE 1
9293
#define OPENTHREAD_CONFIG_LOG_PLATFORM 0
9394
#define OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL 1
9495
#define OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME 0

tests/nexus/test_log_override.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (c) 2026, The OpenThread Authors.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* 3. Neither the name of the copyright holder nor the
13+
* names of its contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
29+
#include <stdarg.h>
30+
#include <stdio.h>
31+
#include <string.h>
32+
33+
#include "platform/nexus_core.hpp"
34+
#include "platform/nexus_node.hpp"
35+
36+
namespace ot {
37+
namespace Nexus {
38+
39+
void TestLogOverride(void)
40+
{
41+
Core nexus;
42+
Node &node = nexus.CreateNode();
43+
44+
Log("---------------------------------------------------------------------------------------");
45+
Log("TestLogOverride");
46+
47+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48+
Log("Check initial log level");
49+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelCrit);
50+
51+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
52+
Log("Check OverrideLogLevel to a higher level");
53+
54+
node.Get<Instance>().OverrideLogLevel(kLogLevelInfo);
55+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelInfo);
56+
57+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
58+
Log("Check RestoreLogLevel");
59+
60+
node.Get<Instance>().RestoreLogLevel();
61+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelCrit);
62+
63+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
64+
Log("Check OverrideLogLevel with a level lower than the current level");
65+
66+
SuccessOrQuit(node.SetLogLevel(kLogLevelWarn));
67+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelWarn);
68+
69+
node.Get<Instance>().OverrideLogLevel(kLogLevelCrit);
70+
71+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelWarn);
72+
73+
node.Get<Instance>().RestoreLogLevel();
74+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelWarn);
75+
76+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
77+
Log("Check user SetLogLevel while an override is active");
78+
79+
node.Get<Instance>().OverrideLogLevel(kLogLevelInfo);
80+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelInfo);
81+
82+
SuccessOrQuit(node.SetLogLevel(kLogLevelCrit));
83+
84+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelInfo);
85+
86+
node.Get<Instance>().RestoreLogLevel();
87+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelCrit);
88+
89+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
90+
Log("Check multiple OverrideLogLevel calls");
91+
92+
node.Get<Instance>().OverrideLogLevel(kLogLevelWarn);
93+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelWarn);
94+
95+
node.Get<Instance>().OverrideLogLevel(kLogLevelInfo);
96+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelInfo);
97+
98+
node.Get<Instance>().RestoreLogLevel();
99+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelCrit);
100+
101+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102+
Log("Check OverrideLogLevel and then SetLogLevel to a higher level than override");
103+
104+
node.Get<Instance>().OverrideLogLevel(kLogLevelWarn);
105+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelWarn);
106+
107+
SuccessOrQuit(node.SetLogLevel(kLogLevelInfo));
108+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelInfo);
109+
110+
node.Get<Instance>().RestoreLogLevel();
111+
VerifyOrQuit(node.Get<Instance>().GetLogLevel() == kLogLevelInfo);
112+
}
113+
114+
} // namespace Nexus
115+
} // namespace ot
116+
117+
int main(void)
118+
{
119+
ot::Nexus::TestLogOverride();
120+
printf("All tests passed\n");
121+
return 0;
122+
}

tests/toranj/openthread-core-toranj-config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@
147147

148148
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 1
149149

150+
#define OPENTHREAD_CONFIG_LOG_LEVEL_OVERRIDE_ENABLE 1
151+
150152
#define OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME 1
151153

152154
#define OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL 1

0 commit comments

Comments
 (0)