This commit is contained in:
2025-11-14 12:11:44 +08:00
commit 39ebf61572
88 changed files with 9999 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
package logging
import "github.com/sirupsen/logrus"
// BaseFields 构建 action + 配置路径等基础字段,便于不同入口复用。
func BaseFields(action, configPath string) logrus.Fields {
return logrus.Fields{
"action": action,
"configPath": configPath,
}
}
// RequestFields 提供 hub/domain/命中状态字段,供代理请求日志复用。
func RequestFields(hub, domain, hubType, authMode string, cacheHit bool) logrus.Fields {
return logrus.Fields{
"hub": hub,
"domain": domain,
"hub_type": hubType,
"auth_mode": authMode,
"cache_hit": cacheHit,
}
}

View File

@@ -0,0 +1,66 @@
package logging
import (
"fmt"
"io"
"os"
"path/filepath"
"time"
"github.com/sirupsen/logrus"
"gopkg.in/natefinch/lumberjack.v2"
"github.com/any-hub/any-hub/internal/config"
)
// InitLogger 根据全局配置初始化 JSON 结构化日志,确保文件/控制台输出一致。
func InitLogger(cfg config.GlobalConfig) (*logrus.Logger, error) {
level, err := logrus.ParseLevel(cfg.LogLevel)
if err != nil {
return nil, fmt.Errorf("无法解析日志级别: %w", err)
}
output, outErr := buildOutput(cfg)
if outErr != nil {
fmt.Fprintf(os.Stderr, "logger_fallback: %v\n", outErr)
}
logger := logrus.New()
logger.SetLevel(level)
logger.SetOutput(output)
logger.SetFormatter(&logrus.JSONFormatter{TimestampFormat: time.RFC3339Nano})
logrus.SetFormatter(logger.Formatter)
logrus.SetOutput(logger.Out)
logrus.SetLevel(logger.GetLevel())
if outErr != nil {
logger.WithFields(logrus.Fields{
"action": "logger_fallback",
"path": cfg.LogFilePath,
}).Warn(outErr.Error())
}
return logger, nil
}
// buildOutput 根据配置创建日志输出 Writer失败时降级到 stdout 并返回错误。
func buildOutput(cfg config.GlobalConfig) (io.Writer, error) {
if cfg.LogFilePath == "" {
return os.Stdout, nil
}
dir := filepath.Dir(cfg.LogFilePath)
if err := os.MkdirAll(dir, 0o755); err != nil {
return os.Stdout, fmt.Errorf("创建日志目录失败: %w", err)
}
rotator := &lumberjack.Logger{
Filename: cfg.LogFilePath,
MaxSize: cfg.LogMaxSize,
MaxBackups: cfg.LogMaxBackups,
Compress: cfg.LogCompress,
LocalTime: true,
}
return rotator, nil
}

View File

@@ -0,0 +1,57 @@
package logging
import (
"os"
"path/filepath"
"testing"
"github.com/any-hub/any-hub/internal/config"
)
func TestConfigureDefaultsToStdout(t *testing.T) {
logger, err := InitLogger(config.GlobalConfig{LogLevel: "info"})
if err != nil {
t.Fatalf("配置失败: %v", err)
}
if logger.Out != os.Stdout {
t.Fatalf("未指定文件时应输出到 stdout")
}
}
func TestInitLoggerFallbackOnPermissionDenied(t *testing.T) {
dir := t.TempDir()
blocked := filepath.Join(dir, "blocked")
if err := os.Mkdir(blocked, 0o755); err != nil {
t.Fatalf("创建目录失败: %v", err)
}
if err := os.Chmod(blocked, 0o000); err != nil {
t.Fatalf("设置目录权限失败: %v", err)
}
t.Cleanup(func() { _ = os.Chmod(blocked, 0o755) })
cfg := config.GlobalConfig{
LogLevel: "info",
LogFilePath: filepath.Join(blocked, "sub", "any-hub.log"),
}
logger, err := InitLogger(cfg)
if err != nil {
t.Fatalf("初始化不应失败: %v", err)
}
if logger.Out != os.Stdout {
t.Fatalf("fallback 时应退回 stdout")
}
}
func TestConfigureCreatesRotatingFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "any-hub.log")
cfg := config.GlobalConfig{LogLevel: "debug", LogFilePath: path}
logger, err := InitLogger(cfg)
if err != nil {
t.Fatalf("配置失败: %v", err)
}
logger.Info("test")
if _, err := os.Stat(path); err != nil {
t.Fatalf("预期创建日志文件: %v", err)
}
}