Parcourir la source

初始化配置文件和命令行参数解析

新增了多个文件以支持配置文件的读写、命令行参数解析以及服务器上下文管理。其中包括配置文件的基础数据结构定义、命令行参数的数据类型定义、文件系统工具函数等,为后续功能实现打下基础。
SongZihuan il y a 3 semaines
commit
fe5bf5092c
73 fichiers modifiés avec 4467 ajouts et 0 suppressions
  1. 71 0
      .gitattributes
  2. 16 0
      .gitignore
  3. 8 0
      LICENSE
  4. 1 0
      NAME
  5. 40 0
      README.md
  6. 13 0
      REEPORT
  7. 1 0
      VERSION
  8. 11 0
      go.mod
  9. 8 0
      go.sum
  10. 36 0
      resource.go
  11. 14 0
      src/cmd/lionv1/main.go
  12. 14 0
      src/cmd/tigerv1/main.go
  13. 118 0
      src/commandlineargs/define_data_type.go
  14. 79 0
      src/commandlineargs/export.go
  15. 43 0
      src/commandlineargs/export_data.go
  16. 19 0
      src/commandlineargs/export_method.go
  17. 59 0
      src/commandlineargs/export_printer.go
  18. 200 0
      src/commandlineargs/internal_data_type_method.go
  19. 62 0
      src/commandlineargs/main.go
  20. 109 0
      src/config/base_data.go
  21. 123 0
      src/config/config.go
  22. 29 0
      src/config/configerror/error.go
  23. 40 0
      src/config/configerror/export.go
  24. 90 0
      src/config/configparser/json.go
  25. 14 0
      src/config/configparser/parser.go
  26. 90 0
      src/config/configparser/yaml.go
  27. 25 0
      src/config/define_data_example.go
  28. 109 0
      src/config/export.go
  29. 116 0
      src/config/global_config.go
  30. 109 0
      src/config/logger_config.go
  31. 96 0
      src/config/logger_writer_config.go
  32. 38 0
      src/config/server_config.go
  33. 40 0
      src/config/signal_config.go
  34. 15 0
      src/global/variabl.go
  35. 86 0
      src/logger/info_export.go
  36. 15 0
      src/logger/init_export.go
  37. 65 0
      src/logger/internal/info_method.go
  38. 51 0
      src/logger/internal/init.go
  39. 19 0
      src/logger/internal/logger.go
  40. 206 0
      src/logger/internal/logger_method.go
  41. 91 0
      src/logger/internal/set_method.go
  42. 27 0
      src/logger/internal/variable.go
  43. 142 0
      src/logger/logger_export.go
  44. 25 0
      src/logger/loglevel/level.go
  45. 47 0
      src/logger/set_export.go
  46. 87 0
      src/logger/write/combiningwriter/writer.go
  47. 119 0
      src/logger/write/datefilewriter/writer.go
  48. 16 0
      src/logger/write/export.go
  49. 66 0
      src/logger/write/filewriter/writer.go
  50. 21 0
      src/logger/write/warp_writer.go
  51. 29 0
      src/logger/write/warp_writer_closer.go
  52. 108 0
      src/mainfunc/lion/v1/main.go
  53. 84 0
      src/mainfunc/tiger/v1/main.go
  54. 251 0
      src/server/controller/controller.go
  55. 118 0
      src/server/example1/server.go
  56. 118 0
      src/server/example2/server.go
  57. 128 0
      src/server/servercontext/context.go
  58. 19 0
      src/server/serverinterface/server.go
  59. 36 0
      src/signalwatcher/signal.go
  60. 89 0
      src/utils/exitutils/exit.go
  61. 25 0
      src/utils/filesystemutils/dir.go
  62. 18 0
      src/utils/filesystemutils/exists.go
  63. 25 0
      src/utils/filesystemutils/file.go
  64. 27 0
      src/utils/filesystemutils/path.go
  65. 77 0
      src/utils/formatutils/export.go
  66. 34 0
      src/utils/osutils/export.go
  67. 16 0
      src/utils/reflectutils/export.go
  68. 16 0
      src/utils/reutils/export.go
  69. 29 0
      src/utils/runtimeutils/runtime.go
  70. 11 0
      src/utils/sliceutils/slice.go
  71. 163 0
      src/utils/strconvutils/time.go
  72. 83 0
      src/utils/typeutils/stringbool.go
  73. 24 0
      test_self/config.yaml

+ 71 - 0
.gitattributes

@@ -0,0 +1,71 @@
+# 设置所有文本文件使用LF作为行尾
+* text=auto eol=lf
+
+# 排除图片资源文件(保持之前的配置)
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.gif binary
+
+# 排除PDF文档(保持之前的配置)
+*.pdf binary
+
+# 排除视频资源文件(保持之前的配置)
+*.mp4 binary
+*.avi binary
+*.mkv binary
+*.mov binary
+*.wmv binary
+
+# 排除字体资源文件
+*.ttf binary
+*.otf binary
+*.woff binary
+*.woff2 binary
+*.eot binary
+
+# 排除音频文件
+*.mp3 binary
+*.wav binary
+*.aac binary
+*.flac binary
+
+# 排除压缩文件和存档
+*.zip binary
+*.tar.gz binary
+*.rar binary
+
+# 排除数据库文件
+*.db binary
+
+# 排除机器学习模型文件
+*.pb binary
+*.ckpt binary
+*.pth binary
+
+# 排除编译后的二进制可执行文件
+*.exe binary
+*.dll binary
+*.so binary
+*.dylib binary
+
+# 排除虚拟机和容器镜像
+*.dockerimage binary
+*.box binary
+*.vmdk binary
+*.vhd binary
+*.ova binary
+
+# 排除游戏资源文件
+*.obj binary
+*.fbx binary
+*.blend binary
+*.dds binary
+*.3ds binary
+
+# 排除日志文件
+*.log binary
+
+# 排除缓存和临时文件
+*.cache binary
+*.tmp binary

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+.idea
+etc
+tmp
+cert
+
+*.exe
+*.out
+
+.DS_Store
+
+testdata
+remote-testdata
+
+pkg
+
+go-remote.sh

+ 8 - 0
LICENSE

@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright © 2025 宋子桓(Song Zihuan)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 1 - 0
NAME

@@ -0,0 +1 @@
+Backend-Server-Template

+ 40 - 0
README.md

