Эх сурвалжийг харах

重构日志输出系统

重构了日志格式化和输出方式,移除了旧的格式化函数和包装器,引入了新的格式化函数和WarpWriter。更新了配置文件以支持ANSI转义序列,并调整了相关接口和实现以适应新的日志处理逻辑。
SongZihuan 6 өдөр өмнө
parent
commit
bbb0d6aba9

+ 4 - 0
CHANGELOG.md

@@ -13,6 +13,10 @@
 
 - 添加线程独占任务功能。
 
+### 重构
+
+- 重构日志输出系统(格式化函数、控制台输出ANSI转义序列)。
+
 ## [0.9.0] - 2025-04-23
 
 ### 修复

+ 4 - 0
README.md

@@ -139,24 +139,28 @@ logger:
     log-tag: enable  # 是否输出tag调试日志。
     
     human-warn-writer:  # 人类可读的 debug、tag、info、warn 日志的输出器
+        ansi: enable  # 当输出到console时,若console支持,是否启用ANSI转义序列优化显示的内容
         write-to-std: stdout  # 输出到标准输出或标准错误输出(为空则不启用)
         write-to-file: ""  # 输出到固定文件(append)模式
         write-to-dir-with-date: ""  # 输出到指定目录,并按日期分割,此处为输出路径
         write-with-date-prefix: ""  # 配合 write-to-dir-with-date ,表示文件的输出前缀
         
     human-error-writer:  # 人类可读的 error、panic 日志的输出器,含义同上
+        ansi: enable
         write-to-std: stdout
         write-to-file: ""
         write-to-dir-with-date: ""
         write-with-date-prefix: ""
 
     machine-warn-writer:  # 机器可读的 debug、tag、info、warn 日志的输出器,含义同上
+      ansi: enable
       write-to-std: stdout
       write-to-file: ""
       write-to-dir-with-date: ""
       write-with-date-prefix: ""
       
     machine-error-writer:  # 机器可读的 error、panic 日志的输出器,含义同上
+      ansi: enable
       write-to-std: stdout
       write-to-file: ""
       write-to-dir-with-date: ""

+ 1 - 2
src/cmd/globalmain/main.go

@@ -6,7 +6,6 @@ package globalmain
 
 import (
 	"github.com/SongZihuan/BackendServerTemplate/src/logger"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/consoleutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
 )
@@ -19,7 +18,7 @@ func PreRun() (exitCode error) {
 		return exitutils.InitFailedErrorForWin32ConsoleModule(err.Error())
 	}
 
-	err = logger.InitBaseLogger(loglevel.LevelDebug, true, nil, nil, nil, nil)
+	err = logger.InitBaseLogger()
 	if err != nil {
 		return exitutils.InitFailedErrorForLoggerModule(err.Error())
 	}

+ 45 - 4
src/config/logger_config.go

@@ -9,6 +9,8 @@ import (
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/combiningwriter"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/nonewriter"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/typeutils"
 )
 
@@ -115,25 +117,64 @@ func (d *LoggerConfig) process(c *configInfo) (cfgErr configerror.Error) {
 		return configerror.NewErrorf("set log tag error: %s", err.Error())
 	}
 
-	cfgErr = d.HumanWarnWriter.process(c, logger.SetHumanWarnWriter)
+	humanWarn, cfgErr := d.HumanWarnWriter.process(c, false)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
-	cfgErr = d.HumanErrWriter.process(c, logger.SetHumanErrWriter)
+	humanErr, cfgErr := d.HumanErrWriter.process(c, false)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
-	cfgErr = d.MachineWarnWriter.process(c, logger.SetMachineWarnWriter)
+	machineWarn, cfgErr := d.MachineWarnWriter.process(c, true)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
-	cfgErr = d.MachineErrWriter.process(c, logger.SetMachineErrWriter)
+	machineErr, cfgErr := d.MachineErrWriter.process(c, true)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
+	logWarn := append(humanWarn, machineWarn...)
+	logErr := append(humanErr, machineErr...)
+
+	if len(logWarn) == 0 {
+		_, err := logger.SetWarnWriter(nonewriter.NewNoneWriter())
+		if err != nil {
+			return configerror.NewErrorf("set warn writer error: %s", err.Error())
+		}
+	} else if len(logWarn) == 1 {
+		_, err := logger.SetWarnWriter(logWarn[0])
+		if err != nil {
+			return configerror.NewErrorf("set warn writer error: %s", err.Error())
+		}
+	} else {
+		combiningWriter := combiningwriter.NewCombiningWriter(logWarn...)
+		_, err := logger.SetWarnWriter(combiningWriter)
+		if err != nil {
+			return configerror.NewErrorf("set warn combining writer error: %s", err.Error())
+		}
+	}
+
+	if len(logErr) == 0 {
+		_, err := logger.SetErrWriter(nonewriter.NewNoneWriter())
+		if err != nil {
+			return configerror.NewErrorf("set error writer error: %s", err.Error())
+		}
+	} else if len(logErr) == 1 {
+		_, err := logger.SetErrWriter(logErr[0])
+		if err != nil {
+			return configerror.NewErrorf("set error writer error: %s", err.Error())
+		}
+	} else {
+		combiningWriter := combiningwriter.NewCombiningWriter(logErr...)
+		_, err := logger.SetErrWriter(combiningWriter)
+		if err != nil {
+			return configerror.NewErrorf("set error combining writer error: %s", err.Error())
+		}
+	}
+
 	return nil
 }

+ 37 - 37
src/config/logger_writer_config.go

