Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,239 @@
package accesslogs
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeDNS/internal/remotelogs"
"gopkg.in/natefinch/lumberjack.v2"
)
const (
defaultDNSLogDir = "/var/log/edge/edge-dns"
envDNSLogDir = "EDGE_DNS_LOG_DIR"
)
var (
sharedDNSFileWriter *DNSFileWriter
sharedDNSFileWriterOnce sync.Once
)
// SharedDNSFileWriter 返回 DNS 本地日志写入器(单例).
func SharedDNSFileWriter() *DNSFileWriter {
sharedDNSFileWriterOnce.Do(func() {
sharedDNSFileWriter = NewDNSFileWriter()
})
return sharedDNSFileWriter
}
// DNSFileWriter 将 DNS 访问日志以 JSON Lines 写入本地文件,供 Fluent Bit 采集。
// 文件轮转由 lumberjack 内建完成。
type DNSFileWriter struct {
dir string
mu sync.Mutex
file *lumberjack.Logger
rotateConfig *serverconfigs.AccessLogRotateConfig
inited bool
}
// NewDNSFileWriter 创建 DNS 本地日志写入器.
func NewDNSFileWriter() *DNSFileWriter {
logDir := resolveDefaultDNSLogDir()
return &DNSFileWriter{
dir: logDir,
rotateConfig: serverconfigs.NewDefaultAccessLogRotateConfig(),
}
}
func resolveDefaultDNSLogDir() string {
logDir := strings.TrimSpace(os.Getenv(envDNSLogDir))
if logDir == "" {
return defaultDNSLogDir
}
return logDir
}
func resolveDNSDirFromPolicyPath(policyPath string) string {
policyPath = strings.TrimSpace(policyPath)
if policyPath == "" {
return ""
}
if strings.HasSuffix(policyPath, "/") || strings.HasSuffix(policyPath, "\\") {
return filepath.Clean(policyPath)
}
baseName := filepath.Base(policyPath)
if strings.Contains(baseName, ".") || strings.Contains(baseName, "${") {
return filepath.Clean(filepath.Dir(policyPath))
}
return filepath.Clean(policyPath)
}
// Dir 返回当前日志目录.
func (w *DNSFileWriter) Dir() string {
return w.dir
}
// SetDirByPolicyPath 使用公用日志策略 path 更新目录,空值时回退到 EDGE_DNS_LOG_DIR/default。
func (w *DNSFileWriter) SetDirByPolicyPath(policyPath string) {
dir := resolveDNSDirFromPolicyPath(policyPath)
w.SetDir(dir)
}
// SetDir 更新目录并重置文件句柄。
func (w *DNSFileWriter) SetDir(dir string) {
if strings.TrimSpace(dir) == "" {
dir = resolveDefaultDNSLogDir()
}
w.mu.Lock()
defer w.mu.Unlock()
if dir == w.dir {
return
}
if w.file != nil {
_ = w.file.Close()
w.file = nil
}
w.inited = false
w.dir = dir
}
// SetRotateConfig 更新日志轮转配置并重建 writer。
func (w *DNSFileWriter) SetRotateConfig(config *serverconfigs.AccessLogRotateConfig) {
normalized := config.Normalize()
w.mu.Lock()
defer w.mu.Unlock()
if equalDNSRotateConfig(w.rotateConfig, normalized) {
return
}
if w.file != nil {
_ = w.file.Close()
w.file = nil
}
w.inited = false
w.rotateConfig = normalized
}
// EnsureInit 在启动时预创建目录与 access.log.
func (w *DNSFileWriter) EnsureInit() error {
if w.dir == "" {
return nil
}
return w.init()
}
func (w *DNSFileWriter) init() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.inited && w.file != nil {
return nil
}
if w.dir == "" {
return nil
}
if err := os.MkdirAll(w.dir, 0755); err != nil {
remotelogs.Error("DNS_ACCESS_LOG_FILE", "mkdir log dir failed: "+err.Error())
return err
}
rotateConfig := w.rotateConfig.Normalize()
w.file = &lumberjack.Logger{
Filename: filepath.Join(w.dir, "access.log"),
MaxSize: rotateConfig.MaxSizeMB,
MaxBackups: rotateConfig.MaxBackups,
MaxAge: rotateConfig.MaxAgeDays,
Compress: *rotateConfig.Compress,
LocalTime: *rotateConfig.LocalTime,
}
w.inited = true
return nil
}
// WriteBatch 批量写入 DNS 访问日志.
func (w *DNSFileWriter) WriteBatch(logs []*pb.NSAccessLog, clusterId int64) {
if len(logs) == 0 || w.dir == "" {
return
}
if err := w.init(); err != nil {
return
}
w.mu.Lock()
file := w.file
w.mu.Unlock()
if file == nil {
return
}
for _, logItem := range logs {
ingestLog := FromNSAccessLog(logItem, clusterId)
if ingestLog == nil {
continue
}
line, err := json.Marshal(ingestLog)
if err != nil {
continue
}
_, _ = file.Write(append(line, '\n'))
}
}
// Reopen 关闭并重建日志 writer供兼容调用.
func (w *DNSFileWriter) Reopen() error {
w.mu.Lock()
if w.file != nil {
_ = w.file.Close()
w.file = nil
}
w.inited = false
w.mu.Unlock()
return w.init()
}
// Close 关闭日志文件.
func (w *DNSFileWriter) Close() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.file == nil {
return nil
}
err := w.file.Close()
w.file = nil
w.inited = false
if err != nil {
remotelogs.Error("DNS_ACCESS_LOG_FILE", fmt.Sprintf("close access.log failed: %v", err))
return err
}
return nil
}
func equalDNSRotateConfig(left *serverconfigs.AccessLogRotateConfig, right *serverconfigs.AccessLogRotateConfig) bool {
if left == nil || right == nil {
return left == right
}
return left.MaxSizeMB == right.MaxSizeMB &&
left.MaxBackups == right.MaxBackups &&
left.MaxAgeDays == right.MaxAgeDays &&
*left.Compress == *right.Compress &&
*left.LocalTime == *right.LocalTime
}

