Files
waf-platform/EdgeDNS/internal/accesslogs/dns_file_writer.go

240 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}