@@ -8,22 +8,23 @@ import (
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
 	"github.com/SongZihuan/BackendServerTemplate/src/global"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/combiningwriter"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/datefilewriter"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/filewriter"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/nonewriter"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/wrapwriter"
-	"io"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/warpwriter"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/termutils"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/typeutils"
 	"os"
 	"strings"
 )
 
 type LoggerWriterConfig struct {
-	WriteToStd          string `json:"write-to-std" yaml:"write-to-std" mapstructure:"write-to-std"` // stdout stderr all no
-	WriteToFile         string `json:"write-to-file" yaml:"write-to-file" mapstructure:"write-to-file"`
-	WriteToDirWithDate  string `json:"write-to-dir-with-date" yaml:"write-to-dir-with-date" mapstructure:"write-to-dir-with-date"`
-	WriteWithDatePrefix string `json:"write-with-date-prefix" yaml:"write-with-date-prefix" mapstructure:"write-with-date-prefix"`
+	ANSI                typeutils.StringBool `json:"ansi" yaml:"ansi" mapstructure:"ansi"`
+	WriteToStd          string               `json:"write-to-std" yaml:"write-to-std" mapstructure:"write-to-std"` // stdout stderr all no
+	WriteToFile         string               `json:"write-to-file" yaml:"write-to-file" mapstructure:"write-to-file"`
+	WriteToDirWithDate  string               `json:"write-to-dir-with-date" yaml:"write-to-dir-with-date" mapstructure:"write-to-dir-with-date"`
+	WriteWithDatePrefix string               `json:"write-with-date-prefix" yaml:"write-with-date-prefix" mapstructure:"write-with-date-prefix"`
 }
 
 func (d *LoggerWriterConfig) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
