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

添加机器可读日志支持

新增 `NoneWriter` 用于处理空日志输出,并在多个文件中修改了日志初始化和配置,以支持人类可读和机器可读(JSON格式)的日志。同时更新了相关文档和配置文件。
SongZihuan 1 долоо хоног өмнө
parent
commit
c41b953105

+ 1 - 0
CHANGELOG.md

@@ -11,6 +11,7 @@
 
 - 新增版本号获取功能(仅输出版本号,不输出其他任何内容,不以字母v或V开头)。
 - 加入对 `Windows Console` 的支持。
+- 添加对机器可读日志的支持(`Json`格式)。
 
 ### 修复
 

+ 14 - 2
README.md

@@ -110,18 +110,30 @@ logger:
     log-level: debug  # 日志记录等级:debug(输出debug和以上的) < info (输出info和以上的)< warn < error < panic < none(什么都不输出)
     log-tag: enable  # 是否输出tag调试日志。
     
-    warn-writer:  # debug、tag、info、warn日志的输出器
+    human-warn-writer:  # 人类可读的 debug、tag、info、warn 日志的输出器
         write-to-std: stdout  # 输出到标准输出或标准错误输出(为空则不启用)
         write-to-file: ""  # 输出到固定文件(append)模式
         write-to-dir-with-date: ""  # 输出到指定目录,并按日期分割,此处为输出路径
         write-with-date-prefix: ""  # 配合 write-to-dir-with-date ,表示文件的输出前缀
         
-    error-writer:  # error、panic日志的输出器,含义同上
+    human-error-writer:  # 人类可读的 error、panic 日志的输出器,含义同上
         write-to-std: stdout
         write-to-file: ""
         write-to-dir-with-date: ""
         write-with-date-prefix: ""
 
+    machine-warn-writer:  # 机器可读的 debug、tag、info、warn 日志的输出器,含义同上
+      write-to-std: stdout
+      write-to-file: ""
+      write-to-dir-with-date: ""
+      write-with-date-prefix: ""
+      
+    machine-error-writer:  # 机器可读的 error、panic 日志的输出器,含义同上
+      write-to-std: stdout
+      write-to-file: ""
+      write-to-dir-with-date: ""
+      write-with-date-prefix: ""
+
 signal: # 信号除了机制(管理接收程序退出信号)。sigkill 等信号是不可捕获的,是强制退出的,因此此处无法控制这类信号。虽然windows本身不具有Linux这种信号机制,但是Go在信号方面做了一层模拟,使得控制它ctrl+c可以转换为相应信号。
     use-on: not-win32  # 启动模式:any表示全平台、only-win32表示仅windows平台、not-win32表示除windows以外所有平台,never表示任何平台均不启用。
     sigint-exit: enable  # 收到 sigint 信号后退出 (Windows中可一半呢由ctrl+c触发)

+ 52 - 10
src/config/logger_config.go

@@ -16,17 +16,29 @@ type LoggerConfig struct {
 	LogLevel loglevel.LoggerLevel `json:"log-level" yaml:"log-level"`
 	LogTag   typeutils.StringBool `json:"log-tag" yaml:"log-tag"`
 
-	WarnWriter LoggerWriterConfig `json:"warn-writer" yaml:"warn-writer"`
-	ErrWriter  LoggerWriterConfig `json:"error-writer" yaml:"error-writer"`
+	HumanWarnWriter   LoggerWriterConfig `json:"human-warn-writer" yaml:"human-warn-writer"`
+	HumanErrWriter    LoggerWriterConfig `json:"human-error-writer" yaml:"human-error-writer"`
+	MachineWarnWriter LoggerWriterConfig `json:"machine-warn-writer" yaml:"machine-warn-writer"`
+	MachineErrWriter  LoggerWriterConfig `json:"machine-error-writer" yaml:"machine-error-writer"`
 }
 
 func (d *LoggerConfig) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
