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

增强日志功能并替换部分 panic 为 logger.Panic

增加了对日志记录中时间戳的支持,并在多个地方将直接调用的 panic 替换为使用 logger.Panic 来统一错误处理和日志记录,以提高代码的健壮性和可维护性。同时新增了日志恢复功能,确保程序在遇到 panic 时能够记录错误信息并尝试恢复。
SongZihuan 1 долоо хоног өмнө
parent
commit
73604aeba8

+ 1 - 0
CHANGELOG.md

@@ -20,6 +20,7 @@
 ### 重构
 
 - 优化了人类可读日志的输出格式。
+- 部分原生 `panic` 语句改写为 `logger.Panic` 日志记录。
 
 ## [0.2.0] - 2025-04-16
 

+ 1 - 1
README.md

@@ -169,7 +169,7 @@ server:  # 系统执行服务所需要的参数
 
 1. 单元测试
 2. GitHub Action
-3. 将部分`panic`转换为`logger.Panic`
+3. 对`Windows`服务的支持
 
 ## 协议
 

+ 4 - 2
src/commandlineargs/export_data.go

@@ -4,11 +4,13 @@
 
 package commandlineargs
 
+import "github.com/SongZihuan/BackendServerTemplate/src/logger"
+
 var CommandLineArgsData CommandLineArgsDataType
 
 func (d *CommandLineArgsDataType) Name() string {
 	if !d.isReady() {
-		panic("flag not ready")
+		logger.Panic("flag not ready")
 	}
 
 	return d.NameData
@@ -16,7 +18,7 @@ func (d *CommandLineArgsDataType) Name() string {
 
 func (d *CommandLineArgsDataType) Help() bool {
 	if !d.isReady() {
-		panic("flag not ready")
+		logger.Panic("flag not ready")
 	}
 
 	return d.HelpData

+ 11 - 10
src/commandlineargs/internal_data_type_method.go

@@ -3,6 +3,7 @@ package commandlineargs
 import (
 	"flag"
 	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/formatutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/reflectutils"
@@ -54,7 +55,7 @@ func (d *CommandLineArgsDataType) writeUsage() {
 			var ok bool
 			optionName, ok = val.FieldByName(option + "Name").Interface().(string)
 			if !ok {
-				panic("can not get option name")
+				logger.Panic("can not get option name")
 			}
 		}
 
@@ -62,7 +63,7 @@ func (d *CommandLineArgsDataType) writeUsage() {
 			var ok bool
 			optionShortName, ok = val.FieldByName(option + "ShortName").Interface().(string)
 			if !ok {
-				panic("can not get option short name")
+				logger.Panic("can not get option short name")
 			}
 		} else if len(optionName) > 1 {
 			optionShortName = optionName[:1]
@@ -72,7 +73,7 @@ func (d *CommandLineArgsDataType) writeUsage() {
 			var ok bool
 			optionUsage, ok = val.FieldByName(option + "Usage").Interface().(string)
 			if !ok {
-				panic("can not get option usage")
+				logger.Panic("can not get option usage")
 			}
 		}
 
@@ -85,12 +86,12 @@ func (d *CommandLineArgsDataType) writeUsage() {
 				var ok bool
 				optionData, ok = val.FieldByName(option + "Data").Interface().(bool)
 				if !ok {
-					panic("can not get option data")
+					logger.Panic("can not get option data")
 				}
 			}
 
 			if optionData == true {
-				panic("bool option can not be true")
+				logger.Panic("bool option can not be true")
 			}
 
 			if optionName != "" {
@@ -106,7 +107,7 @@ func (d *CommandLineArgsDataType) writeUsage() {
 				var ok bool
 				optionData, ok = val.FieldByName(option + "Data").Interface().(string)
 				if !ok {
-					panic("can not get option data")
+					logger.Panic("can not get option data")
 				}
 			}
 
@@ -127,7 +128,7 @@ func (d *CommandLineArgsDataType) writeUsage() {
 				var ok bool
 				optionData, ok = val.FieldByName(option + "Data").Interface().(uint)
 				if !ok {
-					panic("can not get option data")
+					logger.Panic("can not get option data")
 				}
 			}
 
@@ -143,7 +144,7 @@ func (d *CommandLineArgsDataType) writeUsage() {
 				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionShortPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s number", optionShortName), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
 			}
 		} else {
-			panic(fmt.Sprintf("the flag type (%s) is not support", field.Type.Name()))
+			logger.Panic(fmt.Sprintf("the flag type (%s) is not support", field.Type.Name()))
 		}
 
 		if title1 == "" && title2 == "" {
@@ -173,7 +174,7 @@ func (d *CommandLineArgsDataType) parser() {
 	}
 
 	if !d.isFlagSet() {
-		panic("flag not set")
+		logger.Panic("flag not set")
 	}
 
 	flag.Parse()
@@ -194,7 +195,7 @@ func (d *CommandLineArgsDataType) isFlagParser() bool {
 
 func getData[T any](d *CommandLineArgsDataType, data T) T { // 泛型函数无法作为 “方法” 只能作为函数
 	if !d.isReady() {
-		panic("flag not ready")
+		logger.Panic("flag not ready")
 	}
 
 	return data

+ 3 - 2
src/config/config.go

@@ -3,6 +3,7 @@ package config
 import (
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
 	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/filesystemutils"
 )
 
@@ -17,7 +18,7 @@ type configInfo struct {
 
 func newConfig(inputFilePath string, outputFilePath string, provider configparser.ConfigParserProvider) (*configInfo, configerror.Error) {
 	if inputFilePath == "" {
-		panic("config path is empty")
+		logger.Panic("config path is empty")
 	}
 
 	configFilePath, err := filesystemutils.CleanFilePathAbs(inputFilePath)
@@ -117,7 +118,7 @@ func (c *configInfo) GetData() (*ConfigData, configerror.Error) {
 
 func (c *configInfo) Data() *ConfigData {
 	if !c.ready {
-		panic("config is not ready")
+		logger.Panic("config is not ready")
 	}
 
 	return c.data

+ 3 - 3
src/config/export.go

@@ -77,7 +77,7 @@ func InitConfig(opt *ConfigOption) error {
 
 func GetData() (*ConfigData, configerror.Error) {
 	if config == nil {
-		panic("config is not ready")
+		logger.Panic("config is not ready")
 	}
 
 	return config.GetData()
@@ -85,7 +85,7 @@ func GetData() (*ConfigData, configerror.Error) {
 
 func Data() *ConfigData {
 	if config == nil {
-		panic("config is not ready")
+		logger.Panic("config is not ready")
 	}
 
 	return config.Data()
@@ -93,7 +93,7 @@ func Data() *ConfigData {
 
 func Output(filePath string) error {
 	if config == nil {
-		panic("config is not ready")
+		logger.Panic("config is not ready")
 	}
 
 	return config.Output(filePath)

+ 6 - 4
src/logger/internal/format.go

@@ -29,18 +29,19 @@ type FormatMachineJson struct {
 	Msg           string `json:"msg"`
 }
 
-func (l *Logger) formatMachine(_level loglevel.LoggerLevel, msg string) string {
+func (l *Logger) formatMachine(_level loglevel.LoggerLevel, msg string, now time.Time) 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
@@ -72,18 +73,19 @@ func (l *Logger) formatMachine(_level loglevel.LoggerLevel, msg string) string {
 	return string(data) + "\n"
 }
 
-func (l *Logger) formatHuman(_level loglevel.LoggerLevel, msg string) string {
+func (l *Logger) formatHuman(_level loglevel.LoggerLevel, msg string, now time.Time) string {
 	var res = new(strings.Builder)
 
 	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.WriteString(fmt.Sprintf("%s %s | %s | unix-timestamp=\"%ds\" | app=\"%s\" | version=\"%s\"", date, zone, level, now.Unix(), global.Name, global.Version))

+ 82 - 36
src/logger/internal/logger_method.go

@@ -6,8 +6,11 @@ package internal
 
 import (
 	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/global"
 	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/logpanic"
 	"github.com/SongZihuan/BackendServerTemplate/src/utils/runtimeutils"
+	"time"
 )
 
 func (l *Logger) Tagf(format string, args ...interface{}) {
@@ -23,7 +26,8 @@ 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.humanWarnWriter, "%s", l.formatHuman("TAG", msg))
+	now := time.Now().In(global.Location)
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg, now))
 }
 
 func (l *Logger) Debugf(format string, args ...interface{}) {
@@ -32,8 +36,9 @@ func (l *Logger) Debugf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg))
+	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))
 }
 
 func (l *Logger) Infof(format string, args ...interface{}) {
@@ -42,8 +47,9 @@ func (l *Logger) Infof(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg))
+	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))
 }
 
 func (l *Logger) Warnf(format string, args ...interface{}) {
@@ -52,8 +58,9 @@ func (l *Logger) Warnf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg))
+	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))
 }
 
 func (l *Logger) Errorf(format string, args ...interface{}) {
@@ -62,8 +69,9 @@ func (l *Logger) Errorf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelError, msg))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg))
+	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))
 }
 
 func (l *Logger) Panicf(format string, args ...interface{}) {
@@ -72,10 +80,11 @@ func (l *Logger) Panicf(format string, args ...interface{}) {
 	}
 
 	msg := fmt.Sprintf(format, args...)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg))