@@ -31,6 +32,8 @@ func (d *LoggerWriterConfig) init(filePath string, provider configparser.ConfigP
 }
 
 func (d *LoggerWriterConfig) setDefault(c *configInfo) (err configerror.Error) {
+	d.ANSI.SetDefaultEnable()
+
 	d.WriteToStd = strings.ToLower(d.WriteToStd)
 
 	if d.WriteToDirWithDate != "" && d.WriteWithDatePrefix == "" {
@@ -47,57 +50,54 @@ func (d *LoggerWriterConfig) check(c *configInfo) (err configerror.Error) {
 	return nil
 }
 
-func (d *LoggerWriterConfig) process(c *configInfo, setter func(w io.Writer) (io.Writer, error)) (cfgErr configerror.Error) {
-	writerList := make([]write.Writer, 0, 10)
+func (d *LoggerWriterConfig) process(c *configInfo, machine bool) (writerList []write.Writer, cfgErr configerror.Error) {
+	writerList = make([]write.Writer, 0, 10)
+
+	var consoleFn, fileFn, dateFn logformat.FormatFunc
+	if machine {
+		consoleFn = logformat.FormatMachine
+		fileFn = logformat.FormatMachine
+		dateFn = logformat.FormatMachine
+	} else {
+		if d.ANSI.IsEnable(true) && termutils.IsTermAdvanced(os.Stdout) && termutils.IsTermAdvanced(os.Stderr) {
+			consoleFn = logformat.FormatConsolePretty
+		} else {
+			consoleFn = logformat.FormatConsole
+		}
+		fileFn = logformat.FormatFile
+		dateFn = logformat.FormatFile
+	}
 
 	switch d.WriteToStd {
 	case "stdout":
-		writerList = append(writerList, wrapwriter.WrapToWriter(os.Stdout))
+		writerList = append(writerList, warpwriter.NewWarpWriter(os.Stdout, consoleFn))
 	case "stderr":
-		writerList = append(writerList, wrapwriter.WrapToWriter(os.Stderr))
+		writerList = append(writerList, warpwriter.NewWarpWriter(os.Stderr, consoleFn))
 	case "stderr+stdout", "stdout+stderr":
-		writerList = append(writerList, wrapwriter.WrapToWriter(os.Stdout), wrapwriter.WrapToWriter(os.Stderr))
+		writerList = append(writerList, warpwriter.NewWarpWriter(os.Stdout, consoleFn), warpwriter.NewWarpWriter(os.Stderr, consoleFn))
 	case "", "no":
 		// pass
 	default:
-		return configerror.NewErrorf("bad write-to-std: %s", d.WriteToStd)
+		return nil, configerror.NewErrorf("bad write-to-std: %s", d.WriteToStd)
 	}
 
 	if d.WriteToFile != "" {
-		fileWriter, err := filewriter.NewFileWriter(d.WriteToFile)
+		fileWriter, err := filewriter.NewFileWriter(d.WriteToFile, fileFn)
 		if err != nil {
-			return configerror.NewErrorf("new file writer (on %s) error error: %s", d.WriteToFile, err.Error())
+			return nil, configerror.NewErrorf("new file writer (on %s) error error: %s", d.WriteToFile, err.Error())
 		}
 
 		writerList = append(writerList, fileWriter)
 	}
 
 	if d.WriteToDirWithDate != "" {
-		dateFileWriter, err := datefilewriter.NewDateFileWriter(d.WriteToDirWithDate, d.WriteWithDatePrefix)
+		dateFileWriter, err := datefilewriter.NewDateFileWriter(d.WriteToDirWithDate, d.WriteWithDatePrefix, dateFn)
 		if err != nil {
-			return configerror.NewErrorf("new date file writer (on %s) error error: %s", d.WriteToDirWithDate, err.Error())
+			return nil, configerror.NewErrorf("new date file writer (on %s) error error: %s", d.WriteToDirWithDate, err.Error())
 		}
 
 		writerList = append(writerList, dateFileWriter)
 	}
 
-	if len(writerList) == 0 {
-		_, err := setter(nonewriter.NewNoneWriter())
-		if err != nil {
-			return configerror.NewErrorf("set new writer error: %s", err.Error())
-		}
-	} else if len(writerList) == 1 {
-		_, err := setter(writerList[0])
-		if err != nil {
-			return configerror.NewErrorf("set new writer error: %s", err.Error())
-		}
-	} else {
-		combiningWriter := combiningwriter.NewCombiningWriter(writerList...)
-		_, err := setter(combiningWriter)
-		if err != nil {
-			return configerror.NewErrorf("set new combining writer error: %s", err.Error())
-		}
-	}
-
-	return nil
+	return writerList, nil
 }

+ 2 - 3
src/logger/init_export.go

@@ -7,11 +7,10 @@ package logger
 import (
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/internal"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
-	"io"
 )
 
-func InitBaseLogger(level loglevel.LoggerLevel, logTag bool, humanWarnWriter, humanErrWriter, machineWarnWriter, machineErWriter io.Writer) error {
-	return internal.InitLogger(level, logTag, humanWarnWriter, humanErrWriter, machineWarnWriter, machineErWriter)
+func InitBaseLogger() error {
+	return internal.InitLogger(loglevel.LevelDebug, true, nil, nil)
 }
 
 func CloseLogger() {

+ 0 - 128
src/logger/internal/format.go

@@ -1,128 +0,0 @@
-// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package internal
-
-import (
-	"encoding/json"
-	"fmt"
-	"github.com/SongZihuan/BackendServerTemplate/src/global"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
-	"os"
-	"os/user"
-	"strings"
-	"time"
-)
-
-type FormatMachineJson struct {
-	Date          string `json:"date"`
-	Zone          string `json:"zone"`
-	Timestamp     int64  `json:"timestamp"`
-	Level         string `json:"level"`
-	Name          string `json:"name"`
-	Version       string `json:"version"`
-	Uid           string `json:"uid"`
-	Gid           string `json:"gid"`
-	User          string `json:"user"`
-	WorkDirectory string `json:"work-directory"`
-	Msg           string `json:"msg"`
-}
-
-func (l *Logger) formatMachine(_level loglevel.LoggerLevel, msg string, now time.Time) string {
-	var res = new(FormatMachineJson)
-
-	level := string(_level)
-
-	zone := global.Location.String()
-	if strings.ToLower(zone) == "local" {
-		zone, _ = now.Zone()
-	}
-	date := now.Format(time.DateTime)
-
-	msg = strings.Replace(msg, "\"", "'", -1)
-
-	level = strings.ToUpper(level)
-
-	res.Date = date
-	res.Zone = zone
-	res.Level = level
-	res.Timestamp = now.Unix()
-	res.Name = global.Name
-	res.Version = global.Version
-
-	u := getUser()
-	if u != nil {
-		res.Uid = u.Uid
-		res.Gid = u.Gid
-		res.User = u.Name
-	}
-
-	wd := getWorkDir()
-	if wd != "" {
-		res.WorkDirectory = wd
-	}
-
-	res.Msg = msg
-
-	data, err := json.Marshal(res)
-	if err != nil {
-		return fmt.Sprintf("{\"errmgs\":\"%s\"}", err.Error())
-	}
-
-	return string(data) + "\n"
-}
-
-func (l *Logger) formatHuman(_level loglevel.LoggerLevel, msg string, now time.Time) string {
-	var res = new(strings.Builder)
-
-	level := string(_level)
-
-	zone := global.Location.String()
-	if strings.ToLower(zone) == "local" {
-		zone, _ = now.Zone()
-	}
-	date := now.Format(time.DateTime)
-
-	msg = strings.Replace(msg, "\"", "'", -1)
-
-	level = strings.ToUpper(level)
-
-	res.WriteString(fmt.Sprintf("%s %s | %s | unix-timestamp=\"%ds\" | app=\"%s\" | version=\"%s\"", date, zone, level, now.Unix(), global.Name, global.Version))
-
-	u := getUser()
-	if u != nil {
-		res.WriteString(fmt.Sprintf(" | uid=\"%s\" | gid=\"%s\" | user=\"%s\"", u.Uid, u.Gid, u.Name))
-	} else {
-		res.WriteString(" | uid=without | gid=without | user=without")
-	}
-
-	wd := getWorkDir()
-	if wd != "" {
-		res.WriteString(fmt.Sprintf(" | work-directory=\"%s\"", wd))
-	} else {
-		res.WriteString(" | work-directory=without")
-	}
-
-	res.WriteString(fmt.Sprintf(" | msg=\"%s\"\n", msg))
-
-	return res.String()
-}
-
-func getUser() *user.User {
-	currentUser, err := user.Current()
-	if err != nil {
-		return nil
-	}
-
-	return currentUser
-}
-
-func getWorkDir() string {
-	dir, err := os.Getwd()
-	if err != nil {
-		return "未知"
-	}
-
-	return dir
-}

+ 0 - 35
src/logger/internal/info_method.go

@@ -6,8 +6,6 @@ package internal
 
 import (
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
-	"github.com/mattn/go-isatty"
-	"os"
 )
 
 func (l *Logger) GetLevel() loglevel.LoggerLevel {
@@ -17,36 +15,3 @@ func (l *Logger) GetLevel() loglevel.LoggerLevel {
 func (l *Logger) IsLogTag() bool {
 	return l.logTag
 }
-
-func (l *Logger) IsWarnWriterTerm() bool {
-	w, ok := l.humanWarnWriter.(*os.File)
-	if !ok {
-		return false
-	} else if !isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()) { // 非终端
-		return false
-	}
-	return true
-}
-
-func (l *Logger) IsErrWriterTerm() bool {
-	w, ok := l.humanErrWriter.(*os.File)
-	if !ok {
-		return false
-	} else if !isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()) { // 非终端
-		return false
-	}
-	return true
-}
-
-func (l *Logger) IsTermDump() bool {
-	// TERM为dump表示终端为基础模式,不支持高级显示
-	return os.Getenv("TERM") == "dumb"
-}
-
-func (l *Logger) IsWarnWriterTermNoDump() bool {
-	return l.IsWarnWriterTerm() && !l.IsTermDump()
-}
-
-func (l *Logger) IsErrWriterTermNoDump() bool {
-	return l.IsErrWriterTerm() && !l.IsTermDump()
-}

+ 14 - 27
src/logger/internal/init.go

@@ -7,42 +7,31 @@ package internal
 import (
 	"fmt"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/nonewriter"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/wrapwriter"
-	"io"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/warpwriter"
 	"os"
 )
 
-func InitLogger(level loglevel.LoggerLevel, logTag bool, humanWarnWriter, humanErrWriter io.Writer, machineWarnWriter, machineErrWriter io.Writer) error {
+func InitLogger(level loglevel.LoggerLevel, logTag bool, warnWriter, errWriter write.Writer) error {
 	logLevel, ok := levelMap[level]
 	if !ok {
 		return fmt.Errorf("invalid log level: %s", level)
 	}
 
-	if humanWarnWriter == nil {
-		humanWarnWriter = wrapwriter.WrapToWriter(os.Stdout)
+	if warnWriter == nil {
+		warnWriter = warpwriter.NewWarpWriter(os.Stdout, nil)
 	}
 
-	if humanErrWriter == nil {
-		humanErrWriter = wrapwriter.WrapToWriter(os.Stderr)
-	}
-
-	if machineWarnWriter == nil {
-		machineWarnWriter = nonewriter.NewNoneWriter()
-	}
-
-	if machineErrWriter == nil {
-		machineErrWriter = nonewriter.NewNoneWriter()
+	if errWriter == nil {
+		errWriter = warpwriter.NewWarpWriter(os.Stderr, nil)
 	}
 
 	logger := &Logger{
-		level:             level,
-		logLevel:          logLevel,
-		logTag:            logTag,
-		humanWarnWriter:   humanWarnWriter,
-		humanErrWriter:    humanErrWriter,
-		machineWarnWriter: machineWarnWriter,
-		machineErrWriter:  machineErrWriter,
+		level:      level,
+		logLevel:   logLevel,
+		logTag:     logTag,
+		warnWriter: warnWriter,
+		errWriter:  errWriter,
 	}
 
 	GlobalLogger = logger
@@ -54,8 +43,6 @@ func IsReady() bool {
 }
 
 func CloseLogger() {
-	_ = GlobalLogger.CloseHumanWarnWriter()
-	_ = GlobalLogger.CloseHumanErrWriter()
-	_ = GlobalLogger.CloseMachineWarnWriter()
-	_ = GlobalLogger.CloseMachineErrWriter()
+	_ = GlobalLogger.CloseWarnWriter()
+	_ = GlobalLogger.CloseErrWriter()
 }

+ 6 - 8
src/logger/internal/logger.go

@@ -12,12 +12,10 @@ import (
 var GlobalLogger *Logger = nil
 
 type Logger struct {
-	level             loglevel.LoggerLevel
-	logLevel          loggerLevel
-	logTag            bool
-	humanWarnWriter   write.Writer
-	humanErrWriter    write.Writer
-	machineWarnWriter write.Writer
-	machineErrWriter  write.Writer
-	args0Name         string
+	level      loglevel.LoggerLevel
+	logLevel   loggerLevel
+	logTag     bool
+	warnWriter write.Writer
+	errWriter  write.Writer
+	args0Name  string
 }

+ 20 - 35
src/logger/internal/logger_method.go

@@ -7,6 +7,7 @@ package internal
 import (
 	"fmt"
 	"github.com/SongZihuan/BackendServerTemplate/src/global"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/logpanic"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/runtimeutils"
@@ -27,7 +28,7 @@ func (l *Logger) TagSkipf(skip int, format string, args ...interface{}) {
 	content := fmt.Sprintf(format, args...)
 	msg := fmt.Sprintf("%s %s %s:%d", content, funcName, file, line)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.PseudoLevelTag, msg, now))
 }
 
 func (l *Logger) Debugf(format string, args ...interface{}) {
@@ -37,8 +38,7 @@ func (l *Logger) Debugf(format string, args ...interface{}) {
 
 	msg := fmt.Sprintf(format, args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelDebug, msg, now))
 }
 
 func (l *Logger) Infof(format string, args ...interface{}) {
@@ -48,8 +48,7 @@ func (l *Logger) Infof(format string, args ...interface{}) {
 
 	msg := fmt.Sprintf(format, args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelInfo, msg, now))
 }
 
 func (l *Logger) Warnf(format string, args ...interface{}) {
@@ -59,8 +58,7 @@ func (l *Logger) Warnf(format string, args ...interface{}) {
 
 	msg := fmt.Sprintf(format, args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelWarn, msg, now))
 }
 
 func (l *Logger) Errorf(format string, args ...interface{}) {
@@ -70,8 +68,7 @@ func (l *Logger) Errorf(format string, args ...interface{}) {
 
 	msg := fmt.Sprintf(format, args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelError, msg, now))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg, now))
+	_, _ = l.errWriter.Write(logformat.GetLogData(loglevel.LevelError, msg, now))
 }
 
 func (l *Logger) Panicf(format string, args ...interface{}) {
@@ -81,8 +78,7 @@ func (l *Logger) Panicf(format string, args ...interface{}) {
 
 	msg := fmt.Sprintf(format, args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg, now))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg, now))
+	_, _ = l.errWriter.Write(logformat.GetLogData(loglevel.LevelPanic, msg, now))
 
 	logpanic.Panic(now, msg)
 }
@@ -101,7 +97,7 @@ func (l *Logger) TagSkip(skip int, args ...interface{}) {
 	content := fmt.Sprint(args...)
 	now := time.Now().In(global.Location)
 	msg := fmt.Sprintf("%s %s %s:%d", content, funcName, file, line)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.PseudoLevelTag, msg, now))
 }
 
 func (l *Logger) Debug(args ...interface{}) {
@@ -111,8 +107,7 @@ func (l *Logger) Debug(args ...interface{}) {
 
 	msg := fmt.Sprint(args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelDebug, msg, now))
 }
 
 func (l *Logger) Info(args ...interface{}) {
@@ -122,8 +117,7 @@ func (l *Logger) Info(args ...interface{}) {
 
 	msg := fmt.Sprint(args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelInfo, msg, now))
 }
 
 func (l *Logger) Warn(args ...interface{}) {
@@ -133,8 +127,7 @@ func (l *Logger) Warn(args ...interface{}) {
 
 	msg := fmt.Sprint(args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelWarn, msg, now))
 }
 
 func (l *Logger) Error(args ...interface{}) {
@@ -144,8 +137,7 @@ func (l *Logger) Error(args ...interface{}) {
 
 	msg := fmt.Sprint(args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelError, msg, now))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg, now))
+	_, _ = l.errWriter.Write(logformat.GetLogData(loglevel.LevelError, msg, now))
 }
 
 func (l *Logger) Panic(args ...interface{}) {
@@ -155,8 +147,7 @@ func (l *Logger) Panic(args ...interface{}) {
 
 	msg := fmt.Sprint(args...)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg, now))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg, now))
