Files

139 lines
4.3 KiB
Go

package main
import (
"os"
"path/filepath"
"strings"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/truewhile/MeBox/internal/config"
)
// newLogger 根据 cfg.Logging 构建 Zap。
func newLogger(cfg *config.Config) (*zap.Logger, error) {
log, _, err := newLoggerWithCloser(cfg)
return log, err
}
func newLoggerWithCloser(cfg *config.Config) (*zap.Logger, func(), error) {
if cfg.App.Debug {
log, err := zap.NewDevelopment()
return log, func() {}, err
}
level := configuredLogLevel(cfg.Logging.Level)
encoderCfg := zap.NewProductionEncoderConfig()
encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder
var encoder zapcore.Encoder
if strings.EqualFold(strings.TrimSpace(cfg.Logging.Format), "console") {
encoder = zapcore.NewConsoleEncoder(encoderCfg)
} else {
encoder = zapcore.NewJSONEncoder(encoderCfg)
}
cores := []zapcore.Core{
zapcore.NewCore(encoder, zapcore.Lock(os.Stdout), level),
}
var closers []func() error
appPath, warnPath, errorPath := logFilePaths(cfg)
if appPath != "" {
appWriter, err := newRotatingFileWriter(appPath, cfg.Logging)
if err != nil {
return nil, nil, err
}
cores = append(cores, zapcore.NewCore(encoder, appWriter, level))
closers = append(closers, appWriter.Close)
}
if warnPath != "" {
warnWriter, err := newRotatingFileWriter(warnPath, cfg.Logging)
if err != nil {
return nil, nil, err
}
cores = append(cores, zapcore.NewCore(encoder, warnWriter, zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zapcore.WarnLevel && level.Enabled(lvl)
})))
closers = append(closers, warnWriter.Close)
}
if errorPath != "" {
errorWriter, err := newRotatingFileWriter(errorPath, cfg.Logging)
if err != nil {
return nil, nil, err
}
cores = append(cores, zapcore.NewCore(encoder, errorWriter, zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.ErrorLevel && level.Enabled(lvl)
})))
closers = append(closers, errorWriter.Close)
}
closeFn := func() {
for _, c := range closers {
_ = c()
}
}
return zap.New(zapcore.NewTee(cores...), zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel), zap.ErrorOutput(zapcore.Lock(os.Stderr))), closeFn, nil
}
func configuredLogLevel(raw string) zapcore.Level {
level := zapcore.WarnLevel
raw = strings.TrimSpace(raw)
if raw != "" {
var parsed zapcore.Level
if err := parsed.UnmarshalText([]byte(raw)); err == nil {
level = parsed
}
}
return level
}
func logFilePaths(cfg *config.Config) (string, string, string) {
out := strings.TrimSpace(cfg.Logging.OutputPath)
if strings.EqualFold(out, "stdout") || strings.EqualFold(out, "stderr") {
return "", "", ""
}
if out == "" {
out = filepath.Join(cfg.App.DataDir, "logs")
}
if ext := filepath.Ext(out); ext != "" {
base := strings.TrimSuffix(out, ext)
return out, base + ".warn" + ext, base + ".error" + ext
}
return filepath.Join(out, "app.log"), filepath.Join(out, "warn.log"), filepath.Join(out, "error.log")
}
// newEmbyCompatLogger 构建只写入 Emby 兼容日志文件的独立 Zap 实例。
// 它不参与 app.log 的日志级别过滤,始终记录 INFO 及以上,确保成功请求也能
// 用于还原客户端的接口调用顺序;轮转参数沿用 logging 配置。
func newEmbyCompatLogger(cfg *config.Config) (*zap.Logger, func(), error) {
encoderCfg := zap.NewProductionEncoderConfig()
encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder
var encoder zapcore.Encoder
if strings.EqualFold(strings.TrimSpace(cfg.Logging.Format), "console") {
encoder = zapcore.NewConsoleEncoder(encoderCfg)
} else {
encoder = zapcore.NewJSONEncoder(encoderCfg)
}
writer, err := newRotatingFileWriter(embyCompatLogPath(cfg), cfg.Logging)
if err != nil {
return nil, nil, err
}
log := zap.New(
zapcore.NewCore(encoder, writer, zap.InfoLevel),
zap.AddCaller(),
zap.AddStacktrace(zapcore.ErrorLevel),
zap.ErrorOutput(zapcore.Lock(os.Stderr)),
)
return log, func() { _ = writer.Close() }, nil
}
func embyCompatLogPath(cfg *config.Config) string {
out := strings.TrimSpace(cfg.Logging.OutputPath)
if out == "" || strings.EqualFold(out, "stdout") || strings.EqualFold(out, "stderr") {
return filepath.Join(cfg.App.DataDir, "logs", "emby-compat.log")
}
if ext := filepath.Ext(out); ext != "" {
base := strings.TrimSuffix(out, ext)
return base + ".emby-compat" + ext
}
return filepath.Join(out, "emby-compat.log")
}