+	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))
 
-	panic(msg)
+	logpanic.Panic(now, msg)
 }
 
 func (l *Logger) Tag(args ...interface{}) {
@@ -90,8 +99,9 @@ func (l *Logger) TagSkip(skip int, args ...interface{}) {
 	funcName, file, _, line := runtimeutils.GetCallingFunctionInfo(skip + 1)
 
 	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))
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg, now))
 }
 
 func (l *Logger) Debug(args ...interface{}) {
@@ -100,8 +110,9 @@ func (l *Logger) Debug(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg))
+	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))
 }
 
 func (l *Logger) Info(args ...interface{}) {
@@ -110,8 +121,9 @@ func (l *Logger) Info(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg))
+	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))
 }
 
 func (l *Logger) Warn(args ...interface{}) {
@@ -120,8 +132,9 @@ func (l *Logger) Warn(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg))
+	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))
 }
 
 func (l *Logger) Error(args ...interface{}) {
@@ -130,8 +143,9 @@ func (l *Logger) Error(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelError, msg))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg))
+	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))
 }
 
 func (l *Logger) Panic(args ...interface{}) {
@@ -140,10 +154,11 @@ func (l *Logger) Panic(args ...interface{}) {
 	}
 
 	msg := fmt.Sprint(args...)
-	_, _ = fmt.Fprintf(l.humanErrWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg))
+	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))
 
