diff --git a/dailylib/viper/env/config.toml b/dailylib/viper/env/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..aedb3ec3d37d7a6aa4930e470ea86d18ec6431a6 --- /dev/null +++ b/dailylib/viper/env/config.toml @@ -0,0 +1,16 @@ +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" +gp = "test" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = "123456" +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 7381 \ No newline at end of file diff --git a/dailylib/viper/env/main.go b/dailylib/viper/env/main.go new file mode 100644 index 0000000000000000000000000000000000000000..ec3cdbac3f14d93ad1df64ec7aa5a71ea21a90a2 --- /dev/null +++ b/dailylib/viper/env/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "GopherUtils/utils" + "fmt" + "github.com/spf13/pflag" + "github.com/spf13/viper" + "log" +) + +/* +1.设置优先级 + viper.Set() > 环境变量 > pflag.Int --redis.Port > config.toml > pflag.Int default +2.绑定环境变量 + 2.1 viper.AutomaticEnv() + 绑定所有环境变量 + 2.2 viper.BindEnv("redis.port", "gopath") + 将gopath环境变量绑定到redis.port, 注意,如果没有gopath环境变量,则会将其转换为大写再查找一次 + 2.3 viper.BindEnv("gopath") + 将gopath环境变量绑定到gopath的key上(即key与环境变量同名) + 2.4 viper.SetEnvPrefix(in string) + 设置环境变量的前缀, 通过viper.Get(key)获取该环境变量时会自动在key前添加前缀 + + 2.5 viper.SetEnvKeyReplacer(".", "_") + 通过viper.Getxx获取时会自动将"."换成"_"查找环境变量 + */ + +func init() { + pflag.Int("redis.port", 6666, "redis port connect") + viper.AutomaticEnv() + viper.BindEnv("redis.port", "gopath") +} + +func main() { + curPath, err := utils.GetCurrentPath() + if err != nil { + log.Fatalf("get current path error: %v\n", err) + return + } + + viper.SetConfigName("config") + viper.SetConfigType("toml") + viper.AddConfigPath(curPath) + err = viper.ReadInConfig() + if err != nil { + log.Fatalf("read config failed: %v\n", err) + return + } + + fmt.Println(viper.Get("gopath")) + fmt.Println(viper.Get("redis.port")) +} \ No newline at end of file diff --git a/dailylib/viper/flag/config.toml b/dailylib/viper/flag/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..6f0f095f0bd78d4e26df96039ce7e9d57f39985b --- /dev/null +++ b/dailylib/viper/flag/config.toml @@ -0,0 +1,15 @@ +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = "123456" +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 7381 \ No newline at end of file diff --git a/dailylib/viper/flag/main.go b/dailylib/viper/flag/main.go new file mode 100644 index 0000000000000000000000000000000000000000..15e8a7ee5adbe0263c8dc389c63fe71f29e13c8c --- /dev/null +++ b/dailylib/viper/flag/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "GopherUtils/utils" + "fmt" + "github.com/spf13/pflag" + "github.com/spf13/viper" + "log" +) + +/* +1.Go获取当前路径的坑 + 1.1 通过系统函数,例如os.Getwd()获取的是当前工程目录,即go.mod所在的根目录 + 通过 filepath.Abs(".")获取到的也是当前工程目录("."表示就是当前工程目录) + 1.2 通过工程路径+相对路径(例如 ./dailylib/viper/flag/)在直接go run main.go的时可以正常运行 + 但是如果先通过go build -o m.exe main.go编译,然后再运行时获取目录时"."表示的又是当前路径 + os.Getwd()获取的也就当前路径,所以这是个大坑 + 1.3 要想解决1.1和1.2两个问题,正确获取当前文件所在的目录,需要通过runtime.Caller()获取 + // GetCurrentPath 获取当前文件所在的路径 + func GetCurrentPath(levels ...int) (string, error) { + level := 1 + if len(levels) > 0 { + level = levels[0] + } + // 以runtime.Caller外的第一层作为第0层,一直往上到第level层,就是获取到的funcName + // GetCurrentPath(第0层) -> GetCurrentCfgPath(第1层) -> main(第2层) + // 获取的funcName所在的文件,即对应fileName + // 第level-1层的funcName所在的行,即对于lineNo + _, file, _, ok := runtime.Caller(level) + if !ok { + return "", errors.New("get current path error") + } + curPath := path.Dir(file) + return curPath, nil + } + + 1.4 Config File "./dailylib/viper/flag" Not Found in "[]" + 则表示没有找到配置文件,很有可能是路径错了 + +2.viper绑定命令行 + 2.1 func init中定义需要绑定的pflag选项 + // 后面带P的表示可以设置短选项, 参数依次为选项名,短选项名,默认值,使用信息 + pflag.IntP("redis.port", "p", 6379, "redis port to connect") + 2.2 viper.BindPFlags绑定到pflag命令 + viper.BindPFlags(pflag.CommandLine) + 2.3 main中解析命令 + pflag.Parse() + 2.4 go build -o m.exe main.go + m.exe --redis.port 6377 则最终输出的redis port为6377 + 注意: 如果程序中存在通过viper.Set()设置的值,则具有最高优先级 + 否则,如果绑定了命令行,且通过命令行传入该参数,则最终会取从命令行传入的值 + 如果绑定了命令行,且没有通过命令行传入该参数,则会config.toml配置中设置的值 + 如果配置文件中找不到该key,才会取命令行设置的默认值 + viper.Set() > pflag.IntP --redis.port > config.toml> pflag.IntP default +*/ + +func init() { + pflag.IntP("redis.port", "p", 6379, "Redis port to connect") + // viper绑定命令行 + viper.BindPFlags(pflag.CommandLine) +} + +func main() { + pflag.Parse() + + curPath, err := utils.GetCurrentPath() + if err != nil { + fmt.Println(err) + } + viper.SetConfigName("config") + viper.SetConfigType("toml") + viper.AddConfigPath(curPath) + err = viper.ReadInConfig() + if err != nil { + log.Fatalf("read config failed: %v\n", err) + } + + fmt.Println(viper.Get("redis.port")) +} + diff --git a/dailylib/viper/get-func/config.toml b/dailylib/viper/get-func/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..95650ce3535e996cc3ddb84161f5718e6526349c --- /dev/null +++ b/dailylib/viper/get-func/config.toml @@ -0,0 +1,20 @@ +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" + +[server] +protocols = ["http", "https", "port"] +ports = [10000, 10001, 10002] +timeout = "3s" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = "123456" +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 7381 \ No newline at end of file diff --git a/dailylib/viper/get-func/main.go b/dailylib/viper/get-func/main.go new file mode 100644 index 0000000000000000000000000000000000000000..c804c3ddcaaf427ac635e402268d05667ad4331d --- /dev/null +++ b/dailylib/viper/get-func/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "github.com/spf13/viper" + "log" +) + +/* +1.viper的Getxx 系列方法 + 1.1 viper.GetType(string) + 所有的viper.GetType参数均为string类型,返回Type类型的数据,如果没有找到配置项,则返回Type类型对应的零值 + 1.1.1 viper.Get(string) 返回interface类型 + 1.1.2 viper.GetString(string) 返回string类型 + 1.1.3 viper.GetInt(string) 返回int类型 + 1.1.4 viper.GetDuration(string) 返回time.Duration类型 + 配置时应该写成 a="1s"这种形式,必须是time.ParseDuration可以解析的格式 + 1.1.5 viper.GetStringSlice(string) 返回[]string{}类型 + 1.1.6 viper.GetIntSlice(string) 返回[]int{}类型 + 1.1.7 viper.GetStringMap(string) 返回map[string]interface{}类型 + 1.1.8 viper.GetStringMapString(string) 返回map[string]string{}类型 + 注意:如果toml文件中,配置的变量为int类型,则根据GetStringMapString()取出来之后也会变成sting类型的value + 1.1.9 viper.AllSettings() + 返回所有的配置项,返回结果为map[string]interface{}类型,如果配置中包含嵌套的块,则取出来之后对应的是嵌套的字典 + + 1.2 viper.IsSet(string) + 判断某个配置项是否存在 + */ + +func main() { + viper.SetConfigName("config") + viper.SetConfigType("toml") + viper.AddConfigPath("./dailylib/viper/get-func") + err := viper.ReadInConfig() + if err != nil { + log.Fatalf("read config failed: %v\n", err) + } + + fmt.Println("protocols: ", viper.GetStringSlice("server.protocols")) + fmt.Println("ports: ", viper.GetIntSlice("server.ports")) + fmt.Println("timeout: ", viper.GetDuration("server.timeout")) + + fmt.Println("mysql ip: ", viper.GetString("mysql.ip")) + fmt.Println("mysql port: ", viper.GetInt("mysql.port")) + + if viper.IsSet("redis.port") { + fmt.Println("redis.port is set") + } else { + fmt.Println("redis.port is not set") + } + + fmt.Println("mysql settings: ", viper.GetStringMap("mysql")) + fmt.Println("mysql settings: ", viper.GetStringMapString("mysql")) + fmt.Println("redis settings: ", viper.GetStringMap("redis")) + fmt.Println("all settings: ", viper.AllSettings()) +} diff --git a/dailylib/viper/get-started/config.toml b/dailylib/viper/get-started/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..6f0f095f0bd78d4e26df96039ce7e9d57f39985b --- /dev/null +++ b/dailylib/viper/get-started/config.toml @@ -0,0 +1,15 @@ +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = "123456" +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 7381 \ No newline at end of file diff --git a/dailylib/viper/get-started/main.go b/dailylib/viper/get-started/main.go new file mode 100644 index 0000000000000000000000000000000000000000..5e3541575720c16b1f207a2599e19a7031b1f02c --- /dev/null +++ b/dailylib/viper/get-started/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "fmt" + "github.com/spf13/viper" + "log" +) + +/* +1.go get报错:go get github/spf13/viper: malformed module path "github/spf13/viper": missing dot in first path element + 第一个路径缺少. 将github改成github.com即可 +2.toml格式 + 2.1 toml key value + a = "b" + 2.2 配置块[mysql] 嵌套配置通过.来获取,例如获取mysql配置块下的b变量 viper.Get("mysql.b") + [mysql] + a = 1 + b = "xm" + 2.3 类型 + toml文件中 a = 1表示a是整型, b = "xm"表示b是字符串类型 + viper会自动根据toml中设置的类型来读取配置 +3.viper内置函数 + 3.1 viper.SetConfigName("config") + 设置配置文件名,注意这里不需要加后缀 + 3.2 viper.SetConfigType("toml") + 设置配置文件类型 + 3.3 viper.AddConfigPath("./dailylib/viper/get-started") + 设置搜索路径,可以设置多个,但是一个viper.AddConfigPath只能添加一个路径(参数是 in string) + viper会根据设置的路径逐个查找配置文件 + 3.4 viper.ReadInConfig() + 加载配置文件, 返回error类型 + err := viper.ReadInConfig() + if err != nil {xxx} + 3.5 viper.SetDefault("redis.port", 6379) + 当获取不到配置时,设置默认值 + 3.5 viper.Get("mysql.port") + 返回interface类型的值 +*/ + +func main() { + // 不需要带有后缀,后缀名通过toml设置 + viper.SetConfigName("config") + // 设置文件类型 + viper.SetConfigType("toml") + // 设置搜索路径,可以添加多个 + viper.AddConfigPath("./dailylib/viper/get-started") + viper.SetDefault("redis.port", 6381) + err := viper.ReadInConfig() + if err != nil { + log.Fatalf("read config failed: %v\n", err) + } + + fmt.Println(viper.Get("app_name")) + fmt.Println(viper.Get("toml")) + + fmt.Println("mysql ip: ", viper.Get("mysql.ip")) + fmt.Println("mysql port: ", viper.Get("mysql.port")) + fmt.Println("mysql user: ", viper.Get("mysql.user")) + fmt.Println("mysql password: ", viper.Get("mysql.password")) + fmt.Println("mysql database: ", viper.Get("mysql.database")) + + fmt.Println("redis ip: ", viper.Get("redis.ip")) + fmt.Println("redis port: ", viper.Get("redis.port")) +} diff --git a/dailylib/viper/reader/main.go b/dailylib/viper/reader/main.go new file mode 100644 index 0000000000000000000000000000000000000000..8a093c56ad7052870213661095de611f9bef48f9 --- /dev/null +++ b/dailylib/viper/reader/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/spf13/viper" + "log" +) + + +/* +1.viper从io.Reader读取 + 1.1 程序模式 + viper.SetConfigType("toml") + configBytes := []byte(xxx) + viper.ReadConfig(bytes.NewBuffer(configBytes)) + 1.2 io.Reader/io.ReadCloser/io.Writer/io.WriterClose/io.ReadWriter/io.ReadWriteCloser + 基础接口有三个: io.Reader/io.Writer/io.Closer; + 分别包含了Read(p []byte) (n int, err error)/Write(p []byte)(n int, err error)/Close() error三个方法 + 1.2.1 带有Read的均是包含Read(p []byte) (n int, err error)方法的接口 + 1.2.2 带有Write的均是包含Write(p []byte) (n int, err error)方法的接口 + 1.2.3 带有Close的均包含了io.Closer接口 + 1.2.4 io.ReadWriter包含了io.Reader和io.Writer两个接口; io.ReadWriteCloser包含了io.Reader/io.Writer/io.Closer三个接口 + 1.2 viper.ReadConfig(in io.Reader) + 任意实现了io.Reader接口的类型,均可以作为参数传入 + + 1.3 bytes.NewBuffer(buf []byte) *Buffer + 以buf作为初始化内容,创建一个buffer(缓冲器), *Buffer实现了Read()/Write方法,所以相当于既实现了io.Reader接口又实现了io.Writer接口 + 可以作为参数传入viper.ReadConfig() + + + +2.viper.ReadConfig和viper.ReadInConfig区别 +*/ +func main() { + viper.SetConfigType("toml") + tomlConfig := []byte(` +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = 123456 +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 7381 +`) + err := viper.ReadConfig(bytes.NewBuffer(tomlConfig)) + if err != nil { + log.Fatalf("read config failed: %v\n", err) + return + } + fmt.Println("redis port: ", viper.GetInt("redis.port")) +} diff --git a/dailylib/viper/save/config.toml b/dailylib/viper/save/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..5a081df8b0463ebb8c871d83845822ef3e988ab0 --- /dev/null +++ b/dailylib/viper/save/config.toml @@ -0,0 +1,13 @@ +app_name = "awesome web" +log_level = "DEBUG" + +[mysql] + database = "awesome" + ip = "127.0.0.1" + password = "123456" + port = 3306 + user = "dj" + +[redis] + ip = "127.0.0.1" + port = 6379 diff --git a/dailylib/viper/save/main.go b/dailylib/viper/save/main.go new file mode 100644 index 0000000000000000000000000000000000000000..6f0047b79203645b05828bd5a66f0982c42124f4 --- /dev/null +++ b/dailylib/viper/save/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "GopherUtils/utils" + "github.com/spf13/viper" + "log" +) + +/* +1.通过Set设置配置后写入文件 + 1.1 如果不通过viper.ReadInConfig()先把配置文件中的内容读取出来,直接viper.Set()之后便WriteConfig(),则写入的内容只有viper.Set()的部分 + 配置文件中原来存在的内容会清空, 所以要想在保留配置文件原来内容的基础上修改,则需要先通过viper.ReadInConfig()读取出来 + 1.2 viper.WriteConfig() + 将Set的设置写回通过viper.SetConfigName和viper.SetConfigType以及viper.AddConfigPath设置的路径中(下面简称为预设路径) + 如果预设路径不存在,则会报错,如果有则会覆盖 + 1.3 viper.SafeWriteConfig() + 如果预设路径的文件已存在,则不会覆盖 + 1.4 viper.WriteConfigAs(filename string) + 将viper的配置内容写入到指定的filename中,如果存在则会覆盖 + 1.5 viper.SafeWriteConfigAs(filename string) + 将viper的配置内容写入到指定的filename中,如果文件存在则不会覆盖 +*/ + +func main() { + curPath, err := utils.GetCurrentPath() + if err != nil { + log.Fatalf("get current path error: %v\n", err) + return + } + + viper.SetConfigName("config") + viper.SetConfigType("toml") + viper.AddConfigPath(curPath) + err = viper.ReadInConfig() + if err != nil { + log.Fatalf("read config error: %v\n", err) + return + } + + // 原本的port为7777, 设置为6379后写回到预设置的文件,即当前路径下的config.toml + viper.Set("redis.port", 6379) + err = viper.WriteConfig() + if err != nil { + log.Fatalf("write config error: %v\n", err) + return + } +} diff --git a/dailylib/viper/set-func/config.toml b/dailylib/viper/set-func/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..6f0f095f0bd78d4e26df96039ce7e9d57f39985b --- /dev/null +++ b/dailylib/viper/set-func/config.toml @@ -0,0 +1,15 @@ +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = "123456" +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 7381 \ No newline at end of file diff --git a/dailylib/viper/set-func/main.go b/dailylib/viper/set-func/main.go new file mode 100644 index 0000000000000000000000000000000000000000..881f6518c7d5cd5e462e1900f5130599eecdaef4 --- /dev/null +++ b/dailylib/viper/set-func/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "github.com/spf13/viper" + "log" +) + +/* +1.viper四步法 + viper.SetConfigName + viper.SetConfigType + viper.AddConfigPath() + err := viper.ReadInConfig() + if err != nil {xxx} +2.viper.Set(string, value interface{}) //设置配置项的值 + 2.1 注意:只有使用Set设置后,再通过viper.ReadInConfig读取出来才有效,如果在Set之前就读取了,那么设置的值是不会生效的 + 2.2 设置的值不会修改config.toml + 2.3 viper.Set()具有最高优先级 + */ + +func main() { + viper.SetConfigName("config") + viper.SetConfigType("toml") + // 设置查找路径 + viper.AddConfigPath("./dailylib/viper/set-func") + err := viper.ReadInConfig() + if err != nil { + log.Fatalf("read config failed: %v\n", err) + } + fmt.Println("set redis port before, redis.port", viper.Get("redis.port")) + + // 设置键值 + viper.Set("redis.port", 6379) + + err = viper.ReadInConfig() + if err != nil { + log.Fatalf("read config failed: %v\n", err) + } + fmt.Println("set redis port after, redis.port: ", viper.Get("redis.port")) +} diff --git a/dailylib/viper/unmarshal/config.toml b/dailylib/viper/unmarshal/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..6f0f095f0bd78d4e26df96039ce7e9d57f39985b --- /dev/null +++ b/dailylib/viper/unmarshal/config.toml @@ -0,0 +1,15 @@ +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = "123456" +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 7381 \ No newline at end of file diff --git a/dailylib/viper/unmarshal/main.go b/dailylib/viper/unmarshal/main.go new file mode 100644 index 0000000000000000000000000000000000000000..899ed04e1bacb00fd0537ab6ecb231ec921c32ba --- /dev/null +++ b/dailylib/viper/unmarshal/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "GopherUtils/utils" + "fmt" + "github.com/spf13/viper" + "log" +) + +/* +1.从配置文件中读取内容,然后通过viper.Unmarshal()将数据写入到结构体对象中 + var c Config + viper.Unmarshal(&c) + 注意:结构体的嵌套格式应该与配置保持一致 + 例如config.toml中配置为: + app_name = "test" + [mysql] + ip = xxxx + port = xxx + 则对应结构体为: + type Config struct{ + AppName string + MySQLConfig + } + + type MySQLConfig struct { + Ip string + Port int + } + + 1.2 如果结构体中存在配置中没有的key,则反序列化之后该字段值为对应类型的零值 + 1.3 如果同一级配置中,存在多个除了大小写外相同的key,例如: + Ip = "127.0.0.1" + IP = "127.0.0.3" + ip = "127.0.0.4" + Ip = "127.0.0.1" + 则选择优先级: IP > Ip > iP > ip +*/ + +type Config struct { + AppName string + LogLevel string + MySQL MySQLConfig + Redis RedisConfig +} + +// MySQL配置 +type MySQLConfig struct { + IP string + Port int + User string + Password string + Database string +} + +// redis配置 +type RedisConfig struct { + IP string + Port int +} + +func main() { + curPath, err := utils.GetCurrentPath() + if err != nil { + log.Fatalf("get current path error: %v\n", err) + return + } + + viper.SetConfigName("config") + viper.SetConfigType("toml") + viper.AddConfigPath(curPath) + err = viper.ReadInConfig() + if err != nil { + log.Fatalf("read config failed: |%v\n", err) + return + } + + var c Config + viper.Unmarshal(&c) + fmt.Println(c.MySQL) +} diff --git a/dailylib/viper/watch/config.toml b/dailylib/viper/watch/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..a9ea591c2faec56b3b2561e857333d47bbd7646b --- /dev/null +++ b/dailylib/viper/watch/config.toml @@ -0,0 +1,15 @@ +app_name = "awesome web" + +# possible values: DEBUG, INFO, WARNING, ERROR, FATAL +log_level = "DEBUG" + +[mysql] +ip = "127.0.0.1" +port = 3306 +user = "dj" +password = "123456" +database = "awesome" + +[redis] +ip = "127.0.0.1" +port = 6379 \ No newline at end of file diff --git a/dailylib/viper/watch/main.go b/dailylib/viper/watch/main.go new file mode 100644 index 0000000000000000000000000000000000000000..30ff1b995e29e3493438cc58b98f7a2b594be04c --- /dev/null +++ b/dailylib/viper/watch/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "GopherUtils/utils" + "github.com/fsnotify/fsnotify" + "github.com/spf13/viper" + "log" + "time" +) + +/* +1.监听文件修改 + viper.WatchConfig() + 注意:当修改文件之后,需要ctrl+s保存之后viper才能检测到 +2.监听文件事件回调 + // notify 美[ˈnoʊtɪfaɪ] 通知 + // 当检测到文件被修改时,则会调用该回调函数 + // in.String()包含in.Name()和in.Op + // in.Name()是修改的文件名,in.Op是操作的名称 + viper.OnConfigChange(func(in fsnotify.Event){ + fmt.Println(in.Name(), in.Op, in.String()) + }) + + 2021/10/12 15:24:56 redis.port: 7777 + 2021/10/12 15:24:59 Config file:D:\PrivateProject\Utils-Tags\GopherUtils\dailylib\viper\watch\config.toml, Opt: WRITE + , event: "D:\\PrivateProject\\Utils-Tags\\GopherUtils\\dailylib\\viper\\watch\\config.toml": WRITE + 2021/10/12 15:24:59 Config file:D:\PrivateProject\Utils-Tags\GopherUtils\dailylib\viper\watch\config.toml, Opt: WRITE + , event: "D:\\PrivateProject\\Utils-Tags\\GopherUtils\\dailylib\\viper\\watch\\config.toml": WRITE + 2021/10/12 15:24:59 Config file:D:\PrivateProject\Utils-Tags\GopherUtils\dailylib\viper\watch\config.toml, Opt: WRITE + , event: "D:\\PrivateProject\\Utils-Tags\\GopherUtils\\dailylib\\viper\\watch\\config.toml": WRITE + 2021/10/12 15:25:06 redis.port: 6379 +*/ + +func main() { + curPath, err := utils.GetCurrentPath() + if err != nil { + log.Fatalf("get current path error:%v\n", err) + return + } + viper.SetConfigName("config") + viper.SetConfigType("toml") + viper.AddConfigPath(curPath) + err = viper.ReadInConfig() + if err != nil { + log.Fatalf("read config error:%v\n", err) + return + } + + viper.WatchConfig() + // 事件回调,当检测到文件修改时会执行这个回调 + viper.OnConfigChange(func(in fsnotify.Event) { + log.Printf("Config file:%s, Opt: %s\n, event: %s", in.Name, in.Op, in.String()) + }) + log.Println("redis.port: ", viper.Get("redis.port")) + time.Sleep(10 * time.Second) + log.Println("redis.port: ", viper.Get("redis.port")) +} \ No newline at end of file diff --git a/gotrick/README.md b/gotrick/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7b19b89196b3e7e3206f2c7f885cfd740e08562a --- /dev/null +++ b/gotrick/README.md @@ -0,0 +1,4 @@ +gotrick是一个用于存放小工具的地方,这些小工具用于简化日常繁琐工作或提高效率 + +glwifi-在连接WIFI时,经常会弹出一些需要进行用户信息认证的登录页面,每次都要输入账户密码过于麻烦,该库以Chrome插件 + 的形式自动检测浏览器地址栏地址,当检测到事先设置好的登录认证URL后,会自动输入事先配置好的账户密码自动登录 diff --git a/standard/path/example.go b/standard/path/example.go index 11d6affa073fa31d30768eb259939a1ec7173c75..ad02a0c139e986a4feb4fd126ddcb99e1502f4fb 100644 --- a/standard/path/example.go +++ b/standard/path/example.go @@ -1,23 +1,59 @@ -package path - -import ( - "fmt" - "path" -) - -func Example() { - // 1.传入一个表示路径的字符串参数,返回该路径的上一级路径,例如传入"/a/b/c"则返回/a/b; 传入/a/b/c/则返回/a/b/c - a := "/a/b/c" - b := "/a/b/c/" - - ans1 := path.Dir(a) - ans2 := path.Dir(b) - fmt.Printf("before: %s, path.Dir: %s\n", a, ans1) - fmt.Printf("before: %s, path.Dir: %s\n", b, ans2) - - // 2.拼接路径/path.Join() - a = "/a/b" - b = "c/d" - join := path.Join(a, b) - fmt.Printf("before: %s, %s; path.Join: %s", a, b, join) -} +package path + +import ( + "errors" + "fmt" + "os" + "path" + "runtime" +) + +func Example() { + // 1.传入一个表示路径的字符串参数,返回该路径的上一级路径,例如传入"/a/b/c"则返回/a/b; 传入/a/b/c/则返回/a/b/c + a := "/a/b/c" + b := "/a/b/c/" + + ans1 := path.Dir(a) + ans2 := path.Dir(b) + fmt.Printf("before: %s, path.Dir: %s\n", a, ans1) + fmt.Printf("before: %s, path.Dir: %s\n", b, ans2) + + // 2.拼接路径/path.Join() + a = "/a/b" + b = "c/d" + join := path.Join(a, b) + fmt.Printf("before: %s, %s; path.Join: %s", a, b, join) + + // 3.获取当前工程路径(注意,工程路径是指当前工程的根目录路径,即go.mod所在的路径,而不是当前路径) + cur, err := os.Getwd() + if err != nil { + fmt.Println("获取当前工程路径失败: ", err) + return + } + fmt.Println("当前工程路径: ", cur) + + // 4.获取当前路径 需要通过runtime.Caller()获取 + curPath, err := GetCurrentPath() + if err != nil { + fmt.Println("获取当前文件所在路径失败: ", err) + return + } + fmt.Println("当前文件所在路径:", curPath) +} + +// 规律:fileName是funcName所在的文件名,funcName指定函数名,然后lineNo则为funcName里面的调用层级比funcName低一层的函数位置 +// +// n传入0,funcName为直接调用runtime.Caller的函数(GetCurrentPath),fileName为funcName(GetCurrentPath)所在的文件, +// lineNo则为runtime.Caller所在的位置 +// funcName: GetCurrentPath filePath: example.go lineNo: 53 +// +// n传入1,表示调用 直接调用runtime.Caller的函数, 即调用getInfo的函数log,行号表示的是调用getInfo()函数的位置 +// funcName: Example filePath: example.go lineNo: 36 +func GetCurrentPath() (string, error) { + _, file, _, ok := runtime.Caller(1) + if !ok { + return "", errors.New("get cur path error") + } + curPath := path.Dir(file) + return curPath, nil +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..63ee6f3a4cb7c3c82b8c72f4cc30d413cf08c1a3 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,50 @@ +package utils + +import ( + "errors" + "path" + "runtime" +) + +// GetCurrentPath 获取当前文件所在的路径 +func GetCurrentPath(levels ...int) (string, error) { + level := 1 + if len(levels) > 0 { + level = levels[0] + } + // 以runtime.Caller外的第一层作为第0层,一直往上到第level层,就是获取到的funcName + // GetCurrentPath(第0层) -> GetCurrentCfgPath(第1层) -> main(第2层) + // 获取的funcName所在的文件,即对应fileName + // 第level-1层的funcName所在的行,即对于lineNo + _, file, _, ok := runtime.Caller(level) + if !ok { + return "", errors.New("get current path error") + } + curPath := path.Dir(file) + return curPath, nil +} + +// GetCurrentCfgPath 获取当前路径下的配置文件的绝对路径 +func GetCurrentCfgPath(cfgFiles ...string) (string, error) { + cfgFile := "config.toml" + if len(cfgFiles) > 0 { + cfgFile = cfgFiles[0] + } + // levels传入0, funcName: GetCurrentPath(调用runtime.Caller的函数名,即第10行GetCurrentPath的函数声明) + // fileName: GetCurrentPath函数声明的部分所在的文件路径(t.go的绝对路径) + // lineNo: 调用runtime.Caller()的位置 第15行 + // + // levels传入1,funcName: 调用GetCurrentPath()的函数的函数名,即GetCurrentCfgPath + // fileName: GetCurrentCfgPath函数声明部分所在的文件的绝对路径,即t.go的绝对路径 + // lineNo: 调用GetCurrentPath()所在的行 + // + // levels传入2,funcName: 调用GetCurrentCfgPath的函数的函数名,即main + // fileName: main所在的文件,即main.go + // lineNo: 调用GetCurrentCfgPath()所在的行 + curPath, err := GetCurrentPath(2) + if err != nil { + return "", err + } + cfgPath := path.Join(curPath, cfgFile) + return cfgPath, nil +} \ No newline at end of file