Files
dns-server/log/ring_buffer.go
T
Alex Yang efebce3c39 whois
2026-04-01 12:22:55 +08:00

240 lines
5.0 KiB
Go

package log
import (
"sync"
)
// RingBuffer 内存环形缓冲区
type RingBuffer struct {
data []QueryLog
capacity int
head int
tail int
count int
mu sync.RWMutex
}
// NewRingBuffer 创建环形缓冲区
func NewRingBuffer(capacity int) *RingBuffer {
return &RingBuffer{
data: make([]QueryLog, capacity),
capacity: capacity,
head: 0,
tail: 0,
count: 0,
}
}
// Push 添加日志到缓冲区
func (rb *RingBuffer) Push(log QueryLog) {
rb.mu.Lock()
defer rb.mu.Unlock()
// 如果缓冲区已满,覆盖最旧的数据
if rb.count == rb.capacity {
rb.head = (rb.head + 1) % rb.capacity
}
rb.data[rb.tail] = log
rb.tail = (rb.tail + 1) % rb.capacity
if rb.count < rb.capacity {
rb.count++
}
}
// Query 查询日志
func (rb *RingBuffer) Query(filter LogFilter, page PageParams) ([]QueryLog, int64, error) {
rb.mu.RLock()
defer rb.mu.RUnlock()
// 收集所有符合条件的日志
var filtered []QueryLog
for i := 0; i < rb.count; i++ {
idx := (rb.head + i) % rb.capacity
log := rb.data[idx]
// 应用过滤条件
if rb.matchesFilter(log, filter) {
filtered = append(filtered, log)
}
}
total := int64(len(filtered))
// 排序
rb.sortLogs(filtered, page.SortField, page.SortDirection)
// 分页
start := page.Offset
if start >= len(filtered) {
return []QueryLog{}, total, nil
}
end := start + page.Limit
if end > len(filtered) {
end = len(filtered)
}
return filtered[start:end], total, nil
}
// matchesFilter 检查日志是否匹配过滤条件
func (rb *RingBuffer) matchesFilter(log QueryLog, filter LogFilter) bool {
// 结果过滤
if filter.Result != "" && log.Result != filter.Result {
return false
}
// 查询类型过滤
if filter.QueryType != "" && log.QueryType != filter.QueryType {
return false
}
// 时间范围过滤
if !filter.StartTime.IsZero() && log.Timestamp.Before(filter.StartTime) {
return false
}
if !filter.EndTime.IsZero() && log.Timestamp.After(filter.EndTime) {
return false
}
// 搜索过滤
if filter.SearchTerm != "" {
if !contains(log.Domain, filter.SearchTerm) && !contains(log.ClientIP, filter.SearchTerm) {
return false
}
}
return true
}
// contains 检查字符串是否包含子串(不区分大小写)
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || containsIgnoreCase(s, substr))
}
// containsIgnoreCase 不区分大小写的包含检查
func containsIgnoreCase(s, substr string) bool {
s = toLower(s)
substr = toLower(substr)
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
// toLower 转换为小写
func toLower(s string) string {
result := make([]byte, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if c >= 'A' && c <= 'Z' {
c = c + ('a' - 'A')
}
result[i] = c
}
return string(result)
}
// sortLogs 对日志进行排序
func (rb *RingBuffer) sortLogs(logs []QueryLog, sortField, sortDirection string) {
if sortField == "" {
sortField = "timestamp"
}
if sortDirection == "" {
sortDirection = "desc"
}
// 简单的冒泡排序(适用于小数据量)
n := len(logs)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
shouldSwap := rb.compareLogs(logs[j], logs[j+1], sortField)
if sortDirection == "desc" {
shouldSwap = !shouldSwap
}
if shouldSwap {
logs[j], logs[j+1] = logs[j+1], logs[j]
}
}
}
}
// compareLogs 比较两个日志
func (rb *RingBuffer) compareLogs(a, b QueryLog, sortField string) bool {
switch sortField {
case "timestamp", "time":
return a.Timestamp.Before(b.Timestamp)
case "domain":
return a.Domain < b.Domain
case "clientIp", "client_ip":
return a.ClientIP < b.ClientIP
case "responseTime", "response_time":
return a.ResponseTime < b.ResponseTime
default:
return a.Timestamp.Before(b.Timestamp)
}
}
// GetStats 获取统计信息
func (rb *RingBuffer) GetStats(timeRange TimeRange) (*LogStats, error) {
rb.mu.RLock()
defer rb.mu.RUnlock()
stats := &LogStats{
QueryTypes: make(map[string]int64),
}
for i := 0; i < rb.count; i++ {
idx := (rb.head + i) % rb.capacity
log := rb.data[idx]
// 时间范围过滤
if !timeRange.StartTime.IsZero() && log.Timestamp.Before(timeRange.StartTime) {
continue
}
if !timeRange.EndTime.IsZero() && log.Timestamp.After(timeRange.EndTime) {
continue
}
stats.TotalQueries++
stats.AvgResponseTime += float64(log.ResponseTime)
switch log.Result {
case "blocked":
stats.BlockedQueries++
case "allowed":
stats.AllowedQueries++
case "error":
stats.ErrorQueries++
}
stats.QueryTypes[log.QueryType]++
}
if stats.TotalQueries > 0 {
stats.AvgResponseTime /= float64(stats.TotalQueries)
}
return stats, nil
}
// Count 返回当前缓冲区中的日志数量
func (rb *RingBuffer) Count() int {
rb.mu.RLock()
defer rb.mu.RUnlock()
return rb.count
}
// Clear 清空缓冲区
func (rb *RingBuffer) Clear() {
rb.mu.Lock()
defer rb.mu.Unlock()
rb.head = 0
rb.tail = 0
rb.count = 0
}