-	cfgErr := d.WarnWriter.init(filePath, provider)
+	cfgErr := d.HumanWarnWriter.init(filePath, provider)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
-	cfgErr = d.ErrWriter.init(filePath, provider)
+	cfgErr = d.HumanErrWriter.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.MachineWarnWriter.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.MachineErrWriter.init(filePath, provider)
 	if cfgErr != nil {
 		return cfgErr
 	}
@@ -45,12 +57,22 @@ func (d *LoggerConfig) setDefault(c *configInfo) (err configerror.Error) {
 		}
 	}
 
-	cfgErr := d.WarnWriter.setDefault(c)
+	cfgErr := d.HumanWarnWriter.setDefault(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.HumanErrWriter.setDefault(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.MachineWarnWriter.setDefault(c)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
-	cfgErr = d.ErrWriter.setDefault(c)
+	cfgErr = d.MachineErrWriter.setDefault(c)
 	if cfgErr != nil {
 		return cfgErr
 	}
@@ -59,12 +81,22 @@ func (d *LoggerConfig) setDefault(c *configInfo) (err configerror.Error) {
 }
 
 func (d *LoggerConfig) check(c *configInfo) (err configerror.Error) {
-	cfgErr := d.WarnWriter.check(c)
+	cfgErr := d.HumanWarnWriter.check(c)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
-	cfgErr = d.ErrWriter.check(c)
+	cfgErr = d.HumanErrWriter.check(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.MachineWarnWriter.check(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.MachineErrWriter.check(c)
 	if cfgErr != nil {
 		return cfgErr
 	}
@@ -83,12 +115,22 @@ func (d *LoggerConfig) process(c *configInfo) (cfgErr configerror.Error) {
 		return configerror.NewErrorf("set log tag error: %s", err.Error())
 	}
 
-	cfgErr = d.WarnWriter.process(c, logger.SetWarnWriter)
+	cfgErr = d.HumanWarnWriter.process(c, logger.SetHumanWarnWriter)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.HumanErrWriter.process(c, logger.SetHumanErrWriter)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.MachineWarnWriter.process(c, logger.SetMachineWarnWriter)
 	if cfgErr != nil {
 		return cfgErr
 	}
 
-	cfgErr = d.ErrWriter.process(c, logger.SetErrWriter)
+	cfgErr = d.MachineErrWriter.process(c, logger.SetMachineErrWriter)
 	if cfgErr != nil {
 		return cfgErr
 	}

+ 13 - 7
src/config/logger_writer_config.go

@@ -12,6 +12,7 @@ import (
 	"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"
 	"os"
@@ -32,10 +33,6 @@ func (d *LoggerWriterConfig) init(filePath string, provider configparser.ConfigP
 func (d *LoggerWriterConfig) setDefault(c *configInfo) (err configerror.Error) {
 	d.WriteToStd = strings.ToLower(d.WriteToStd)
 
-	if d.WriteToStd == "" {
-		d.WriteToStd = "stderr"
-	}
-
 	if d.WriteToDirWithDate != "" && d.WriteWithDatePrefix == "" {
 		d.WriteWithDatePrefix = global.Name
 	}
@@ -44,8 +41,8 @@ func (d *LoggerWriterConfig) setDefault(c *configInfo) (err configerror.Error) {
 }
 
 func (d *LoggerWriterConfig) check(c *configInfo) (err configerror.Error) {
-	if d.WriteToStd != "stdout" && d.WriteToStd != "stderr" && d.WriteToStd != "no" {
-		return configerror.NewErrorf("bad write-to-std")
+	if d.WriteToStd != "stdout" && d.WriteToStd != "stderr" && d.WriteToStd != "no" && d.WriteToStd != "stdout+stderr" && d.WriteToStd != "stderr+stdout" && d.WriteToStd != "" {
+		return configerror.NewErrorf("bad write-to-std: %s", d.WriteToStd)
 	}
 	return nil
 }
@@ -58,6 +55,12 @@ func (d *LoggerWriterConfig) process(c *configInfo, setter func(w io.Writer) (io
 		writerList = append(writerList, wrapwriter.WrapToWriter(os.Stdout))
 	case "stderr":
 		writerList = append(writerList, wrapwriter.WrapToWriter(os.Stderr))
+	case "stderr+stdout", "stdout+stderr":
+		writerList = append(writerList, wrapwriter.WrapToWriter(os.Stdout), wrapwriter.WrapToWriter(os.Stderr))
+	case "", "no":
+		// pass
+	default:
+		return configerror.NewErrorf("bad write-to-std: %s", d.WriteToStd)
 	}
 
 	if d.WriteToFile != "" {
@@ -79,7 +82,10 @@ func (d *LoggerWriterConfig) process(c *configInfo, setter func(w io.Writer) (io
 	}
 
 	if len(writerList) == 0 {
-		return nil
+		_, 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 {

+ 0 - 55
src/logger/info_export.go

@@ -7,7 +7,6 @@ package logger
 import (
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/internal"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
-	"io"
 )
 
 func GetLevel() loglevel.LoggerLevel {
@@ -23,57 +22,3 @@ func IsLogTag() bool {
 	}
 	return internal.GlobalLogger.IsLogTag()
 }
-
-func GetWarnWriter() io.Writer {
-	if !internal.IsReady() {
-		return nil
-	}
-	return internal.GlobalLogger.GetWarnWriter()
-}
-
-func GetErrWriter() io.Writer {
-	if !internal.IsReady() {
-		return nil
-	}
-	return internal.GlobalLogger.GetErrWriter()
-}
-
-func IsWarnWriterTerm() bool {
-	if !internal.IsReady() {
-		return false
-	}
-
-	return internal.GlobalLogger.IsWarnWriterTerm()
-}
-
-func IsErrWriterTerm() bool {
-	if !internal.IsReady() {
-		return false
-	}
-
-	return internal.GlobalLogger.IsErrWriterTerm()
-}
-
-func IsTermDump() bool {
-	if !internal.IsReady() {
-		return false
-	}
-
-	return internal.GlobalLogger.IsTermDump()
-}
-
-func IsWarnWriterTermNoDump() bool {
-	if !internal.IsReady() {
-		return false
-	}
-
-	return internal.GlobalLogger.IsWarnWriterTermNoDump()
-}
-
-func IsErrWriterTermNoDump() bool {
-	if !internal.IsReady() {
-		return false
-	}
-
-	return internal.GlobalLogger.IsErrWriterTermNoDump()
-}

+ 2 - 2
src/logger/init_export.go

@@ -10,8 +10,8 @@ import (
 	"io"
 )
 
-func InitBaseLogger(level loglevel.LoggerLevel, logTag bool, warnWriter, errWriter io.Writer) error {
-	return internal.InitLogger(level, logTag, warnWriter, errWriter)
+func InitBaseLogger(level loglevel.LoggerLevel, logTag bool, humanWarnWriter, humanErrWriter, machineWarnWriter, machineErWriter io.Writer) error {
+	return internal.InitLogger(level, logTag, humanWarnWriter, humanErrWriter, machineWarnWriter, machineErWriter)
 }
 
 func CloseLogger() {

+ 59 - 1
src/logger/internal/format.go

@@ -5,6 +5,7 @@
 package internal
 
 import (
+	"encoding/json"
 	"fmt"
 	"github.com/SongZihuan/BackendServerTemplate/src/global"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
@@ -14,7 +15,64 @@ import (
 	"time"
 )
 
-func (l *Logger) format(_level loglevel.LoggerLevel, msg string) string {
+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) string {
+	var res = new(FormatMachineJson)
+
+	level := string(_level)
+
+	now := time.Now().In(global.Location)
+	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) string {
 	var res = new(strings.Builder)
 
 	level := string(_level)

+ 2 - 11
src/logger/internal/info_method.go

@@ -7,7 +7,6 @@ package internal
 import (
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
 	"github.com/mattn/go-isatty"
-	"io"
 	"os"
 )
 
@@ -19,16 +18,8 @@ func (l *Logger) IsLogTag() bool {
 	return l.logTag
 }
 
-func (l *Logger) GetWarnWriter() io.Writer {
-	return l.warnWriter
-}
-
-func (l *Logger) GetErrWriter() io.Writer {
-	return l.errWriter
-}
-
 func (l *Logger) IsWarnWriterTerm() bool {
-	w, ok := l.warnWriter.(*os.File)
+	w, ok := l.humanWarnWriter.(*os.File)
 	if !ok {
 		return false
 	} else if !isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()) { // 非终端
@@ -38,7 +29,7 @@ func (l *Logger) IsWarnWriterTerm() bool {
 }
 
 func (l *Logger) IsErrWriterTerm() bool {
-	w, ok := l.errWriter.(*os.File)
+	w, ok := l.humanErrWriter.(*os.File)
 	if !ok {
 		return false
 	} else if !isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()) { // 非终端

+ 25 - 11
src/logger/internal/init.go

@@ -7,31 +7,42 @@ 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"
 	"os"
 )
 
-func InitLogger(level loglevel.LoggerLevel, logTag bool, warnWriter, errWriter io.Writer) error {
+func InitLogger(level loglevel.LoggerLevel, logTag bool, humanWarnWriter, humanErrWriter io.Writer, machineWarnWriter, machineErrWriter io.Writer) error {
 	logLevel, ok := levelMap[level]
 	if !ok {
 		return fmt.Errorf("invalid log level: %s", level)
 	}
 
-	if warnWriter == nil {
-		warnWriter = wrapwriter.WrapToWriter(os.Stdout)
+	if humanWarnWriter == nil {
+		humanWarnWriter = wrapwriter.WrapToWriter(os.Stdout)
 	}
 
-	if errWriter == nil {
-		errWriter = wrapwriter.WrapToWriter(os.Stderr)
+	if humanErrWriter == nil {
+		humanErrWriter = wrapwriter.WrapToWriter(os.Stderr)
+	}
+
+	if machineWarnWriter == nil {
+		machineWarnWriter = nonewriter.NewNoneWriter()
+	}
+
+	if machineErrWriter == nil {
+		machineErrWriter = nonewriter.NewNoneWriter()
 	}
 
 	logger := &Logger{
-		level:      level,
-		logLevel:   logLevel,
-		logTag:     logTag,
-		warnWriter: warnWriter,
-		errWriter:  errWriter,
+		level:             level,
+		logLevel:          logLevel,
+		logTag:            logTag,
+		humanWarnWriter:   humanWarnWriter,
+		humanErrWriter:    humanErrWriter,
+		machineWarnWriter: machineWarnWriter,
+		machineErrWriter:  machineErrWriter,
 	}
 
 	GlobalLogger = logger
@@ -43,5 +54,8 @@ func IsReady() bool {
 }
 
 func CloseLogger() {
-
+	_ = GlobalLogger.CloseHumanWarnWriter()
+	_ = GlobalLogger.CloseHumanErrWriter()
+	_ = GlobalLogger.CloseMachineWarnWriter()
+	_ = GlobalLogger.CloseMachineErrWriter()
 }

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

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

+ 33 - 18
src/logger/internal/logger_method.go

@@ -23,7 +23,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)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format("TAG", msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg))
 }
 
 func (l *Logger) Debugf(format string, args ...interface{}) {
@@ -32,7 +32,8 @@ func (l *Logger) Debugf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelDebug, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg))
 }
 
 func (l *Logger) Infof(format string, args ...interface{}) {
@@ -41,7 +42,8 @@ func (l *Logger) Infof(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelInfo, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg))
 }
 
 func (l *Logger) Warnf(format string, args ...interface{}) {
@@ -50,7 +52,8 @@ func (l *Logger) Warnf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelWarn, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg))
 }
 
 func (l *Logger) Errorf(format string, args ...interface{}) {
@@ -59,7 +62,8 @@ func (l *Logger) Errorf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.errWriter, "%s", l.format(loglevel.LevelError, msg))
+	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelError, msg))
+	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg))
 }
 
 func (l *Logger) Panicf(format string, args ...interface{}) {
@@ -68,7 +72,8 @@ func (l *Logger) Panicf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.errWriter, "%s", l.format(loglevel.LevelPanic, msg))
+	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg))
+	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg))
 
 	panic(msg)
 }
@@ -86,7 +91,7 @@ func (l *Logger) TagSkip(skip int, args ...interface{}) {
 
 	content := fmt.Sprint(args...)
 	msg := fmt.Sprintf("%s %s %s:%d", content, funcName, file, line)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format("TAG", msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg))
 }
 
 func (l *Logger) Debug(args ...interface{}) {
@@ -95,7 +100,8 @@ func (l *Logger) Debug(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelDebug, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg))
 }
 
 func (l *Logger) Info(args ...interface{}) {
@@ -104,7 +110,8 @@ func (l *Logger) Info(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelInfo, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg))
 }
 
 func (l *Logger) Warn(args ...interface{}) {
@@ -113,7 +120,8 @@ func (l *Logger) Warn(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelWarn, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg))
 }
 
 func (l *Logger) Error(args ...interface{}) {
@@ -122,7 +130,8 @@ func (l *Logger) Error(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.errWriter, "%s", l.format(loglevel.LevelError, msg))
+	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelError, msg))
+	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg))
 }
 
 func (l *Logger) Panic(args ...interface{}) {
@@ -131,7 +140,8 @@ func (l *Logger) Panic(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.errWriter, "%s", l.format(loglevel.LevelPanic, msg))
+	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg))
+	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg))
 
 	panic(msg)
 }