+	_, _ = l.errWriter.Write(logformat.GetLogData(loglevel.LevelPanic, msg, now))
 
 	logpanic.Panic(now, msg)
 }
@@ -174,7 +165,7 @@ func (l *Logger) TagSkipWrite(skip int, content string) {
 
 	msg := fmt.Sprintf("%s %s %s:%d", content, funcName, file, line)
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.PseudoLevelTag, msg, now))
 }
 
 func (l *Logger) DebugWrite(msg string) {
@@ -183,8 +174,7 @@ func (l *Logger) DebugWrite(msg string) {
 	}
 
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelDebug, msg, now))
 }
 
 func (l *Logger) InfoWrite(msg string) {
@@ -193,8 +183,7 @@ func (l *Logger) InfoWrite(msg string) {
 	}
 
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelInfo, msg, now))
 }
 
 func (l *Logger) WarnWrite(msg string) {
@@ -203,8 +192,7 @@ func (l *Logger) WarnWrite(msg string) {
 	}
 
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg, now))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelWarn, msg, now))
 }
 
 func (l *Logger) ErrorWrite(msg string) {
@@ -213,8 +201,7 @@ func (l *Logger) ErrorWrite(msg string) {
 	}
 
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelError, msg, now))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelError, msg, now))
 }
 
 func (l *Logger) PanicWrite(msg string) {
@@ -223,8 +210,7 @@ func (l *Logger) PanicWrite(msg string) {
 	}
 
 	now := time.Now().In(global.Location)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg, now))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelPanic, msg, now))
 
 	logpanic.Panic(now, msg)
 }
