Bladeren bron

添加路径处理工具和配置文件监听功能

新增 `utils` 包中的路径处理函数,包括清理和标准化路径、比较路径是否相等。重构 `config` 包以支持自动重载配置文件,并在 `flagparser` 中增加了禁用自动重载的选项。同时,更新了日志和配置文件的相关逻辑以适应这些改动。
SongZihuan 3 maanden geleden
bovenliggende
commit
2148145a28

+ 82 - 28
src/config/config.go

@@ -3,34 +3,57 @@ package config
 import (
 import (
 	"github.com/SongZihuan/huan-proxy/src/config/configerr"
 	"github.com/SongZihuan/huan-proxy/src/config/configerr"
 	"github.com/SongZihuan/huan-proxy/src/config/rulescompile"
 	"github.com/SongZihuan/huan-proxy/src/config/rulescompile"
+	"github.com/SongZihuan/huan-proxy/src/flagparser"
+	"github.com/SongZihuan/huan-proxy/src/utils"
 	"github.com/fsnotify/fsnotify"
 	"github.com/fsnotify/fsnotify"
 	"os"
 	"os"
+	"path/filepath"
 	"sync"
 	"sync"
 )
 )
 
 
 type ConfigStruct struct {
 type ConfigStruct struct {
 	ConfigLock sync.Mutex
 	ConfigLock sync.Mutex
 
 
-	configReady   bool
-	yamlHasParser bool
-	sigchan       chan os.Signal
-	configPath    string
-	watcher       *fsnotify.Watcher
+	configReady    bool
+	yamlHasParser  bool
+	sigchan        chan os.Signal
+	configPath     string
+	configDir      string
+	configFileName string
+	watcher        *fsnotify.Watcher
 
 
 	Yaml  *YamlConfig
 	Yaml  *YamlConfig
 	Rules *rulescompile.RuleListCompileConfig
 	Rules *rulescompile.RuleListCompileConfig
 }
 }
 
 
