init
This commit is contained in:
22
internal/logging/fields.go
Normal file
22
internal/logging/fields.go
Normal 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,
|
||||
}
|
||||
}
|
||||
66
internal/logging/logger.go
Normal file
66
internal/logging/logger.go
Normal 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
|
||||
}
|
||||
57
internal/logging/logger_test.go
Normal file
57
internal/logging/logger_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user