-	panic(msg)
+	logpanic.Panic(now, msg)
 }
 
 func (l *Logger) TagWrite(msg string) {
@@ -158,7 +173,8 @@ 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.humanWarnWriter, "%s", l.formatHuman("TAG", msg))
+	now := time.Now().In(global.Location)
+	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman("TAG", msg, now))
 }
 
 func (l *Logger) DebugWrite(msg string) {
@@ -166,8 +182,9 @@ func (l *Logger) DebugWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelDebug, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelDebug, msg))
+	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))
 }
 
 func (l *Logger) InfoWrite(msg string) {
@@ -175,8 +192,9 @@ func (l *Logger) InfoWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelInfo, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelInfo, msg))
+	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))
 }
 
 func (l *Logger) WarnWrite(msg string) {
@@ -184,8 +202,9 @@ func (l *Logger) WarnWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelWarn, msg))
-	_, _ = fmt.Fprintf(l.machineWarnWriter, "%s", l.formatMachine(loglevel.LevelWarn, msg))
+	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))
 }
 
 func (l *Logger) ErrorWrite(msg string) {
@@ -193,8 +212,9 @@ func (l *Logger) ErrorWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelError, msg))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelError, msg))
+	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))
 }
 
 func (l *Logger) PanicWrite(msg string) {
@@ -202,8 +222,34 @@ func (l *Logger) PanicWrite(msg string) {
 		return
 	}
 
-	_, _ = fmt.Fprintf(l.humanWarnWriter, "%s", l.formatHuman(loglevel.LevelPanic, msg))
-	_, _ = fmt.Fprintf(l.machineErrWriter, "%s", l.formatMachine(loglevel.LevelPanic, msg))
+	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))
 
