Files
core/logger/logger.go
2026-06-20 23:29:58 +08:00

364 lines
8.0 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 logger
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
"git.apinb.com/bsm-sdk/core/conf"
"git.apinb.com/bsm-sdk/core/utils"
"git.apinb.com/bsm-sdk/core/vars"
)
// Logger 日志器结构
type Logger struct {
level vars.LogLevel
infoLogger *log.Logger
warnLogger *log.Logger
errorLogger *log.Logger
fatalLogger *log.Logger
debugLogger *log.Logger
fileWriter io.Writer
consoleWriter io.Writer
mu sync.RWMutex
name string
logDir string
currentDate string
onRemote bool
endpoint string
}
var (
globalLogger *Logger
once sync.Once
)
// 初始化Logger配置
func New(cfg *conf.LogConf) {
if cfg == nil {
cfg = &conf.LogConf{
Name: strings.ToLower(vars.ServiceKey),
Level: vars.LogLevel(vars.DEBUG),
Dir: "./logs/",
Endpoint: "",
Console: true,
File: true,
Remote: false,
}
}
InitLogger(cfg)
}
// InitLogger 初始化全局日志器
func InitLogger(cfg *conf.LogConf) error {
var err error
once.Do(func() {
globalLogger, err = NewLogger(cfg)
})
return err
}
// NewLogger 创建新的日志器
func NewLogger(cfg *conf.LogConf) (*Logger, error) {
// 确保日志目录存在
if err := os.MkdirAll(cfg.Dir, 0755); err != nil {
return nil, fmt.Errorf("创建日志目录失败: %v", err)
}
// 控制台输出
consoleWriter := os.Stdout
// 文件输出
fileWriter, err := createLogFile(cfg.Dir, cfg.Name)
if err != nil {
return nil, fmt.Errorf("创建日志文件失败: %v", err)
}
// 创建多输出写入器
multiWriter := io.MultiWriter(consoleWriter, fileWriter)
logger := &Logger{
level: cfg.Level,
fileWriter: fileWriter,
consoleWriter: consoleWriter,
logDir: cfg.Dir,
name: strings.ToLower(cfg.Name),
currentDate: time.Now().Format("2006-01-02"),
onRemote: cfg.Remote,
endpoint: cfg.Endpoint,
}
// 创建不同级别的日志器
logger.infoLogger = log.New(multiWriter, "[INFO] ", log.LstdFlags)
logger.warnLogger = log.New(multiWriter, "[WARN] ", log.LstdFlags)
logger.errorLogger = log.New(multiWriter, "[ERROR] ", log.LstdFlags)
logger.fatalLogger = log.New(multiWriter, "[FATAL] ", log.LstdFlags)
logger.debugLogger = log.New(multiWriter, "[DEBUG] ", log.LstdFlags)
return logger, nil
}
// createLogFile 创建日志文件
func createLogFile(logDir, name string) (io.Writer, error) {
filename := fmt.Sprintf("%s_%s.log", name, time.Now().Format("2006-01-02"))
filepath := filepath.Join(logDir, filename)
file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, err
}
return file, nil
}
// checkAndRotateLog 检查并轮转日志文件
func (l *Logger) checkAndRotateLog() error {
today := time.Now().Format("2006-01-02")
if l.currentDate != today {
l.mu.Lock()
defer l.mu.Unlock()
if l.currentDate != today {
// 关闭旧文件
if closer, ok := l.fileWriter.(io.Closer); ok {
closer.Close()
}
// 创建新文件
newFileWriter, err := createLogFile(l.logDir, l.name)
if err != nil {
return err
}
l.fileWriter = newFileWriter
l.currentDate = today
// 重新创建多输出写入器
multiWriter := io.MultiWriter(l.consoleWriter, l.fileWriter)
l.infoLogger = log.New(multiWriter, "[INFO] ", log.LstdFlags)
l.warnLogger = log.New(multiWriter, "[WARN] ", log.LstdFlags)
l.errorLogger = log.New(multiWriter, "[ERROR] ", log.LstdFlags)
l.fatalLogger = log.New(multiWriter, "[FATAL] ", log.LstdFlags)
l.debugLogger = log.New(multiWriter, "[DEBUG] ", log.LstdFlags)
}
}
return nil
}
func (l *Logger) sendToRemote(level, name, out string) {
if l.endpoint == "" {
return
}
data := map[string]any{
"level": level,
"name": name,
"out": out,
}
jsonBytes, _ := json.Marshal(data)
utils.HttpPost(l.endpoint, nil, jsonBytes)
}
func (l *Logger) loggerFor(level vars.LogLevel) (*log.Logger, string) {
switch level {
case vars.DEBUG:
return l.debugLogger, "DEBUG"
case vars.INFO:
return l.infoLogger, "INFO"
case vars.WARN:
return l.warnLogger, "WARN"
case vars.ERROR:
return l.errorLogger, "ERROR"
default:
return l.fatalLogger, "FATAL"
}
}
func (l *Logger) output(level vars.LogLevel, out string, fatal bool) {
if !fatal && l.level > level {
return
}
_ = l.checkAndRotateLog()
logger, levelName := l.loggerFor(level)
if fatal {
logger = l.fatalLogger
levelName = "FATAL"
}
if l.onRemote {
go l.sendToRemote(levelName, l.name, out)
}
_ = logger.Output(3, out)
if fatal {
os.Exit(1)
}
}
func (l *Logger) outputf(level vars.LogLevel, format string, v ...any) {
l.output(level, fmt.Sprintf(format, v...), false)
}
// Debug 输出调试信息
func (l *Logger) Debug(v ...any) {
l.output(vars.DEBUG, fmt.Sprint(v...), false)
}
// Debugf 格式化输出调试信息
func (l *Logger) Debugf(format string, v ...any) {
l.outputf(vars.DEBUG, format, v...)
}
// Info 输出信息
func (l *Logger) Info(v ...any) {
l.output(vars.INFO, fmt.Sprint(v...), false)
}
// Infof 格式化输出信息
func (l *Logger) Infof(format string, v ...any) {
l.outputf(vars.INFO, format, v...)
}
// Warn 输出警告
func (l *Logger) Warn(v ...any) {
l.output(vars.WARN, fmt.Sprint(v...), false)
}
// Warnf 格式化输出警告
func (l *Logger) Warnf(format string, v ...any) {
l.outputf(vars.WARN, format, v...)
}
// Error 输出错误
func (l *Logger) Error(v ...any) {
l.output(vars.ERROR, fmt.Sprint(v...), false)
}
// Errorf 格式化输出错误
func (l *Logger) Errorf(format string, v ...any) {
l.outputf(vars.ERROR, format, v...)
}
// Fatal 输出致命错误并退出程序
func (l *Logger) Fatal(v ...any) {
l.output(vars.ERROR, fmt.Sprint(v...), true)
}
// Fatalf 格式化输出致命错误并退出程序
func (l *Logger) Fatalf(format string, v ...any) {
l.output(vars.ERROR, fmt.Sprintf(format, v...), true)
}
// Print 输出信息兼容标准log包
func (l *Logger) Print(v ...any) {
l.Info(v...)
}
// Printf 格式化输出信息兼容标准log包
func (l *Logger) Printf(format string, v ...any) {
l.Infof(format, v...)
}
// Println 输出信息并换行兼容标准log包
func (l *Logger) Println(v ...any) {
l.Info(v...)
}
// SetLevel 设置日志级别
func (l *Logger) SetLevel(level vars.LogLevel) {
l.mu.Lock()
defer l.mu.Unlock()
l.level = level
}
// GetLevel 获取日志级别
func (l *Logger) GetLevel() vars.LogLevel {
l.mu.RLock()
defer l.mu.RUnlock()
return l.level
}
// Close 关闭日志器
func (l *Logger) Close() error {
if closer, ok := l.fileWriter.(io.Closer); ok {
return closer.Close()
}
return nil
}
// 全局日志函数兼容标准log包
// Global logger functions.
func withGlobalLogger(fn func(*Logger)) {
if globalLogger != nil {
fn(globalLogger)
}
}
func Debug(v ...any) {
withGlobalLogger(func(l *Logger) { l.Debug(v...) })
}
func Debugf(format string, v ...any) {
withGlobalLogger(func(l *Logger) { l.Debugf(format, v...) })
}
func Info(v ...any) {
withGlobalLogger(func(l *Logger) { l.Info(v...) })
}
func Infof(format string, v ...any) {
withGlobalLogger(func(l *Logger) { l.Infof(format, v...) })
}
func Warn(v ...any) {
withGlobalLogger(func(l *Logger) { l.Warn(v...) })
}
func Warnf(format string, v ...any) {
withGlobalLogger(func(l *Logger) { l.Warnf(format, v...) })
}
func Error(v ...any) {
withGlobalLogger(func(l *Logger) { l.Error(v...) })
}
func Errorf(format string, v ...any) {
withGlobalLogger(func(l *Logger) { l.Errorf(format, v...) })
}
func Fatal(v ...any) {
withGlobalLogger(func(l *Logger) { l.Fatal(v...) })
}
func Fatalf(format string, v ...any) {
withGlobalLogger(func(l *Logger) { l.Fatalf(format, v...) })
}
func Print(v ...any) {
withGlobalLogger(func(l *Logger) { l.Print(v...) })
}
func Printf(format string, v ...any) {
withGlobalLogger(func(l *Logger) { l.Printf(format, v...) })
}
func Println(v ...any) {
withGlobalLogger(func(l *Logger) { l.Println(v...) })
}
// GetLogger 获取全局日志器实例
func GetLogger() *Logger {
return globalLogger
}