View File

@@ -0,0 +1,57 @@
package accesslogs
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
// DNSIngestLog DNS 访问日志单行结构JSONEachRow.
type DNSIngestLog struct {
Timestamp int64 `json:"timestamp"`
RequestId string `json:"request_id"`
NodeId int64 `json:"node_id"`
ClusterId int64 `json:"cluster_id"`
DomainId int64 `json:"domain_id"`
RecordId int64 `json:"record_id"`
RemoteAddr string `json:"remote_addr"`
QuestionName string `json:"question_name"`
QuestionType string `json:"question_type"`
RecordName string `json:"record_name"`
RecordType string `json:"record_type"`
RecordValue string `json:"record_value"`
Networking string `json:"networking"`
IsRecursive bool `json:"is_recursive"`
Error string `json:"error"`
NSRouteCodes []string `json:"ns_route_codes,omitempty"`
ContentJSON string `json:"content_json,omitempty"`
}
// FromNSAccessLog 将 pb.NSAccessLog 转为 DNSIngestLog.
func FromNSAccessLog(log *pb.NSAccessLog, clusterId int64) *DNSIngestLog {
if log == nil {
return nil
}
contentBytes, _ := json.Marshal(log)
return &DNSIngestLog{
Timestamp: log.GetTimestamp(),
RequestId: log.GetRequestId(),
NodeId: log.GetNsNodeId(),
ClusterId: clusterId,
DomainId: log.GetNsDomainId(),
RecordId: log.GetNsRecordId(),
RemoteAddr: log.GetRemoteAddr(),
QuestionName: log.GetQuestionName(),
QuestionType: log.GetQuestionType(),
RecordName: log.GetRecordName(),
RecordType: log.GetRecordType(),
RecordValue: log.GetRecordValue(),
Networking: log.GetNetworking(),
IsRecursive: log.GetIsRecursive(),
Error: log.GetError(),
NSRouteCodes: log.GetNsRouteCodes(),
ContentJSON: string(contentBytes),
}
}