@@ -0,0 +1,40 @@
+# Golang 后端服务 模板程序 - 简单版
+
+使用 `Golang` 实现的后端服务模板程序。
+
+## 介绍
+
+本模板程序旨在实现一个 `Golang` 的后端服务,可以是 `Http` 也可以是其他。为了完成这个目的,我设计了一部分基础套件。
+
+* 日志(支持投递到标准输出、文件、日期切割的文件、自定义输出、多输出合并)
+* 命令行参数(支持`string`、`bool`、`uint`、`int`)
+* 配置文件(支持`json`和`yaml`格式,也可以自定义解析器)
+* 退出信号量捕获(在`posix`系统上可以使用信号量捕获退出信号,并做清理操作。在`win32`上,命令行的`ctrl+c`也可被捕获,但当程序作为服务在后台运行时,相关停止、重启操作暂未内捕获)
+* 全局变量和资源(打包了`Version`、`License`、`Name`、`Report`等变量)
+* 服务模式(可使用控制单元启动多服务,或直接启动单服务)
+
+## 入口
+
+入口文件在`src/cmd`下,目前分别有两个程序:`lionv1`和`tigerv1`。
+
+* `lionv1` 是使用控制单元的多服务演示程序。
+* `tigerv1` 是直接运行服务的单服务演示程序。
+
+入口程序不直接包含太多的实际代码,真正的`main`函数位于`src\mainfunc`下。
+程序的返回值代表程序的`Exit Code`。
+一般采用`0`表示正确结束。
+
+## 编译
+
+使用`go build`进行正常编译即可。
+
+日后支持:
+
+* 添加编译参数,允许编译时注入编译时间和`git commit id`。
+
+### 运行
+执行编译好的可执行文件即可。具体命令行参数可参见上文。注意编译时选择的目标平台要与运行平台一致。
+
+## 协议
+本软件基于 [MIT LICENSE](/LICENSE) 发布。
+了解更多关于 MIT LICENSE , 请 [点击此处](https://mit-license.song-zh.com) 。

+ 13 - 0
REEPORT

@@ -0,0 +1,13 @@
+How to report of BackendServerTemplate
+
+Author: 宋子桓(Song Zihuan)
+Author Github: https://github.com/SongZihuan
+Author Website: https://song-zh.com
+Author Email: contact@song-zh.com
+
+Github: https://github.com/SongZihuan/BackendServerTemplate
+Github Issues: https://github.com/SongZihuan/BackendServerTemplate/issues
+
+Report: You can report issues and contact the author through Github Issues or Author Email.
+Quality Assurance: If you only have the license in the LICENSE file in the root directory of this project, you will not get any quality assurance. But the author is happy to solve the problem for you, unless this project has been archived as read-only.
+Other Fork versions: Please contact the author of the Fork version for assistance.

+ 1 - 0
VERSION

@@ -0,0 +1 @@
+v1.0.0

+ 11 - 0
go.mod

@@ -0,0 +1,11 @@
+module github.com/SongZihuan/BackendServerTemplate
+
+go 1.23.0
+
+toolchain go1.23.4
+
+require (
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	golang.org/x/sys v0.31.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 8 - 0
go.sum

@@ -0,0 +1,8 @@
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 36 - 0
resource.go

@@ -0,0 +1,36 @@
+package resource
+
+import (
+	_ "embed"
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/reutils"
+	"strings"
+)
+
+//go:embed VERSION
+var Version string
+
+var SemanticVersioning string
+
+//go:embed LICENSE
+var License string
+
+//go:embed REEPORT
+var Report string
+
+//go:embed NAME
+var Name string
+
+func init() {
+	if Name == "" {
+		Name = "Backend-Server"
+	}
+
+	Version = strings.ToLower(Version)
+	ver := strings.TrimPrefix(Version, "v")
+	if !reutils.IsSemanticVersion(ver) {
+		panic(fmt.Sprintf("%s is not a semantic versioning.", Version))
+	} else {
+		SemanticVersioning = ver
+	}
+}

+ 14 - 0
src/cmd/lionv1/main.go

@@ -0,0 +1,14 @@
+// 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 main
+
+import (
+	lionv1 "github.com/SongZihuan/BackendServerTemplate/src/mainfunc/lion/v1"
+	"os"
+)
+
+func main() {
+	os.Exit(lionv1.MainV1())
+}

+ 14 - 0
src/cmd/tigerv1/main.go

@@ -0,0 +1,14 @@
+// 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 main
+
+import (
+	tigerv1 "github.com/SongZihuan/BackendServerTemplate/src/mainfunc/tiger/v1"
+	"os"
+)
+
+func main() {
+	os.Exit(tigerv1.MainV1())
+}

+ 118 - 0
src/commandlineargs/define_data_type.go

@@ -0,0 +1,118 @@
+// 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 commandlineargs
+
+import (
+	"flag"
+	"fmt"
+	resource "github.com/SongZihuan/BackendServerTemplate"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
+)
+
+type CommandLineArgsDataType struct {
+	flagReady  bool
+	flagSet    bool
+	flagParser bool
+
+	NameData  string
+	NameName  string
+	NameUsage string
+
+	HelpData  bool
+	HelpName  string
+	HelpUsage string
+
+	VersionData  bool
+	VersionName  string
+	VersionUsage string
+
+	LicenseData  bool
+	LicenseName  string
+	LicenseUsage string
+
+	ReportData  bool
+	ReportName  string
+	ReportUsage string
+
+	ConfigFileData  string
+	ConfigFileName  string
+	ConfigFileUsage string
+
+	ConfigOutputFileData  string
+	ConfigOutputFileName  string
+	ConfigOutputFileUsage string
+
+	Usage string
+}
+
+func initData() {
+	CommandLineArgsData = CommandLineArgsDataType{
+		flagReady:  false,
+		flagSet:    false,
+		flagParser: false,
+
+		NameData:  "", // 默认值为空,具体Name为什么则由config决定
+		NameName:  "name",
+		NameUsage: fmt.Sprintf("Set the name of the running program, the default is %s.", resource.Name),
+
+		HelpData:  false,
+		HelpName:  "help",
+		HelpUsage: fmt.Sprintf("Show usage of %s. If this option is set, the backend service will not run.", osutils.GetArgs0Name()),
+
+		VersionData:  false,
+		VersionName:  "version",
+		VersionUsage: fmt.Sprintf("Show version of %s. If this option is set, the backend service will not run.", osutils.GetArgs0Name()),
+
+		LicenseData:  false,
+		LicenseName:  "license",
+		LicenseUsage: fmt.Sprintf("Show license of %s. If this option is set, the backend service will not run.", osutils.GetArgs0Name()),
+
+		ReportData:  false,
+		ReportName:  "report",
+		ReportUsage: fmt.Sprintf("Show how to report questions/errors of %s. If this option is set, the backend service will not run.", osutils.GetArgs0Name()),
+
+		ConfigFileData:  "config.yaml",
+		ConfigFileName:  "config",
+		ConfigFileUsage: fmt.Sprintf("%s", "The location of the running configuration file of the backend service. The option is a string, the default value is config.yaml in the running directory."),
+
+		ConfigOutputFileData:  "",
+		ConfigOutputFileName:  "output-config",
+		ConfigOutputFileUsage: fmt.Sprintf("%s", "Reverse output configuration file (can be used for corresponding inspection work), the default value is empty (no output)."),
+
+		Usage: "",
+	}
+
+	CommandLineArgsData.ready()
+}
+
+func (d *CommandLineArgsDataType) setFlag() {
+	if d.isFlagSet() {
+		return
+	}
+
+	flag.StringVar(&d.NameData, d.NameName, d.NameData, d.NameUsage)
+	flag.StringVar(&d.NameData, d.NameName[0:1], d.NameData, d.NameUsage)
+
+	flag.BoolVar(&d.HelpData, d.HelpName, d.HelpData, d.HelpUsage)
+	flag.BoolVar(&d.HelpData, d.HelpName[0:1], d.HelpData, d.HelpUsage)
+
+	flag.BoolVar(&d.VersionData, d.VersionName, d.VersionData, d.VersionUsage)
+	flag.BoolVar(&d.VersionData, d.VersionName[0:1], d.VersionData, d.VersionUsage)
+
+	flag.BoolVar(&d.LicenseData, d.LicenseName, d.LicenseData, d.LicenseUsage)
+	flag.BoolVar(&d.LicenseData, d.LicenseName[0:1], d.LicenseData, d.LicenseUsage)
+
+	flag.BoolVar(&d.ReportData, d.ReportName, d.ReportData, d.ReportUsage)
+	flag.BoolVar(&d.ReportData, d.ReportName[0:1], d.ReportData, d.ReportUsage)
+
+	flag.StringVar(&d.ConfigFileData, d.ConfigFileName, d.ConfigFileData, d.ConfigFileUsage)
+	flag.StringVar(&d.ConfigFileData, d.ConfigFileName[0:1], d.ConfigFileData, d.ConfigFileUsage)
+
+	flag.Usage = func() {
+		_, _ = d.PrintUsage()
+	}
+
+	d.flagSet = true
+}

+ 79 - 0
src/commandlineargs/export.go

@@ -0,0 +1,79 @@
+package commandlineargs
+
+import (
+	"fmt"
+	"io"
+)
+
+var StopRun = fmt.Errorf("stop run and exit with success")
+
+var isReady = false
+
+func InitCommandLineArgsParser(output io.Writer) (err error) {
+	if IsReady() {
+		return nil
+	}
+
+	defer func() {
+		if e := recover(); e != nil {
+			err = fmt.Errorf("%v", e)
+		}
+	}()
+
+	isReady = false
+
+	initData()
+
+	SetOutput(output)
+
+	isReady = true
+
+	err = helpInfoRun()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func IsReady() bool {
+	return CommandLineArgsData.isReady() && isReady
+}
+
+func helpInfoRun() error {
+	var stopFlag = false
+
+	if Version() {
+		_, _ = PrintVersion()
+		stopFlag = true
+	}
+
+	if License() {
+		if stopFlag {
+			_, _ = PrintLF()
+		}
+		_, _ = PrintLicense()
+		stopFlag = true
+	}
+
+	if Report() {
+		if stopFlag {
+			_, _ = PrintLF()
+		}
+		_, _ = PrintReport()
+	}
+
+	if Help() {
+		if stopFlag {
+			_, _ = PrintLF()
+		}
+		_, _ = PrintUsage()
+		stopFlag = true
+	}
+
+	if stopFlag {
+		return StopRun
+	}
+
+	return nil
+}

+ 43 - 0
src/commandlineargs/export_data.go

@@ -0,0 +1,43 @@
+// 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 commandlineargs
+
+var CommandLineArgsData CommandLineArgsDataType
+
+func (d *CommandLineArgsDataType) Name() string {
+	if !d.isReady() {
+		panic("flag not ready")
+	}
+
+	return d.NameData
+}
+
+func (d *CommandLineArgsDataType) Help() bool {
+	if !d.isReady() {
+		panic("flag not ready")
+	}
+
+	return d.HelpData
+}
+
+func (d *CommandLineArgsDataType) Version() bool {
+	return getData(d, d.VersionData)
+}
+
+func (d *CommandLineArgsDataType) License() bool {
+	return getData(d, d.LicenseData)
+}
+
+func (d *CommandLineArgsDataType) Report() bool {
+	return getData(d, d.ReportData)
+}
+
+func (d *CommandLineArgsDataType) ConfigFile() string {
+	return getData(d, d.ConfigFileData)
+}
+
+func (d *CommandLineArgsDataType) OutPutConfig() string {
+	return getData(d, d.ConfigOutputFileData)
+}

+ 19 - 0
src/commandlineargs/export_method.go

@@ -0,0 +1,19 @@
+// 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 commandlineargs
+
+import (
+	"flag"
+	"io"
+	"os"
+)
+
+func (d *CommandLineArgsDataType) SetOutput(writer io.Writer) {
+	if writer == nil {
+		writer = os.Stdout
+	}
+
+	flag.CommandLine.SetOutput(writer)
+}

+ 59 - 0
src/commandlineargs/export_printer.go

@@ -0,0 +1,59 @@
+// 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 commandlineargs
+
+import (
+	"flag"
+	"fmt"
+	resource "github.com/SongZihuan/BackendServerTemplate"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/formatutils"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
+	"io"
+)
+
+func (d *CommandLineArgsDataType) FprintUsage(writer io.Writer) (int, error) {
+	return fmt.Fprintf(writer, "%s\n", d.Usage)
+}
+
+func (d *CommandLineArgsDataType) PrintUsage() (int, error) {
+	return d.FprintUsage(flag.CommandLine.Output())
+}
+
+func (d *CommandLineArgsDataType) FprintVersion(writer io.Writer) (int, error) {
+	version := formatutils.FormatTextToWidth(fmt.Sprintf("Version of %s: %s", osutils.GetArgs0Name(), resource.Version), formatutils.NormalConsoleWidth)
+	return fmt.Fprintf(writer, "%s\n", version)
+}
+
+func (d *CommandLineArgsDataType) PrintVersion() (int, error) {
+	return d.FprintVersion(flag.CommandLine.Output())
+}
+
+func (d *CommandLineArgsDataType) FprintLicense(writer io.Writer) (int, error) {
+	title := formatutils.FormatTextToWidth(fmt.Sprintf("License of %s:", osutils.GetArgs0Name()), formatutils.NormalConsoleWidth)
+	license := formatutils.FormatTextToWidth(resource.License, formatutils.NormalConsoleWidth)
+	return fmt.Fprintf(writer, "%s\n%s\n", title, license)
+}
+
+func (d *CommandLineArgsDataType) PrintLicense() (int, error) {
+	return d.FprintLicense(flag.CommandLine.Output())
+}
+
+func (d *CommandLineArgsDataType) FprintReport(writer io.Writer) (int, error) {
+	// 不需要title
+	report := formatutils.FormatTextToWidth(resource.Report, formatutils.NormalConsoleWidth)
+	return fmt.Fprintf(writer, "%s\n", report)
+}
+
+func (d *CommandLineArgsDataType) PrintReport() (int, error) {
+	return d.FprintReport(flag.CommandLine.Output())
+}
+
+func (d *CommandLineArgsDataType) FprintLF(writer io.Writer) (int, error) {
+	return fmt.Fprintf(writer, "\n")
+}
+
+func (d *CommandLineArgsDataType) PrintLF() (int, error) {
+	return d.FprintLF(flag.CommandLine.Output())
+}

+ 200 - 0
src/commandlineargs/internal_data_type_method.go

@@ -0,0 +1,200 @@
+package commandlineargs
+
+import (
+	"flag"
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/formatutils"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/reflectutils"
+	"reflect"
+	"strings"
+)
+
+const OptionIdent = "  "
+const OptionPrefix = "--"
+const UsagePrefixWidth = 10
+
+func (d *CommandLineArgsDataType) ready() {
+	if d.isReady() {
+		return
+	}
+
+	d.setFlag()
+	d.writeUsage()
+	d.parser()
+	d.flagReady = true
+}
+
+func (d *CommandLineArgsDataType) writeUsage() {
+	if len(d.Usage) != 0 {
+		return
+	}
+
+	var result strings.Builder
+	result.WriteString(formatutils.FormatTextToWidth(fmt.Sprintf("Usage of %s:", osutils.GetArgs0Name()), formatutils.NormalConsoleWidth))
+	result.WriteString("\n")
+
+	val := reflect.ValueOf(*d)
+	typ := val.Type()
+
+	for i := 0; i < val.NumField(); i++ {
+		field := typ.Field(i)
+
+		if !strings.HasSuffix(field.Name, "Data") {
+			continue
+		}
+
+		option := field.Name[:len(field.Name)-4]
+		optionName := ""
+		optionShortName := ""
+		optionUsage := ""
+
+		if reflectutils.HasFieldByReflect(typ, option+"Name") {
+			var ok bool
+			optionName, ok = val.FieldByName(option + "Name").Interface().(string)
+			if !ok {
+				panic("can not get option name")
+			}
+		}
+
+		if reflectutils.HasFieldByReflect(typ, option+"ShortName") {
+			var ok bool
+			optionShortName, ok = val.FieldByName(option + "ShortName").Interface().(string)
+			if !ok {
+				panic("can not get option short name")
+			}
+		} else if len(optionName) > 1 {
+			optionShortName = optionName[:1]
+		}
+
+		if reflectutils.HasFieldByReflect(typ, option+"Usage") {
+			var ok bool
+			optionUsage, ok = val.FieldByName(option + "Usage").Interface().(string)
+			if !ok {
+				panic("can not get option usage")
+			}
+		}
+
+		var title string
+		var title1 string
+		var title2 string
+		if field.Type.Kind() == reflect.Bool {
+			var optionData bool
+			if reflectutils.HasFieldByReflect(typ, option+"Data") {
+				var ok bool
+				optionData, ok = val.FieldByName(option + "Data").Interface().(bool)
+				if !ok {
+					panic("can not get option data")
+				}
+			}
+
+			if optionData == true {
+				panic("bool option can not be true")
+			}
+
+			if optionName != "" {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(optionName, formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+
+			if optionShortName != "" {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(optionShortName, formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+		} else if field.Type.Kind() == reflect.String {
+			var optionData string
+			if reflectutils.HasFieldByReflect(typ, option+"Data") {
+				var ok bool
+				optionData, ok = val.FieldByName(option + "Data").Interface().(string)
+				if !ok {
+					panic("can not get option data")
+				}
+			}
+
+			if optionName != "" && optionData != "" {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s string, default: '%s'", optionName, optionData), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionName != "" && optionData == "" {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s string", optionName), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+
+			if optionShortName != "" && optionData != "" {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s string, default: '%s'", optionShortName, optionData), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionShortName != "" && optionData == "" {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s string", optionShortName), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+		} else if field.Type.Kind() == reflect.Uint || field.Type.Kind() == reflect.Int {
+			var optionData uint
+			if reflectutils.HasFieldByReflect(typ, option+"Data") {
+				var ok bool
+				optionData, ok = val.FieldByName(option + "Data").Interface().(uint)
+				if !ok {
+					panic("can not get option data")
+				}
+			}
+
+			if optionName != "" && optionData != 0 {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s number, default: %d", optionName, optionData), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionName != "" && optionData == 0 {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s number", optionName), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+
+			if optionShortName != "" && optionData != 0 {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, formatutils.FormatTextToWidth(fmt.Sprintf("%s number, default: %d", optionShortName, optionData), formatutils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionShortName != "" && optionData == 0 {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, 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()))
+		}
+
+		if title1 == "" && title2 == "" {
+			continue
+		} else if title1 != "" && title2 == "" {
+			title = title1
+		} else if title1 == "" {
+			title = title2
+		} else {
+			title = fmt.Sprintf("%s\n%s", title1, title2)
+		}
+
+		result.WriteString(title)
+		result.WriteString("\n")
+
+		usage := formatutils.FormatTextToWidthAndPrefix(optionUsage, UsagePrefixWidth, formatutils.NormalConsoleWidth)
+		result.WriteString(usage)
+		result.WriteString("\n\n")
+	}
+
+	d.Usage = strings.TrimRight(result.String(), "\n")
+}
+
+func (d *CommandLineArgsDataType) parser() {
+	if d.flagParser {
+		return
+	}
+
+	if !d.isFlagSet() {
+		panic("flag not set")
+	}
+
+	flag.Parse()
+	d.flagParser = true
+}
+
+func (d *CommandLineArgsDataType) isReady() bool {
+	return d.isFlagSet() && d.isFlagParser() && d.flagReady
+}
+
+func (d *CommandLineArgsDataType) isFlagSet() bool {
+	return d.flagSet
+}
+
+func (d *CommandLineArgsDataType) isFlagParser() bool {
+	return d.flagParser
+}
+
+func getData[T any](d *CommandLineArgsDataType, data T) T { // 泛型函数无法作为 “方法” 只能作为函数
+	if !d.isReady() {
+		panic("flag not ready")
+	}
+
+	return data
+}

+ 62 - 0
src/commandlineargs/main.go

@@ -0,0 +1,62 @@
+package commandlineargs
+
+import (
+	"io"
+	"os"
+)
+
+func Name() string {
+	return CommandLineArgsData.NameData
+}
+
+func Help() bool {
+	return CommandLineArgsData.Help()
+}
+
+func PrintUsage() (int, error) {
+	return CommandLineArgsData.PrintUsage()
+}
+
+func PrintVersion() (int, error) {
+	return CommandLineArgsData.PrintVersion()
+}
+
+func PrintLicense() (int, error) {
+	return CommandLineArgsData.PrintLicense()
+}
+
+func PrintReport() (int, error) {
+	return CommandLineArgsData.PrintReport()
+}
+
+func PrintLF() (int, error) {
+	return CommandLineArgsData.PrintLF()
+}
+
+func Version() bool {
+	return CommandLineArgsData.Version()
+}
+
+func License() bool {
+	return CommandLineArgsData.License()
+}
+
+func Report() bool {
+	return CommandLineArgsData.Report()
+}
+
+func ConfigFile() string {
+	return CommandLineArgsData.ConfigFile()
+}
+
+func OutputConfigFile() string {
+	return CommandLineArgsData.ConfigFile()
+}
+
+func SetOutput(writer io.Writer) {
+	if writer == nil {
+		writer = os.Stdout
+	}
+
+	CommandLineArgsData.SetOutput(writer)
+}

+ 109 - 0
src/config/base_data.go

@@ -0,0 +1,109 @@
+package config
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+)
+
+type ConfigData struct {
+	GlobalConfig `json:",inline" yaml:",inline"`
+	Logger       LoggerConfig `json:"logger" yaml:"logger"`
+	Signal       SignalConfig `json:"signal" yaml:"signal"`
+	Server       ServerConfig `json:"server" yaml:"server"`
+}
+
+func (d *ConfigData) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
+	cfgErr := d.GlobalConfig.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Logger.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Signal.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Server.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}
+
+func (d *ConfigData) setDefault(c *configInfo) (err configerror.Error) {
+	cfgErr := d.GlobalConfig.process(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Logger.setDefault(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Signal.setDefault(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Server.setDefault(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}
+
+func (d *ConfigData) check(c *configInfo) (err configerror.Error) {
+	cfgErr := d.GlobalConfig.process(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Logger.check(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Signal.check(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Server.check(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}
+
+func (d *ConfigData) process(c *configInfo) (err configerror.Error) {
+	cfgErr := d.GlobalConfig.process(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Logger.process(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Signal.process(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.Server.process(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}

+ 123 - 0
src/config/config.go

@@ -0,0 +1,123 @@
+package config
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/filesystemutils"
+)
+
+type configInfo struct {
+	data *ConfigData
+
+	ready    bool
+	file     string
+	provider configparser.ConfigParserProvider
+}
+
+func newConfig(filePath string, provider configparser.ConfigParserProvider) (*configInfo, configerror.Error) {
+	if filePath == "" {
+		panic("config path is empty")
+	}
+
+	configFilePath, err := filesystemutils.CleanFilePathAbs(filePath)
+	if err != nil {
+		return nil, configerror.NewErrorf("change config file path (%s) to abs error: %s", configFilePath, err.Error())
+	}
+
+	if provider == nil {
+		provider = configparser.NewYamlProvider()
+	}
+
+	if !provider.CanUTF8() {
+		return nil, configerror.NewErrorf("config file parser provider new support UTF-8")
+	}
+
+	data := new(ConfigData)
+	dataInitErr := data.init(configFilePath, provider)
+	if dataInitErr != nil && dataInitErr.IsError() {
+		return nil, dataInitErr
+	}
+
+	return &configInfo{
+		data: data,
+
+		ready:    false,
+		file:     configFilePath,
+		provider: provider,
+	}, nil
+}
+
+func (c *configInfo) init() (err configerror.Error) {
+	if c.ready { // 使用IsReady而不是isReady,确保上锁
+		return configerror.NewErrorf("config is ready")
+	}
+
+	err = c.provider.ReadFile(c.file)
+	if err != nil && err.IsError() {
+		return err
+	}
+
+	err = c.provider.ParserFile(c.data)
+	if err != nil && err.IsError() {
+		return err
+	}
+
+	err = c.data.setDefault(c)
+	if err != nil && err.IsError() {
+		return err
+	}
+
+	err = c.data.check(c)
+	if err != nil && err.IsError() {
+		return err
+	}
+
+	err = c.data.process(c)
+	if err != nil && err.IsError() {
+		return err
+	}
+
+	c.ready = true
+	return nil
+}
+
+func (c *configInfo) output(filePath string) configerror.Error {
+	if !c.ready {
+		return configerror.NewErrorf("config is not ready")
+	}
+
+	if filePath == "" {
+		return configerror.NewErrorf("config output file path is empty")
+	}
+
+	err := c.provider.WriteFile(filePath, c.data)
+	if err != nil && err.IsError() {
+		return err
+	}
+
+	return nil
+}
+
+func (c *configInfo) GetData() (*ConfigData, configerror.Error) {
+	if !c.ready {
+		return nil, configerror.NewErrorf("config is not ready")
+	}
+
+	return c.data, nil
+}
+
+func (c *configInfo) Data() *ConfigData {
+	if !c.ready {
+		panic("config is not ready")
+	}
+
+	return c.data
+}
+
+func (c *configInfo) ConfigFilePath() string {
+	return c.file
+}
+
+func (c *configInfo) Output(filePath string) configerror.Error {
+	return c.output(filePath)
+}

+ 29 - 0
src/config/configerror/error.go

@@ -0,0 +1,29 @@
+package configerror
+
+type configError struct {
+	msg     string
+	isError bool
+}
+
+func (e *configError) Msg() string {
+	if e.isError {
+		return e.Error()
+	}
+	return e.Warning()
+}
+
+func (e *configError) Error() string {
+	return e.msg
+}
+
+func (e *configError) Warning() string {
+	return e.msg
+}
+
+func (e *configError) IsError() bool {
+	return e.isError
+}
+
+func (e *configError) IsWarning() bool {
+	return !e.isError
+}

+ 40 - 0
src/config/configerror/export.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 configerror
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+)
+
+type Error interface {
+	Msg() string
+	Error() string
+	Warning() string
+	IsError() bool
+	IsWarning() bool
+}
+
+func NewErrorf(format string, args ...any) Error {
+	msg := fmt.Sprintf(format, args...)
+
+	logger.Errorf("config error: %s", msg)
+	return &configError{msg: msg, isError: true}
+}
+
+func NewWarningf(format string, args ...any) Error {
+	msg := fmt.Sprintf(format, args...)
+
+	logger.Errorf("config warning: %s", msg)
+	return &configError{msg: msg, isError: false}
+}
+
+func _test() { // 测试函数,确保 Error 符合 error
+	var a Error
+	var b error
+
+	b = a
+	_ = b
+}

+ 90 - 0
src/config/configparser/json.go

@@ -0,0 +1,90 @@
+// 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 configparser
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"os"
+	"reflect"
+)
+
+type JsonProvider struct {
+	HasRead  bool
+	FileData []byte
+}
+
+func NewJsonProvider() *JsonProvider {
+	return &JsonProvider{
+		HasRead:  false,
+		FileData: nil,
+	}
+}
+
+func (j *JsonProvider) CanUTF8() bool {
+	return true
+}
+
+func (j *JsonProvider) ReadFile(filepath string) configerror.Error {
+	if j.HasRead {
+		return configerror.NewErrorf("config file has been read")
+	}
+
+	data, err := os.ReadFile(filepath)
+	if err != nil {
+		return configerror.NewErrorf(fmt.Sprintf("read file error: %s", err.Error()))
+	}
+
+	j.FileData = data
+	j.HasRead = true
+
+	return nil
+}
+
+func (j *JsonProvider) ParserFile(target any) configerror.Error {
+	if !j.HasRead || j.FileData == nil {
+		return configerror.NewErrorf("config file has not been read")
+	}
+
+	if reflect.TypeOf(target).Kind() != reflect.Pointer {
+		return configerror.NewErrorf("target must be a pointer")
+	}
+
+	err := json.Unmarshal(j.FileData, target)
+	if err != nil {
+		return configerror.NewErrorf("json parser error: %s", err.Error())
+	}
+
+	return nil
+}
+
+func (j *JsonProvider) WriteFile(filepath string, src any) configerror.Error {
+	if !j.HasRead {
+		return configerror.NewErrorf("config file has not been read")
+	}
+
+	if reflect.TypeOf(src).Kind() != reflect.Pointer {
+		return configerror.NewErrorf("target must be a pointer")
+	}
+
+	target, err := json.Marshal(src)
+	if err != nil {
+		return configerror.NewErrorf("json marshal error: %s", err.Error())
+	}
+
+	err = os.WriteFile(filepath, target, 0644)
+	if err != nil {
+		return configerror.NewErrorf("write file error: %s", err.Error())
+	}
+
+	return nil
+}
+
+func _testJson() {
+	var a ConfigParserProvider
+	a = &JsonProvider{}
+	_ = a
+}

+ 14 - 0
src/config/configparser/parser.go

@@ -0,0 +1,14 @@
+// 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 configparser
+
+import "github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+
+type ConfigParserProvider interface {
+	CanUTF8() bool // Must return true
+	ReadFile(filepath string) configerror.Error
+	ParserFile(target any) configerror.Error
+	WriteFile(filepath string, data any) configerror.Error
+}

+ 90 - 0
src/config/configparser/yaml.go

@@ -0,0 +1,90 @@
+// 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 configparser
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"gopkg.in/yaml.v3"
+	"os"
+	"reflect"
+)
+
+type YamlProvider struct {
+	HasRead  bool
+	FileData []byte
+}
+
+func NewYamlProvider() *YamlProvider {
+	return &YamlProvider{
+		HasRead:  false,
+		FileData: nil,
+	}
+}
+
+func (y *YamlProvider) CanUTF8() bool {
+	return true
+}
+
+func (y *YamlProvider) ReadFile(filepath string) configerror.Error {
+	if y.HasRead {
+		return configerror.NewErrorf("config file has been read")
+	}
+
+	data, err := os.ReadFile(filepath)
+	if err != nil {
+		return configerror.NewErrorf(fmt.Sprintf("read file error: %s", err.Error()))
+	}
+
+	y.FileData = data
+	y.HasRead = true
+
+	return nil
+}
+
+func (y *YamlProvider) ParserFile(target any) configerror.Error {
+	if !y.HasRead {
+		return configerror.NewErrorf("config file has not been read")
+	}
+
+	if reflect.TypeOf(target).Kind() != reflect.Pointer {
+		return configerror.NewErrorf("target must be a pointer")
+	}
+
+	err := yaml.Unmarshal(y.FileData, target)
+	if err != nil {
+		return configerror.NewErrorf("yaml unmarshal error: %s", err.Error())
+	}
+
+	return nil
+}
+
+func (y *YamlProvider) WriteFile(filepath string, src any) configerror.Error {
+	if !y.HasRead {
+		return configerror.NewErrorf("config file has not been read")
+	}
+
+	if reflect.TypeOf(src).Kind() != reflect.Pointer {
+		return configerror.NewErrorf("target must be a pointer")
+	}
+
+	target, err := yaml.Marshal(src)
+	if err != nil {
+		return configerror.NewErrorf("yaml marshal error: %s", err.Error())
+	}
+
+	err = os.WriteFile(filepath, target, 0644)
+	if err != nil {
+		return configerror.NewErrorf("write file error: %s", err.Error())
+	}
+
+	return nil
+}
+
+func _testYaml() {
+	var a ConfigParserProvider
+	a = &YamlProvider{}
+	_ = a
+}

+ 25 - 0
src/config/define_data_example.go

@@ -0,0 +1,25 @@
+package config
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+)
+
+type ExampleData struct {
+}
+
+func (d *ExampleData) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
+	return nil
+}
+
+func (d *ExampleData) setDefault(c *configInfo) (err configerror.Error) {
+	return nil
+}
+
+func (d *ExampleData) check(c *configInfo) (err configerror.Error) {
+	return nil
+}
+
+func (d *ExampleData) process(c *configInfo) (cfgErr configerror.Error) {
+	return nil
+}

+ 109 - 0
src/config/export.go

@@ -0,0 +1,109 @@
+// 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 config
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"os"
+	"path"
+	"strings"
+)
+
+var config *configInfo
+
+type ConfigOption struct {
+	ConfigFilePath string
+	Provider       configparser.ConfigParserProvider
+}
+
+func (opt *ConfigOption) setDefault() error {
+	if opt.ConfigFilePath == "" {
+		wd, err := os.Getwd()
+		if err != nil {
+			logger.Errorf("can not get work directory: %s", err.Error())
+			return err
+		}
+
+		opt.ConfigFilePath = path.Join(wd, "config.yaml")
+		opt.Provider = configparser.NewYamlProvider()
+	}
+
+	if opt.Provider == nil {
+		if strings.HasSuffix(opt.ConfigFilePath, ".yaml") || strings.HasSuffix(opt.ConfigFilePath, ".yml") {
+			opt.Provider = configparser.NewYamlProvider()
+		} else if strings.HasSuffix(opt.ConfigFilePath, ".json") {
+			opt.Provider = configparser.NewJsonProvider()
+		} else {
+			opt.Provider = configparser.NewYamlProvider()
+		}
+	}
+
+	return nil
+}
+
+func InitConfig(opt *ConfigOption) error {
+	if config != nil {
+		return fmt.Errorf("config already init")
+	}
+
+	if opt == nil {
+		opt = new(ConfigOption)
+	}
+
+	err := opt.setDefault()
+	if err != nil {
+		return err
+	}
+
+	_cfg, cfgErr := newConfig(opt.ConfigFilePath, opt.Provider)
+	if cfgErr != nil && cfgErr.IsError() {
+		return cfgErr
+	}
+
+	cfgErr = _cfg.init()
+	if cfgErr != nil && cfgErr.IsError() {
+		return cfgErr
+	}
+
+	config = _cfg
+	return nil
+}
+
+func GetData() (*ConfigData, configerror.Error) {
+	if config == nil {
+		panic("config is not ready")
+	}
+
+	data, err := config.GetData()
+	if err != nil && err.IsError() {
+		panic(err.Error())
+	}
+
+	return data, nil
+}
+
+func Data() *ConfigData {
+	if config == nil {
+		panic("config is not ready")
+	}
+
+	data, err := config.GetData()
+	if err != nil && err.IsError() {
+		panic(err.Error())
+	}
+
+	return data
+}
+
+func OutputConfig(filePath string) error {
+	if config == nil {
+		panic("config is not ready")
+	}
+
+	return config.output(filePath)
+}

+ 116 - 0
src/config/global_config.go

@@ -0,0 +1,116 @@
+package config
+
+import (
+	resource "github.com/SongZihuan/BackendServerTemplate"
+	"github.com/SongZihuan/BackendServerTemplate/src/commandlineargs"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+	"github.com/SongZihuan/BackendServerTemplate/src/global"
+	"strings"
+	"time"
+)
+
+type RunMode string
+
+const (
+	RunModeDebug   RunMode = "debug"
+	RunModeRelease RunMode = "release"
+	RunModeTest    RunMode = "test"
+)
+
+type GlobalConfig struct {
+	Name     string  `json:"name" yaml:"name"`
+	Mode     RunMode `json:"mode" yaml:"mode"`
+	Timezone string  `json:"time-zone" yaml:"time-zone"`
+}
+
+func (d *GlobalConfig) init(filePath string, provider configparser.ConfigParserProvider) configerror.Error {
+	return nil
+}
+
+func (d *GlobalConfig) setDefault() configerror.Error {
+	if d.Mode == "" {
+		d.Mode = RunModeDebug
+	}
+
+	d.Mode = RunMode(strings.ToLower(string(d.Mode)))
+
+	if d.Timezone == "" {
+		d.Timezone = "local"
+	} else {
+		d.Timezone = strings.ToLower(d.Timezone)
+	}
+
+	return nil
+}
+
+func (d *GlobalConfig) check() configerror.Error {
+	if d.Mode != RunModeDebug && d.Mode != RunModeRelease && d.Mode != RunModeTest {
+		return configerror.NewErrorf("bad mode: %s", d.Mode)
+	}
+
+	return nil
+}
+
+func (d *GlobalConfig) process(c *configInfo) (cfgErr configerror.Error) {
+	if commandlineargs.Name() != "" {
+		global.Name = commandlineargs.Name()
+	} else if d.Name != "" {
+		global.Name = d.Name
+	} else {
+		global.Name = resource.Name
+	}
+
+	d.Name = global.Name
+
+	var location *time.Location
+	if strings.ToLower(d.Timezone) == "utc" {
+		location = time.UTC
+		if location == nil {
+			location = time.Local
+		}
+	} else if strings.ToLower(d.Timezone) == "local" {
+		location = time.Local
+		if location == nil {
+			location = time.UTC
+		}
+	} else {
+		var err error
+		location, err = time.LoadLocation(d.Timezone)
+		if err != nil || location == nil {
+			location = time.UTC
+		}
+
+		if location != nil {
+			location = time.Local
+		}
+	}
+
+	if location == nil {
+		if d.Timezone == "utc" || d.Timezone == "local" {
+			return configerror.NewErrorf("can not get location UTC or Local")
+		}
+
+		return configerror.NewErrorf("can not get location UTC, Local or %s", d.Timezone)
+	} else {
+		global.Location = location
+	}
+
+	return nil
+}
+
+func (d *GlobalConfig) GetRunMode() RunMode {
+	return d.Mode
+}
+
+func (d *GlobalConfig) IsDebug() bool {
+	return d.Mode == RunModeDebug
+}
+
+func (d *GlobalConfig) IsRelease() bool {
+	return d.Mode == RunModeRelease
+}
+
+func (d *GlobalConfig) IsTest() bool {
+	return d.Mode == RunModeTest
+}

+ 109 - 0
src/config/logger_config.go

@@ -0,0 +1,109 @@
+// 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 config
+
+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"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/typeutils"
+)
+
+type LoggerConfig struct {
+	LogName  string               `json:"log-name" yaml:"log-name"`
+	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"`
+}
+
+func (d *LoggerConfig) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
+	cfgErr := d.WarnWriter.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.ErrWriter.init(filePath, provider)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}
+
+func (d *LoggerConfig) setDefault(c *configInfo) (err configerror.Error) {
+	if d.LogLevel == "" {
+		if c.data.GlobalConfig.IsRelease() {
+			d.LogLevel = loglevel.LevelInfo
+			d.LogTag.SetDefaultDisable()
+		} else {
+			d.LogLevel = loglevel.LevelDebug
+			d.LogTag.SetDefaultEnable()
+		}
+	}
+
+	cfgErr := d.WarnWriter.setDefault(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.ErrWriter.setDefault(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}
+
+func (d *LoggerConfig) check(c *configInfo) (err configerror.Error) {
+	cfgErr := d.WarnWriter.check(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.ErrWriter.check(c)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}
+
+func (d *LoggerConfig) process(c *configInfo) (cfgErr configerror.Error) {
+	logName := d.LogName
+	if logName == "" {
+		logName = global.Name
+	}
+
+	err := logger.SetArgs0Name("", global.Name)
+	if err != nil {
+		return configerror.NewErrorf("set log name error: %s", err.Error())
+	}
+
+	err = logger.SetLevel(d.LogLevel)
+	if err != nil {
+		return configerror.NewErrorf("set log level error: %s", err.Error())
+	}
+
+	err = logger.SetLogTag(d.LogTag.IsEnable(false))
+	if err != nil {
+		return configerror.NewErrorf("set log tag error: %s", err.Error())
+	}
+
+	cfgErr = d.WarnWriter.process(c, logger.SetWarnWriter)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	cfgErr = d.ErrWriter.process(c, logger.SetErrWriter)
+	if cfgErr != nil {
+		return cfgErr
+	}
+
+	return nil
+}

+ 96 - 0
src/config/logger_writer_config.go

@@ -0,0 +1,96 @@
+// 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 config
+
+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/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"
+	"io"
+	"os"
+	"strings"
+)
+
+type LoggerWriterConfig struct {
+	WriteToStd          string `json:"write-to-std" yaml:"write-to-std"` // stdout stderr all no
+	WriteToFile         string `json:"write-to-file" yaml:"write-to-file"`
+	WriteToDirWithDate  string `json:"write-to-dir-with-date" yaml:"write-to-dir-with-date"`
+	WriteWithDatePrefix string `json:"write-with-date-prefix" yaml:"write-with-date-prefix"`
+}
+
+func (d *LoggerWriterConfig) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
+	return nil
+}
+
+func (d *LoggerWriterConfig) setDefault(c *configInfo) (err configerror.Error) {
+	d.WriteToStd = strings.ToLower(d.WriteToStd)
+
+	if d.WriteToStd == "" {
+		d.WriteToStd = "stdout"
+	}
+
+	if d.WriteToDirWithDate != "" && d.WriteWithDatePrefix == "" {
+		d.WriteWithDatePrefix = global.Name
+	}
+
+	return nil
+}
+
+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")
+	}
+	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)
+
+	switch d.WriteToStd {
+	case "stdout":
+		writerList = append(writerList, write.ChangeToWriter(os.Stdout))
+	case "stderr":
+		writerList = append(writerList, write.ChangeToWriter(os.Stdout))
+	}
+
+	if d.WriteToFile != "" {
+		fileWriter, err := filewriter.NewFileWriter(d.WriteToFile)
+		if err != nil {
+			return 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)
+		if err != nil {
+			return configerror.NewErrorf("new date file writer (on %s) error error: %s", d.WriteToDirWithDate, err.Error())
+		}
+
+		writerList = append(writerList, dateFileWriter)
+	}
+
+	if len(writerList) == 0 {
+		return nil
+	} 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
+}

+ 38 - 0
src/config/server_config.go

@@ -0,0 +1,38 @@
+package config
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/strconvutils"
+	"time"
+)
+
+type ServerConfig struct {
+	StopWaitTime string `json:"stop-wait-time" yaml:"stop-wait-time"`
+
+	StopWaitTimeDuration time.Duration `yaml:"-"`
+}
+
+func (d *ServerConfig) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
+	return nil
+}
+
+func (d *ServerConfig) setDefault(c *configInfo) (err configerror.Error) {
+	if d.StopWaitTime == "" {
+		d.StopWaitTime = "10s"
+	}
+	return nil
+}
+
+func (d *ServerConfig) check(c *configInfo) (err configerror.Error) {
+	return nil
+}
+
+func (d *ServerConfig) process(c *configInfo) (cfgErr configerror.Error) {
+	d.StopWaitTimeDuration = strconvutils.ReadTimeDuration(d.StopWaitTime)
+	if d.StopWaitTimeDuration < 10*time.Second {
+		_ = configerror.NewWarningf("stop-wait-time (value: %s) is less than the recommended value of 10s", strconvutils.TimeDurationToString(d.StopWaitTimeDuration))
+	}
+
+	return nil
+}

+ 40 - 0
src/config/signal_config.go

@@ -0,0 +1,40 @@
+package config
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configerror"
+	"github.com/SongZihuan/BackendServerTemplate/src/config/configparser"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/typeutils"
+)
+
+type SignalConfig struct {
+	SigIntExit  typeutils.StringBool `json:"sigint-exit" yaml:"sigint-exit"`
+	SigTermExit typeutils.StringBool `json:"sigterm-exit" yaml:"sigterm-exit"`
+	SigHupExit  typeutils.StringBool `json:"sighup-exit" yaml:"sighup-exit"`
+	SigQuitExit typeutils.StringBool `json:"sigquit-exit" yaml:"sigquit-exit"`
+}
+
+func (d *SignalConfig) init(filePath string, provider configparser.ConfigParserProvider) (err configerror.Error) {
+	return nil
+}
+
+func (d *SignalConfig) setDefault(c *configInfo) (err configerror.Error) {
+	d.SigIntExit.SetDefaultEnable()
+	d.SigTermExit.SetDefaultEnable()
+	d.SigHupExit.SetDefaultEnable()
+
+	if c.data.IsRelease() {
+		d.SigIntExit.SetDefaultDisable()
+	} else {
+		d.SigIntExit.SetDefaultEnable()
+	}
+
+	return nil
+}
+
+func (d *SignalConfig) check(c *configInfo) (err configerror.Error) {
+	return nil
+}
+
+func (d *SignalConfig) process(c *configInfo) (cfgErr configerror.Error) {
+	return nil
+}

+ 15 - 0
src/global/variabl.go

@@ -0,0 +1,15 @@
+// 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 global
+
+import (
+	resource "github.com/SongZihuan/BackendServerTemplate"
+	"time"
+)
+
+var Version = resource.Version
+var Name = resource.Name
+
+var Location *time.Location

+ 86 - 0
src/logger/info_export.go

@@ -0,0 +1,86 @@
+// 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 logger
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/internal"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"io"
+)
+
+func GetLevel() loglevel.LoggerLevel {
+	if !internal.IsReady() {
+		return loglevel.LevelDebug
+	}
+	return internal.GlobalLogger.GetLevel()
+}
+
+func IsLogTag() bool {
+	if !internal.IsReady() {
+		return false
+	}
+	return internal.GlobalLogger.IsLogTag()
+}
+
+func GetArgs0Name() (string, string) {
+	if !internal.IsReady() {
+		return "", ""
+	}
+	return internal.GlobalLogger.GetArgs0Name()
+}
+
+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()
+}

+ 15 - 0
src/logger/init_export.go

@@ -0,0 +1,15 @@
+// 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 logger
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/internal"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"io"
+)
+
+func InitBaseLogger(level loglevel.LoggerLevel, logTag bool, realPanic bool, warnWriter, errWriter io.Writer) error {
+	return internal.InitLogger(level, logTag, realPanic, warnWriter, errWriter)
+}

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

@@ -0,0 +1,65 @@
+// 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 (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/mattn/go-isatty"
+	"io"
+	"os"
+)
+
+func (l *Logger) GetLevel() loglevel.LoggerLevel {
+	return l.level
+}
+
+func (l *Logger) IsLogTag() bool {
+	return l.logTag
+}
+
+func (l *Logger) GetArgs0Name() (string, string) {
+	return l.args0, l.args0Name
+}
+
+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)
+	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.errWriter.(*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()
+}