-	panic(msg)
+	logpanic.Panic(now, msg)
+}
+
+func (l *Logger) Recover() {
+	err := recover()
+	if err == nil {
+		return
+	}
+
+	msg := ""
+	now := time.Now().In(global.Location)
+
+	if _, ok := err.(*logpanic.PanicData); ok {
+		return
+	}
+
+	if str, ok := err.(fmt.Stringer); ok {
+		msg = str.String()
+	} else if _err, ok := err.(error); ok {
+		msg = _err.Error()
+	} else {
+		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))
 }

+ 7 - 0
src/logger/logger_export.go

@@ -133,3 +133,10 @@ func PanicWrite(msg string) {
 	}
 	internal.GlobalLogger.PanicWrite(msg)
 }
+
+func Recover() {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Recover()
+}

+ 31 - 0
src/logger/logpanic/panic.go

@@ -0,0 +1,31 @@
+// 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 logpanic
+
+import "time"
+
+type PanicData struct {
+	time time.Time
+	msg  string
+}
+
+func Panic(t time.Time, msg string) *PanicData {
+	panic(NewPanicData(t, msg))
+}
+
+func NewPanicData(t time.Time, msg string) *PanicData {
+	return &PanicData{
+		time: t,
+		msg:  msg,
+	}
+}
+
+func (p *PanicData) Time() time.Time {
+	return p.time
+}
+
+func (p *PanicData) Msg() string {
+	return p.msg
+}

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

@@ -34,6 +34,7 @@ func MainV1() (exitCode int) {
 		return exitutils.InitFailedErrorForLoggerModule(err.Error())
 	}
 	defer logger.CloseLogger()
+	defer logger.Recover()
 
 	err = commandlineargs.InitCommandLineArgsParser(nil)
 	if err != nil {

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

@@ -32,6 +32,7 @@ func MainV1() (exitCode int) {
 		return exitutils.InitFailedErrorForLoggerModule(err.Error())
 	}
 	defer logger.CloseLogger()
+	defer logger.Recover()
 
 	err = commandlineargs.InitCommandLineArgsParser(nil)
 	if err != nil {

+ 0 - 4
src/utils/exitutils/exit.go

@@ -61,7 +61,6 @@ func InitFailedErrorForLoggerModule(reason string, exitCode ...int) int {
 
 func InitFailedError(module string, reason string, exitCode ...int) int {
 	if !logger.IsReady() {
-		panic("Logger must be ready!!!")
 		return exitCodeErrorLogMustBeReady
 	}
 
@@ -79,7 +78,6 @@ func InitFailedError(module string, reason string, exitCode ...int) int {
 
 func RunError(reason string, exitCode ...int) int {
 	if !logger.IsReady() {
-		panic("Logger must be ready!!!")
 		return exitCodeErrorLogMustBeReady
 	}
 
@@ -97,7 +95,6 @@ func RunError(reason string, exitCode ...int) int {
 
 func SuccessExit(reason string, exitCode ...int) int {
 	if !logger.IsReady() {
-		panic("Logger must be ready!!!")
 		return exitCodeErrorLogMustBeReady
 	}
 
@@ -114,7 +111,6 @@ func SuccessExit(reason string, exitCode ...int) int {
 
 func SuccessExitQuite(exitCode ...int) int {
 	if !logger.IsReady() {
-		panic("Logger must be ready!!!")
 		return exitCodeErrorLogMustBeReady
 	}