web日志查询增加修复以及日志数据持久化
This commit is contained in:
@@ -98,7 +98,7 @@
|
|||||||
"enabled": true
|
"enabled": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"updateInterval": 3600,
|
"updateInterval": 30,
|
||||||
"hostsFile": "data/hosts.txt",
|
"hostsFile": "data/hosts.txt",
|
||||||
"blockMethod": "NXDOMAIN",
|
"blockMethod": "NXDOMAIN",
|
||||||
"customBlockIP": "",
|
"customBlockIP": "",
|
||||||
|
|||||||
BIN
dns-server
Executable file
BIN
dns-server
Executable file
Binary file not shown.
108
dns/server.go
108
dns/server.go
@@ -638,7 +638,7 @@ func (s *Server) GetStats() *Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetQueryLogs 获取查询日志
|
// GetQueryLogs 获取查询日志
|
||||||
func (s *Server) GetQueryLogs(limit, offset int, sortField, sortDirection string) []QueryLog {
|
func (s *Server) GetQueryLogs(limit, offset int, sortField, sortDirection, resultFilter, searchTerm string) []QueryLog {
|
||||||
s.queryLogsMutex.RLock()
|
s.queryLogsMutex.RLock()
|
||||||
defer s.queryLogsMutex.RUnlock()
|
defer s.queryLogsMutex.RUnlock()
|
||||||
|
|
||||||
@@ -650,9 +650,26 @@ func (s *Server) GetQueryLogs(limit, offset int, sortField, sortDirection string
|
|||||||
limit = 100 // 默认返回100条日志
|
limit = 100 // 默认返回100条日志
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建日志副本用于排序
|
// 创建日志副本用于过滤和排序
|
||||||
logsCopy := make([]QueryLog, len(s.queryLogs))
|
var logsCopy []QueryLog
|
||||||
copy(logsCopy, s.queryLogs)
|
|
||||||
|
// 先过滤日志
|
||||||
|
for _, log := range s.queryLogs {
|
||||||
|
// 应用结果过滤
|
||||||
|
if resultFilter != "" && log.Result != resultFilter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用搜索过滤
|
||||||
|
if searchTerm != "" {
|
||||||
|
// 搜索域名或客户端IP
|
||||||
|
if !strings.Contains(log.Domain, searchTerm) && !strings.Contains(log.ClientIP, searchTerm) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logsCopy = append(logsCopy, log)
|
||||||
|
}
|
||||||
|
|
||||||
// 排序日志
|
// 排序日志
|
||||||
if sortField != "" {
|
if sortField != "" {
|
||||||
@@ -957,6 +974,58 @@ func (s *Server) loadStatsData() {
|
|||||||
s.clientStatsMutex.Unlock()
|
s.clientStatsMutex.Unlock()
|
||||||
|
|
||||||
logger.Info("统计数据加载成功")
|
logger.Info("统计数据加载成功")
|
||||||
|
|
||||||
|
// 加载查询日志
|
||||||
|
s.loadQueryLogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadQueryLogs 从文件加载查询日志
|
||||||
|
func (s *Server) loadQueryLogs() {
|
||||||
|
if s.config.StatsFile == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取绝对路径
|
||||||
|
statsFilePath, err := filepath.Abs(s.config.StatsFile)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("获取统计文件绝对路径失败", "path", s.config.StatsFile, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建查询日志文件路径
|
||||||
|
queryLogPath := filepath.Join(filepath.Dir(statsFilePath), "querylog.json")
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if _, err := os.Stat(queryLogPath); os.IsNotExist(err) {
|
||||||
|
logger.Info("查询日志文件不存在,将使用空列表", "file", queryLogPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
data, err := ioutil.ReadFile(queryLogPath)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("读取查询日志文件失败", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析数据
|
||||||
|
var logs []QueryLog
|
||||||
|
err = json.Unmarshal(data, &logs)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("解析查询日志失败", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新查询日志
|
||||||
|
s.queryLogsMutex.Lock()
|
||||||
|
s.queryLogs = logs
|
||||||
|
// 确保日志数量不超过限制
|
||||||
|
if len(s.queryLogs) > s.maxQueryLogs {
|
||||||
|
s.queryLogs = s.queryLogs[:s.maxQueryLogs]
|
||||||
|
}
|
||||||
|
s.queryLogsMutex.Unlock()
|
||||||
|
|
||||||
|
logger.Info("查询日志加载成功", "count", len(logs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveStatsData 保存统计数据到文件
|
// saveStatsData 保存统计数据到文件
|
||||||
@@ -1045,6 +1114,37 @@ func (s *Server) saveStatsData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("统计数据保存成功", "file", statsFilePath)
|
logger.Info("统计数据保存成功", "file", statsFilePath)
|
||||||
|
|
||||||
|
// 保存查询日志到文件
|
||||||
|
s.saveQueryLogs(statsDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveQueryLogs 保存查询日志到文件
|
||||||
|
func (s *Server) saveQueryLogs(dataDir string) {
|
||||||
|
// 构建查询日志文件路径
|
||||||
|
queryLogPath := filepath.Join(dataDir, "querylog.json")
|
||||||
|
|
||||||
|
// 获取查询日志数据
|
||||||
|
s.queryLogsMutex.RLock()
|
||||||
|
logsCopy := make([]QueryLog, len(s.queryLogs))
|
||||||
|
copy(logsCopy, s.queryLogs)
|
||||||
|
s.queryLogsMutex.RUnlock()
|
||||||
|
|
||||||
|
// 序列化数据
|
||||||
|
jsonData, err := json.MarshalIndent(logsCopy, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("序列化查询日志失败", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入文件
|
||||||
|
err = os.WriteFile(queryLogPath, jsonData, 0644)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("保存查询日志到文件失败", "file", queryLogPath, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("查询日志保存成功", "file", queryLogPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// startCpuUsageMonitor 启动CPU使用率监控
|
// startCpuUsageMonitor 启动CPU使用率监控
|
||||||
|
|||||||
@@ -1247,6 +1247,8 @@ func (s *Server) handleLogsQuery(w http.ResponseWriter, r *http.Request) {
|
|||||||
offset := 0
|
offset := 0
|
||||||
sortField := r.URL.Query().Get("sort")
|
sortField := r.URL.Query().Get("sort")
|
||||||
sortDirection := r.URL.Query().Get("direction")
|
sortDirection := r.URL.Query().Get("direction")
|
||||||
|
resultFilter := r.URL.Query().Get("result")
|
||||||
|
searchTerm := r.URL.Query().Get("search")
|
||||||
|
|
||||||
if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
|
if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
|
||||||
fmt.Sscanf(limitStr, "%d", &limit)
|
fmt.Sscanf(limitStr, "%d", &limit)
|
||||||
@@ -1257,7 +1259,7 @@ func (s *Server) handleLogsQuery(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取日志数据
|
// 获取日志数据
|
||||||
logs := s.dnsServer.GetQueryLogs(limit, offset, sortField, sortDirection)
|
logs := s.dnsServer.GetQueryLogs(limit, offset, sortField, sortDirection, resultFilter, searchTerm)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(logs)
|
json.NewEncoder(w).Encode(logs)
|
||||||
|
|||||||
16466
logs/dns-server.log
16466
logs/dns-server.log
File diff suppressed because it is too large
Load Diff
@@ -892,7 +892,12 @@
|
|||||||
<!-- 日志详情表格 -->
|
<!-- 日志详情表格 -->
|
||||||
<div class="bg-white rounded-lg p-6 card-shadow">
|
<div class="bg-white rounded-lg p-6 card-shadow">
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="flex items-center justify-between mb-6">
|
||||||
<h3 class="text-lg font-semibold">查询日志详情</h3>
|
<div class="flex items-center">
|
||||||
|
<h3 class="text-lg font-semibold">查询日志详情</h3>
|
||||||
|
<button id="logs-refresh-btn" class="ml-3 p-2 text-gray-500 hover:text-primary hover:bg-gray-100 rounded-full transition-colors" title="刷新日志">
|
||||||
|
<i class="fa fa-refresh"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div id="logs-loading" class="flex items-center text-sm text-gray-500 hidden">
|
<div id="logs-loading" class="flex items-center text-sm text-gray-500 hidden">
|
||||||
<i class="fa fa-spinner fa-spin mr-2"></i>
|
<i class="fa fa-spinner fa-spin mr-2"></i>
|
||||||
<span>加载中...</span>
|
<span>加载中...</span>
|
||||||
|
|||||||
@@ -132,6 +132,16 @@ function bindLogsEvents() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 刷新按钮事件
|
||||||
|
const refreshBtn = document.getElementById('logs-refresh-btn');
|
||||||
|
if (refreshBtn) {
|
||||||
|
refreshBtn.addEventListener('click', () => {
|
||||||
|
// 重新加载日志
|
||||||
|
currentPage = 1;
|
||||||
|
loadLogs();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 排序按钮事件
|
// 排序按钮事件
|
||||||
const sortHeaders = document.querySelectorAll('th[data-sort]');
|
const sortHeaders = document.querySelectorAll('th[data-sort]');
|
||||||
sortHeaders.forEach(header => {
|
sortHeaders.forEach(header => {
|
||||||
|
|||||||
Reference in New Issue
Block a user