+ 51 - 0
src/logger/internal/init.go

@@ -0,0 +1,51 @@
+// 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 (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/osutils"
+	"io"
+	"os"
+)
+
+func InitLogger(level loglevel.LoggerLevel, logTag bool, realPanic bool, warnWriter, errWriter io.Writer) error {
+	logLevel, ok := levelMap[level]
+	if !ok {
+		return fmt.Errorf("invalid log level: %s", level)
+	}
+
+	if warnWriter == nil {
+		warnWriter = write.ChangeToWriter(os.Stdout)
+	}
+
+	if errWriter == nil {
+		errWriter = write.ChangeToWriter(os.Stderr)
+	}
+
+	logger := &Logger{
+		level:      level,
+		logLevel:   logLevel,
+		logTag:     logTag,
+		warnWriter: warnWriter,
+		errWriter:  errWriter,
+		args0:      osutils.GetArgs0(),
+		args0Name:  osutils.GetArgs0Name(),
+		realPanic:  realPanic,
+	}
+
+	GlobalLogger = logger
+	return nil
+}
+
+func IsReady() bool {
+	return GlobalLogger != nil
+}
+
+func CloseLogger() {
+
+}

+ 19 - 0
src/logger/internal/logger.go

@@ -0,0 +1,19 @@
+package internal
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+)
+
+var GlobalLogger *Logger = nil
+
+type Logger struct {
+	level      loglevel.LoggerLevel
+	logLevel   loggerLevel
+	logTag     bool
+	warnWriter write.Writer
+	errWriter  write.Writer
+	args0      string
+	args0Name  string
+	realPanic  bool
+}

