345 lines
7.1 KiB
Go
345 lines
7.1 KiB
Go
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
|
||
}
|