golang-yitisheng-server/server/common/logger.go

211 lines
4.4 KiB
Go

// Package common 日志工具
package common
import (
"fmt"
"io"
"os"
"path/filepath"
"sync"
"time"
"server/config"
)
// 日志级别
const (
LevelDebug = iota
LevelInfo
LevelWarn
LevelError
)
var levelNames = map[int]string{
LevelDebug: "DEBUG",
LevelInfo: "INFO",
LevelWarn: "WARN",
LevelError: "ERROR",
}
var levelValues = map[string]int{
"debug": LevelDebug,
"info": LevelInfo,
"warn": LevelWarn,
"error": LevelError,
}
// Logger 日志记录器
type Logger struct {
level int
file *os.File
htmlWriter *htmlLogWriter
mu sync.Mutex
console bool
}
var (
defaultLogger *Logger
startCount int
once sync.Once
)
// InitLogger 初始化日志
func InitLogger() {
once.Do(func() {
cfg := config.AppConfig.Log
level := levelValues[cfg.Level]
// 创建日志目录
if err := os.MkdirAll(cfg.Dir, 0755); err != nil {
fmt.Println("创建日志目录失败:", err)
return
}
// 计算启动次数
startCount = getStartCount(cfg.Dir)
// 创建日志文件
filename := fmt.Sprintf("%s-%d.html", time.Now().Format("2006-01-02"), startCount)
logFilepath := filepath.Join(cfg.Dir, filename)
file, err := os.OpenFile(logFilepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("创建日志文件失败:", err)
return
}
htmlWriter := newHtmlLogWriter(file)
defaultLogger = &Logger{
level: level,
file: file,
htmlWriter: htmlWriter,
console: cfg.Console,
}
fmt.Printf("日志文件: %s\n", logFilepath)
})
}
// getStartCount 获取今日启动次数
func getStartCount(dir string) int {
today := time.Now().Format("2006-01-02")
pattern := filepath.Join(dir, today+"*.html")
matches, _ := filepath.Glob(pattern)
return len(matches) + 1
}
// CloseLogger 关闭日志
func CloseLogger() {
if defaultLogger != nil && defaultLogger.file != nil {
defaultLogger.htmlWriter.writeFooter()
defaultLogger.file.Close()
}
}
func (l *Logger) log(level int, format string, args ...interface{}) {
if l == nil || level < l.level {
return
}
l.mu.Lock()
defer l.mu.Unlock()
msg := fmt.Sprintf(format, args...)
timestamp := time.Now().Format("2006-01-02 15:04:05.000")
levelName := levelNames[level]
// 写入HTML文件
l.htmlWriter.writeLog(timestamp, levelName, msg)
// 输出到控制台
if l.console {
fmt.Printf("[%s] [%s] %s\n", timestamp, levelName, msg)
}
}
// LogDebug 调试日志
func LogDebug(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelDebug, format, args...)
}
}
// LogInfo 信息日志
func LogInfo(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelInfo, format, args...)
}
}
// LogWarn 警告日志
func LogWarn(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelWarn, format, args...)
}
}
// LogError 错误日志
func LogError(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelError, format, args...)
}
}
// 简短别名
var (
Debug = LogDebug
Info = LogInfo
Warn = LogWarn
)
// htmlLogWriter HTML日志写入器
type htmlLogWriter struct {
writer io.Writer
initialized bool
}
func newHtmlLogWriter(w io.Writer) *htmlLogWriter {
hw := &htmlLogWriter{writer: w}
hw.writeHeader()
return hw
}
func (h *htmlLogWriter) writeHeader() {
html := `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>应用日志</title>
<style>
body { font-family: Consolas, monospace; background: #1e1e1e; color: #d4d4d4; padding: 20px; }
.log { margin: 2px 0; padding: 4px 8px; border-radius: 3px; }
.DEBUG { background: #2d2d2d; color: #9cdcfe; }
.INFO { background: #1e3a1e; color: #4ec9b0; }
.WARN { background: #3a3a1e; color: #dcdcaa; }
.ERROR { background: #3a1e1e; color: #f14c4c; }
.time { color: #808080; }
.level { font-weight: bold; width: 60px; display: inline-block; }
</style>
</head>
<body>
<h2>应用日志 - ` + time.Now().Format("2006-01-02") + `</h2>
<div id="logs">
`
h.writer.Write([]byte(html))
h.initialized = true
}
func (h *htmlLogWriter) writeLog(timestamp, level, msg string) {
html := fmt.Sprintf(`<div class="log %s"><span class="time">[%s]</span> <span class="level">[%s]</span> %s</div>
`, level, timestamp, level, msg)
h.writer.Write([]byte(html))
}
func (h *htmlLogWriter) writeFooter() {
html := `</div>
</body>
</html>`
h.writer.Write([]byte(html))
}