@@ -148,7 +158,7 @@ func (l *Logger) TagSkipWrite(skip int, content string) {
 	funcName, file, _, line := runtimeutils.GetCallingFunctionInfo(skip + 1)
 
 	msg := fmt.Sprintf("%s %s %s:%d", content, funcName, file, line)
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format("TAG", msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg))
 }
 
 func (l *Logger) DebugWrite(msg string) {
@@ -156,7 +166,8 @@ func (l *Logger) DebugWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelDebug, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg))
 }
 
 func (l *Logger) InfoWrite(msg string) {
@@ -164,7 +175,8 @@ func (l *Logger) InfoWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelInfo, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg))
 }
 
 func (l *Logger) WarnWrite(msg string) {
@@ -172,7 +184,8 @@ func (l *Logger) WarnWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelWarn, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg))
+	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg))
 }
 
 func (l *Logger) ErrorWrite(msg string) {
@@ -180,7 +193,8 @@ func (l *Logger) ErrorWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelError, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelError, msg))
+	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg))
 }
 
 func (l *Logger) PanicWrite(msg string) {
@@ -188,7 +202,8 @@ func (l *Logger) PanicWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.warnWriter, "%s", l.format(loglevel.LevelPanic, msg))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg))
+	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg))
 
 	panic(msg)
 }

