增加了日志功能,修复了bug
This commit is contained in:
@@ -54,6 +54,11 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ev == MG_EV_WS_MSG) {
|
||||
// WebSocket消息处理
|
||||
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
|
||||
std::string msg(wm->data.buf, wm->data.len);
|
||||
handleLogRequest(c, msg);
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
// HTTP请求处理
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
@@ -216,11 +221,18 @@ bool HttpServer::serverThreadFunc() {
|
||||
|
||||
// 定期推送系统数据
|
||||
static uint64_t last_push_time = 0;
|
||||
static uint64_t last_log_check_time = 0;
|
||||
uint64_t now = mg_millis();
|
||||
|
||||
if (now - last_push_time > 1000) { // 每秒推送一次
|
||||
last_push_time = now;
|
||||
pushSystemData();
|
||||
}
|
||||
|
||||
if (now - last_log_check_time > 500) { // 每500ms检查一次日志
|
||||
last_log_check_time = now;
|
||||
monitorLogFile();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -550,3 +562,154 @@ void HttpServer::stop() {
|
||||
bool HttpServer::isRunning() const {
|
||||
return running_;
|
||||
}
|
||||
|
||||
void HttpServer::handleLogRequest(struct mg_connection* c, const std::string& msg) {
|
||||
try {
|
||||
Json::Value json;
|
||||
Json::CharReaderBuilder builder;
|
||||
std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
|
||||
const char* msg_cstr = msg.c_str();
|
||||
std::string errors;
|
||||
|
||||
if (!reader->parse(msg_cstr, msg_cstr + msg.size(), &json, &errors)) {
|
||||
LOG_F(ERROR, "Failed to parse log request JSON: %s", errors.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string type = json["type"].asString();
|
||||
if (type == "get_logs") {
|
||||
int page = json["page"].asInt();
|
||||
int pageSize = json["pageSize"].asInt();
|
||||
std::string level = json["filter"]["level"].asString();
|
||||
std::string search = json["filter"]["search"].asString();
|
||||
bool reverse = json["reverse"].asBool(); // 新增参数,控制是否倒序显示
|
||||
|
||||
auto logs = readLogFile(page, pageSize, level, search);
|
||||
|
||||
Json::Value response;
|
||||
response["type"] = "log";
|
||||
response["page"] = page;
|
||||
response["pageSize"] = pageSize;
|
||||
response["total"] = logs.size(); // 返回总条数
|
||||
response["content"] = Json::arrayValue;
|
||||
|
||||
if (reverse) {
|
||||
// 如果需要倒序,反转日志顺序
|
||||
for (auto it = logs.rbegin(); it != logs.rend(); ++it) {
|
||||
response["content"].append(*it);
|
||||
}
|
||||
} else {
|
||||
// 否则按原顺序返回
|
||||
for (const auto& log : logs) {
|
||||
response["content"].append(log);
|
||||
}
|
||||
}
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
std::string responseStr = Json::writeString(builder, response);
|
||||
mg_ws_send(c, responseStr.c_str(), responseStr.size(), WEBSOCKET_OP_TEXT);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_F(ERROR, "Error handling log request: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> HttpServer::readLogFile(int page, int pageSize, const std::string& level, const std::string& search) {
|
||||
std::vector<std::string> logs;
|
||||
std::ifstream file(m_logFilePath);
|
||||
if (!file.is_open()) {
|
||||
LOG_F(ERROR, "Failed to open log file: %s", m_logFilePath.c_str());
|
||||
return logs;
|
||||
}
|
||||
|
||||
// 获取文件大小
|
||||
file.seekg(0, std::ios::end);
|
||||
std::streampos fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::string line;
|
||||
std::vector<std::string> filteredLogs;
|
||||
|
||||
// 读取并过滤所有行
|
||||
while (std::getline(file, line)) {
|
||||
// 过滤日志级别和搜索内容
|
||||
if ((level.empty() || level == "ALL" || line.find(level) != std::string::npos) &&
|
||||
(search.empty() || line.find(search) != std::string::npos)) {
|
||||
filteredLogs.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算总页数
|
||||
int totalLines = filteredLogs.size();
|
||||
|
||||
// 如果没有内容,直接返回空
|
||||
if (totalLines == 0) {
|
||||
return logs;
|
||||
}
|
||||
|
||||
// 计算当前页的起始和结束索引
|
||||
int start = std::max(0, totalLines - (page * pageSize));
|
||||
int end = std::max(0, totalLines - ((page - 1) * pageSize));
|
||||
|
||||
// 返回当前页的日志(已经是倒序的)
|
||||
return std::vector<std::string>(filteredLogs.begin() + start, filteredLogs.begin() + end);
|
||||
}
|
||||
|
||||
void HttpServer::monitorLogFile() {
|
||||
static std::streampos lastPos = 0;
|
||||
std::ifstream file(m_logFilePath);
|
||||
|
||||
if (!file.is_open()) {
|
||||
LOG_F(ERROR, "Failed to open log file for monitoring: %s", m_logFilePath.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取文件大小
|
||||
file.seekg(0, std::ios::end);
|
||||
std::streampos currentPos = file.tellg();
|
||||
|
||||
// 如果文件大小没有变化,直接返回
|
||||
if (currentPos == lastPos) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是第一次读取或文件被截断,从头开始读
|
||||
if (currentPos < lastPos) {
|
||||
lastPos = 0;
|
||||
}
|
||||
|
||||
// 移动到上次读取的位置
|
||||
file.seekg(lastPos);
|
||||
|
||||
std::string line;
|
||||
std::vector<std::string> newLogs;
|
||||
|
||||
// 读取新的日志行
|
||||
while (std::getline(file, line)) {
|
||||
newLogs.push_back(line);
|
||||
}
|
||||
|
||||
// 更新最后读取位置
|
||||
lastPos = currentPos;
|
||||
|
||||
// 如果有新日志,推送给所有客户端
|
||||
if (!newLogs.empty()) {
|
||||
Json::Value json;
|
||||
json["type"] = "new_log";
|
||||
json["content"] = Json::arrayValue;
|
||||
|
||||
// 只保留最新的100行
|
||||
size_t start = newLogs.size() > 100 ? newLogs.size() - 100 : 0;
|
||||
for (size_t i = start; i < newLogs.size(); ++i) {
|
||||
json["content"].append(newLogs[i]);
|
||||
}
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
std::string msg = Json::writeString(builder, json);
|
||||
|
||||
std::lock_guard<std::mutex> lock(ws_connections_mutex_);
|
||||
for (auto c : ws_connections_) {
|
||||
mg_ws_send(c, msg.c_str(), msg.size(), WEBSOCKET_OP_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,12 @@ public:
|
||||
// 监控日志文件变化并推送新内容
|
||||
void monitorLogFile();
|
||||
|
||||
// 处理日志请求
|
||||
void handleLogRequest(struct mg_connection* c, const std::string& msg);
|
||||
|
||||
// 读取日志文件
|
||||
std::vector<std::string> readLogFile(int page, int pageSize, const std::string& level, const std::string& search);
|
||||
|
||||
private:
|
||||
// WebSocket事件处理函数
|
||||
void handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_data, void* fn_data);
|
||||
@@ -72,12 +78,6 @@ private:
|
||||
std::vector<mg_connection*> ws_connections_; // WebSocket连接列表
|
||||
std::unordered_map<mg_connection*, uint64_t> ws_last_io_time_; // 连接最后活动时间
|
||||
|
||||
// 读取日志文件
|
||||
std::vector<std::string> readLogFile(int page, int pageSize, const std::string& level, const std::string& search);
|
||||
|
||||
// 处理WebSocket消息
|
||||
void handleWebSocketMessage(struct mg_connection* c, const std::string& msg);
|
||||
|
||||
// 日志文件相关
|
||||
std::string m_logFilePath = "pPowerManger.log";
|
||||
std::ifstream m_logFile;
|
||||
@@ -87,4 +87,4 @@ private:
|
||||
|
||||
};
|
||||
|
||||
#endif // HTTP_SERVER_H
|
||||
#endif // HTTP_SERVER_H
|
||||
|
||||
@@ -213,7 +213,11 @@ body {
|
||||
<h1>Power Manager System</h1>
|
||||
<div class="status-bar">
|
||||
<div class="status-info">
|
||||
<span>🟢 系统状态: 运行中</span>
|
||||
<span>🟢 系统状态: <span id="systemStatus">运行中</span></span>
|
||||
<span>⚡ 应用频率: <span id="appFreq">0</span> Hz</span>
|
||||
<span>📡 通信频率: <span id="commFreq">0</span> Hz</span>
|
||||
<span>💻 CPU负载: <span id="cpuLoad">0</span>%</span>
|
||||
<span>⏱️ 迭代间隔: <span id="timeSinceIterate">0</span>ms</span>
|
||||
<span>🕒 更新时间: <time id="updateTime">2025-05-18 15:52</time></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -243,6 +247,15 @@ function initWebSocket() {
|
||||
ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('收到系统数据:', data);
|
||||
|
||||
// 更新MOOSDB信息
|
||||
if (data.moosdb) {
|
||||
document.getElementById('appFreq').textContent = data.moosdb.appFreq.toFixed(2);
|
||||
document.getElementById('commFreq').textContent = data.moosdb.commFreq.toFixed(2);
|
||||
document.getElementById('cpuLoad').textContent = data.moosdb.cpuLoad.toFixed(1);
|
||||
document.getElementById('timeSinceIterate').textContent = data.moosdb.timeSinceIterate.toFixed(0);
|
||||
}
|
||||
|
||||
updateTime();
|
||||
};
|
||||
|
||||
|
||||
@@ -373,17 +373,37 @@ function initWebSocket() {
|
||||
ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'log') {
|
||||
// 初始加载的日志数据
|
||||
allLogs = data.content;
|
||||
filterAndDisplayLogs();
|
||||
} else if (data.type === 'new_log') {
|
||||
// 新的日志条目
|
||||
if (Array.isArray(data.content)) {
|
||||
// 初始加载的日志数据
|
||||
allLogs = data.content;
|
||||
filterAndDisplayLogs();
|
||||
} else {
|
||||
// 新的日志条目
|
||||
allLogs.push(data.content);
|
||||
if (shouldShowLog(data.content)) {
|
||||
appendLogEntry(data.content);
|
||||
let shouldUpdate = false;
|
||||
data.content.forEach(log => {
|
||||
allLogs.push(log);
|
||||
if (shouldShowLog(log)) {
|
||||
filteredLogs.push(log);
|
||||
shouldUpdate = true;
|
||||
// 如果在最后一页或自动滚动模式下,直接添加到显示
|
||||
if (currentPage === totalPages || autoScroll) {
|
||||
appendLogEntry(log);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldUpdate) {
|
||||
totalPages = Math.ceil(filteredLogs.length / pageSize);
|
||||
updatePagination();
|
||||
|
||||
// 如果开启了自动滚动,确保滚动到底部
|
||||
if (autoScroll) {
|
||||
logContent.scrollTop = logContent.scrollHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data.type === 'system_status') {
|
||||
updateStatus(data.status, true);
|
||||
}
|
||||
updateTime();
|
||||
};
|
||||
@@ -391,7 +411,12 @@ function initWebSocket() {
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket连接已关闭');
|
||||
updateStatus('已断开', false);
|
||||
setTimeout(initWebSocket, 5000);
|
||||
// 增加重连延迟,避免快速重连
|
||||
setTimeout(() => {
|
||||
if (!ws || ws.readyState === WebSocket.CLOSED) {
|
||||
initWebSocket();
|
||||
}
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
@@ -505,9 +530,9 @@ function appendLogEntry(content) {
|
||||
entry.className = 'log-entry';
|
||||
|
||||
// 根据日志内容设置不同的样式
|
||||
if (content.includes('ERROR')) {
|
||||
if (content.includes('ERROR') || content.includes('ERR')) {
|
||||
entry.classList.add('error');
|
||||
} else if (content.includes('WARNING')) {
|
||||
} else if (content.includes('WARNING') || content.includes('WARN')) {
|
||||
entry.classList.add('warning');
|
||||
} else if (content.includes('INFO')) {
|
||||
entry.classList.add('info');
|
||||
@@ -515,7 +540,8 @@ function appendLogEntry(content) {
|
||||
|
||||
entry.textContent = content;
|
||||
logContent.appendChild(entry);
|
||||
|
||||
|
||||
// 只在自动滚动模式下滚动到底部
|
||||
if (autoScroll) {
|
||||
logContent.scrollTop = logContent.scrollHeight;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user