+ 206 - 0
src/logger/internal/logger_method.go

@@ -0,0 +1,206 @@
+// 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 (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/runtimeutils"
+)
+
+func (l *Logger) Executablef(format string, args ...interface{}) string {
+	str := fmt.Sprintf(format, args...)
+	if str == "" {
+		_, _ = fmt.Fprintf(l.warnWriter, "[Executable]: %s\n", l.args0)
+	} else {
+		_, _ = fmt.Fprintf(l.warnWriter, "[Executable %s]: %s\n", l.args0, str)
+	}
+	return l.args0
+}
+
+func (l *Logger) Tagf(format string, args ...interface{}) {
+	l.TagSkipf(1, format, args...)
+}
+
+func (l *Logger) TagSkipf(skip int, format string, args ...interface{}) {
+	if !l.logTag {
+		return
+	}
+
+	funcName, file, _, line := runtimeutils.GetCallingFunctionInfo(skip + 1)
+
+	str := fmt.Sprintf(format, args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Tag %s]: %s %s %s:%d\n", l.args0Name, str, funcName, file, line)
+}
+
+func (l *Logger) Debugf(format string, args ...interface{}) {
+	if l.logLevel >= levelDebug {
+		return
+	}
+
+	str := fmt.Sprintf(format, args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Debug %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Infof(format string, args ...interface{}) {
+	if l.logLevel >= levelInfo {
+		return
+	}
+
+	str := fmt.Sprintf(format, args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Info %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Warnf(format string, args ...interface{}) {
+	if l.logLevel >= levelWarn {
+		return
+	}
+
+	str := fmt.Sprintf(format, args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Warning %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Errorf(format string, args ...interface{}) {
+	if l.logLevel >= levelError {
+		return
+	}
+
+	str := fmt.Sprintf(format, args...)
+	_, _ = fmt.Fprintf(l.errWriter, "[Error %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Panicf(format string, args ...interface{}) {
+	if l.logLevel >= levelPanic {
+		return
+	}
+
+	str := fmt.Sprintf(format, args...)
+	_, _ = fmt.Fprintf(l.errWriter, "[Panic %s]: %s\n", l.args0Name, str)
+
+	if l.realPanic {
+		panic(str)
+	}
+}
+
+func (l *Logger) Tag(args ...interface{}) {
+	l.TagSkip(1, args...)
+}
+
+func (l *Logger) TagSkip(skip int, args ...interface{}) {
+	if !l.logTag {
+		return
+	}
+
+	funcName, file, _, line := runtimeutils.GetCallingFunctionInfo(skip + 1)
+
+	str := fmt.Sprint(args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Tag %s]: %s %s %s:%d\n", l.args0Name, str, funcName, file, line)
+}
+
+func (l *Logger) Debug(args ...interface{}) {
+	if l.logLevel >= levelDebug {
+		return
+	}
+
+	str := fmt.Sprint(args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Debug %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Info(args ...interface{}) {
+	if l.logLevel >= levelInfo {
+		return
+	}
+
+	str := fmt.Sprint(args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Info %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Warn(args ...interface{}) {
+	if l.logLevel >= levelWarn {
+		return
+	}
+
+	str := fmt.Sprint(args...)
+	_, _ = fmt.Fprintf(l.warnWriter, "[Warning %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Error(args ...interface{}) {
+	if l.logLevel >= levelError {
+		return
+	}
+
+	str := fmt.Sprint(args...)
+	_, _ = fmt.Fprintf(l.errWriter, "[Error %s]: %s\n", l.args0Name, str)
+}
+
+func (l *Logger) Panic(args ...interface{}) {
+	if l.logLevel >= levelPanic {
+		return
+	}
+
+	str := fmt.Sprint(args...)
+	_, _ = fmt.Fprintf(l.errWriter, "[Panic %s]: %s\n", l.args0Name, str)
+
+	if l.realPanic {
+		panic(str)
+	}
+}
+
+func (l *Logger) TagWrite(msg string) {
+	l.TagSkipWrite(1, msg)
+}
+
+func (l *Logger) TagSkipWrite(skip int, msg string) {
+	if !l.logTag {
+		return
+	}
+
+	funcName, file, _, line := runtimeutils.GetCallingFunctionInfo(skip + 1)
+
+	_, _ = fmt.Fprintf(l.warnWriter, "[Debug %s]: %s %s %s:%d\n", l.args0Name, msg, funcName, file, line)
+}
+
+func (l *Logger) DebugWrite(msg string) {
+	if l.logLevel >= levelDebug {
+		return
+	}
+
+	_, _ = fmt.Fprintf(l.warnWriter, "[Debug %s]: %s\n", l.args0Name, msg)
+}
+
+func (l *Logger) InfoWrite(msg string) {
+	if l.logLevel >= levelInfo {
+		return
+	}
+
+	_, _ = fmt.Fprintf(l.warnWriter, "[Info %s]: %s\n", l.args0Name, msg)
+}
+
+func (l *Logger) WarnWrite(msg string) {
+	if l.logLevel >= levelWarn {
+		return
+	}
+
+	_, _ = fmt.Fprintf(l.warnWriter, "[Warning %s]: %s\n", l.args0Name, msg)
+}
+
+func (l *Logger) ErrorWrite(msg string) {
+	if l.logLevel >= levelError {
+		return
+	}
+
+	_, _ = fmt.Fprintf(l.errWriter, "[Error %s]: %s\n", l.args0Name, msg)
+}
+
+func (l *Logger) PanicWrite(msg string) {
+	if l.logLevel >= levelPanic {
+		return
+	}
+
+	_, _ = fmt.Fprintf(l.errWriter, "[Panic %s]: %s\n", l.args0Name, msg)
+
+	if l.realPanic {
+		panic(msg)
+	}
+}

+ 91 - 0
src/logger/internal/set_method.go

@@ -0,0 +1,91 @@
+// 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 (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"os"
+)
+
+func (l *Logger) SetLevel(level loglevel.LoggerLevel) error {
+	logLevel, ok := levelMap[level]
+	if !ok {
+		return fmt.Errorf("invalid log level: %s", level)
+	}
+
+	l.level = level
+	l.logLevel = logLevel
+
+	return nil
+}
+
+func (l *Logger) SetLogTag(logTag bool) error {
+	l.logTag = logTag
+	return nil
+}
+
+func (l *Logger) SetArgs0Name(args0 string, args0Name string) error {
+	if args0 == "" && args0Name == "" {
+		return fmt.Errorf("args0 can not be empty")
+	}
+
+	if args0 != "" {
+		l.args0 = args0
+	}
+
+	if args0Name != "" {
+		l.args0Name = args0Name
+	}
+
+	return nil
+}
+
+func (l *Logger) SetWarnWriter(w write.Writer) (write.Writer, error) {
+	if w == nil {
+		w = write.ChangeToWriter(os.Stdout)
+	}
+
+	last := l.warnWriter
+	l.warnWriter = w
+	return last, nil
+}
+
+func (l *Logger) SetErrWriter(w write.Writer) (write.Writer, error) {
+	if w == nil {
+		w = write.ChangeToWriter(os.Stderr)
+	}
+
+	last := l.errWriter
+	l.errWriter = w
+	return last, nil
+}
+
+func (l *Logger) CloseWarnWriter() error {
+	if l.warnWriter == nil {
+		return fmt.Errorf("warn writer not set")
+	}
+
+	w, ok := l.warnWriter.(write.WriteCloser)
+	if !ok {
+		return nil
+	}
+
+	return w.ExitClose()
+}
+
+func (l *Logger) CloseErrWriter() error {
+	if l.errWriter == nil {
+		return fmt.Errorf("error writer not set")
+	}
+
+	w, ok := l.errWriter.(write.WriteCloser)
+	if !ok {
+		return nil
+	}
+
+	return w.ExitClose()
+}

+ 27 - 0
src/logger/internal/variable.go

@@ -0,0 +1,27 @@
+// 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 "github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+
+type loggerLevel int64
+
+const (
+	levelDebug loggerLevel = 1
+	levelInfo  loggerLevel = 2
+	levelWarn  loggerLevel = 3
+	levelError loggerLevel = 4
+	levelPanic loggerLevel = 5
+	levelNone  loggerLevel = 6
+)
+
+var levelMap = map[loglevel.LoggerLevel]loggerLevel{
+	loglevel.LevelDebug: levelDebug,
+	loglevel.LevelInfo:  levelInfo,
+	loglevel.LevelWarn:  levelWarn,
+	loglevel.LevelError: levelError,
+	loglevel.LevelPanic: levelPanic,
+	loglevel.LevelNone:  levelNone,
+}

+ 142 - 0
src/logger/logger_export.go

@@ -0,0 +1,142 @@
+package logger
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/internal"
+)
+
+func IsReady() bool {
+	return internal.IsReady()
+}
+
+func Executablef(format string, args ...interface{}) string {
+	if !internal.IsReady() {
+		return ""
+	}
+	return internal.GlobalLogger.Executablef(format, args...)
+}
+
+func Tagf(format string, args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.TagSkipf(1, format, args...)
+}
+
+func Debugf(format string, args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Debugf(format, args...)
+}
+
+func Infof(format string, args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Infof(format, args...)
+}
+
+func Warnf(format string, args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Warnf(format, args...)
+}
+
+func Errorf(format string, args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Errorf(format, args...)
+}
+
+func Panicf(format string, args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Panicf(format, args...)
+}
+
+func Tag(args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.TagSkip(1, args...)
+}
+
+func Debug(args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Debug(args...)
+}
+
+func Info(args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Info(args...)
+}
+
+func Warn(args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Warn(args...)
+}
+
+func Error(args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Error(args...)
+}
+
+func Panic(args ...interface{}) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.Panic(args...)
+}
+
+func TagWrite(msg string) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.TagSkip(1, msg)
+}
+
+func DebugWrite(msg string) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.DebugWrite(msg)
+}
+
+func InfoWrite(msg string) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.InfoWrite(msg)
+}
+
+func WarnWrite(msg string) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.WarnWrite(msg)
+}
+
+func ErrorWrite(msg string) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.ErrorWrite(msg)
+}
+
+func PanicWrite(msg string) {
+	if !internal.IsReady() {
+		return
+	}
+	internal.GlobalLogger.PanicWrite(msg)
+}

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

@@ -0,0 +1,25 @@
+// 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 loglevel
+
+type LoggerLevel string
+
+const (
+	LevelDebug LoggerLevel = "debug"
+	LevelInfo  LoggerLevel = "info"
+	LevelWarn  LoggerLevel = "warn"
+	LevelError LoggerLevel = "error"
+	LevelPanic LoggerLevel = "panic"
+	LevelNone  LoggerLevel = "none"
+)
+
+var LoggerLevelMap = map[LoggerLevel]bool{
+	LevelDebug: true,
+	LevelInfo:  true,
+	LevelWarn:  true,
+	LevelError: true,
+	LevelPanic: true,
+	LevelNone:  true,
+}

+ 47 - 0
src/logger/set_export.go

@@ -0,0 +1,47 @@
+// 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 logger
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/internal"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/loglevel"
+	"io"
+)
+
+func SetLevel(level loglevel.LoggerLevel) error {
+	if !internal.IsReady() {
+		return fmt.Errorf("logger not ready")
+	}
+	return internal.GlobalLogger.SetLevel(level)
+}
+
+func SetLogTag(logTag bool) error {
+	if !internal.IsReady() {
+		return fmt.Errorf("logger not ready")
+	}
+	return internal.GlobalLogger.SetLogTag(logTag)
+}
+
+func SetArgs0Name(args0 string, args0Name string) error {
+	if !internal.IsReady() {
+		return fmt.Errorf("logger not ready")
+	}
+	return internal.GlobalLogger.SetArgs0Name(args0, args0Name)
+}
+
+func SetWarnWriter(w io.Writer) (io.Writer, error) {
+	if !internal.IsReady() {
+		return nil, fmt.Errorf("logger not ready")
+	}
+	return internal.GlobalLogger.SetWarnWriter(w)
+}
+
+func SetErrWriter(w io.Writer) (io.Writer, error) {
+	if !internal.IsReady() {
+		return nil, fmt.Errorf("logger not ready")
+	}
+	return internal.GlobalLogger.SetErrWriter(w)
+}

+ 87 - 0
src/logger/write/combiningwriter/writer.go

@@ -0,0 +1,87 @@
+// 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 combiningwriter
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/sliceutils"
+)
+
+type CombiningWriter struct {
+	writer []write.Writer
+	closer []write.WriteCloser
+	close  bool
+}
+
+func (c *CombiningWriter) Write(p []byte) (n int, err error) {
+	if c.close {
+		return 0, fmt.Errorf("combining writer has been close")
+	}
+
+	n = 0
+	errMsg := ""
+	for _, w := range c.writer {
+		if w == nil {
+			continue
+		}
+
+		nTmp, errTmp := w.Write(p)
+		if errTmp != nil {
+			errMsg += errTmp.Error() + ";"
+		}
+
+		n = min(n, nTmp)
+	}
+
+	if errMsg == "" {
+		return n, nil
+	}
+
+	return n, fmt.Errorf(errMsg)
+}
+
+func (c *CombiningWriter) Close() error {
+	return c.ExitClose()
+}
+
+func (c *CombiningWriter) ExitClose() error {
+	defer func() {
+		c.close = true
+	}()
+
+	errMsg := ""
+	for _, w := range c.closer {
+		if w == nil {
+			continue
+		}
+
+		errTmp := w.Close()
+		if errTmp != nil {
+			errMsg += errTmp.Error() + ";"
+		}
+	}
+
+	if errMsg == "" {
+		return nil
+	}
+
+	return fmt.Errorf(errMsg)
+}
+
+func NewCombiningWriter(w ...write.Writer) *CombiningWriter {
+	var res = new(CombiningWriter)
+
+	res.writer = sliceutils.CopySlice(w)
+	res.closer = make([]write.WriteCloser, 0, len(w))
+	for _, i := range w {
+		if wc, ok := i.(write.WriteCloser); ok {
+			res.closer = append(res.closer, wc)
+		}
+	}
+	res.close = false
+
+	return res
+}

+ 119 - 0
src/logger/write/datefilewriter/writer.go

@@ -0,0 +1,119 @@
+// 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 datefilewriter
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/filesystemutils"
+	"os"
+	"path"
+	"time"
+)
+
+type DateFileWriter struct {
+	dirPath        string
+	filenamePrefix string
+	filenameSuffix string
+	file           *os.File
+	close          bool
+}
+
+func (f *DateFileWriter) Write(p []byte) (n int, err error) {
+	if f.close {
+		return 0, fmt.Errorf("date file writer has been close")
+	}
+
+	suffix := time.Now().Format(time.DateOnly)
+	if suffix != f.filenameSuffix {
+		_ = f.closeFile()
+		err := f.openFile(suffix)
+		if err == nil {
+			return 0, err
+		}
+	}
+
+	if f.file == nil {
+		return 0, fmt.Errorf("file writer has been close")
+	}
+
+	if f.file.Fd() == ^(uintptr(0)) { // 检查文件描述符是否为 -1
+		return 0, fmt.Errorf("file writer has been close")
+	}
+
+	return f.file.Write(p)
+}
+
+func (f *DateFileWriter) closeFile() error {
+	defer func() {
+		f.file = nil
+	}()
+
+	if f.file != nil {
+		return f.file.Close()
+	}
+
+	return nil
+}
+
+func (f *DateFileWriter) openFile(newSuffix string) error {
+	defer func() {
+		f.filenameSuffix = newSuffix
+	}()
+
+	if f.file != nil {
+		return fmt.Errorf("last file has not been closse")
+	}
+
+	filename := fmt.Sprintf("%s-%s", f.filenamePrefix, newSuffix)
+	filePath := path.Join(f.dirPath, filename)
+
+	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
+	if err != nil {
+		return err
+	}
+
+	f.file = file
+
+	return nil
+}
+
+func (f *DateFileWriter) Close() error {
+	return f.ExitClose()
+}
+
+func (f *DateFileWriter) ExitClose() error {
+	defer func() {
+		f.file = nil
+	}()
+
+	if f.file != nil {
+		return f.file.Close()
+	}
+
+	return nil
+}
+
+func NewDateFileWriter(dirpath string, filenamePrefix string) (*DateFileWriter, error) {
+	var writer write.WriteCloser
+	var res = new(DateFileWriter)
+
+	if filesystemutils.IsFile(dirpath) {
+		return nil, fmt.Errorf("dir not exists")
+	}
+
+	err := os.MkdirAll(dirpath, 0644)
+	if err != nil {
+		return nil, err
+	}
+
+	res.filenamePrefix = filenamePrefix
+	res.close = false
+
+	writer = res // 用于检验StdWriter实现了io.WriteCloser
+	_ = writer
+
+	return res, nil
+}

+ 16 - 0
src/logger/write/export.go

@@ -0,0 +1,16 @@
+// 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 write
+
+import "io"
+
+type Writer interface {
+	io.Writer
+}
+
+type WriteCloser interface {
+	io.WriteCloser
+	ExitClose() error
+}

+ 66 - 0
src/logger/write/filewriter/writer.go

@@ -0,0 +1,66 @@
+// 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 filewriter
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger/write"
+	"os"
+)
+
+type FileWriter struct {
+	filePath string
+	file     *os.File
+}
+
+func (f *FileWriter) Write(p []byte) (n int, err error) {
+	if f.file == nil {
+		return 0, fmt.Errorf("file writer has been close")
+	}
+
+	if f.file.Fd() == ^(uintptr(0)) { // 检查文件描述符是否为 -1
+		return 0, fmt.Errorf("file writer has been close")
+	}
+
+	return f.file.Write(p)
+}
+
+func (f *FileWriter) Close() error {
+	return f.ExitClose()
+}
+
+func (f *FileWriter) ExitClose() error {
+	defer func() {
+		f.file = nil
+	}()
+
+	if f.file != nil {
+		return f.file.Close()
+	}
+
+	return nil
+}
+
+func NewFileWriter(filepath string) (*FileWriter, error) {
+	var res = new(FileWriter)
+
+	file, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+	if err != nil {
+		return nil, err
+	}
+
+	res.filePath = filepath
+	res.file = file
+
+	return res, nil
+}
+
+func _testFileWriter() {
+	var a write.WriteCloser
+	var b *FileWriter
+
+	a = b
+	_ = a
+}