+ 59 - 12
src/logger/internal/set_method.go

@@ -8,6 +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"
 	"os"
 )
@@ -29,32 +30,78 @@ func (l *Logger) SetLogTag(logTag bool) error {
 	return nil
 }
 
-func (l *Logger) SetWarnWriter(w write.Writer) (write.Writer, error) {
+func (l *Logger) SetHumanWarnWriter(w write.Writer) (write.Writer, error) {
 	if w == nil {
 		w = wrapwriter.WrapToWriter(os.Stdout)
 	}
 
-	last := l.warnWriter
-	l.warnWriter = w
+	last := l.humanWarnWriter
+	l.humanWarnWriter = w
 	return last, nil
 }
 
-func (l *Logger) SetErrWriter(w write.Writer) (write.Writer, error) {
+func (l *Logger) SetHumanErrWriter(w write.Writer) (write.Writer, error) {
 	if w == nil {
 		w = wrapwriter.WrapToWriter(os.Stderr)
 	}
 
-	last := l.errWriter
-	l.errWriter = w
+	last := l.humanErrWriter
+	l.humanErrWriter = w
 	return last, nil
 }
 
-func (l *Logger) CloseWarnWriter() error {
-	if l.warnWriter == 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 {
 		return fmt.Errorf("warn writer not set")
 	}
 
-	w, ok := l.warnWriter.(write.WriteCloser)
+	w, ok := l.machineWarnWriter.(write.WriteCloser)
 	if !ok {
 		return nil
 	}
@@ -62,12 +109,12 @@ func (l *Logger) CloseWarnWriter() error {
 	return w.ExitClose()
 }
 
-func (l *Logger) CloseErrWriter() error {
-	if l.errWriter == nil {
+func (l *Logger) CloseMachineErrWriter() error {
+	if l.machineErrWriter == nil {
 		return fmt.Errorf("error writer not set")
 	}
 
-	w, ok := l.errWriter.(write.WriteCloser)
+	w, ok := l.machineErrWriter.(write.WriteCloser)
 	if !ok {
 		return nil
 	}

+ 18 - 4
src/logger/set_export.go

@@ -25,16 +25,30 @@ func SetLogTag(logTag bool) error {
 	return internal.GlobalLogger.SetLogTag(logTag)
 }
 
-func SetWarnWriter(w io.Writer) (io.Writer, error) {
+func SetHumanWarnWriter(w io.Writer) (io.Writer, error) {
 	if !internal.IsReady() {
 		return nil, fmt.Errorf("logger not ready")
 	}
-	return internal.GlobalLogger.SetWarnWriter(w)
+	return internal.GlobalLogger.SetHumanWarnWriter(w)
 }
 
-func SetErrWriter(w io.Writer) (io.Writer, error) {
+func SetHumanErrWriter(w io.Writer) (io.Writer, error) {
 	if !internal.IsReady() {
 		return nil, fmt.Errorf("logger not ready")
 	}
-	return internal.GlobalLogger.SetErrWriter(w)
+	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)
 }

+ 36 - 0
src/logger/write/nonewriter/writer.go

@@ -0,0 +1,36 @@
+// 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 nonewriter
+
+import (
+	"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) Close() error {
+	return f.ExitClose()
+}
+
+func (f *NoneWriter) ExitClose() error {
+	return nil
+}
+
+func NewNoneWriter() *NoneWriter {
+	return new(NoneWriter)
+}
+
+func _testNoneWriter() {
+	var a write.WriteCloser
+	var b *NoneWriter
+
+	a = b
+	_ = a
+}

+ 1 - 1
src/mainfunc/lion/v1/main.go

@@ -29,7 +29,7 @@ func MainV1() (exitCode int) {
 		return exitutils.InitFailedErrorForWin32ConsoleModule(err.Error())
 	}
 
-	err = logger.InitBaseLogger(loglevel.LevelDebug, true, nil, nil)
+	err = logger.InitBaseLogger(loglevel.LevelDebug, true, nil, nil, nil, nil)
 	if err != nil {
 		return exitutils.InitFailedErrorForLoggerModule(err.Error())
 	}

+ 1 - 1
src/mainfunc/tiger/v1/main.go

@@ -27,7 +27,7 @@ func MainV1() (exitCode int) {
 		return exitutils.InitFailedErrorForWin32ConsoleModule(err.Error())
 	}
 
-	err = logger.InitBaseLogger(loglevel.LevelDebug, true, nil, nil)
+	err = logger.InitBaseLogger(loglevel.LevelDebug, true, nil, nil, nil, nil)
 	if err != nil {
 		return exitutils.InitFailedErrorForLoggerModule(err.Error())
 	}