-func newConfig(configPath string) ConfigStruct {
-	return ConfigStruct{
-		// Lock不用初始化
-		configReady:   false,
-		yamlHasParser: false,
-		sigchan:       make(chan os.Signal),
-		configPath:    configPath,
-		Yaml:          nil,
-		Rules:         nil,
+func newConfig(configPath string) (*ConfigStruct, error) {
+	if configPath == "" {
+		if !flagparser.IsReady() {
+			panic("flag is not ready")
+		}
+
+		configPath = flagparser.ConfigFile()
 	}
 	}
+
+	configPath, err := utils.ClearFilePathAbs(configPath)
+	if err != nil {
+		return nil, err
+	}
+
+	configDir := filepath.Dir(configPath)
+	configFileName := filepath.Base(configPath)
+
+	return &ConfigStruct{
+		// Lock不用初始化
+		configReady:    false,
+		yamlHasParser:  false,
+		sigchan:        make(chan os.Signal),
+		configPath:     configPath,
+		configDir:      configDir,
+		configFileName: configFileName,
+		Yaml:           nil,
+		Rules:          nil,
+	}, nil
 }
 }
 
 
 func (c *ConfigStruct) Init() (err configerr.ConfigError) {
 func (c *ConfigStruct) Init() (err configerr.ConfigError) {
@@ -72,26 +95,30 @@ func (c *ConfigStruct) Reload() (err configerr.ConfigError) {
 	}
 	}
 
 
 	bak := ConfigStruct{
 	bak := ConfigStruct{
-		configReady:   c.configReady,
-		yamlHasParser: c.yamlHasParser,
-		sigchan:       c.sigchan,
-		configPath:    c.configPath,
-		watcher:       c.watcher,
-		Yaml:          c.Yaml,
-		Rules:         c.Rules,
+		configReady:    c.configReady,
+		yamlHasParser:  c.yamlHasParser,
+		sigchan:        c.sigchan,
+		configPath:     c.configPath,
+		configDir:      c.configDir,
+		configFileName: c.configFileName,
+		watcher:        c.watcher,
+		Yaml:           c.Yaml,
+		Rules:          c.Rules,
 		// 新建类型
 		// 新建类型
 	}
 	}
 
 
 	defer func() {
 	defer func() {
 		if err != nil {
 		if err != nil {
 			*c = ConfigStruct{
 			*c = ConfigStruct{
-				configReady:   bak.configReady,
-				yamlHasParser: bak.yamlHasParser,
-				sigchan:       bak.sigchan,
-				configPath:    bak.configPath,
-				watcher:       c.watcher,
-				Yaml:          bak.Yaml,
-				Rules:         bak.Rules,
+				configReady:    bak.configReady,
+				yamlHasParser:  bak.yamlHasParser,
+				sigchan:        bak.sigchan,
+				configPath:     bak.configPath,
+				configDir:      bak.configDir,
+				configFileName: bak.configFileName,
+				watcher:        bak.watcher,
+				Yaml:           bak.Yaml,
+				Rules:          bak.Rules,
 				// 新建类型 Lock不需要复制
 				// 新建类型 Lock不需要复制
 			}
 			}
 		}
 		}
@@ -247,3 +274,30 @@ func (c *ConfigStruct) GetRulesList() *rulescompile.RuleListCompileConfig {
 
 
 	return c.Rules
 	return c.Rules
 }
 }
+
+func (c *ConfigStruct) GetConfigPathFile() string {
+	c.ConfigLock.Lock()
+	defer c.ConfigLock.Unlock()
+
+	// 不需要检查Ready
+
+	return c.configPath
+}
+
+func (c *ConfigStruct) GetConfigFileDir() string {
+	c.ConfigLock.Lock()
+	defer c.ConfigLock.Unlock()
+
+	// 不需要检查Ready
+
+	return c.configDir
+}
+
+func (c *ConfigStruct) GetConfigFileName() string {
+	c.ConfigLock.Lock()
+	defer c.ConfigLock.Unlock()
+
+	// 不需要检查Ready
+
+	return c.configFileName
+}

+ 91 - 0
src/config/configwatcher/watcher.go

@@ -0,0 +1,91 @@
+package configwatcher
+
+import (
+	"errors"
+	"github.com/SongZihuan/huan-proxy/src/config"
+	"github.com/SongZihuan/huan-proxy/src/logger"
+	"github.com/SongZihuan/huan-proxy/src/utils"
+	"github.com/fsnotify/fsnotify"
+)
+
+var watcher *fsnotify.Watcher
+
+func WatcherConfigFile() error {
+	if watcher != nil {
+		return nil
+	}
+
+	if !config.IsReady() {
+		panic("config file path is empty")
+	}
+
+	_watcher, err := fsnotify.NewWatcher()
+	if err != nil {
+		return err
+	}
+
+	// Add a path.
+	watcherDir := config.GetConfigFileDir()
+	err = _watcher.Add(watcherDir)
+	if err != nil {
+		return err
+	}
+
+	// Start listening for events.
+	go func() {
+		defer closeNotifyConfigFile()
+
+	OutSideCycle:
+		for {
+			select {
+			case event, ok := <-_watcher.Events:
+				if !ok {
+					return
+				}
+
+				// github.com/fsnotify/fsnotify v1.8.0
+				// 根据2024.1月的消息,暂时无法导出RenameFrom,无法跟着重命名
+				// issues: https://github.com/fsnotify/fsnotify/issues/630
+				if !utils.FilePathEqual(event.Name, config.GetConfigPathFile()) {
+					continue OutSideCycle
+				}
+
+				if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) {
+					err := config.ReloadConfig()
+					if err != nil && err.IsError() {
+						logger.Errorf("Config file reload error: %s", err.Error())
+					} else if err != nil && err.IsWarning() {
+						logger.Warnf("Config file reload error: %s", err.Warning())
+					} else {
+						logger.Infof("%s", "Config file reload success")
+					}
+				} else if event.Has(fsnotify.Rename) {
+					logger.Warnf("%s", "Config file has been rename")
+				} else if event.Has(fsnotify.Remove) {
+					logger.Warnf("%s", "Config file has been remove")
+				}
+			case err, ok := <-_watcher.Errors:
+				if !ok || errors.Is(err, fsnotify.ErrClosed) {
+					return
+				}
+				logger.Errorf("Config file notify error: %s", err.Error())
+			}
+		}
+	}()
+
+	watcher = _watcher
+	return nil
+}
+
+func CloseNotifyConfigFile() {
+	closeNotifyConfigFile()
+}
+
+func closeNotifyConfigFile() {
+	if watcher == nil {
+		return
+	}
+
+	_ = watcher.Close()
+	watcher = nil
+}

+ 28 - 11
src/config/export.go

@@ -3,22 +3,35 @@ package config
 import (
 import (
 	"github.com/SongZihuan/huan-proxy/src/config/configerr"
 	"github.com/SongZihuan/huan-proxy/src/config/configerr"
 	"github.com/SongZihuan/huan-proxy/src/config/rulescompile"
 	"github.com/SongZihuan/huan-proxy/src/config/rulescompile"
-	"github.com/SongZihuan/huan-proxy/src/flagparser"
 	"os"
 	"os"
 )
 )
 
 
 func InitConfig(configPath string) configerr.ConfigError {
 func InitConfig(configPath string) configerr.ConfigError {
-	if !flagparser.IsReady() {
-		return configerr.NewConfigError("flag not ready")
+	var err error
+	config, err = newConfig(configPath)
+	if err != nil {
+		return configerr.NewConfigError(err.Error())
 	}
 	}
 
 
-	config = newConfig(configPath)
-	err := config.Init()
+	cfgErr := config.Init()
+	if cfgErr != nil && cfgErr.IsError() {
+		return cfgErr
+	}
+
+	if !config.IsReady() {
+		return configerr.NewConfigError("config not ready")
+	}
+
+	return nil
+}
+
+func ReloadConfig() configerr.ConfigError {
+	err := config.Reload()
 	if err != nil && err.IsError() {
 	if err != nil && err.IsError() {
 		return err
 		return err
 	}
 	}
 
 
-	if !config.configReady {
+	if !config.IsReady() {
 		return configerr.NewConfigError("config not ready")
 		return configerr.NewConfigError("config not ready")
 	}
 	}
 
 
@@ -41,12 +54,16 @@ func GetSignalChan() chan os.Signal {
 	return config.GetSignalChan()
 	return config.GetSignalChan()
 }
 }
 
 
-func NotifyConfigFile() error {
-	return config.NotifyConfigFile()
+func GetConfigPathFile() string {
+	return config.GetConfigPathFile()
+}
+
+func GetConfigFileDir() string {
+	return config.GetConfigFileDir()
 }
 }
 
 
-func CloseNotifyConfigFile() {
-	config.CloseNotifyConfigFile()
+func GetConfigFileName() string {
+	return config.GetConfigFileName()
 }
 }
 
 
-var config ConfigStruct
+var config *ConfigStruct

+ 0 - 64
src/config/notifyconfig.go

@@ -1,64 +0,0 @@
-package config
-
-import (
-	"fmt"
-	"github.com/fsnotify/fsnotify"
-)
-
-func (c *ConfigStruct) NotifyConfigFile() error {
-	if c.watcher != nil {
-		return nil
-	}
-
-	watcher, err := fsnotify.NewWatcher()
-	if err != nil {
-		return err
-	}
-
-	// Add a path.
-	err = watcher.Add(c.configPath)
-	if err != nil {
-		return err
-	}
-
-	var stop = make(chan error)
-
-	// Start listening for events.
-	go func() {
-		for {
-			select {
-			case event, ok := <-watcher.Events:
-				if !ok {
-					stop <- nil
-					return
-				}
-				if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) {
-					err := c.Reload()
-					if err != nil {
-						fmt.Printf("Config file reload error: %s\n", err.Error())
-					} else {
-						fmt.Printf("Config file reload success\n")
-					}
-				} else if event.Has(fsnotify.Remove) || event.Has(fsnotify.Rename) {
-					fmt.Printf("Config file has been remove\n")
-				}
-			case err, ok := <-watcher.Errors:
-				if !ok {
-					stop <- nil
-					return
-				}
-				fmt.Printf("Config file notify error: %s\n", err.Error())
-			}
-		}
-	}()
-
-	return nil
-}
-
-func (c *ConfigStruct) CloseNotifyConfigFile() {
-	if c.watcher == nil {
-		return
-	}
-
-	_ = c.watcher.Close()
-}

+ 127 - 55
src/flagparser/data.go

@@ -20,21 +20,25 @@ type flagData struct {
 	flagSet    bool
 	flagSet    bool
 	flagParser bool
 	flagParser bool
 
 
-	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
+	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
+	NotAutoReloadData      bool
+	NotAutoReloadName      string
+	NotAutoReloadShortName string
+	NotAutoReloadUsage     string
 
 
 	Usage string
 	Usage string
 }
 }
