240 lines
5.0 KiB
Go
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
|
|
}
|