diff --git a/cfg_parse/cfg_parser.cpp b/cfg_parse/cfg_parser.cpp index 6e490f1..e9bf1f5 100644 --- a/cfg_parse/cfg_parser.cpp +++ b/cfg_parse/cfg_parser.cpp @@ -11833,6 +11833,79 @@ void value_print(const char *variableName, QTcpSocket *clientSocket) { clientSocket->flush(); } +std::list* getLogList(const QString& level) { + if (level == "ERROR") return &errorList; + if (level == "WARN") return &warnList; + if (level == "NORMAL") return &normalList; + if (level == "DEBUG") return &debugList; + return NULL; +} + +pthread_mutex_t* getLogMutex(const QString& level) { + if (level == "ERROR") return &errorListMutex; + if (level == "WARN") return &warnListMutex; + if (level == "NORMAL") return &normalListMutex; + if (level == "DEBUG") return &debugListMutex; + return NULL; +} + +void Worker::handleViewLogCommand(const QString& command, QTcpSocket* clientSocket) { + QStringList parts = command.split(" "); + if (parts.size() != 2) { + clientSocket->write("Usage: viewlog [ERROR|WARN|NORMAL|DEBUG]\n> "); + clientSocket->flush(); + return; + } + + QString logLevel = parts[1].toUpper(); + std::list* logList = getLogList(logLevel); + pthread_mutex_t* logMutex = getLogMutex(logLevel); + + if (!logList || !logMutex) { + clientSocket->write("Invalid log level! Use ERROR, WARN, NORMAL, or DEBUG.\n> "); + clientSocket->flush(); + return; + } + + stopViewLog = false; + activeClient = clientSocket; // 记录当前 shell socket + + clientSocket->write(QString("Viewing logs for level: %1 (Press 'q' to exit)\n> ").arg(logLevel).toUtf8()); + clientSocket->flush(); + + while (!stopViewLog) { + // **1. 监听输入,用户输入 `q` 退出** + if (clientSocket->waitForReadyRead(500)) { // ? 监听输入 + QByteArray input = clientSocket->readAll().trimmed(); + if (input == "q") { // ? 用户输入 `q`,退出日志模式 + std::cout << "Received 'q' from shell socket! Exiting viewlog...\n"; + stopViewLog = true; + break; + } + } + + // **2. 获取日志内容并发送** + pthread_mutex_lock(logMutex); + if (!logList->empty()) { + std::string logEntry = logList->front(); + logList->pop_front(); + pthread_mutex_unlock(logMutex); + + if (!logEntry.empty()) { + clientSocket->write((logEntry + "\n").c_str()); + clientSocket->flush(); + } + } else { + pthread_mutex_unlock(logMutex); + usleep(500000); // ? 防止 CPU 100% 占用 + } + } + + // **3. 退出 `viewlog`,返回 Shell** + clientSocket->write("\nLog view stopped. Returning to shell.\n> "); + clientSocket->flush(); +} + ////////////////////////////////////////////////////////////////////////////////////////////////// // 解析 JSON 的函数 多前置动态均分 int terminal_ledger_web(QMap* terminal_dev_map, @@ -15170,15 +15243,18 @@ void rocketmq_test_300(int mpnum,int front_index) { std::list errorList; std::list warnList; std::list normalList; +std::list debugList; // 新增 debugList pthread_mutex_t errorListMutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t warnListMutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t normalListMutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t debugListMutex = PTHREAD_MUTEX_INITIALIZER; // 新增 debugList 互斥锁 // ------------------ 输出开关 ------------------ bool errorOutputEnabled = false; // 是否将 error 级别写入 errorList bool warnOutputEnabled = false; // 是否将 warn 级别写入 warnList bool normalOutputEnabled = false; // 是否将 normal 级别写入 normalList +bool debugOutputEnabled = false; // 新增 debug 开关 // ------------------ 用于恢复原始缓冲区 ------------------ static std::streambuf* g_originalCoutBuf = NULL; @@ -15187,9 +15263,10 @@ static std::streambuf* g_originalCerrBuf = NULL; // ------------------ 日志级别枚举(C++98) ------------------ enum LogLevel { - LOG_ERROR, - LOG_WARN, - LOG_NORMAL + LOGERROR, + LOGWARN, + LOGNORMAL, + LOGDEBUG // 新增 debug 级别 }; // ------------------------------------------------------------------ @@ -15200,7 +15277,7 @@ class TeeStreamBuf : public std::streambuf public: // 默认构造:先把指针设为NULL TeeStreamBuf() - : m_originalBuf(NULL), m_level(LOG_NORMAL) + : m_originalBuf(NULL), m_level(LOGNORMAL) { } @@ -15263,7 +15340,7 @@ private: // 根据等级 + 对应开关 → 写哪几个list switch (m_level) { - case LOG_ERROR: + case LOGERROR: if (errorOutputEnabled) { pthread_mutex_lock(&errorListMutex); errorList.push_back(m_buffer); @@ -15279,9 +15356,14 @@ private: normalList.push_back(m_buffer); pthread_mutex_unlock(&normalListMutex); } + if (debugOutputEnabled) { + pthread_mutex_lock(&debugListMutex); + debugList.push_back(m_buffer); + pthread_mutex_unlock(&debugListMutex); + } break; - case LOG_WARN: + case LOGWARN: if (warnOutputEnabled) { pthread_mutex_lock(&warnListMutex); warnList.push_back(m_buffer); @@ -15292,22 +15374,41 @@ private: normalList.push_back(m_buffer); pthread_mutex_unlock(&normalListMutex); } + if (debugOutputEnabled) { + pthread_mutex_lock(&debugListMutex); + debugList.push_back(m_buffer); + pthread_mutex_unlock(&debugListMutex); + } break; - case LOG_NORMAL: + case LOGNORMAL: if (normalOutputEnabled) { pthread_mutex_lock(&normalListMutex); normalList.push_back(m_buffer); pthread_mutex_unlock(&normalListMutex); } + if (debugOutputEnabled) { + pthread_mutex_lock(&debugListMutex); + debugList.push_back(m_buffer); + pthread_mutex_unlock(&debugListMutex); + } break; + case LOGDEBUG: + // 如果 debug 开关开了,才放进 debugList + if (debugOutputEnabled) { + pthread_mutex_lock(&debugListMutex); + debugList.push_back(m_buffer); + pthread_mutex_unlock(&debugListMutex); + } + + break; } m_buffer.clear(); } private: - // 禁止自动生成的赋值函数 (C++98常规做法) + // 禁止自动生成的赋值函数 TeeStreamBuf& operator=(const TeeStreamBuf&); // 不实现 private: @@ -15320,6 +15421,14 @@ private: static TeeStreamBuf g_errorTeeBuf; static TeeStreamBuf g_warnTeeBuf; static TeeStreamBuf g_normalTeeBuf; +static TeeStreamBuf g_debugTeeBuf; // 新增 debug Tee + +// ------------------ 我们另外提供一个全局的 debug 输出流 ------------------ +// 其原始buf默认为 NULL(不输出到终端),只存到 debugList(若开关开) +static std::ostream g_debug(&g_debugTeeBuf); + +// 让 qDebug() 映射到这个全局流 +#define qDebug() g_debug // ------------------ 重定向函数 ------------------ // 只在第一次启用时,用 init(...) 初始化 TeeStreamBuf; @@ -15330,7 +15439,7 @@ void redirectErrorOutput(bool enabled) if (enabled) { if (g_originalCerrBuf == NULL) { g_originalCerrBuf = std::cerr.rdbuf(); - g_errorTeeBuf.init(g_originalCerrBuf, LOG_ERROR); + g_errorTeeBuf.init(g_originalCerrBuf, LOGERROR); } std::cerr.rdbuf(&g_errorTeeBuf); } else { @@ -15346,7 +15455,7 @@ void redirectWarnOutput(bool enabled) if (enabled) { if (g_originalClogBuf == NULL) { g_originalClogBuf = std::clog.rdbuf(); - g_warnTeeBuf.init(g_originalClogBuf, LOG_WARN); + g_warnTeeBuf.init(g_originalClogBuf, LOGWARN); } std::clog.rdbuf(&g_warnTeeBuf); } else { @@ -15362,7 +15471,7 @@ void redirectNormalOutput(bool enabled) if (enabled) { if (g_originalCoutBuf == NULL) { g_originalCoutBuf = std::cout.rdbuf(); - g_normalTeeBuf.init(g_originalCoutBuf, LOG_NORMAL); + g_normalTeeBuf.init(g_originalCoutBuf, LOGNORMAL); } std::cout.rdbuf(&g_normalTeeBuf); } else { @@ -15372,6 +15481,26 @@ void redirectNormalOutput(bool enabled) } } +// ------------------ 新增:debug 重定向函数 ------------------ +// 由于没有标准的“debug流”,我们这里只切换“是否把内容存入 debugList” +// 并让 g_debugTeeBuf 是否也要输出到某个原始缓冲(可选) +void redirectDebugOutput(bool enabled) +{ + debugOutputEnabled = enabled; + if (enabled) { + // 如果希望 debug 同时也显示到终端,可以指定原始缓冲区 + // 比如用 std::clog.rdbuf() 作为基底: + g_debugTeeBuf.init(std::clog.rdbuf(), LOGDEBUG); + // 这里我们选择 NULL → 不在终端输出, 只保存在 debugList. + //g_debugTeeBuf.init(NULL, LOGDEBUG); + } else { + // 关闭debug→ 不再写 debugList(虽然还会写 buffer, 但 push_back时不开关) + // 不需要“恢复原始”——因为没有给std::ostream还原。 + g_debugTeeBuf.init(NULL, LOGDEBUG); + // 仅保留init, 让 debugOutputEnabled=false 即可。 + } +} + // ------------------ 自定义 printf 输出 ------------------ // 这里示例:把它当成 normal 级别 => 最终会进入normalList(若开关开) int customPrintf(const char* format, ...) @@ -15394,6 +15523,15 @@ int customPrintf(const char* format, ...) return written; } +// **原始 `echo_msgX()` 的实现** +void real_echo_msg(const char *format, ...) { + char buffer[1024]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + printf("%s", buffer); // 正常打印日志 +} /////////////////////////////////////////////////////////////////////////////// diff --git a/cfg_parse/custom_printf.h b/cfg_parse/custom_printf.h index e58350c..772b303 100644 --- a/cfg_parse/custom_printf.h +++ b/cfg_parse/custom_printf.h @@ -13,16 +13,51 @@ extern std::list errorList; extern std::list warnList; extern std::list normalList; +extern std::list debugList; // 寮鍏 extern bool errorOutputEnabled; extern bool warnOutputEnabled; extern bool normalOutputEnabled; +extern bool debugOutputEnabled; void redirectErrorOutput(bool enabled); void redirectWarnOutput(bool enabled); void redirectNormalOutput(bool enabled); +void redirectDebugOutput(bool enabled); +extern pthread_mutex_t errorListMutex; +extern pthread_mutex_t warnListMutex; +extern pthread_mutex_t normalListMutex; +extern pthread_mutex_t debugListMutex; + +// **澹版槑鍘熷 echo_msgX()锛岃缂栬瘧鍣ㄧ煡閬撳畠浠瓨鍦** +void real_echo_msg(const char *format, ...); + +// **鎷︽埅鎵鏈 `echo_msgX()`锛岃瀹冨悓鏃跺瓨鍏 `normalList`** +#define echo_msgX(format, ...) \ + do { \ + real_echo_msg(format, ##__VA_ARGS__); /* 璋冪敤鍘熷 `echo_msgX()` */ \ + char buffer[1024]; \ + snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \ + pthread_mutex_lock(&normalListMutex); \ + normalList.push_back(buffer); \ + pthread_mutex_unlock(&normalListMutex); \ + } while (0) + +// **瀹忚嚜鍔ㄥ睍寮涓嶅悓鐨 `echo_msg` 鍙樹綋** +#define echo_msg1 echo_msgX +#define echo_msg2 echo_msgX +#define echo_msg3 echo_msgX +#define echo_msg4 echo_msgX +#define echo_msg5 echo_msgX +#define echo_msg6 echo_msgX +#define echo_msg7 echo_msgX +#define echo_msg8 echo_msgX +#define echo_msg9 echo_msgX +#define echo_msg10 echo_msgX +#define echo_msg11 echo_msgX +// **濡傛灉鏈夋洿澶氱殑 `echo_msgX()`锛岀户缁畾涔** extern "C" { diff --git a/json/save2json.cpp b/json/save2json.cpp index bed2c89..d3bcee2 100644 --- a/json/save2json.cpp +++ b/json/save2json.cpp @@ -125,9 +125,9 @@ extern std::string G_MQCONSUMER_KEY_LOG;//key extern std::string G_LOG_TOPIC;//topie extern std::string G_LOG_TAG;//tag extern std::string G_LOG_KEY;//key -extern pthread_mutex_t errorListMutex; -extern pthread_mutex_t warnListMutex; -extern pthread_mutex_t normalListMutex; + +bool showinshellflag =false; + #define APRTIME_8H (28800000000ULL) #define APRTIME_1H (3600000000ULL) @@ -599,42 +599,77 @@ void KafkaSendThread::run() bool log_gotten; log_gotten = false; - - - if (normalOutputEnabled) { - // 如果 normalOutputEnabled 为 1,优先从 normalList 获取输出 - // 处理 normalList 的输出 - pthread_mutex_lock(&normalListMutex); - if (!normalList.empty()) { - - //qDebug() << "flag of list:" << normalOutputEnabled << " " << warnOutputEnabled << " " << errorOutputEnabled << " " << "warnList size: " << warnList.size() << "normalList size: " << normalList.size() << "errorList size: " << errorList.size()< #include + #include //lnk20250106 #include "../include/rocketmq/SimpleProducer.h" +//日志功能 +#include "../cfg_parse/custom_printf.h"//lnk20250225 +#include +#include + #include #include #include @@ -102,6 +108,8 @@ protected: void run(); };*/ //lnk20250106 +extern bool showinshellflag; + class Worker : public QObject { Q_OBJECT @@ -112,7 +120,9 @@ public: server(NULL), TEST_NUM(G_TEST_NUM), timer(NULL), - historyIndex(-1) + historyIndex(-1), + stopViewLog(false), + activeClient(NULL) { } @@ -121,6 +131,8 @@ public: stopServer(); } + void handleViewLogCommand(const QString& command, QTcpSocket* clientSocket); + public slots: void startServer() { if (server) { @@ -225,65 +237,67 @@ private slots: for (int i = 0; i < data.size(); ++i) { char c = data[i]; - switch (c) { - case '\r': - case '\n': - // 检测到回车或换行,就认为用户输入了一条完整命令 - if (!currentCommand.isEmpty()) { - // 将命令存入历史记录(避免连续相同命令重复记录) - if (commandHistory.isEmpty() || commandHistory.last() != currentCommand) { - commandHistory.append(currentCommand); - } - historyIndex = commandHistory.size(); // 指向最新一条之后 - - // 处理这条命令 - processCommand(currentCommand, clientSocket); - currentCommand.clear(); - } else { - // 如果只是空行,还是给出提示符 - clientSocket->write("\n> "); + if (c == 'q') { // ? 用户输入 `q` 退出 `viewlog` + std::cout << "Received 'q' from shell socket! Exiting viewlog...\n"; + if (activeClient == clientSocket) { + stopViewLog = true; // ? 让 `viewlog` 退出 + showinshellflag = false; + clientSocket->write("\nLog view stopped. Returning to shell.\n> "); clientSocket->flush(); } - break; - - case '\x1b': - // 可能是方向键或其他 ESC 序列 - // 上箭头: \x1b[A - // 下箭头: \x1b[B - if (i + 2 < data.size() && data[i+1] == '[') { - char arrow = data[i+2]; - if (arrow == 'A') { - // 上箭头 - handleUpArrow(clientSocket); - } else if (arrow == 'B') { - // 下箭头 - handleDownArrow(clientSocket); - } - // 已经处理了3个字节: \x1b [ X - i += 2; - } - break; - - case '\x7f': - case '\x08': - // 退格键(可能是Del或Backspace) - if (!currentCommand.isEmpty()) { - currentCommand.chop(1); // 删除最后一个字符 - // 回显:光标回退 + 覆盖 + 再回退 - // "\b \b"可以在绝大多数纯终端上实现退格 - clientSocket->write("\b \b"); - clientSocket->flush(); - } - break; - - default: - // 普通字符,追加到 currentCommand 里 - currentCommand.append(c); - // 回显到客户端 - clientSocket->write(&c, 1); - clientSocket->flush(); - break; + return; // ? 立即返回,避免继续处理其他字符 } + + switch (c) { + case '\r': + case '\n': + // ? 处理回车,执行命令 + if (!currentCommand.isEmpty()) { + if (commandHistory.isEmpty() || commandHistory.last() != currentCommand) { + commandHistory.append(currentCommand); + } + historyIndex = commandHistory.size(); // 指向最新一条之后 + + processCommand(currentCommand, clientSocket); + currentCommand.clear(); + } else { + clientSocket->write("\n> "); + clientSocket->flush(); + } + break; + + case '\x1b': + // ? 处理方向键 (上箭头 `\x1b[A`,下箭头 `\x1b[B`) + if (i + 2 < data.size() && data[i+1] == '[') { + char arrow = data[i+2]; + if (arrow == 'A') { + handleUpArrow(clientSocket); // **调用上箭头处理** + } else if (arrow == 'B') { + handleDownArrow(clientSocket); // **调用下箭头处理** + } + i += 2; // ? **跳过 ESC 序列 `\x1b[A` 或 `\x1b[B`** + continue; + } + break; + + case '\x7f': + case '\x08': + // ? 处理退格键 + if (!currentCommand.isEmpty()) { + currentCommand.chop(1); + clientSocket->write("\b \b"); + clientSocket->flush(); + } + break; + + default: + // ? 普通字符,追加到 `currentCommand` + currentCommand.append(c); + clientSocket->write(&c, 1); + clientSocket->flush(); + break; + } + } } @@ -291,6 +305,10 @@ signals: void serverError(); private: + // ====================== 新增日志功能 ========================= + bool stopViewLog; // ? 这里不需要 static,针对每个 client + QTcpSocket* activeClient; // 记录当前正在 `viewlog` 的客户端 + // -------------------- // 以下为新增的辅助函数 // -------------------- @@ -315,11 +333,16 @@ private: helpText += "only - Execute rocketmq_test_only\n"; helpText += "log - Execute rocketmq_test_log\n"; helpText += "ledger - Execute ledger with optional terminal_id\n"; + helpText += "viewlog - View logs (ERROR, WARN, NORMAL, DEBUG)\n"; helpText += "value - Execute value print with valuename : iedcount frontfun frontindex remtable\n"; helpText += "exit - Exit the shell\n"; helpText += "help - Show this help message\n"; clientSocket->write(helpText.toUtf8()); } + else if (command.startsWith("viewlog")) { + showinshellflag = true; + handleViewLogCommand(command, clientSocket); + } // 设置多点模拟的个数 else if (command.startsWith("TEST_NUM=")) { bool ok; @@ -439,12 +462,11 @@ private: historyIndex--; currentCommand = commandHistory[historyIndex]; - // 将光标移到行首,并清空当前行 (简单做法: 回车后用空格覆盖) - clientSocket->write("\r"); - clientSocket->write(" "); - clientSocket->write("\r> "); - // 回显历史命令 - clientSocket->write(currentCommand.toUtf8()); + // **清空当前行** + clientSocket->write("\r"); // 移动光标到行首 + clientSocket->write(" "); // 用空格覆盖 + clientSocket->write("\r> "); // 重新打印提示符 + clientSocket->write(currentCommand.toUtf8()); // 回显历史命令 clientSocket->flush(); } } @@ -457,14 +479,15 @@ private: historyIndex++; currentCommand = commandHistory[historyIndex]; + // **清空当前行** clientSocket->write("\r"); clientSocket->write(" "); clientSocket->write("\r> "); - clientSocket->write(currentCommand.toUtf8()); + clientSocket->write(currentCommand.toUtf8()); // 回显历史命令 clientSocket->flush(); } else if (historyIndex == commandHistory.size() - 1) { - // 如果已经是最后一条,再按下箭头,则清空 + // **如果已经是最后一条,再按下箭头,则清空当前输入** historyIndex = commandHistory.size(); currentCommand.clear();