+ 21 - 0
src/logger/write/warp_writer.go

@@ -0,0 +1,21 @@
+// 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 write
+
+import "io"
+
+type wrapperWriter struct {
+	writer io.Writer
+}
+
+func (w *wrapperWriter) Write(p []byte) (n int, err error) {
+	return w.writer.Write(p)
+}
+
+func ChangeToWriter(w io.Writer) Writer {
+	return &wrapperWriter{
+		writer: w,
+	}
+}

+ 29 - 0
src/logger/write/warp_writer_closer.go

@@ -0,0 +1,29 @@
+// 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 write
+
+import "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() error {
+	return w.writer.Close()
+}
+
+func (w *wrapperWriterClose) ExitClose() error {
+	return w.Close()
+}
+
+func ChangeToWriteCloser(w io.WriteCloser) WriteCloser {
+	return &wrapperWriterClose{
+		writer: w,
+	}
+}

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

@@ -0,0 +1,108 @@
+// 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 v1
+
+import (
+	"errors"
+	"github.com/SongZihuan/BackendServerTemplate/src/commandlineargs"
+	"github.com/SongZihuan/BackendServerTemplate/src/config"
+	"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/server/controller"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/example1"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/example2"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/servercontext"
+	"github.com/SongZihuan/BackendServerTemplate/src/signalwatcher"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
+)
+
+func MainV1() (exitCode int) {
+	var err error
+
+	err = logger.InitBaseLogger(loglevel.LevelDebug, true, true, nil, nil)
+	if err != nil {
+		return exitutils.InitFailedErrorForLoggerModule(err.Error())
+	}
+
+	err = commandlineargs.InitCommandLineArgsParser(nil)
+	if err != nil {
+		if errors.Is(err, commandlineargs.StopRun) {
+			return exitutils.SuccessExit("server not run")
+		}
+
+		return exitutils.InitFailedError("Command Line Args Parser", err.Error())
+	}
+
+	err = config.InitConfig(&config.ConfigOption{
+		ConfigFilePath: commandlineargs.ConfigFile(),
+		Provider:       configparser.NewYamlProvider(),
+	})
+	if err != nil {
+		return exitutils.InitFailedError("Config file read and parser", err.Error())
+	}
+
+	if commandlineargs.OutputConfigFile() != "" {
+		err = config.OutputConfig(commandlineargs.OutputConfigFile())
+		if err != nil {
+			return exitutils.InitFailedError("Config file output", err.Error())
+		}
+	}
+
+	sigchan := signalwatcher.NewSignalExitChannel()
+	defer close(sigchan)
+
+	ctrl, err := controller.NewController(&controller.ControllerOption{
+		StopWaitTime: config.Data().Server.StopWaitTimeDuration,
+	})
+	if err != nil {
+		return exitutils.InitFailedError("Server Controller", err.Error())
+	}
+
+	ser1, _, err := example1.NewServerExample1(nil)
+	if err != nil {
+		return exitutils.InitFailedError("Server Example1", err.Error())
+	}
+
+	err = ctrl.AddServer(ser1)
+	if err != nil {
+		return exitutils.InitFailedError("Add Server Example1", err.Error())
+	}
+
+	ser2, _, err := example2.NewServerExample2(nil)
+	if err != nil {
+		return exitutils.InitFailedError("Server Example2", err.Error())
+	}
+
+	err = ctrl.AddServer(ser2)
+	if err != nil {
+		return exitutils.InitFailedError("Add Server Example2", err.Error())
+	}
+
+	logger.Infof("Start to run server controller")
+	go ctrl.Run()
+
+	select {
+	case <-sigchan:
+		logger.Infof("stop by signal")
+		err = nil
+	case <-ctrl.GetCtx().Listen():
+		err = ctrl.GetCtx().Error()
+		if err == nil || errors.Is(err, servercontext.StopAllTask) {
+			err = nil
+			logger.Infof("stop by controller")
+		} else {
+			logger.Errorf("stop by controller with error")
+		}
+	}
+
+	ctrl.Stop()
+
+	if err != nil {
+		return exitutils.RunError(err.Error())
+	}
+
+	return exitutils.SuccessExit("all tasks are completed and the main go routine exits")
+}

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

