diff --git a/examples/fabric-admin/commands/common/CHIPCommand.h b/examples/fabric-admin/commands/common/CHIPCommand.h index 856a4daa48753d..af833f5f718652 100644 --- a/examples/fabric-admin/commands/common/CHIPCommand.h +++ b/examples/fabric-admin/commands/common/CHIPCommand.h @@ -67,6 +67,9 @@ class CHIPCommand : public Command CHIPCommand(const char * commandName, CredentialIssuerCommands * credIssuerCmds, const char * helpText = nullptr) : Command(commandName, helpText), mCredIssuerCmds(credIssuerCmds) { + AddArgument("log-file-path", &mLogFilePath, + "Path to the log file where the output is redirected. Can be absolute or relative to the current working " + "directory."); AddArgument("paa-trust-store-path", &mPaaTrustStorePath, "Path to directory holding PAA certificate information. Can be absolute or relative to the current working " "directory."); @@ -156,6 +159,7 @@ class CHIPCommand : public Command // identity without shutting it down or something in between... PersistentStorage mCommissionerStorage; #endif // CONFIG_USE_LOCAL_STORAGE + chip::Optional mLogFilePath; chip::PersistentStorageOperationalKeystore mOperationalKeystore; chip::Credentials::PersistentStorageOpCertStore mOpCertStore; static chip::Crypto::RawKeySessionKeystore sSessionKeystore; diff --git a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp index 9ef07e79450300..aaf3c36461cffb 100644 --- a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp +++ b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp @@ -19,9 +19,12 @@ #include "InteractiveCommands.h" #include +#include #include +#include +#include #include #include @@ -31,6 +34,27 @@ constexpr char kInteractiveModeStopCommand[] = "quit()"; namespace { +// File pointer for the log file +FILE * sLogFile = nullptr; + +void OpenLogFile(const char * filePath) +{ + sLogFile = fopen(filePath, "a"); + if (sLogFile == nullptr) + { + perror("Failed to open log file"); + } +} + +void CloseLogFile() +{ + if (sLogFile != nullptr) + { + fclose(sLogFile); + sLogFile = nullptr; + } +} + void ClearLine() { printf("\r\x1B[0J"); // Move cursor to the beginning of the line and clear from cursor to end of the screen @@ -38,9 +62,24 @@ void ClearLine() void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, const char * msg, va_list args) { - ClearLine(); - chip::Logging::Platform::LogV(module, category, msg, args); - ClearLine(); + if (sLogFile == nullptr) + { + return; + } + + uint64_t timeMs = chip::System::SystemClock().GetMonotonicMilliseconds64().count(); + uint64_t seconds = timeMs / 1000; + uint64_t milliseconds = timeMs % 1000; + + flockfile(sLogFile); + + fprintf(sLogFile, "[%llu.%06llu] CHIP:%s: ", static_cast(seconds), + static_cast(milliseconds), module); + vfprintf(sLogFile, msg, args); + fprintf(sLogFile, "\n"); + fflush(sLogFile); + + funlockfile(sLogFile); } } // namespace @@ -90,9 +129,13 @@ CHIP_ERROR InteractiveStartCommand::RunCommand() { read_history(GetHistoryFilePath().c_str()); - // Logs needs to be redirected in order to refresh the screen appropriately when something - // is dumped to stdout while the user is typing a command. - chip::Logging::SetLogRedirectCallback(LoggingCallback); + if (mLogFilePath.HasValue()) + { + OpenLogFile(mLogFilePath.Value()); + + // Redirect logs to the custom logging callback + chip::Logging::SetLogRedirectCallback(LoggingCallback); + } char * command = nullptr; int status; @@ -112,6 +155,8 @@ CHIP_ERROR InteractiveStartCommand::RunCommand() } SetCommandExitStatus(CHIP_NO_ERROR); + CloseLogFile(); + return CHIP_NO_ERROR; } diff --git a/examples/fabric-admin/main.cpp b/examples/fabric-admin/main.cpp index e517c67f6b403f..cf1122bda1ec8a 100644 --- a/examples/fabric-admin/main.cpp +++ b/examples/fabric-admin/main.cpp @@ -23,11 +23,26 @@ #include "commands/pairing/Commands.h" #include +#include +#include +#include + // ================================================================================ // Main Code // ================================================================================ int main(int argc, char * argv[]) { + // Convert command line arguments to a vector of strings for easier manipulation + std::vector args(argv, argv + argc); + + // Check if "interactive" and "start" are not in the arguments + if (args.size() < 3 || args[1] != "interactive" || args[2] != "start") + { + // Insert "interactive" and "start" after the executable name + args.insert(args.begin() + 1, "interactive"); + args.insert(args.begin() + 2, "start"); + } + ExampleCredentialIssuerCommands credIssuerCommands; Commands commands; @@ -36,5 +51,11 @@ int main(int argc, char * argv[]) registerClusters(commands, &credIssuerCommands); registerCommandsSubscriptions(commands, &credIssuerCommands); - return commands.Run(argc, argv); + std::vector c_args; + for (auto & arg : args) + { + c_args.push_back(const_cast(arg.c_str())); + } + + return commands.Run(static_cast(c_args.size()), c_args.data()); }