[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<id>.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()`.
This commit is contained in:
Abtin Keshavarzian
2026-03-25 21:23:52 -05:00
committed by GitHub
parent 1f218fd056
commit f1794af0a7
7 changed files with 227 additions and 46 deletions
+1
View File
@@ -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
+27
View File
@@ -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);
+1 -2
View File
@@ -129,12 +129,11 @@ private:
Array<TestVar, 128> 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
+137
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <openthread/platform/logging.h>
#include "nexus_core.hpp"
#include "nexus_node.hpp"
namespace ot {
namespace Nexus {
static constexpr uint16_t kTimestampStringSize = 40;
typedef String<kTimestampStringSize> 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
+58
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#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_
-44
View File
@@ -30,7 +30,6 @@
#include <stdlib.h>
#include <openthread/platform/entropy.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/misc.h>
#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
+3
View File
@@ -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;