@@ -0,0 +1,84 @@
+// 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 v1
+
+import (
+	"errors"
+	"github.com/SongZihuan/BackendServerTemplate/src/commandlineargs"
+	"github.com/SongZihuan/BackendServerTemplate/src/config"
+	"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/server/example1"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/servercontext"
+	"github.com/SongZihuan/BackendServerTemplate/src/signalwatcher"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/exitutils"
+)
+
+func MainV1() (exitCode int) {
+	var err error
+
+	err = logger.InitBaseLogger(loglevel.LevelDebug, true, true, nil, nil)
+	if err != nil {
+		return exitutils.InitFailedErrorForLoggerModule(err.Error())
+	}
+
+	err = commandlineargs.InitCommandLineArgsParser(nil)
+	if err != nil {
+		if errors.Is(err, commandlineargs.StopRun) {
+			return exitutils.SuccessExit("server not run")
+		}
+
+		return exitutils.InitFailedError("Command Line Args Parser", err.Error())
+	}
+
+	err = config.InitConfig(&config.ConfigOption{
+		ConfigFilePath: commandlineargs.ConfigFile(),
+		Provider:       configparser.NewYamlProvider(),
+	})
+	if err != nil {
+		return exitutils.InitFailedError("Config file read and parser", err.Error())
+	}
+
+	if commandlineargs.OutputConfigFile() != "" {
+		err = config.OutputConfig(commandlineargs.OutputConfigFile())
+		if err != nil {
+			return exitutils.InitFailedError("Config file output", err.Error())
+		}
+	}
+
+	sigchan := signalwatcher.NewSignalExitChannel()
+	defer close(sigchan)
+
+	ser, _, err := example1.NewServerExample1(nil)
+	if err != nil {
+		return exitutils.InitFailedError("Server Example1", err.Error())
+	}
+
+	logger.Infof("Start to run server controller")
+	go ser.Run()
+
+	select {
+	case <-sigchan:
+		logger.Infof("stop by signal")
+		err = nil
+	case <-ser.GetCtx().Listen():
+		err = ser.GetCtx().Error()
+		if err == nil || errors.Is(err, servercontext.StopAllTask) {
+			err = nil
+			logger.Infof("stop by controller")
+		} else {
+			logger.Errorf("stop by controller with error")
+		}
+	}
+
+	ser.Stop()
+
+	if err != nil {
+		return exitutils.RunError(err.Error())
+	}
+
+	return exitutils.SuccessExit("all tasks are completed and the main go routine exits")
+}

+ 251 - 0
src/server/controller/controller.go

