// Cfg 是全局配置
var Cfg *models.Config
func InitConfig() {
// 创建 Koanf 实例
k := koanf.New(".")
proxy := file.Provider("conf/proxy.yaml")
// 加载第一个配置文件
if err := k.Load(proxy, yaml.Parser()); err != nil {
log.Fatalf("Error loading config file 'proxy.yaml': %v", err)
}
// 加载第二个配置文件并合并
if err := k.Load(file.Provider("conf/backup_upstream.yaml"), yaml.Parser()); err != nil {
log.Fatalf("Error loading config file 'backup_upstream.yaml': %v", err)
}
// 将配置解码到结构体中
if err := k.Unmarshal("", &Cfg); err != nil {
log.Fatalf("Unable to unmarshal into struct: %v", err)
}
setDefaults(Cfg)
fmt.Println("配置文件加载成功")
}
// 从结构体标签中提取默认值并设置到 koanf 中
func setDefaults(v interface{}, k *koanf.Koanf, parentPath string, isNil bool) {
if v == nil {
return
}
val := reflect.ValueOf(v).Elem() // 获取指向结构体的指针
if !val.IsValid() {
// 处理无效值的情况
return
}
typ := val.Type()
// 遍历结构体的每个字段
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
// 获取字段标签中的默认值
defaultValue := fieldType.Tag.Get("default")
yamlPath := fieldType.Tag.Get("yaml")
// 如果字段不可设置 (unexported field), 跳过它
if !field.CanSet() || yamlPath == "" {
continue
}
// Construct the full key path (e.g., "log.pull.enable")
fullPath := ""
if parentPath != "" {
fullPath = parentPath + "." + yamlPath
} else {
fullPath = yamlPath
}
// 如果是嵌套结构体
if field.Kind() == reflect.Struct {
// 递归调用 setDefaults 以设置嵌套结构体的默认值
setDefaults(field.Addr().Interface(), k, fullPath, isNil)
} else if field.Kind() == reflect.Slice {
// 如果字段是 nil,初始化为一个空的切片
if field.IsNil() && isNil {
newSlice := reflect.MakeSlice(field.Type(), 0, 0)
field.Set(newSlice)
}
// 获取切片的当前长度
sliceLen := field.Len()
// 如果切片为空并且存在默认值,插入默认值
defaultValue := fieldType.Tag.Get("default")
if sliceLen == 0 && defaultValue != "" {
// 为切片元素类型实例化一个元素
sliceElem := reflect.New(field.Type().Elem()).Elem()
// 根据字段的默认值类型设置元素值
if sliceElem.Kind() == reflect.String {
sliceElem.SetString(defaultValue)
} else if sliceElem.Kind() == reflect.Int {
defaultValueInt, err := strconv.Atoi(defaultValue)
if err != nil {
log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
}
sliceElem.SetInt(int64(defaultValueInt))
} else if sliceElem.Kind() == reflect.Bool {
defaultValueBool, err := strconv.ParseBool(defaultValue)
if err != nil {
log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
}
sliceElem.SetBool(defaultValueBool)
}
// 将新元素追加到切片中
field.Set(reflect.Append(field, sliceElem))
}
// 现在可以安全地访问切片元素
for i := 0; i < field.Len(); i++ {
sliceElem := field.Index(i)
// 如果元素是结构体,递归设置它的默认值
if sliceElem.Kind() == reflect.Struct {
setDefaults(sliceElem.Addr().Interface(), k, fullPath, isNil)
} else if sliceElem.Kind() == reflect.Ptr {
// 如果切片元素是指针类型并且为 nil,实例化为新的指针
if sliceElem.IsNil() && isNil {
newPtr := reflect.New(sliceElem.Type().Elem())
sliceElem.Set(newPtr)
// 如果指针指向结构体或 map,递归设置默认值
if newPtr.Elem().Kind() == reflect.Struct {
setDefaults(newPtr.Interface(), k, fullPath, isNil)
} else if newPtr.Elem().Kind() == reflect.Map {
if newPtr.Elem().IsNil() {
newPtr.Elem().Set(reflect.MakeMap(newPtr.Elem().Type()))
}
}
} else {
// 如果指针不为 nil,递归设置它指向的值
setDefaults(sliceElem.Interface(), k, fullPath, isNil)
}
}
}
} else if field.Kind() == reflect.Ptr {
// 如果是指针类型,检查是否为 nil 并且是结构体类型
if field.IsNil() && isNil {
// 如果字段为空,实例化为默认的零值
newPtr := reflect.New(field.Type().Elem())
field.Set(newPtr)
// Check if the newly created pointer is a struct or map, and instantiate if needed
if newPtr.Elem().Kind() == reflect.Struct {
setDefaults(newPtr.Interface(), k, fullPath, isNil) // Recursively set defaults for the nested struct
} else if newPtr.Elem().Kind() == reflect.Map {
if newPtr.Elem().IsNil() {
newPtr.Elem().Set(reflect.MakeMap(newPtr.Elem().Type()))
}
}
} else {
// If pointer is not nil, recursively set defaults for the value it points to
setDefaults(field.Interface(), k, fullPath, isNil)
}
} else if field.Kind() == reflect.Map {
// 如果是 map 类型 (例如 Upstreams),检查是否为 nil 并实例化
if field.IsNil() && isNil {
newMap := reflect.MakeMap(field.Type())
// 实例化为一个空的 map
field.Set(newMap)
}
// 遍历 map 的值
for _, key := range field.MapKeys() {
mapValue := field.MapIndex(key)
if mapValue.Kind() == reflect.Ptr && !mapValue.IsNil() {
// 对 map 中的每个指针值递归调用 setDefaults
setDefaults(mapValue.Interface(), k, fullPath, isNil)
}
}
} else if defaultValue != "" && field.IsZero() && !k.Exists(fullPath) {
// 仅当字段是零值时,才设置默认值
if field.Kind() == reflect.String {
field.SetString(defaultValue)
} else if field.Kind() == reflect.Int {
defaultValueInt, err := strconv.Atoi(defaultValue)
if err != nil {
log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
}
field.SetInt(int64(defaultValueInt))
} else if field.Kind() == reflect.Bool {
defaultValueBool, err := strconv.ParseBool(defaultValue)
if err != nil {
log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
}
field.SetBool(defaultValueBool)
}
}
}
}
根据koanf项判断key项不存在再使用默认值
func setDefaults(v interface{}, k *koanf.Koanf, parentPath string) {
if v == nil {
return
}
val := reflect.ValueOf(v).Elem() // 获取指向结构体的指针
typ := val.Type()
// 遍历结构体的每个字段
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
// 获取字段标签中的默认值
defaultValue := fieldType.Tag.Get("default")
yamlPath := fieldType.Tag.Get("yaml")
// 如果字段不可设置 (unexported field), 跳过它
if !field.CanSet() || yamlPath == "" {
continue
}
// Construct the full key path (e.g., "log.pull.enable")
fullPath := ""
if parentPath != "" {
fullPath = parentPath + "." + yamlPath
} else {
fullPath = yamlPath
}
// 如果是嵌套结构体
if field.Kind() == reflect.Struct {
// 递归调用 setDefaults 以设置嵌套结构体的默认值
setDefaults(field.Addr().Interface(), k, fullPath)
} else if field.Kind() == reflect.Slice {
// 如果是切片类型,检查是否为 nil 并实例化
if field.IsNil() {
// 如果字段为空,实例化为一个空的切片
newSlice := reflect.MakeSlice(field.Type(), 0, 0)
field.Set(newSlice)
}
} else if field.Kind() == reflect.Ptr {
// 如果是指针类型,检查是否为 nil 并且是结构体类型
if field.IsNil() {
// 如果字段为空,实例化为默认的零值
newPtr := reflect.New(field.Type().Elem())
field.Set(newPtr)
// Check if the newly created pointer is a struct or map, and instantiate if needed
if newPtr.Elem().Kind() == reflect.Struct {
setDefaults(newPtr.Interface(), k, fullPath) // Recursively set defaults for the nested struct
} else if newPtr.Elem().Kind() == reflect.Map {
if newPtr.Elem().IsNil() {
newPtr.Elem().Set(reflect.MakeMap(newPtr.Elem().Type()))
}
}
} else {
// If pointer is not nil, recursively set defaults for the value it points to
setDefaults(field.Interface(), k, fullPath)
}
} else if field.Kind() == reflect.Map {
// 如果是 map 类型 (例如 Upstreams),检查是否为 nil 并实例化
if field.IsNil() {
newMap := reflect.MakeMap(field.Type())
// 实例化为一个空的 map
field.Set(newMap)
}
// 遍历 map 的值
for _, key := range field.MapKeys() {
mapValue := field.MapIndex(key)
if mapValue.Kind() == reflect.Ptr && !mapValue.IsNil() {
// 对 map 中的每个指针值递归调用 setDefaults
setDefaults(mapValue.Interface(), k, fullPath)
}
}
} else if defaultValue != "" && field.IsZero() && !k.Exists(fullPath) {
fmt.Println(fullPath)
// 仅当字段是零值时,才设置默认值
if field.Kind() == reflect.String {
field.SetString(defaultValue)
} else if field.Kind() == reflect.Int {
defaultValueInt, err := strconv.Atoi(defaultValue)
if err != nil {
log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
}
field.SetInt(int64(defaultValueInt))
} else if field.Kind() == reflect.Bool {
defaultValueBool, err := strconv.ParseBool(defaultValue)
if err != nil {
log.Fatalf("Failed to parse default value for %s: %v", yamlPath, err)
}
field.SetBool(defaultValueBool)
}
}
}
}
结构体
// Config 结构体定义配置数据的结构
type Config struct {
Logs *LogsConfig `yaml:"log"`
}
type LogsConfig struct {
Pull *LogsInfoConfig `yaml:"pull"`
Request *LogsInfoConfig `yaml:"request"`
}
type LogsInfoConfig struct {
Enable bool `yaml:"enable" default:"false"`
Info bool `yaml:"info" default:"false"`
Debug bool `yaml:"debug" default:"false"`
Error bool `yaml:"error" default:"false"`
}
评论区