@@ -45,22 +49,26 @@ func initData() {
 		flagSet:    false,
 		flagSet:    false,
 		flagParser: false,
 		flagParser: false,
 
 
-		HelpData:        false,
-		HelpName:        "help",
-		HelpUsage:       fmt.Sprintf("Show usage of %s. If this option is set, the backend service will not run.", utils.GetArgs0Name()),
-		VersionData:     false,
-		VersionName:     "version",
-		VersionUsage:    fmt.Sprintf("Show version of %s. If this option is set, the backend service will not run.", utils.GetArgs0Name()),
-		LicenseData:     false,
-		LicenseName:     "license",
-		LicenseUsage:    fmt.Sprintf("Show license of %s. If this option is set, the backend service will not run.", utils.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.", utils.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."),
-		Usage:           "",
+		HelpData:               false,
+		HelpName:               "help",
+		HelpUsage:              fmt.Sprintf("Show usage of %s. If this option is set, the backend service will not run.", utils.GetArgs0Name()),
+		VersionData:            false,
+		VersionName:            "version",
+		VersionUsage:           fmt.Sprintf("Show version of %s. If this option is set, the backend service will not run.", utils.GetArgs0Name()),
+		LicenseData:            false,
+		LicenseName:            "license",
+		LicenseUsage:           fmt.Sprintf("Show license of %s. If this option is set, the backend service will not run.", utils.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.", utils.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."),
+		NotAutoReloadData:      false,
+		NotAutoReloadName:      "not-auto-reload",
+		NotAutoReloadShortName: "",
+		NotAutoReloadUsage:     fmt.Sprintf("%s", "Disable automatic detection of configuration file changes and reloading of system programs. This feature is enabled by default. This feature consumes a certain amount of performance. If your performance is not enough, you can choose to disable it."),
+		Usage:                  "",
 	}
 	}
 
 
 	data.ready()
 	data.ready()
