ok-1
This commit is contained in:
344
internal/log/logger.go
Normal file
344
internal/log/logger.go
Normal file
@@ -0,0 +1,344 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// 日志级别
|
||||
type Level int
|
||||
|
||||
const (
|
||||
DebugLevel Level = iota
|
||||
InfoLevel
|
||||
WarnLevel
|
||||
ErrorLevel
|
||||
FatalLevel
|
||||
)
|
||||
|
||||
// 级别名称
|
||||
var levelNames = map[Level]string{
|
||||
DebugLevel: "DEBUG",
|
||||
InfoLevel: "INFO",
|
||||
WarnLevel: "WARN",
|
||||
ErrorLevel: "ERROR",
|
||||
FatalLevel: "FATAL",
|
||||
}
|
||||
|
||||
// 级别简写
|
||||
var levelShort = map[Level]string{
|
||||
DebugLevel: "[d]",
|
||||
InfoLevel: "[i]",
|
||||
WarnLevel: "[w]",
|
||||
ErrorLevel: "[e]",
|
||||
FatalLevel: "[f]",
|
||||
}
|
||||
|
||||
// 级别颜色
|
||||
var levelColor = map[Level]func(format string, a ...interface{}) string{
|
||||
DebugLevel: color.CyanString, // 青色
|
||||
InfoLevel: color.GreenString, // 绿色
|
||||
WarnLevel: color.YellowString, // 黄色
|
||||
ErrorLevel: color.RedString, // 红色
|
||||
FatalLevel: color.MagentaString, // 品红
|
||||
}
|
||||
|
||||
// Logger 日志器结构体
|
||||
type Logger struct {
|
||||
mu sync.Mutex
|
||||
consoleOut io.Writer // 控制台输出
|
||||
fileOut io.Writer // 文件输出
|
||||
minLevel Level // 最小输出级别
|
||||
showColor bool // 是否显示颜色
|
||||
showCaller bool // 是否显示调用者信息
|
||||
callerSkip int // 调用者跳过的层级
|
||||
timeFormat string // 时间格式
|
||||
}
|
||||
|
||||
// 默认日志器实例
|
||||
var defaultLogger *Logger
|
||||
|
||||
const (
|
||||
defaultTimeFormat = "2006-01-02 15:04:05"
|
||||
logFile = "/var/log/sunhpc/sunhpc.log"
|
||||
)
|
||||
|
||||
// Init 初始化日志系统
|
||||
func Init(verbose bool) {
|
||||
// 确保日志目录存在
|
||||
if err := os.MkdirAll(filepath.Dir(logFile), 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "创建日志目录失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 打开日志文件
|
||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "打开日志文件失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 控制台输出
|
||||
consoleOut := os.Stdout
|
||||
|
||||
// 创建日志器
|
||||
defaultLogger = &Logger{
|
||||
consoleOut: consoleOut,
|
||||
fileOut: file,
|
||||
minLevel: InfoLevel,
|
||||
showColor: true,
|
||||
showCaller: false,
|
||||
callerSkip: 2,
|
||||
timeFormat: defaultTimeFormat,
|
||||
}
|
||||
|
||||
// 详细模式下显示调试信息
|
||||
if verbose {
|
||||
defaultLogger.minLevel = DebugLevel
|
||||
defaultLogger.showCaller = true
|
||||
}
|
||||
|
||||
// 初始化颜色支持
|
||||
if runtime.GOOS == "windows" {
|
||||
color.NoColor = false
|
||||
}
|
||||
}
|
||||
|
||||
// log 核心日志输出方法
|
||||
func (l *Logger) log(level Level, format string, args ...interface{}) {
|
||||
if level < l.minLevel {
|
||||
return
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
// 生成时间戳
|
||||
timestamp := time.Now().Format(l.timeFormat)
|
||||
|
||||
// 获取调用者信息
|
||||
caller := ""
|
||||
if l.showCaller {
|
||||
_, file, line, ok := runtime.Caller(l.callerSkip)
|
||||
if ok {
|
||||
// 只保留文件名和行号
|
||||
file = filepath.Base(file)
|
||||
caller = fmt.Sprintf(" %s:%d", file, line)
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化消息
|
||||
var message string
|
||||
if format == "" {
|
||||
message = fmt.Sprint(args...)
|
||||
} else {
|
||||
message = fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// ---- 控制台输出(带颜色和简写)----
|
||||
if l.consoleOut != nil {
|
||||
// 获取级别简写
|
||||
shortPrefix := levelShort[level]
|
||||
|
||||
// 构建控制台行
|
||||
var consoleLine string
|
||||
|
||||
if l.showColor {
|
||||
// 带颜色输出 - 简写有颜色,时间戳灰色
|
||||
colorFunc := levelColor[level]
|
||||
consoleLine = fmt.Sprintf("%s %s %s",
|
||||
color.HiBlackString(timestamp), // 时间戳灰色
|
||||
colorFunc(shortPrefix), // 级别简写彩色
|
||||
message) // 消息普通颜色
|
||||
} else {
|
||||
// 不带颜色输出
|
||||
consoleLine = fmt.Sprintf("%s %s %s",
|
||||
timestamp,
|
||||
shortPrefix,
|
||||
message)
|
||||
}
|
||||
|
||||
// 添加调用者信息(灰色)
|
||||
if caller != "" {
|
||||
if l.showColor {
|
||||
consoleLine += fmt.Sprintf(" %s", color.HiBlackString(caller))
|
||||
} else {
|
||||
consoleLine += fmt.Sprintf(" %s", caller)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(l.consoleOut, consoleLine)
|
||||
}
|
||||
|
||||
// ---- 文件输出(完整格式)----
|
||||
if l.fileOut != nil {
|
||||
// 获取级别全名
|
||||
levelName := levelNames[level]
|
||||
|
||||
// 文件使用完整格式:时间 [级别] 消息 调用者
|
||||
fileLine := fmt.Sprintf("%s [%s] %s%s\n",
|
||||
timestamp,
|
||||
levelName,
|
||||
message,
|
||||
caller)
|
||||
|
||||
fmt.Fprint(l.fileOut, fileLine)
|
||||
}
|
||||
|
||||
// 致命错误退出程序
|
||||
if level == FatalLevel {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// 全局日志函数
|
||||
|
||||
// Debug 调试日志
|
||||
func Debug(args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(DebugLevel, "", args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf 格式化调试日志
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(DebugLevel, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info 信息日志
|
||||
func Info(args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(InfoLevel, "", args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Infof 格式化信息日志
|
||||
func Infof(format string, args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(InfoLevel, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn 警告日志
|
||||
func Warn(args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(WarnLevel, "", args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warnf 格式化警告日志
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(WarnLevel, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error 错误日志
|
||||
func Error(args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(ErrorLevel, "", args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf 格式化错误日志
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(ErrorLevel, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal 致命错误日志,输出后退出程序
|
||||
func Fatal(args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(FatalLevel, "", args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatalf 格式化致命错误日志,输出后退出程序
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.log(FatalLevel, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Writer 返回一个 io.Writer,可将子命令的输出写入日志(Debug级别)
|
||||
func Writer() *io.PipeWriter {
|
||||
r, w := io.Pipe()
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, err := r.Read(buf)
|
||||
if n > 0 {
|
||||
Debug(string(buf[:n]))
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return w
|
||||
}
|
||||
|
||||
// SetLevel 设置日志级别
|
||||
func SetLevel(level Level) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.mu.Lock()
|
||||
defer defaultLogger.mu.Unlock()
|
||||
defaultLogger.minLevel = level
|
||||
}
|
||||
}
|
||||
|
||||
// EnableColor 启用/禁用颜色输出
|
||||
func EnableColor(enable bool) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.mu.Lock()
|
||||
defer defaultLogger.mu.Unlock()
|
||||
defaultLogger.showColor = enable
|
||||
}
|
||||
}
|
||||
|
||||
// EnableCaller 启用/禁用调用者信息
|
||||
func EnableCaller(enable bool) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.mu.Lock()
|
||||
defer defaultLogger.mu.Unlock()
|
||||
defaultLogger.showCaller = enable
|
||||
}
|
||||
}
|
||||
|
||||
// SetTimeFormat 设置时间格式
|
||||
func SetTimeFormat(format string) {
|
||||
if defaultLogger != nil {
|
||||
defaultLogger.mu.Lock()
|
||||
defer defaultLogger.mu.Unlock()
|
||||
defaultLogger.timeFormat = format
|
||||
}
|
||||
}
|
||||
|
||||
// Sync 同步日志文件
|
||||
func Sync() {
|
||||
if defaultLogger != nil && defaultLogger.fileOut != nil {
|
||||
if f, ok := defaultLogger.fileOut.(*os.File); ok {
|
||||
f.Sync()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭日志文件
|
||||
func Close() error {
|
||||
if defaultLogger != nil && defaultLogger.fileOut != nil {
|
||||
if f, ok := defaultLogger.fileOut.(*os.File); ok {
|
||||
return f.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user