mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
channel: master version: 1.0.0 source-ref: master published-at-utc: 2026-03-13T15:19:28Z
547 lines
12 KiB
Go
547 lines
12 KiB
Go
package logger
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// Level 日志级别
|
||
type Level int
|
||
|
||
const (
|
||
DEBUG Level = iota
|
||
INFO
|
||
WARN
|
||
ERROR
|
||
)
|
||
|
||
// String 返回日志级别的字符串表示
|
||
func (l Level) String() string {
|
||
switch l {
|
||
case DEBUG:
|
||
return "DEBUG"
|
||
case INFO:
|
||
return "INFO"
|
||
case WARN:
|
||
return "WARN"
|
||
case ERROR:
|
||
return "ERROR"
|
||
default:
|
||
return "UNKNOWN"
|
||
}
|
||
}
|
||
|
||
// ParseLevel 解析日志级别字符串
|
||
func ParseLevel(levelStr string) Level {
|
||
switch strings.ToLower(levelStr) {
|
||
case "debug":
|
||
return DEBUG
|
||
case "info":
|
||
return INFO
|
||
case "warn", "warning":
|
||
return WARN
|
||
case "error":
|
||
return ERROR
|
||
default:
|
||
return INFO
|
||
}
|
||
}
|
||
|
||
// Field 结构化日志字段
|
||
type Field struct {
|
||
Key string
|
||
Value interface{}
|
||
}
|
||
|
||
// LoggerConfig 日志配置
|
||
type LoggerConfig struct {
|
||
Level string
|
||
FileEnabled bool
|
||
FilePath string
|
||
Format string // "text" or "json"
|
||
BufferSize int // 缓冲区大小(KB)
|
||
AsyncQueueSize int // 异步队列大小
|
||
FlushIntervalMs int // 刷新间隔(毫秒)
|
||
|
||
// 分片配置
|
||
Rotation RotationConfig
|
||
}
|
||
|
||
// RotationConfig 日志分片配置
|
||
type RotationConfig struct {
|
||
Enabled bool
|
||
MaxSizeMB int // 单文件最大大小(MB)
|
||
MaxAge int // 保留天数
|
||
MaxBackups int // 保留文件数
|
||
TimeInterval string // 时间间隔: "daily", "hourly"
|
||
}
|
||
|
||
// Logger 日志记录器
|
||
type Logger struct {
|
||
level Level
|
||
component string
|
||
ctx context.Context
|
||
|
||
// 写入器
|
||
writers []Writer
|
||
consoleWriter Writer
|
||
fileWriter *FileWriter
|
||
|
||
// 分片管理器
|
||
rotationManager *RotationManager
|
||
|
||
// 并发安全
|
||
mu sync.RWMutex
|
||
|
||
// 文件写入失败标志
|
||
fileWriteFailed bool
|
||
}
|
||
|
||
// 全局日志实例
|
||
var (
|
||
globalLogger *Logger
|
||
globalMu sync.RWMutex
|
||
)
|
||
|
||
// DefaultLoggerConfig 返回默认日志配置
|
||
func DefaultLoggerConfig() LoggerConfig {
|
||
return LoggerConfig{
|
||
Level: "info",
|
||
FileEnabled: false,
|
||
FilePath: "data/logs/app.log",
|
||
Format: "text",
|
||
BufferSize: 4, // 4KB
|
||
AsyncQueueSize: 1000,
|
||
FlushIntervalMs: 1000, // 1秒
|
||
Rotation: RotationConfig{
|
||
Enabled: false,
|
||
MaxSizeMB: 100,
|
||
MaxAge: 7,
|
||
MaxBackups: 5,
|
||
TimeInterval: "daily",
|
||
},
|
||
}
|
||
}
|
||
|
||
// Init 初始化全局日志(简单版本,仅控制台输出)
|
||
func Init(ctx context.Context, levelStr string) {
|
||
InitWithConfig(ctx, LoggerConfig{
|
||
Level: levelStr,
|
||
FileEnabled: false,
|
||
Format: "text",
|
||
})
|
||
}
|
||
|
||
// InitWithConfig 使用配置初始化全局日志
|
||
func InitWithConfig(ctx context.Context, config LoggerConfig) {
|
||
globalMu.Lock()
|
||
defer globalMu.Unlock()
|
||
|
||
// 解析日志级别,无效级别使用默认 INFO
|
||
level := ParseLevel(config.Level)
|
||
if config.Level != "" && level == INFO && strings.ToLower(config.Level) != "info" {
|
||
// 无效级别,记录警告(使用 fmt 因为 logger 还未初始化)
|
||
fmt.Printf("[WARN] Invalid log level '%s', using default 'INFO'\n", config.Level)
|
||
}
|
||
|
||
// 创建格式化器
|
||
var formatter Formatter
|
||
switch strings.ToLower(config.Format) {
|
||
case "json":
|
||
formatter = NewJSONFormatter()
|
||
default:
|
||
formatter = NewTextFormatter()
|
||
}
|
||
|
||
// 创建控制台写入器
|
||
consoleWriter := NewConsoleWriter(formatter)
|
||
|
||
logger := &Logger{
|
||
level: level,
|
||
ctx: ctx,
|
||
writers: []Writer{consoleWriter, globalMemoryWriter},
|
||
consoleWriter: consoleWriter,
|
||
}
|
||
|
||
// 如果启用文件日志,创建文件写入器
|
||
if config.FileEnabled && config.FilePath != "" {
|
||
fileWriter, rotationManager, err := createFileWriterWithRotation(config, formatter)
|
||
if err != nil {
|
||
// 文件写入器创建失败,回退到仅控制台输出
|
||
fmt.Printf("[WARN] Failed to create file writer: %v, falling back to console only\n", err)
|
||
logger.fileWriteFailed = true
|
||
} else {
|
||
logger.fileWriter = fileWriter
|
||
logger.rotationManager = rotationManager
|
||
logger.writers = append(logger.writers, fileWriter)
|
||
}
|
||
}
|
||
|
||
globalLogger = logger
|
||
}
|
||
|
||
// createFileWriterWithRotation 创建带分片功能的文件写入器
|
||
func createFileWriterWithRotation(config LoggerConfig, formatter Formatter) (*FileWriter, *RotationManager, error) {
|
||
// 确保目录存在
|
||
dir := filepath.Dir(config.FilePath)
|
||
if dir != "" && dir != "." {
|
||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||
return nil, nil, fmt.Errorf("failed to create log directory: %w", err)
|
||
}
|
||
}
|
||
|
||
// 计算缓冲区大小(KB -> 字节)
|
||
bufferSize := config.BufferSize * 1024
|
||
if bufferSize <= 0 {
|
||
bufferSize = 4 * 1024 // 默认 4KB
|
||
}
|
||
|
||
// 计算刷新间隔
|
||
flushInterval := time.Duration(config.FlushIntervalMs) * time.Millisecond
|
||
if flushInterval <= 0 {
|
||
flushInterval = time.Second
|
||
}
|
||
|
||
// 异步队列大小
|
||
asyncQueueSize := config.AsyncQueueSize
|
||
if asyncQueueSize <= 0 {
|
||
asyncQueueSize = 1000
|
||
}
|
||
|
||
fileConfig := FileWriterConfig{
|
||
FilePath: config.FilePath,
|
||
BufferSize: bufferSize,
|
||
FlushInterval: flushInterval,
|
||
AsyncQueueSize: asyncQueueSize,
|
||
}
|
||
|
||
// 使用异步文件写入器
|
||
fileWriter, err := NewAsyncFileWriter(fileConfig, formatter)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
// 创建分片管理器(如果启用)
|
||
var rotationManager *RotationManager
|
||
if config.Rotation.Enabled {
|
||
rotationPolicy := createRotationPolicy(config.Rotation)
|
||
rotationManager = NewRotationManager(RotationManagerConfig{
|
||
BasePath: config.FilePath,
|
||
MaxBackups: config.Rotation.MaxBackups,
|
||
MaxAge: config.Rotation.MaxAge,
|
||
Policy: rotationPolicy,
|
||
})
|
||
}
|
||
|
||
return fileWriter, rotationManager, nil
|
||
}
|
||
|
||
// createRotationPolicy 根据配置创建分片策略
|
||
func createRotationPolicy(config RotationConfig) RotationPolicy {
|
||
var policies []RotationPolicy
|
||
|
||
// 时间分片策略
|
||
if config.TimeInterval != "" {
|
||
var interval TimeInterval
|
||
switch strings.ToLower(config.TimeInterval) {
|
||
case "hourly":
|
||
interval = Hourly
|
||
default:
|
||
interval = Daily
|
||
}
|
||
policies = append(policies, NewTimeRotationPolicy(interval))
|
||
}
|
||
|
||
// 大小分片策略
|
||
if config.MaxSizeMB > 0 {
|
||
policies = append(policies, NewSizeRotationPolicyMB(config.MaxSizeMB))
|
||
}
|
||
|
||
// 如果有多个策略,使用组合策略
|
||
if len(policies) > 1 {
|
||
return NewCompositeRotationPolicy(policies...)
|
||
} else if len(policies) == 1 {
|
||
return policies[0]
|
||
}
|
||
|
||
// 默认按天分片
|
||
return NewTimeRotationPolicy(Daily)
|
||
}
|
||
|
||
// Close 关闭全局日志
|
||
func Close() error {
|
||
globalMu.Lock()
|
||
defer globalMu.Unlock()
|
||
|
||
if globalLogger == nil {
|
||
return nil
|
||
}
|
||
|
||
var lastErr error
|
||
for _, writer := range globalLogger.writers {
|
||
if err := writer.Close(); err != nil {
|
||
lastErr = err
|
||
}
|
||
}
|
||
|
||
globalLogger = nil
|
||
return lastErr
|
||
}
|
||
|
||
// New 创建新的日志记录器
|
||
func New(component string) *Logger {
|
||
globalMu.RLock()
|
||
defer globalMu.RUnlock()
|
||
|
||
if globalLogger == nil {
|
||
// 如果全局日志未初始化,创建一个默认的
|
||
consoleWriter := NewConsoleWriter(NewTextFormatter())
|
||
return &Logger{
|
||
level: INFO,
|
||
component: component,
|
||
writers: []Writer{consoleWriter},
|
||
consoleWriter: consoleWriter,
|
||
}
|
||
}
|
||
|
||
return &Logger{
|
||
level: globalLogger.level,
|
||
component: component,
|
||
ctx: globalLogger.ctx,
|
||
writers: globalLogger.writers,
|
||
consoleWriter: globalLogger.consoleWriter,
|
||
fileWriter: globalLogger.fileWriter,
|
||
rotationManager: globalLogger.rotationManager,
|
||
fileWriteFailed: globalLogger.fileWriteFailed,
|
||
}
|
||
}
|
||
|
||
// SetLevel 动态设置日志级别(并发安全)
|
||
func (l *Logger) SetLevel(level Level) {
|
||
l.mu.Lock()
|
||
defer l.mu.Unlock()
|
||
l.level = level
|
||
}
|
||
|
||
// SetLevelString 通过字符串动态设置日志级别
|
||
func (l *Logger) SetLevelString(levelStr string) {
|
||
l.SetLevel(ParseLevel(levelStr))
|
||
}
|
||
|
||
// GetLevel 获取当前日志级别
|
||
func (l *Logger) GetLevel() Level {
|
||
l.mu.RLock()
|
||
defer l.mu.RUnlock()
|
||
return l.level
|
||
}
|
||
|
||
// SetGlobalLevel 设置全局日志级别
|
||
func SetGlobalLevel(level Level) {
|
||
globalMu.Lock()
|
||
defer globalMu.Unlock()
|
||
|
||
if globalLogger != nil {
|
||
globalLogger.mu.Lock()
|
||
globalLogger.level = level
|
||
globalLogger.mu.Unlock()
|
||
}
|
||
}
|
||
|
||
// SetGlobalLevelString 通过字符串设置全局日志级别
|
||
func SetGlobalLevelString(levelStr string) {
|
||
SetGlobalLevel(ParseLevel(levelStr))
|
||
}
|
||
|
||
// Debug 记录调试日志
|
||
func (l *Logger) Debug(msg string, fields ...Field) {
|
||
l.mu.RLock()
|
||
level := l.level
|
||
l.mu.RUnlock()
|
||
|
||
if level <= DEBUG {
|
||
l.log(DEBUG, msg, fields...)
|
||
}
|
||
}
|
||
|
||
// Info 记录信息日志
|
||
func (l *Logger) Info(msg string, fields ...Field) {
|
||
l.mu.RLock()
|
||
level := l.level
|
||
l.mu.RUnlock()
|
||
|
||
if level <= INFO {
|
||
l.log(INFO, msg, fields...)
|
||
}
|
||
}
|
||
|
||
// Warn 记录警告日志
|
||
func (l *Logger) Warn(msg string, fields ...Field) {
|
||
l.mu.RLock()
|
||
level := l.level
|
||
l.mu.RUnlock()
|
||
|
||
if level <= WARN {
|
||
l.log(WARN, msg, fields...)
|
||
}
|
||
}
|
||
|
||
// Error 记录错误日志
|
||
func (l *Logger) Error(msg string, fields ...Field) {
|
||
l.mu.RLock()
|
||
level := l.level
|
||
l.mu.RUnlock()
|
||
|
||
if level <= ERROR {
|
||
l.log(ERROR, msg, fields...)
|
||
}
|
||
}
|
||
|
||
// log 内部日志记录方法
|
||
func (l *Logger) log(level Level, msg string, fields ...Field) {
|
||
// 创建日志条目
|
||
entry := NewLogEntry(level, l.component, msg)
|
||
|
||
// 添加字段
|
||
if len(fields) > 0 {
|
||
fieldMap := make(map[string]interface{}, len(fields))
|
||
for _, field := range fields {
|
||
fieldMap[field.Key] = field.Value
|
||
}
|
||
entry.WithFields(fieldMap)
|
||
}
|
||
|
||
// 写入所有写入器
|
||
l.writeEntry(entry)
|
||
}
|
||
|
||
// writeEntry 写入日志条目到所有写入器
|
||
func (l *Logger) writeEntry(entry *LogEntry) {
|
||
l.mu.RLock()
|
||
writers := l.writers
|
||
fileWriter := l.fileWriter
|
||
consoleWriter := l.consoleWriter
|
||
fileWriteFailed := l.fileWriteFailed
|
||
l.mu.RUnlock()
|
||
|
||
// 如果文件写入已失败,只写入控制台
|
||
if fileWriteFailed {
|
||
if consoleWriter != nil {
|
||
_ = consoleWriter.Write(entry)
|
||
}
|
||
return
|
||
}
|
||
|
||
// 写入所有写入器
|
||
for _, writer := range writers {
|
||
if err := writer.Write(entry); err != nil {
|
||
// 如果是文件写入器失败,标记并回退到控制台
|
||
if writer == fileWriter {
|
||
l.handleFileWriteError(entry, err)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// handleFileWriteError 处理文件写入错误
|
||
func (l *Logger) handleFileWriteError(entry *LogEntry, err error) {
|
||
l.mu.Lock()
|
||
if !l.fileWriteFailed {
|
||
l.fileWriteFailed = true
|
||
// 记录错误到控制台
|
||
fmt.Printf("[ERROR] File write failed: %v, falling back to console only\n", err)
|
||
}
|
||
l.mu.Unlock()
|
||
}
|
||
|
||
// LogEntry 直接写入日志条目(用于拦截器等高级用法)
|
||
func (l *Logger) LogEntry(entry *LogEntry) {
|
||
l.mu.RLock()
|
||
level := l.level
|
||
l.mu.RUnlock()
|
||
|
||
// 检查日志级别
|
||
if entry.Level < level {
|
||
return
|
||
}
|
||
|
||
l.writeEntry(entry)
|
||
}
|
||
|
||
// WithComponent 创建带有组件名的新日志记录器
|
||
func (l *Logger) WithComponent(component string) *Logger {
|
||
l.mu.RLock()
|
||
defer l.mu.RUnlock()
|
||
|
||
return &Logger{
|
||
level: l.level,
|
||
component: component,
|
||
ctx: l.ctx,
|
||
writers: l.writers,
|
||
consoleWriter: l.consoleWriter,
|
||
fileWriter: l.fileWriter,
|
||
rotationManager: l.rotationManager,
|
||
fileWriteFailed: l.fileWriteFailed,
|
||
}
|
||
}
|
||
|
||
// Flush 刷新所有写入器的缓冲区
|
||
func (l *Logger) Flush() error {
|
||
l.mu.RLock()
|
||
fileWriter := l.fileWriter
|
||
l.mu.RUnlock()
|
||
|
||
if fileWriter != nil {
|
||
return fileWriter.Flush()
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetRotationManager 获取分片管理器
|
||
func (l *Logger) GetRotationManager() *RotationManager {
|
||
l.mu.RLock()
|
||
defer l.mu.RUnlock()
|
||
return l.rotationManager
|
||
}
|
||
|
||
// F 创建字段的便捷函数
|
||
func F(key string, value interface{}) Field {
|
||
return Field{Key: key, Value: value}
|
||
}
|
||
|
||
// Fs 创建多个字段的便捷函数
|
||
func Fs(keyValues ...interface{}) []Field {
|
||
fields := make([]Field, 0, len(keyValues)/2)
|
||
for i := 0; i < len(keyValues)-1; i += 2 {
|
||
if key, ok := keyValues[i].(string); ok {
|
||
fields = append(fields, Field{Key: key, Value: keyValues[i+1]})
|
||
}
|
||
}
|
||
return fields
|
||
}
|
||
|
||
// IsFileEnabled 检查文件日志是否启用
|
||
func (l *Logger) IsFileEnabled() bool {
|
||
l.mu.RLock()
|
||
defer l.mu.RUnlock()
|
||
return l.fileWriter != nil && !l.fileWriteFailed
|
||
}
|
||
|
||
// GetWriters 获取所有写入器(用于测试)
|
||
func (l *Logger) GetWriters() []Writer {
|
||
l.mu.RLock()
|
||
defer l.mu.RUnlock()
|
||
return l.writers
|
||
}
|
||
|
||
// ShouldLog 检查指定级别是否应该被记录
|
||
func (l *Logger) ShouldLog(level Level) bool {
|
||
l.mu.RLock()
|
||
defer l.mu.RUnlock()
|
||
return level >= l.level
|
||
}
|