From f1794af0a721e9954d9d6d95d378b7a512dceabf Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 25 Mar 2026 19:23:52 -0700 Subject: [PATCH] [nexus] support node-specific log files (#12767) This commit introduces node-specific log files for Nexus tests. Each created node can save its OpenThread logs into a separate file `ot-logs.log`. The generation of log files is controlled by the environment variable `OT_NEXUS_SAVE_LOGS`. By default, it is disabled, but it can be activated by setting the environment variable to "1", "yes", "true", "on", or "t". This commit also refactors the Nexus platform logging logic into a new `nexus_logging.cpp` file and improves the log message format in `stdout` to include a standard timestamp using `UptimeToString()`. --- tests/nexus/CMakeLists.txt | 1 + tests/nexus/platform/nexus_core.cpp | 27 +++++ tests/nexus/platform/nexus_core.hpp | 3 +- tests/nexus/platform/nexus_logging.cpp | 137 +++++++++++++++++++++++++ tests/nexus/platform/nexus_logging.hpp | 58 +++++++++++ tests/nexus/platform/nexus_misc.cpp | 44 -------- tests/nexus/platform/nexus_node.hpp | 3 + 7 files changed, 227 insertions(+), 46 deletions(-) create mode 100644 tests/nexus/platform/nexus_logging.cpp create mode 100644 tests/nexus/platform/nexus_logging.hpp diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index 9fdc01bc0..d90ffc737 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -45,6 +45,7 @@ add_library(ot-nexus-platform platform/nexus_alarm.cpp platform/nexus_core.cpp platform/nexus_infra_if.cpp + platform/nexus_logging.cpp platform/nexus_mdns.cpp platform/nexus_misc.cpp platform/nexus_node.cpp diff --git a/tests/nexus/platform/nexus_core.cpp b/tests/nexus/platform/nexus_core.cpp index c0401ed4f..d046f43c3 100644 --- a/tests/nexus/platform/nexus_core.cpp +++ b/tests/nexus/platform/nexus_core.cpp @@ -43,9 +43,11 @@ bool Core::sInUse = false; Core::Core(void) : mCurNodeId(0) , mPendingAction(false) + , mSaveNodeLogs(false) , mNow(0) { const char *pcapFile; + const char *saveLogs; VerifyOrQuit(!sInUse); sCore = this; @@ -59,6 +61,26 @@ Core::Core(void) { mPcap.Open(pcapFile); } + + saveLogs = getenv("OT_NEXUS_SAVE_LOGS"); + + if (saveLogs != nullptr) + { + static const char *kActivateStrings[] = {"1", "yes", "y", "true", "t", "on"}; + + bool activate = false; + + for (const char *activateString : kActivateStrings) + { + if (StringMatch(saveLogs, activateString, kStringCaseInsensitiveMatch)) + { + activate = true; + break; + } + } + + mSaveNodeLogs = activate; + } } void Core::SaveTestInfo(const char *aFilename, Node *aLeaderNode) @@ -301,6 +323,11 @@ Node &Core::CreateNode(void) node->GetInstance().SetId(mCurNodeId++); + if (mSaveNodeLogs) + { + node->mLogging.Init(node->GetId()); + } + node->mInfraIf.Init(*node); node->mMdns.Init(*node); diff --git a/tests/nexus/platform/nexus_core.hpp b/tests/nexus/platform/nexus_core.hpp index b66ea7034..4db442582 100644 --- a/tests/nexus/platform/nexus_core.hpp +++ b/tests/nexus/platform/nexus_core.hpp @@ -129,12 +129,11 @@ private: Array mTestVars; uint16_t mCurNodeId; bool mPendingAction; + bool mSaveNodeLogs; uint64_t mNow; uint64_t mNextAlarmTime; }; -void Log(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2); - } // namespace Nexus } // namespace ot diff --git a/tests/nexus/platform/nexus_logging.cpp b/tests/nexus/platform/nexus_logging.cpp new file mode 100644 index 000000000..0f2721aa1 --- /dev/null +++ b/tests/nexus/platform/nexus_logging.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include "nexus_core.hpp" +#include "nexus_node.hpp" + +namespace ot { +namespace Nexus { + +static constexpr uint16_t kTimestampStringSize = 40; + +typedef String TimestampString; + +static TimestampString GetTimestamp(TimeMilli aNow) +{ + TimestampString string; + + UptimeToString(aNow.GetValue(), string, /* aIncludeMsec */ true); + return string; +} + +static TimestampString GetTimestamp(void) { return GetTimestamp(Core::Get().GetNow()); } + +void Log(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + + printf("%s ", GetTimestamp().AsCString()); + vprintf(aFormat, args); + printf("\n"); + fflush(stdout); + + va_end(args); +} + +//--------------------------------------------------------------------------------------------------------------------- +// Logging + +Logging::Logging(void) + : mLogFile(nullptr) +{ +} + +Logging::~Logging(void) +{ + if (mLogFile != nullptr) + { + fclose(mLogFile); + } +} + +void Logging::Init(uint32_t aId) +{ + String<32> fileName; + + if (mLogFile != nullptr) + { + fclose(mLogFile); + } + + fileName.Append("ot-logs%lu.log", ToUlong(aId)); + mLogFile = fopen(fileName.AsCString(), "wt"); + + VerifyOrQuit(mLogFile != nullptr); + + fprintf(mLogFile, "OpenThread logs\r\n"); + fprintf(mLogFile, "- Platform: nexus\r\n"); + fprintf(mLogFile, "- Node ID : %lu\r\n", ToUlong(aId)); + fprintf(mLogFile, "\r\n"); +} + +void Logging::SaveLog(const char *aLogLine, TimeMilli aNow) +{ + VerifyOrExit(mLogFile != nullptr); + fprintf(mLogFile, "%s %s\r\n", GetTimestamp(aNow).AsCString(), aLogLine); + +exit: + return; +} + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatLog + +extern "C" { + +void otPlatLogOutput(otInstance *aInstance, otLogLevel aLogLevel, const char *aLogLine) +{ + OT_UNUSED_VARIABLE(aLogLevel); + + TimeMilli now = Core::Get().GetNow(); + + VerifyOrExit(aInstance != nullptr); + + printf("<%03u> %s %s\n", AsNode(aInstance).GetId(), GetTimestamp(now).AsCString(), aLogLine); + fflush(stdout); + + AsNode(aInstance).mLogging.SaveLog(aLogLine, now); + +exit: + return; +} +} + +} // namespace Nexus +} // namespace ot diff --git a/tests/nexus/platform/nexus_logging.hpp b/tests/nexus/platform/nexus_logging.hpp new file mode 100644 index 000000000..e5de3ff46 --- /dev/null +++ b/tests/nexus/platform/nexus_logging.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_NEXUS_PLATFORM_NEXUS_LOGGING_HPP_ +#define OT_NEXUS_PLATFORM_NEXUS_LOGGING_HPP_ + +#include +#include + +#include "common/const_cast.hpp" +#include "common/owning_list.hpp" +#include "instance/instance.hpp" + +namespace ot { +namespace Nexus { + +void Log(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2); + +struct Logging +{ + Logging(void); + ~Logging(void); + + void Init(uint32_t aId); + void SaveLog(const char *aLogLine, TimeMilli aNow); + + FILE *mLogFile; +}; + +} // namespace Nexus +} // namespace ot + +#endif // OT_NEXUS_PLATFORM_NEXUS_LOGGING_HPP_ diff --git a/tests/nexus/platform/nexus_misc.cpp b/tests/nexus/platform/nexus_misc.cpp index 646551534..5df75373d 100644 --- a/tests/nexus/platform/nexus_misc.cpp +++ b/tests/nexus/platform/nexus_misc.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include "nexus_core.hpp" @@ -39,8 +38,6 @@ namespace ot { namespace Nexus { -static void LogTime(void); - extern "C" { //--------------------------------------------------------------------------------------------------------------------- @@ -53,23 +50,6 @@ void otTaskletsSignalPending(otInstance *aInstance) Core::Get().MarkPendingAction(); } -//--------------------------------------------------------------------------------------------------------------------- -// otPlatLog - -void otPlatLogOutput(otInstance *aInstance, otLogLevel aLogLevel, const char *aLogLine) -{ - OT_UNUSED_VARIABLE(aLogLevel); - - VerifyOrExit(aInstance != nullptr); - - LogTime(); - printf("%03u %s\n", AsNode(aInstance).GetId(), aLogLine); - fflush(stdout); - -exit: - return; -} - //--------------------------------------------------------------------------------------------------------------------- // Heap allocation APIs @@ -134,29 +114,5 @@ void otPlatWakeHost(void) {} } // extern "C" -//--------------------------------------------------------------------------------------------------------------------- -// Log related function - -static void LogTime(void) -{ - uint32_t now = Core::Get().GetNow().GetValue(); - - printf("%02u:%02u:%02u.%03u ", now / 3600000, (now / 60000) % 60, (now / 1000) % 60, now % 1000); -} - -void Log(const char *aFormat, ...) -{ - va_list args; - - va_start(args, aFormat); - - LogTime(); - vprintf(aFormat, args); - printf("\n"); - fflush(stdout); - - va_end(args); -} - } // namespace Nexus } // namespace ot diff --git a/tests/nexus/platform/nexus_node.hpp b/tests/nexus/platform/nexus_node.hpp index 2173aaa3a..cd08cd789 100644 --- a/tests/nexus/platform/nexus_node.hpp +++ b/tests/nexus/platform/nexus_node.hpp @@ -34,6 +34,7 @@ #include "nexus_alarm.hpp" #include "nexus_core.hpp" #include "nexus_infra_if.hpp" +#include "nexus_logging.hpp" #include "nexus_mdns.hpp" #include "nexus_radio.hpp" #include "nexus_settings.hpp" @@ -49,6 +50,7 @@ public: Radio mRadio; Alarm mAlarmMilli; Alarm mAlarmMicro; + Logging mLogging; Mdns mMdns; InfraIf mInfraIf; Settings mSettings; @@ -135,6 +137,7 @@ public: using Platform::mAlarmMicro; using Platform::mAlarmMilli; using Platform::mInfraIf; + using Platform::mLogging; using Platform::mMdns; using Platform::mPendingTasklet; using Platform::mRadio;