@@ -90,52 +98,116 @@ func (d *flagData) writeUsage() {
 		}
 		}
 
 
 		option := field.Name[:len(field.Name)-4]
 		option := field.Name[:len(field.Name)-4]
-		optionName, ok := val.FieldByName(option + "Name").Interface().(string)
-		if !ok {
-			panic("can not get option name")
+		optionName := ""
+		optionShortName := ""
+		optionUsage := ""
+
+		if utils.HasFieldByReflect(typ, option+"Name") {
+			var ok bool
+			optionName, ok = val.FieldByName(option + "Name").Interface().(string)
+			if !ok {
+				panic("can not get option name")
+			}
 		}
 		}
 
 
-		optionUsage, ok := val.FieldByName(option + "Usage").Interface().(string)
-		if !ok {
-			panic("can not get option Usage")
+		if utils.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 utils.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 title string
+		var title1 string
+		var title2 string
 		if field.Type.Kind() == reflect.Bool {
 		if field.Type.Kind() == reflect.Bool {
-			optionData, ok := val.FieldByName(option + "Data").Interface().(bool)
-			if !ok {
-				panic("can not get option data")
+			var optionData bool
+			if utils.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 {
 			if optionData == true {
 				panic("bool option can not be true")
 				panic("bool option can not be true")
 			}
 			}
 
 
-			title1 := fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(optionName, utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
-			title2 := fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(optionName[0:1], utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
-			title = fmt.Sprintf("%s\n%s", title1, title2)
+			if optionName != "" {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(optionName, utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+
+			if optionShortName != "" {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(optionShortName, utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
 		} else if field.Type.Kind() == reflect.String {
 		} else if field.Type.Kind() == reflect.String {
-			optionData, ok := val.FieldByName(option + "Data").Interface().(string)
-			if !ok {
-				panic("can not get option data")
+			var optionData string
+			if utils.HasFieldByReflect(typ, option+"Data") {
+				var ok bool
+				optionData, ok = val.FieldByName(option + "Data").Interface().(string)
+				if !ok {
+					panic("can not get option data")
+				}
 			}
 			}
 
 
-			title1 := fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s string, default: '%s'", optionName, optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
-			title2 := fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s string, default: '%s'", optionName[0:1], optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
-			title = fmt.Sprintf("%s\n%s", title1, title2)
+			if optionName != "" && optionData != "" {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s string, default: '%s'", optionName, optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionName != "" && optionData == "" {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s string", optionName), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+
+			if optionShortName != "" && optionData != "" {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s string, default: '%s'", optionShortName, optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionShortName != "" && optionData == "" {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s string", optionShortName), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
 		} else if field.Type.Kind() == reflect.Uint {
 		} else if field.Type.Kind() == reflect.Uint {
-			optionData, ok := val.FieldByName(option + "Data").Interface().(uint)
-			if !ok {
-				panic("can not get option data")
+			var optionData uint
+			if utils.HasFieldByReflect(typ, option+"Data") {
+				var ok bool
+				optionData, ok = val.FieldByName(option + "Data").Interface().(uint)
+				if !ok {
+					panic("can not get option data")
+				}
 			}
 			}
 
 
-			title1 := fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s number, default: %d", optionName, optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
-			title2 := fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s number, default: %d", optionName[0:1], optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
-			title = fmt.Sprintf("%s\n%s", title1, title2)
+			if optionName != "" && optionData != 0 {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s number, default: %d", optionName, optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionName != "" && optionData == 0 {
+				title1 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s number", optionName), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
+
+			if optionShortName != "" && optionData != 0 {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s number, default: %d", optionShortName, optionData), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			} else if optionShortName != "" && optionData == 0 {
+				title2 = fmt.Sprintf("%s%s%s", OptionIdent, OptionPrefix, utils.FormatTextToWidth(fmt.Sprintf("%s number", optionShortName), utils.NormalConsoleWidth-len(OptionIdent)-len(OptionPrefix)))
+			}
 		} else {
 		} else {
 			panic("error flag type")
 			panic("error flag type")
 		}
 		}
 
 
+		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(title)
 		result.WriteString("\n")
 		result.WriteString("\n")
 
 

+ 8 - 0
src/flagparser/getter.go → src/flagparser/export.go

@@ -98,6 +98,14 @@ func ConfigFile() string {
 	return data.ConfigFile()
 	return data.ConfigFile()
 }
 }
 
 
+func NotRunAutoReload() bool {
+	return data.NotAutoReloadData
+}
+
+func RunAutoReload() bool {
+	return !NotRunAutoReload()
+}
+
 func SetOutput(writer io.Writer) {
 func SetOutput(writer io.Writer) {
 	data.SetOutput(writer)
 	data.SetOutput(writer)
 }
 }

+ 0 - 7
src/logger/export.go

@@ -11,13 +11,6 @@ func Executablef(format string, args ...interface{}) string {
 	return globalLogger.Executablef(format, args...)
 	return globalLogger.Executablef(format, args...)
 }
 }
 
 
-func Executable() string {
-	if !IsReady() {
-		return ""
-	}
-	return globalLogger.Executable()
-}
-
 func Tagf(format string, args ...interface{}) {
 func Tagf(format string, args ...interface{}) {
 	if !IsReady() {
 	if !IsReady() {
 		return
 		return

+ 0 - 4
src/logger/logger.go

@@ -101,10 +101,6 @@ func (l *Logger) Executablef(format string, args ...interface{}) string {
 	return l.args0
 	return l.args0
 }
 }
 
 
-func (l *Logger) Executable() string {
-	return l.Executablef("")
-}
-
 func (l *Logger) Tagf(format string, args ...interface{}) {
 func (l *Logger) Tagf(format string, args ...interface{}) {
 	l.TagSkipf(1, format, args...)
 	l.TagSkipf(1, format, args...)
 }
 }

+ 23 - 12
src/mainfunc/v1.go

@@ -3,6 +3,7 @@ package mainfunc
 import (
 import (
 	"errors"
 	"errors"
 	"github.com/SongZihuan/huan-proxy/src/config"
 	"github.com/SongZihuan/huan-proxy/src/config"
+	"github.com/SongZihuan/huan-proxy/src/config/configwatcher"
 	"github.com/SongZihuan/huan-proxy/src/flagparser"
 	"github.com/SongZihuan/huan-proxy/src/flagparser"
 	"github.com/SongZihuan/huan-proxy/src/logger"
 	"github.com/SongZihuan/huan-proxy/src/logger"
 	"github.com/SongZihuan/huan-proxy/src/server"
 	"github.com/SongZihuan/huan-proxy/src/server"
@@ -29,22 +30,15 @@ func MainV1() int {
 		utils.SayGoodByef("%s", "The backend service program is offline/shutdown normally, thank you.")
 		utils.SayGoodByef("%s", "The backend service program is offline/shutdown normally, thank you.")
 	}()
 	}()
 
 
-	err = config.InitConfig(flagparser.ConfigFile())
-	if err != nil {
-		return utils.ExitByError(err)
+	cfgErr := config.InitConfig(flagparser.ConfigFile())
+	if cfgErr != nil && cfgErr.IsError() {
+		return utils.ExitByError(cfgErr)
 	}
 	}
 
 
 	if !config.IsReady() {
 	if !config.IsReady() {
 		return utils.ExitByErrorMsg("config parser unknown error")
 		return utils.ExitByErrorMsg("config parser unknown error")
 	}
 	}
 
 
-	cfg := config.GetConfig()
-	err = config.NotifyConfigFile()
-	if err != nil {
-		return utils.ExitByError(err)
-	}
-	defer config.CloseNotifyConfigFile()
-
 	err = logger.InitLogger(os.Stdout, os.Stderr)
 	err = logger.InitLogger(os.Stdout, os.Stderr)
 	if err != nil {
 	if err != nil {
 		return utils.ExitByError(err)
 		return utils.ExitByError(err)
@@ -54,8 +48,25 @@ func MainV1() int {
 		return utils.ExitByErrorMsg("logger unknown error")
 		return utils.ExitByErrorMsg("logger unknown error")
 	}
 	}
 
 
-	logger.Executable()
-	logger.Infof("run mode: %s", cfg.GlobalConfig.GetRunMode())
+	if flagparser.RunAutoReload() {
+		err = configwatcher.WatcherConfigFile()
+		if err != nil {
+			return utils.ExitByError(err)
+		}
+		defer configwatcher.CloseNotifyConfigFile()
+
+		err = logger.InitLogger(os.Stdout, os.Stderr)
+		if err != nil {
+			return utils.ExitByError(err)
+		}
+
+		logger.Infof("Auto reload enable.")
+	} else {
+		logger.Infof("Auto reload disable.")
+	}
+
+	logger.Executablef("%s", "ready")
+	logger.Infof("run mode: %s", config.GetConfig().GlobalConfig.GetRunMode())
 
 
 	ser := server.NewServer()
 	ser := server.NewServer()
 
 

+ 37 - 0
src/utils/path.go

@@ -0,0 +1,37 @@
+package utils
+
+import (
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+func ClearFilePathAbs(pathstr string) (string, error) {
+	pathstr, err := filepath.Abs(filepath.Clean(pathstr))
+	if err != nil {
+		return "", err
+	}
+
+	if runtime.GOOS == "windows" {
+		index := strings.Index(pathstr, `:\`)
+		pf := strings.ToUpper(pathstr[:index])
+		ph := pathstr[index:]
+		pathstr = pf + ph
+	}
+
+	return pathstr, nil
+}
+
+func FilePathEqual(path1, path2 string) bool {
+	path1, err := ClearFilePathAbs(path1)
+	if err != nil {
+		return false
+	}
+
+	path2, err = ClearFilePathAbs(path2)
+	if err != nil {
+		return false
+	}
+
+	return path1 == path2
+}

+ 12 - 0
src/utils/struct.go

@@ -0,0 +1,12 @@
+package utils
+
+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
+}