@@ -0,0 +1,251 @@
+// 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 controller
+
+import (
+	"errors"
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/servercontext"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/serverinterface"
+	"github.com/SongZihuan/BackendServerTemplate/src/utils/strconvutils"
+	"reflect"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+type Controller struct {
+	server       map[string]serverinterface.Server
+	context      map[string]*servercontext.ServerContext
+	ctx          *servercontext.ServerContext
+	running      atomic.Bool
+	name         string
+	stopWaitTime time.Duration
+	wg           *sync.WaitGroup
+}
+
+type ControllerOption struct {
+	StopWaitTime time.Duration
+}
+
+func NewController(opt *ControllerOption) (*Controller, error) {
+	ctx := servercontext.NewServerContext()
+
+	if opt == nil {
+		opt = &ControllerOption{
+			StopWaitTime: 10 * time.Second,
+		}
+	} else {
+		if opt.StopWaitTime == 0 {
+			opt.StopWaitTime = 10 * time.Second
+		}
+	}
+
+	controller := &Controller{
+		server:       make(map[string]serverinterface.Server, 10),
+		context:      make(map[string]*servercontext.ServerContext, 10),
+		ctx:          ctx,
+		name:         serverinterface.ControllerName,
+		wg:           new(sync.WaitGroup),
+		stopWaitTime: opt.StopWaitTime,
+	}
+
+	{
+		controller.server[controller.name] = controller
+		controller.context[controller.name] = ctx
+	}
+
+	return controller, nil
+}
+
+func (s *Controller) AddServer(ser serverinterface.Server) error {
+	if s.running.Load() {
+		return fmt.Errorf("controller is running")
+	}
+
+	name := ser.Name()
+	if name == serverinterface.ControllerName {
+		return fmt.Errorf("name can not be %s", serverinterface.ControllerName)
+	} else if name == "" {
+		return fmt.Errorf("name can not be empty")
+	}
+
+	_, ok := s.server[name]
+	if ok {
+		return fmt.Errorf("server is exists")
+	}
+
+	s.server[name] = ser
+	s.context[name] = ser.GetCtx()
+
+	return nil
+}
+
+func (s *Controller) DelServer(ser serverinterface.Server) error {
+	if s.running.Load() {
+		return fmt.Errorf("controller is running")
+	}
+
+	name := ser.Name()
+	if name == serverinterface.ControllerName {
+		return fmt.Errorf("name can not be %s", serverinterface.ControllerName)
+	} else if name == "" {
+		return fmt.Errorf("name can not be empty")
+	}
+
+	_, ok := s.server[name]
+	if !ok {
+		return fmt.Errorf("server is not exists")
+	}
+
+	delete(s.server, name)
+	delete(s.server, name)
+
+	return nil
+}
+
+func (s *Controller) Name() string {
+	return s.name
+}
+
+func (s *Controller) GetCtx() *servercontext.ServerContext {
+	return s.ctx
+}
+
+func (s *Controller) Run() {
+	if s.running.Swap(true) {
+		return
+	}
+	defer func() {
+		s.running.Store(false)
+	}()
+
+	s.wg = new(sync.WaitGroup)
+	s.wg.Add(1)
+	defer s.wg.Done()
+
+	for name, server := range s.server {
+		if name == s.name {
+			continue
+		}
+
+		_, ok := s.context[name]
+		if !ok {
+			logger.Errorf("server %s context not found", name)
+			continue
+		}
+
+		logger.Infof("start to run server: %s", name)
+
+		s.wg.Add(1)
+		go func() {
+			defer s.wg.Done()
+			server.Run()
+		}()
+	}
+
+SelectCycle:
+	for {
+		cases := make([]reflect.SelectCase, 0, len(s.context))
+		serverName := make([]string, 0, len(s.context))
+
+		for name, ctx := range s.context {
+			if name == s.name {
+				continue
+			}
+
+			cases = append(cases, reflect.SelectCase{
+				Dir:  reflect.SelectRecv,
+				Chan: reflect.ValueOf(ctx.Listen()),
+			})
+			serverName = append(serverName, name)
+		}
+
+		cases = append(cases, reflect.SelectCase{
+			Dir:  reflect.SelectRecv,
+			Chan: reflect.ValueOf(s.ctx.Listen()),
+		})
+		serverName = append(serverName, s.name)
+
+		chosen, _, _ := reflect.Select(cases)
+		stopServerName := serverName[chosen]
+
+		if stopServerName != s.name {
+			ctx, ok := s.context[stopServerName]
+			if !ok {
+				logger.Errorf("unknown server stop: %s", stopServerName)
+				break SelectCycle
+			} else {
+				switch ctx.Reason() {
+				default:
+					fallthrough
+				case servercontext.StopReasonStop:
+					logger.Infof("server %s stop", stopServerName)
+					break SelectCycle
+				case servercontext.StopReasonFinish:
+					// 不会停止其他任务
+					logger.Infof("server %s run finished", stopServerName)
+					delete(s.context, stopServerName)
+					delete(s.server, stopServerName)
+					continue SelectCycle
+				case servercontext.StopReasonError:
+					err := ctx.Error()
+					if errors.Is(err, servercontext.StopAllTask) {
+						logger.Infof("server %s run finished (stop all task)", stopServerName)
+						s.ctx.RunError(err)
+					} else if err != nil {
+						logger.Infof("server %s stop with error: %s", stopServerName, err.Error())
+						s.ctx.FinishAndStopAllTask()
+					} else {
+						logger.Infof("server %s stop with error: unknown", stopServerName)
+						s.ctx.RunError(err)
+					}
+					break SelectCycle
+				}
+			}
+		} else {
+			break SelectCycle
+		}
+	}
+
+	for name, ctx := range s.context {
+		if name == s.name {
+			continue
+		}
+
+		ctx.StopTask()
+	}
+}
+
+func (s *Controller) Stop() {
+	s.ctx.StopTask()
+	if s.wg != nil {
+		wgchan := make(chan any)
+
+		go func() {
+			s.wg.Wait()
+			close(wgchan)
+		}()
+
+		select {
+		case <-time.After(s.stopWaitTime):
+			logger.Errorf("stop timeout (%s)", strconvutils.TimeDurationToString(s.stopWaitTime))
+		case <-wgchan:
+		}
+	}
+}
+
+func (s *Controller) IsRunning() bool {
+	return s.running.Load()
+}
+
+func _test() {
+	var a serverinterface.Server
+	var b *Controller
+
+	a = b
+	_ = a
+}

+ 118 - 0
src/server/example1/server.go

@@ -0,0 +1,118 @@
+// 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 example1
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/servercontext"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/serverinterface"
+	"sync"
+	"time"
+)
+
+type ServerExample1 struct {
+	running      bool
+	ctx          *servercontext.ServerContext
+	name         string
+	wg           *sync.WaitGroup
+	stopWaitTime time.Duration
+}
+
+type ServerExample1Option struct {
+	StopWaitTime time.Duration
+}
+
+func NewServerExample1(opt *ServerExample1Option) (*ServerExample1, *servercontext.ServerContext, error) {
+	ctx := servercontext.NewServerContext()
+
+	if opt == nil {
+		opt = &ServerExample1Option{
+			StopWaitTime: 10 * time.Second,
+		}
+	} else {
+		if opt.StopWaitTime == 0 {
+			opt.StopWaitTime = 10 * time.Second
+		}
+	}
+
+	server := &ServerExample1{
+		ctx:          ctx,
+		running:      false,
+		name:         "example1",
+		wg:           new(sync.WaitGroup),
+		stopWaitTime: opt.StopWaitTime,
+	}
+	err := server.init()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return server, ctx, nil
+}
+
+func (s *ServerExample1) init() error {
+	return nil
+}
+
+func (s *ServerExample1) Name() string {
+	return s.name
+}
+
+func (s *ServerExample1) GetCtx() *servercontext.ServerContext {
+	return s.ctx
+}
+
+func (s *ServerExample1) Run() {
+	s.running = true
+	defer func() {
+		s.running = false
+	}()
+
+	s.wg = new(sync.WaitGroup)
+	s.wg.Add(1)
+	defer s.wg.Done()
+
+MainCycle:
+	for {
+		fmt.Println("Example1: I am running!")
+
+		select {
+		case <-s.ctx.Listen():
+			fmt.Println("Example1: I am stop!")
+			break MainCycle
+		case <-time.After(1 * time.Second):
+			continue
+		}
+	}
+}
+
+func (s *ServerExample1) Stop() {
+	s.ctx.StopTask()
+	if s.wg != nil {
+		wgchan := make(chan any)
+
+		go func() {
+			s.wg.Wait()
+			close(wgchan)
+		}()
+
+		select {
+		case <-time.After(s.stopWaitTime):
+		case <-wgchan:
+		}
+	}
+}
+
+func (s *ServerExample1) IsRunning() bool {
+	return s.running
+}
+
+func _test() {
+	var a serverinterface.Server
+	var b *ServerExample1
+
+	a = b
+	_ = a
+}

+ 118 - 0
src/server/example2/server.go

@@ -0,0 +1,118 @@
+// 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 example2
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/servercontext"
+	"github.com/SongZihuan/BackendServerTemplate/src/server/serverinterface"
+	"sync"
+	"time"
+)
+
+type ServerExample2 struct {
+	running      bool
+	ctx          *servercontext.ServerContext
+	name         string
+	wg           *sync.WaitGroup
+	stopWaitTime time.Duration
+}
+
+type ServerExample2Option struct {
+	StopWaitTime time.Duration
+}
+
+func NewServerExample2(opt *ServerExample2Option) (*ServerExample2, *servercontext.ServerContext, error) {
+	ctx := servercontext.NewServerContext()
+
+	if opt == nil {
+		opt = &ServerExample2Option{
+			StopWaitTime: 10 * time.Second,
+		}
+	} else {
+		if opt.StopWaitTime == 0 {
+			opt.StopWaitTime = 10 * time.Second
+		}
+	}
+
+	server := &ServerExample2{
+		ctx:          ctx,
+		running:      false,
+		name:         "example2",
+		wg:           new(sync.WaitGroup),
+		stopWaitTime: opt.StopWaitTime,
+	}
+	err := server.init()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return server, ctx, nil
+}
+
+func (s *ServerExample2) init() error {
+	return nil
+}
+
+func (s *ServerExample2) Name() string {
+	return s.name
+}
+
+func (s *ServerExample2) GetCtx() *servercontext.ServerContext {
+	return s.ctx
+}
+
+func (s *ServerExample2) Run() {
+	s.running = true
+	defer func() {
+		s.running = false
+	}()
+
+	s.wg = new(sync.WaitGroup)
+	s.wg.Add(1)
+	defer s.wg.Done()
+
+MainCycle:
+	for {
+		fmt.Println("Example2: I am running!")
+
+		select {
+		case <-s.ctx.Listen():
+			fmt.Println("Example2: I am stop!")
+			break MainCycle
+		case <-time.After(2 * time.Second):
+			continue
+		}
+	}
+}
+
+func (s *ServerExample2) Stop() {
+	s.ctx.StopTask()
+	if s.wg != nil {
+		wgchan := make(chan any)
+
+		go func() {
+			s.wg.Wait()
+			close(wgchan)
+		}()
+
+		select {
+		case <-time.After(s.stopWaitTime):
+		case <-wgchan:
+		}
+	}
+}
+
+func (s *ServerExample2) IsRunning() bool {
+	return s.running
+}
+
+func _test() {
+	var a serverinterface.Server
+	var b *ServerExample2
+
+	a = b
+	_ = a
+}

+ 128 - 0
src/server/servercontext/context.go

@@ -0,0 +1,128 @@
+// 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 servercontext
+
+import (
+	"fmt"
+	"sync"
+)
+
+type StopReason int
+
+var StopAllTask = fmt.Errorf("stop all task")
+
+const (
+	StopReasonStop StopReason = iota + 1
+	StopReasonFinish
+	StopReasonError
+)
+
+type ServerContext struct {
+	mutex    sync.Mutex
+	stopchan chan any
+	err      error
+	reason   StopReason
+}
+
+func NewServerContext() *ServerContext {
+	return &ServerContext{
+		stopchan: make(chan any),
+		err:      nil,
+		reason:   0,
+	}
+}
+
+func (c *ServerContext) Listen() <-chan any {
+	return c.stopchan
+}
+
+// StopTask 表示外部环境终止任务
+func (c *ServerContext) StopTask() {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	if c.isStop() {
+		return
+	}
+
+	c.reason = StopReasonStop
+	close(c.stopchan)
+}
+
+// Finish 表示任务内部环境完成任务
+func (c *ServerContext) Finish() {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	if c.isStop() {
+		return
+	}
+
+	c.reason = StopReasonFinish
+	close(c.stopchan)
+}
+
+// FinishAndStopAllTask 表示任务内部环境完成任务,并且退出所有任务
+func (c *ServerContext) FinishAndStopAllTask() {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	if c.isStop() {
+		return
+	}
+
+	c.err = StopAllTask
+	c.reason = StopReasonError
+	close(c.stopchan)
+}
+
+// RunError 表示任务内部环境运行遇到错误
+func (c *ServerContext) RunError(err error) {
+	if err == nil {
+		c.Finish()
+		return
+	}
+
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	if c.isStop() {
+		return
+	}
+
+	c.err = err
+	c.reason = StopReasonError
+	close(c.stopchan)
+}
+
+func (c *ServerContext) IsStop() bool {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	return c.isStop()
+}
+
+func (c *ServerContext) Error() error {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	return c.err
+}
+
+func (c *ServerContext) Reason() StopReason {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	return c.reason
+}
+
+func (c *ServerContext) isStop() bool {
+	select {
+	case <-c.stopchan:
+		return true
+	default:
+		return false
+	}
+}

+ 19 - 0
src/server/serverinterface/server.go

@@ -0,0 +1,19 @@
+// 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 serverinterface
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/server/servercontext"
+)
+
+const ControllerName = "controller"
+
+type Server interface {
+	Name() string
+	Run()
+	GetCtx() *servercontext.ServerContext
+	Stop()
+	IsRunning() bool
+}

+ 36 - 0
src/signalwatcher/signal.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 signalwatcher
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/config"
+	"os"
+	"os/signal"
+	"syscall"
+)
+
+func NewSignalExitChannel() chan os.Signal {
+	var exitChannel = make(chan os.Signal)
+	var signalList = make([]os.Signal, 0, 4)
+
+	if config.Data().Signal.SigIntExit.IsEnable(true) {
+		signalList = append(signalList, syscall.SIGINT)
+	}
+
+	if config.Data().Signal.SigTermExit.IsEnable(true) {
+		signalList = append(signalList, syscall.SIGTERM)
+	}
+
+	if config.Data().Signal.SigHupExit.IsEnable(true) {
+		signalList = append(signalList, syscall.SIGHUP)
+	}
+
+	if config.Data().Signal.SigQuitExit.IsEnable(false) {
+		signalList = append(signalList, syscall.SIGQUIT)
+	}
+
+	signal.Notify(exitChannel, signalList...)
+	return exitChannel
+}

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