@@ -250,6 +236,5 @@ func (l *Logger) Recover() {
 		msg = fmt.Sprintf("%v", err)
 	}
 
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg, now))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg, now))
+	_, _ = l.warnWriter.Write(logformat.GetLogData(loglevel.LevelPanic, msg, now))
 }

+ 17 - 64
src/logger/internal/set_method.go

@@ -8,8 +8,7 @@ import (
 	"fmt"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/nonewriter"
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/wrapwriter"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write/warpwriter"
 	"os"
 )
 
@@ -30,94 +29,48 @@ func (l *Logger) SetLogTag(logTag bool) error {
 	return nil
 }
 
-func (l *Logger) SetHumanWarnWriter(w write.Writer) (write.Writer, error) {
+func (l *Logger) SetWarnWriter(w write.Writer) (write.Writer, error) {
 	if w == nil {
-		w = wrapwriter.WrapToWriter(os.Stdout)
+		w = warpwriter.NewWarpWriter(os.Stdout, nil)
 	}
 
-	last := l.humanWarnWriter
-	l.humanWarnWriter = w
+	last := l.warnWriter
+	l.warnWriter = w
 	return last, nil
 }
 
-func (l *Logger) SetHumanErrWriter(w write.Writer) (write.Writer, error) {
+func (l *Logger) SetErrWriter(w write.Writer) (write.Writer, error) {
 	if w == nil {
-		w = wrapwriter.WrapToWriter(os.Stderr)
+		w = warpwriter.NewWarpWriter(os.Stderr, nil)
 	}
 
-	last := l.humanErrWriter
-	l.humanErrWriter = w
+	last := l.errWriter
+	l.errWriter = w
 	return last, nil
 }
 
-func (l *Logger) SetMachineWarnWriter(w write.Writer) (write.Writer, error) {
-	if w == nil {
-		w = nonewriter.NewNoneWriter()
-	}
-
-	last := l.machineWarnWriter
-	l.machineWarnWriter = w
-	return last, nil
-}
-
-func (l *Logger) SetMachineErrWriter(w write.Writer) (write.Writer, error) {
-	if w == nil {
-		w = nonewriter.NewNoneWriter()
-	}
-
-	last := l.machineErrWriter
-	l.machineErrWriter = w
-	return last, nil
-}
-
-func (l *Logger) CloseHumanWarnWriter() error {
-	if l.humanWarnWriter == nil {
-		return fmt.Errorf("warn writer not set")
-	}
-
-	w, ok := l.humanWarnWriter.(write.WriteCloser)
-	if !ok {
-		return nil
-	}
-
-	return w.ExitClose()
-}
-
-func (l *Logger) CloseHumanErrWriter() error {
-	if l.humanErrWriter == nil {
-		return fmt.Errorf("error writer not set")
-	}
-
-	w, ok := l.humanErrWriter.(write.WriteCloser)
-	if !ok {
-		return nil
-	}
-
-	return w.ExitClose()
-}
-
-func (l *Logger) CloseMachineWarnWriter() error {
-	if l.machineWarnWriter == nil {
+func (l *Logger) CloseWarnWriter() error {
+	if l.warnWriter == nil {
 		return fmt.Errorf("warn writer not set")
 	}
 
-	w, ok := l.machineWarnWriter.(write.WriteCloser)
+	w, ok := l.warnWriter.(write.WriteCloser)
 	if !ok {
 		return nil
 	}
 
-	return w.ExitClose()
+	return w.Close()
 }
 
-func (l *Logger) CloseMachineErrWriter() error {
-	if l.machineErrWriter == nil {
+func (l *Logger) CloseErrWriter() error {
+	if l.errWriter == nil {
 		return fmt.Errorf("error writer not set")
 	}
 
-	w, ok := l.machineErrWriter.(write.WriteCloser)
+	w, ok := l.errWriter.(write.WriteCloser)
 	if !ok {
 		return nil
 	}
 
-	return w.ExitClose()
+	return w.Close()
 }

+ 70 - 0
src/logger/logformat/format.go

@@ -0,0 +1,70 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package logformat
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"strings"
+)
+
+type FormatFunc func(data *LogData) string
+
+func FormatMachine(data *LogData) string {
+	d, err := json.Marshal(data)
+	if err != nil {
+		return fmt.Sprintf("{\"errmgs\":\"%s\"}", err.Error())
+	}
+
+	return strings.TrimRight(string(d), "\n")
+}
+
+func FormatFile(data *LogData) string {
+	var res = new(strings.Builder)
+
+	res.WriteString(fmt.Sprintf("%s %s | %s | unix-timestamp=\"%ds\" | exec=\"%s\"  | app=\"%s\" | version=\"%s\"", data.Date, data.Zone, data.Level, data.Timestamp, data.Exec, data.Name, data.Version))
+
+	if data.User != nil {
+		res.WriteString(fmt.Sprintf(" | uid=\"%s\" | gid=\"%s\" | user=\"%s\"", data.Uid, data.Gid, data.UserName))
+	} else {
+		res.WriteString(" | uid=without | gid=without | user=without")
+	}
+
+	if data.WorkDirectory != "" {
+		res.WriteString(fmt.Sprintf(" | work-directory=\"%s\"", data.WorkDirectory))
+	} else {
+		res.WriteString(" | work-directory=without")
+	}
+
+	res.WriteString(fmt.Sprintf(" | pid=\"%d\" | ppid=\"%d\"", data.Pid, data.ParentPid))
+
+	res.WriteString(fmt.Sprintf(" | msg=\"%s\"", data.Msg))
+
+	return res.String()
+}
+
+func FormatConsole(data *LogData) string {
+	return fmt.Sprintf("%s %s | %s | %s | pid=%d | msg=\"%s\"", data.Date, data.Zone, data.Level, data.Exec, data.Pid, data.Msg)
+}
+
+func FormatConsolePretty(data *LogData) string {
+	if data.LogLevel == loglevel.LevelWarn || data.LogLevel == loglevel.LevelInfo || data.LogLevel == loglevel.LevelDebug {
+		return fmt.Sprintf("\u001B[1;3;37;44m %s %s \u001B[0m|\033[1;37;42m %s \033[0m| \u001B[1;3;4m%s\u001B[0m | pid=\u001B[1;3;4m%d\u001B[0m | msg=\"\u001B[1;7m%s\u001B[0m\"", data.Date, data.Zone, data.Level, data.Exec, data.Pid, data.Msg)
+	} else if data.LogLevel == loglevel.PseudoLevelTag || data.LogLevel == loglevel.LevelError || data.LogLevel == loglevel.LevelPanic {
+		return fmt.Sprintf("\u001B[1;3;37;44m %s %s \u001B[0m|\033[1;37;41m %s \033[0m| \u001B[1;3;4m%s\u001B[0m | pid=\u001B[1;3;4m%d\u001B[0m | msg=\"\u001B[1;7m%s\u001B[0m\"", data.Date, data.Zone, data.Level, data.Exec, data.Pid, data.Msg)
+	}
+	panic(fmt.Sprintf("unknown loglevel: %s", data.Level))
+}
+
+func _test_func() {
+	var a FormatFunc
+
+	a = FormatMachine
+	a = FormatFile
+	a = FormatConsole
+
+	_ = a
+}

+ 111 - 0
src/logger/logformat/logdata.go

@@ -0,0 +1,111 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package logformat
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/global"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
+	"os"
+	"os/user"
+	"strings"
+	"time"
+)
+
+type LogData struct {
+	LogLevel      loglevel.LoggerLevel `json:"-"`
+	Level         string               `json:"level"`
+	Now           time.Time            `json:"-"`
+	Date          string               `json:"date"`
+	Zone          string               `json:"zone"`
+	Timestamp     int64                `json:"timestamp"`
+	Exec          string               `json:"exec"`
+	Name          string               `json:"name"`
+	Version       string               `json:"version"`
+	User          *user.User           `json:"-"`
+	Uid           string               `json:"uid"`
+	Gid           string               `json:"gid"`
+	UserName      string               `json:"username"`
+	WorkDirectory string               `json:"work-directory"`
+	Pid           int                  `json:"pid"`
+	ParentPid     int                  `json:"ppid"`
+	Msg           string               `json:"msg"`
+}
+
+func GetLogData(level loglevel.LoggerLevel, msg string, now time.Time) *LogData {
+	var res = new(LogData)
+
+	zone := global.Location.String()
+	if strings.ToLower(zone) == "local" {
+		zone, _ = now.Zone()
+	}
+
+	res.LogLevel = level
+	res.Level = strings.ToUpper(string(level))
+	res.Now = now
+	res.Date = now.Format(time.DateTime)
+	res.Zone = zone
+	res.Timestamp = now.Unix()
+	res.Name = global.Name
+	res.Version = global.Version
+
+	u := getUser()
+	if u != nil {
+		res.User = u
+		res.Uid = u.Uid
+		res.Gid = u.Gid
+		res.UserName = getUserName(u)
+	} else {
+		res.User = nil
+		res.Uid = ""
+		res.Gid = ""
+		res.UserName = ""
+	}
+
+	wd := getWorkDir()
+	if wd != "" {
+		res.WorkDirectory = wd
+	} else {
+		res.WorkDirectory = ""
+	}
+
+	res.Pid = os.Getpid()
+	res.ParentPid = os.Getppid()
+	res.Exec = osutils.GetArgs0Name()
+
+	res.Msg = strings.Replace(msg, "\"", "'", -1)
+
+	return res
+}
+
+func getUserName(u *user.User) (name string) {
+	if name = u.Name; name != "" {
+		return name
+	}
+
+	if name = u.Username; name != "" {
+		return name
+	}
+
+	return "-"
+}
+
+func getUser() *user.User {
+	currentUser, err := user.Current()
+	if err != nil {
+		return nil
+	}
+
+	return currentUser
+}
+
+func getWorkDir() string {
+	dir, err := os.Getwd()
+	if err != nil {
+		return "未知"
+	}
+
+	return dir
+}

+ 2 - 0
src/logger/loglevel/level.go

@@ -6,6 +6,8 @@ package loglevel
 
 type LoggerLevel string
 
+const PseudoLevelTag LoggerLevel = "TAG"
+
 const (
 	LevelDebug LoggerLevel = "debug"
 	LevelInfo  LoggerLevel = "info"

+ 5 - 19
src/logger/set_export.go

@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/internal"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
-	"io"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
 )
 
 func SetLevel(level loglevel.LoggerLevel) error {
@@ -25,30 +25,16 @@ func SetLogTag(logTag bool) error {
 	return internal.GlobalLogger.SetLogTag(logTag)
 }
 
-func SetHumanWarnWriter(w io.Writer) (io.Writer, error) {
+func SetWarnWriter(w write.Writer) (write.Writer, error) {
 	if !internal.IsReady() {
 		return nil, fmt.Errorf("logger not ready")
 	}
-	return internal.GlobalLogger.SetHumanWarnWriter(w)
+	return internal.GlobalLogger.SetWarnWriter(w)
 }
 
-func SetHumanErrWriter(w io.Writer) (io.Writer, error) {
+func SetErrWriter(w write.Writer) (write.Writer, error) {
 	if !internal.IsReady() {
 		return nil, fmt.Errorf("logger not ready")
 	}
-	return internal.GlobalLogger.SetHumanErrWriter(w)
-}
-
-func SetMachineWarnWriter(w io.Writer) (io.Writer, error) {
-	if !internal.IsReady() {
-		return nil, fmt.Errorf("logger not ready")
-	}
-	return internal.GlobalLogger.SetMachineWarnWriter(w)
-}
-
-func SetMachineErrWriter(w io.Writer) (io.Writer, error) {
-	if !internal.IsReady() {
-		return nil, fmt.Errorf("logger not ready")
-	}
-	return internal.GlobalLogger.SetMachineErrWriter(w)
+	return internal.GlobalLogger.SetErrWriter(w)
 }

+ 3 - 6
src/logger/write/combiningwriter/writer.go

@@ -6,6 +6,7 @@ package combiningwriter
 
 import (
 	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/sliceutils"
 )
@@ -16,7 +17,7 @@ type CombiningWriter struct {
 	close  bool
 }
 
-func (c *CombiningWriter) Write(p []byte) (n int, err error) {
+func (c *CombiningWriter) Write(data *logformat.LogData) (n int, err error) {
 	if c.close {
 		return 0, fmt.Errorf("combining writer has been close")
 	}
@@ -28,7 +29,7 @@ func (c *CombiningWriter) Write(p []byte) (n int, err error) {
 			continue
 		}
 
-		nTmp, errTmp := w.Write(p)
+		nTmp, errTmp := w.Write(data)
 		if errTmp != nil {
 			errMsg += errTmp.Error() + ";"
 		}
@@ -44,10 +45,6 @@ func (c *CombiningWriter) Write(p []byte) (n int, err error) {
 }
 
 func (c *CombiningWriter) Close() error {
-	return c.ExitClose()
-}
-
-func (c *CombiningWriter) ExitClose() error {
 	defer func() {
 		c.close = true
 	}()

+ 7 - 7
src/logger/write/datefilewriter/writer.go

@@ -6,6 +6,7 @@ package datefilewriter
 
 import (
 	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/filesystemutils"
 	"os"
@@ -19,9 +20,10 @@ type DateFileWriter struct {
 	filenameSuffix string
 	file           *os.File
 	close          bool
+	fn             logformat.FormatFunc
 }
 
-func (f *DateFileWriter) Write(p []byte) (n int, err error) {
+func (f *DateFileWriter) Write(data *logformat.LogData) (n int, err error) {
 	if f.close {
 		return 0, fmt.Errorf("date file writer has been close")
 	}
@@ -43,7 +45,7 @@ func (f *DateFileWriter) Write(p []byte) (n int, err error) {
 		return 0, fmt.Errorf("file writer has been close")
 	}
 
-	return f.file.Write(p)
+	return fmt.Fprintf(f.file, "%s\n", f.fn(data))
 }
 
 func (f *DateFileWriter) closeFile() error {
@@ -78,12 +80,9 @@ func (f *DateFileWriter) openFile(newSuffix string) error {
 }
 
 func (f *DateFileWriter) Close() error {
-	return f.ExitClose()
-}
-
-func (f *DateFileWriter) ExitClose() error {
 	defer func() {
 		f.file = nil
+		f.close = true
 	}()
 
 	if f.file != nil {
@@ -93,7 +92,7 @@ func (f *DateFileWriter) ExitClose() error {
 	return nil
 }
 
-func NewDateFileWriter(dirpath string, filenamePrefix string) (*DateFileWriter, error) {
+func NewDateFileWriter(dirpath string, filenamePrefix string, fn logformat.FormatFunc) (*DateFileWriter, error) {
 	var writer write.WriteCloser
 	var res = new(DateFileWriter)
 
@@ -109,6 +108,7 @@ func NewDateFileWriter(dirpath string, filenamePrefix string) (*DateFileWriter,
 	res.dirPath = dirpath
 	res.filenamePrefix = filenamePrefix
 	res.close = false
+	res.fn = fn
 
 	writer = res // 用于检验StdWriter实现了io.WriteCloser
 	_ = writer

+ 14 - 11
src/logger/write/filewriter/writer.go

@@ -6,6 +6,7 @@ package filewriter
 
 import (
 	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
 	"os"
 )
@@ -13,9 +14,10 @@ import (
 type FileWriter struct {
 	filePath string
 	file     *os.File
+	fn       logformat.FormatFunc
 }
 
-func (f *FileWriter) Write(p []byte) (n int, err error) {
+func (f *FileWriter) Write(data *logformat.LogData) (n int, err error) {
 	if f.file == nil {
 		return 0, fmt.Errorf("file writer has been close")
 	}
@@ -24,28 +26,28 @@ func (f *FileWriter) Write(p []byte) (n int, err error) {
 		return 0, fmt.Errorf("file writer has been close")
 	}
 
-	return f.file.Write(p)
+	return fmt.Fprintf(f.file, "%s\n", f.fn(data))
 }
 
 func (f *FileWriter) Close() error {
-	return f.ExitClose()
-}
+	if f.file == nil {
+		return nil
+	}
 
-func (f *FileWriter) ExitClose() error {
 	defer func() {
 		f.file = nil
 	}()
 
-	if f.file != nil {
-		return f.file.Close()
-	}
-
-	return nil
+	return f.file.Close()
 }
 
-func NewFileWriter(filepath string) (*FileWriter, error) {
+func NewFileWriter(filepath string, fn logformat.FormatFunc) (*FileWriter, error) {
 	var res = new(FileWriter)
 
+	if fn == nil {
+		fn = logformat.FormatFile
+	}
+
 	file, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
 	if err != nil {
 		return nil, err
@@ -53,6 +55,7 @@ func NewFileWriter(filepath string) (*FileWriter, error) {
 
 	res.filePath = filepath
 	res.file = file
+	res.fn = fn
 
 	return res, nil
 }

+ 3 - 6
src/logger/write/nonewriter/writer.go

@@ -5,21 +5,18 @@
 package nonewriter
 
 import (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
 )
 
 type NoneWriter struct {
 }
 
-func (f *NoneWriter) Write(p []byte) (n int, err error) {
-	return len(p), nil
+func (f *NoneWriter) Write(_ *logformat.LogData) (n int, err error) {
+	return 0, nil
 }
 
 func (f *NoneWriter) Close() error {
-	return f.ExitClose()
-}
-
-func (f *NoneWriter) ExitClose() error {
 	return nil
 }
 

+ 40 - 0
src/logger/write/warpwritecloser/writecloser.go

@@ -0,0 +1,40 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package warpwritecloser
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"io"
+)
+
+type WarpWriteCloser struct {
+	writer io.WriteCloser
+	fn     logformat.FormatFunc
+}
+
+func (w *WarpWriteCloser) Write(data *logformat.LogData) (n int, err error) {
+	return fmt.Fprintf(w.writer, "%s\n", w.fn(data))
+}
+
+func (w *WarpWriteCloser) Close() error {
+	return w.writer.Close()
+}
+
+func NewWarpWriteCloser(w io.WriteCloser, fn logformat.FormatFunc) *WarpWriteCloser {
+	return &WarpWriteCloser{
+		writer: w,
+		fn:     fn,
+	}
+}
+
+func _testWrapWriteCloser() {
+	var a write.Writer
+	var b *WarpWriteCloser
+
+	a = b
+	_ = a
+}

+ 40 - 0
src/logger/write/warpwriter/writer.go

@@ -0,0 +1,40 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package warpwriter
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"io"
+)
+
+type WarpWriter struct {
+	writer io.Writer
+	fn     logformat.FormatFunc
+}
+
+func (w *WarpWriter) Write(data *logformat.LogData) (n int, err error) {
+	return fmt.Fprintf(w.writer, "%s\n", w.fn(data))
+}
+
+func NewWarpWriter(w io.Writer, fn logformat.FormatFunc) *WarpWriter {
+	if fn == nil {
+		fn = logformat.FormatConsole
+	}
+
+	return &WarpWriter{
+		writer: w,
+		fn:     fn,
+	}
+}
+
+func _testWrapWriter() {
+	var a write.Writer
+	var b *WarpWriter
+
+	a = b
+	_ = a
+}

+ 0 - 32
src/logger/write/wrapwriter/wrap_writer.go

@@ -1,32 +0,0 @@
-// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package wrapwriter
-
-import (
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
-	"io"
-)
-
-type WrapWriter struct {
-	writer io.Writer
-}
-
-func (w *WrapWriter) Write(p []byte) (n int, err error) {
-	return w.writer.Write(p)
-}
-
-func WrapToWriter(w io.Writer) *WrapWriter {
-	return &WrapWriter{
-		writer: w,
-	}
-}
-
-func _testWrapWriter() {
-	var a write.Writer
-	var b *WrapWriter
-
-	a = b
-	_ = a
-}

+ 0 - 50
src/logger/write/wrapwritercloser/wrap_writer_closer.go

@@ -1,50 +0,0 @@
-// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package wrapwritercloser
-
-import (
-	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
-	"io"
-)
-
-type WrapperWriterClose struct {
-	writer io.WriteCloser
-}
-
-func (w *WrapperWriterClose) Write(p []byte) (n int, err error) {
-	return w.writer.Write(p)
-}
-
-func (w *WrapperWriterClose) Close() (err error) {
-	if w.writer != nil {
-		return nil
-	}
-
-	defer func() {
-		if err == nil {
-			w.writer = nil
-		}
-	}()
-
-	return w.writer.Close()
-}
-
-func (w *WrapperWriterClose) ExitClose() error {
-	return w.Close()
-}
-
-func WraToWriteCloser(w io.WriteCloser) *WrapperWriterClose {
-	return &WrapperWriterClose{
-		writer: w,
-	}
-}
-
-func _testWrapWriteCloser() {
-	var a write.WriteCloser
-	var b *WrapperWriterClose
-
-	a = b
-	_ = a
-}

+ 7 - 4
src/logger/write/export.go → src/logger/write/writer.go

@@ -4,13 +4,16 @@
 
 package write
 
-import "io"
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logformat"
+	"io"
+)
 
 type Writer interface {
-	io.Writer
+	Write(data *logformat.LogData) (n int, err error)
 }
 
 type WriteCloser interface {
-	io.WriteCloser
-	ExitClose() error
+	Writer
+	io.Closer
 }

+ 37 - 0
src/utils/termutils/term.go

@@ -0,0 +1,37 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package termutils
+
+import (
+	"github.com/mattn/go-isatty"
+	"io"
+	"os"
+)
+
+func IsTerm(writer io.Writer) bool {
+	w, ok := writer.(*os.File)
+	if !ok {
+		return false
+	}
+
+	if !isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()) { // 非终端
+		return false
+	}
+
+	return true
+}
+
+func EnvHasTermDump() bool {
+	// TERM为dump表示终端为基础模式,不支持高级显示
+	return os.Getenv("TERM") == "dumb"
+}
+
+func IsTermDump(writer io.Writer) bool {
+	return EnvHasTermDump() && IsTerm(writer)
+}
+
+func IsTermAdvanced(writer io.Writer) bool {
+	return !EnvHasTermDump() && IsTerm(writer)
+}