@@ -0,0 +1,89 @@
+// 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 exitutils
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/src/logger"
+	"log"
+)
+
+const (
+	exitCodeMin = 0
+	exitCodeMax = 255
+)
+
+func getExitCode(defaultExitCode int, exitCode ...int) (ec int) {
+	if len(exitCode) == 1 {
+		ec = exitCode[0]
+	} else {
+		ec = defaultExitCode
+	}
+
+	if ec < exitCodeMin {
+		ec = -ec
+	}
+
+	if ec > exitCodeMax {
+		ec = exitCodeMax
+	}
+
+	return ec
+}
+
+func InitFailedErrorForLoggerModule(reason string, exitCode ...int) int {
+	if reason == "" {
+		reason = "no reason"
+	}
+
+	ec := getExitCode(1, exitCode...)
+
+	log.Printf("The module `Logger` init failed (reason: `%s`) .", reason)
+	log.Printf("Now we should exit with code %d.", ec)
+
+	return ec
+}
+
+func InitFailedError(module string, reason string, exitCode ...int) int {
+	if reason == "" {
+		reason = "no reason"
+	}
+
+	ec := getExitCode(1, exitCode...)
+
+	logger.Errorf("The module `%s` init failed (reason: `%s`) .", module, reason)
+	logger.Errorf("Now we should exit with code %d.", ec)
+
+	return ec
+}
+
+func RunError(reason string, exitCode ...int) int {
+	if reason == "" {
+		reason = "no reason"
+	}
+
+	ec := getExitCode(1, exitCode...)
+
+	logger.Errorf("Run error (reason: `%s`) .", reason)
+	logger.Errorf("Now we should exit with code %d.", ec)
+
+	return ec
+}
+
+func SuccessExit(reason string, exitCode ...int) int {
+	if !logger.IsReady() {
+		panic("Logger must be ready!!!")
+		return -1
+	}
+
+	if reason == "" {
+		reason = "no reason"
+	}
+
+	ec := getExitCode(0, exitCode...)
+
+	logger.Errorf("Now we should exit with code %d (reason: %s) .", ec, reason)
+
+	return ec
+}

+ 25 - 0
src/utils/filesystemutils/dir.go

@@ -0,0 +1,25 @@
+// 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 filesystemutils
+
+import "os"
+
+func IsDir(path string) bool {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false
+	}
+
+	return s.IsDir()
+}
+
+func IsExistsAndDir(path string) (exists, isDir bool) {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false, false
+	}
+
+	return s.IsDir(), true
+}

+ 18 - 0
src/utils/filesystemutils/exists.go

@@ -0,0 +1,18 @@
+// 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 filesystemutils
+
+import (
+	"errors"
+	"os"
+)
+
+func IsExists(path string) bool {
+	_, err := os.Stat(path)
+	if err != nil && errors.Is(err, os.ErrNotExist) {
+		return false
+	}
+	return true
+}

+ 25 - 0
src/utils/filesystemutils/file.go

@@ -0,0 +1,25 @@
+// 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 filesystemutils
+
+import "os"
+
+func IsFile(path string) bool {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false
+	}
+
+	return !s.IsDir()
+}
+
+func IsExistsAndFile(path string) (exists, isDir bool) {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false, false
+	}
+
+	return !s.IsDir(), true
+}

+ 27 - 0
src/utils/filesystemutils/path.go

@@ -0,0 +1,27 @@
+// 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 filesystemutils
+
+import (
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+func CleanFilePathAbs(path string) (string, error) {
+	path, err := filepath.Abs(filepath.Clean(path))
+	if err != nil {
+		return "", err
+	}
+
+	if runtime.GOOS == "windows" {
+		index := strings.Index(path, `:\`)
+		pf := strings.ToUpper(path[:index])
+		ph := path[index:]
+		path = pf + ph
+	}
+
+	return path, nil
+}

+ 77 - 0
src/utils/formatutils/export.go

@@ -0,0 +1,77 @@
+// 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 formatutils
+
+import "strings"
+
+const NormalConsoleWidth = 80
+
+func FormatTextToWidth(text string, width int) string {
+	return FormatTextToWidthAndPrefix(text, 0, width)
+}
+
+func FormatTextToWidthAndPrefix(text string, prefixWidth int, overallWidth int) string {
+	var result strings.Builder
+
+	width := overallWidth - prefixWidth
+	if width <= 0 {
+		panic("bad width")
+	}
+
+	text = strings.ReplaceAll(text, "\r\n", "\n")
+
+	for _, line := range strings.Split(text, "\n") {
+		result.WriteString(strings.Repeat(" ", prefixWidth))
+
+		if line == "" {
+			result.WriteString("\n")
+			continue
+		}
+
+		spaceCount := CountSpaceInStringPrefix(line) % width
+		newLineLength := 0
+		if spaceCount < 80 {
+			result.WriteString(strings.Repeat(" ", spaceCount))
+			newLineLength = spaceCount
+		}
+
+		for _, word := range strings.Fields(line) {
+			if newLineLength+len(word) >= width {
+				result.WriteString("\n")
+				result.WriteString(strings.Repeat(" ", prefixWidth))
+				newLineLength = 0
+			}
+
+			// 不是第一个词时,添加空格
+			if newLineLength != 0 {
+				result.WriteString(" ")
+				newLineLength += 1
+			}
+
+			result.WriteString(word)
+			newLineLength += len(word)
+		}
+
+		if newLineLength != 0 {
+			result.WriteString("\n")
+			newLineLength = 0
+		}
+	}
+
+	return strings.TrimRight(result.String(), "\n")
+}
+
+func CountSpaceInStringPrefix(str string) int {
+	var res int
+	for _, r := range str {
+		if r == ' ' {
+			res += 1
+		} else {
+			break
+		}
+	}
+
+	return res
+}

+ 34 - 0
src/utils/osutils/export.go

@@ -0,0 +1,34 @@
+// 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 osutils
+
+import (
+	"os"
+	"path/filepath"
+)
+
+var _args0 = ""
+
+func init() {
+	var err error
+	if len(os.Args) > 0 {
+		_args0, err = os.Executable()
+		if err != nil {
+			_args0 = os.Args[0]
+		}
+	}
+
+	if _args0 == "" {
+		panic("args was empty")
+	}
+}
+
+func GetArgs0() string {
+	return _args0
+}
+
+func GetArgs0Name() string {
+	return filepath.Base(_args0)
+}

+ 16 - 0
src/utils/reflectutils/export.go

@@ -0,0 +1,16 @@
+// 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 reflectutils
+
+import "reflect"
+
+func HasFieldByReflect(typ reflect.Type, fieldName string) bool {
+	for i := 0; i < typ.NumField(); i++ {
+		if typ.Field(i).Name == fieldName {
+			return true
+		}
+	}
+	return false
+}

+ 16 - 0
src/utils/reutils/export.go

@@ -0,0 +1,16 @@
+// 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 reutils
+
+import "regexp"
+
+const semVerRegexStr = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$`
+
+var semVerRegex = regexp.MustCompile(semVerRegexStr)
+
+// IsSemanticVersion checks if the given string is a valid semantic version.
+func IsSemanticVersion(version string) bool {
+	return semVerRegex.MatchString(version)
+}

+ 29 - 0
src/utils/runtimeutils/runtime.go

@@ -0,0 +1,29 @@
+// 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 runtimeutils
+
+import (
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+func GetCallingFunctionInfo(skip int) (string, string, string, int) {
+	pc, file, line, ok := runtime.Caller(skip + 1)
+	if !ok {
+		return "", "", "", 0
+	}
+
+	var funcName string
+	tmp := runtime.FuncForPC(pc).Name()
+	tmpLst := strings.Split(tmp, "/")
+	if len(tmpLst) == 0 {
+		funcName = tmp
+	} else {
+		funcName = tmpLst[len(tmpLst)-1]
+	}
+
+	return funcName, file, filepath.Base(file), line
+}

+ 11 - 0
src/utils/sliceutils/slice.go

@@ -0,0 +1,11 @@
+// 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 sliceutils
+
+func CopySlice[T any](src []T) []T {
+	dest := make([]T, len(src))
+	copy(dest, src)
+	return dest
+}

+ 163 - 0
src/utils/strconvutils/time.go

@@ -0,0 +1,163 @@
+// 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 strconvutils
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func ReadTimeDuration(str string) time.Duration {
+	if str == "forever" || str == "none" {
+		return -1
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "Y") {
+		numStr := str[:len(str)-1]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * 365 * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "year") {
+		numStr := str[:len(str)-4]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * 365 * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "M") {
+		numStr := str[:len(str)-1]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * 31 * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "month") {
+		numStr := str[:len(str)-5]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * 31 * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "W") {
+		numStr := str[:len(str)-1]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * 7 * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "week") {
+		numStr := str[:len(str)-4]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * 7 * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "D") {
+		numStr := str[:len(str)-1]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "day") {
+		numStr := str[:len(str)-3]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * 24 * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "H") {
+		numStr := str[:len(str)-1]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "hour") {
+		numStr := str[:len(str)-4]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Hour * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "Min") { // 不能用M,否则会和 Month 冲突
+		numStr := str[:len(str)-3]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Minute * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "minute") {
+		numStr := str[:len(str)-6]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Minute * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "S") {
+		numStr := str[:len(str)-1]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Second * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "second") {
+		numStr := str[:len(str)-6]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Second * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "MS") {
+		numStr := str[:len(str)-2]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Millisecond * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "millisecond") {
+		numStr := str[:len(str)-11]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Millisecond * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "MiS") { // 不能用 MS , 否则会和 millisecond 冲突
+		numStr := str[:len(str)-3]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Microsecond * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToUpper(str), "MicroS") {
+		numStr := str[:len(str)-6]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Microsecond * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "microsecond") {
+		numStr := str[:len(str)-11]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Microsecond * time.Duration(num)
+	}
+
+	if strings.HasSuffix(strings.ToUpper(str), "NS") {
+		numStr := str[:len(str)-2]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Nanosecond * time.Duration(num)
+	} else if strings.HasSuffix(strings.ToLower(str), "nanosecond") {
+		numStr := str[:len(str)-10]
+		num, _ := strconv.ParseUint(numStr, 10, 64)
+		return time.Nanosecond * time.Duration(num)
+	}
+
+	num, _ := strconv.ParseUint(str, 10, 64)
+	return time.Duration(num) * time.Second
+}
+
+func TimeDurationToStringCN(t time.Duration) string {
+	const day = 24 * time.Hour
+	const year = 365 * day
+
+	if t > year {
+		return fmt.Sprintf("%d年", t/year)
+	} else if t > day {
+		return fmt.Sprintf("%d天", t/day)
+	} else if t > time.Hour {
+		return fmt.Sprintf("%d小时", t/time.Hour)
+	} else if t > time.Minute {
+		return fmt.Sprintf("%d分钟", t/time.Minute)
+	} else if t > time.Second {
+		return fmt.Sprintf("%d秒", t/time.Second)
+	}
+
+	return "0秒"
+}
+
+func TimeDurationToString(t time.Duration) string {
+	const day = 24 * time.Hour
+	const year = 365 * day
+
+	if t > year {
+		return fmt.Sprintf("%dY", t/year)
+	} else if t > day {
+		return fmt.Sprintf("%dD", t/day)
+	} else if t > time.Hour {
+		return fmt.Sprintf("%dh", t/time.Hour)
+	} else if t > time.Minute {
+		return fmt.Sprintf("%dmin", t/time.Minute)
+	} else if t > time.Second {
+		return fmt.Sprintf("%ds", t/time.Second)
+	}
+
+	return "0s"
+}

+ 83 - 0
src/utils/typeutils/stringbool.go

@@ -0,0 +1,83 @@
+// 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 typeutils
+
+import (
+	"strings"
+)
+
+type StringBool string
+
+const enable StringBool = "enable"
+const disable StringBool = "disable"
+const enableBool StringBool = "true"
+const disableBool StringBool = "false"
+
+func (s *StringBool) check() bool {
+	*s = StringBool(strings.ToLower(string(*s)))
+	return *s == enable || *s == disable || *s == enableBool || *s == disableBool
+}
+
+func (s *StringBool) is(v StringBool, defaultVal ...bool) (res bool) {
+	if !s.check() {
+		if len(defaultVal) == 1 {
+			res = defaultVal[0]
+			return
+		} else {
+			return false
+		}
+	}
+
+	return *s == v
+}
+
+func (s *StringBool) IsEnable(defaultVal ...bool) (res bool) {
+	res = s.is(enable, defaultVal...) || s.is(enableBool, defaultVal...)
+	return
+}
+
+func (s *StringBool) IsDisable(defaultVal ...bool) (res bool) {
+	res = s.is(disable, defaultVal...) || s.is(disableBool, defaultVal...)
+	return
+}
+
+func (s *StringBool) setDefault(v StringBool) {
+	if !s.check() {
+		*s = v
+	}
+}
+
+func (s *StringBool) SetDefaultEnable() {
+	s.setDefault(enable)
+}
+
+func (s *StringBool) SetDefaultDisable() {
+	s.setDefault(disable)
+}
+
+func (s *StringBool) ToString() string {
+	if s.IsEnable() {
+		return string(enable)
+	}
+	return string(disable)
+}
+
+func (s *StringBool) ToStringDefaultEnable() string {
+	if s.IsEnable(true) {
+		return string(enable)
+	}
+	return string(disable)
+}
+
+func (s *StringBool) ToStringDefaultDisable() string {
+	if s.IsEnable(false) {
+		return string(enable)
+	}
+	return string(disable)
+}
+
+func (s *StringBool) ToBool(defaultVal ...bool) bool {
+	return s.IsEnable(defaultVal...)
+}

+ 24 - 0
test_self/config.yaml

@@ -0,0 +1,24 @@
+name: Backend-Server-Template
+mode: debug
+time-zone: ""
+logger:
+    log-name: ""
+    log-level: debug
+    log-tag: enable
+    warn-writer:
+        write-to-std: stdout
+        write-to-file: ""
+        write-to-dir-with-date: ""
+        write-with-date-prefix: ""
+    error-writer:
+        write-to-std: stdout
+        write-to-file: ""
+        write-to-dir-with-date: ""
+        write-with-date-prefix: ""
+signal:
+    sigint-exit: enable
+    sigterm-exit: enable
+    sighup-exit: enable
+    sigquit-exit: ""
+server:
+    stop-wait-time: 10s