SkillAgentSearch skills...

Xconf

Dead simple yet complete and powerful configuration manager for Go.

Install / Use

/learn @sandwich-go/Xconf

README

XCONF

GitHub Workflow Status Go Version Go Report Card GoDoc

README | English

Golang配置文件加载解析, goconf v2,扩充了功能支持。

Run XConf Example: run on repl.it

Run XCmd Example: run on repl.it

功能简介

  • 支持默认值配置、解析
  • 支持多种格式,内置JSON, TOML, YAML,FLAG, ENV支持,并可注册解码器扩展格式支持
  • 支持多文件、多io.Reader数据加载,支持文件继承
  • 支持由OS ENV变量数据加载配置
  • 支持由命令行参数FLAGS加载数据
  • 支持由远程URL加载配置数据
  • 支持数据覆盖合并,加载多份数据时将按照加载文件的顺序按FieldPath自动合并
  • 支持通过${READ_TIMEOUT|5s}${IP_ADDRESS}等方式绑定Env参数
  • 支持配置热加载、实时同步,内置内存热加载支持,支持异步更新通知, 支持xconf-providers: ETCD、文件系统.
  • 支持WATCH具体的FieldPath变动
  • 支持导出配置到多种配置文件
  • 支持配置HASH,便于比对配置一致性
  • FLAGSENVFieldPath支持复杂类型,支持自定义复杂类型扩展支持
  • 支持配置访问秘钥
  • 支持自定义基于Label的灰度更新
  • 支持数值别名,如:math.MaxInt,runtime.NumCPU
  • 支持",squash"将子结构体的字段提到父结构中便于做配置扩充

名词解释

  • FieldTag
    • xconf在将配置由Strut与JSON, TOML, YAML,FLAG, ENV等转换时使用的字段别名,如:示例配置中的HttpAddressFieldTaghttp_address
    • 如果没有配置xconf:"http_address",则默认会采用字段名的SnakeCase作为FieldTag,可以通过xconf.WithFieldTagConvertor指定为其他方案,如字段名的小写等,注意FieldTag策略必须与配置源中使用的字符串一致,否则会导致解析数据失败。
  • FieldPath,由FieldTag组成的Field访问路径,如示例配置中的Config.SubTest.HTTPAddressFieldPathconfig.sub_test.http_address.
  • Leaf,xconf中配置的最小单位,基础类型、slice类型都是最小单位,Struct不是配置的最小单位,会根据配置的属性字段进行赋值、覆盖。
    • 默认情况下map是xconf配置的最小单位,但是可以通过指定notleaf标签使map不作为最小单位,而是基于key进行合并.但是这种情况下map的Value依然是xconf中的最小单位,即使value是Struct也将作为配置合并的最小单位
    • 通过xconf.WithMapMerge(true)可以激活MapMerge模式,在这个模式下map及其Value都不再是配置的最小单位,配置的最小单位为基础类型和slice类型。

快速开始

定义配置结构

  • 参考xconf/tests/conf.go使用optiongen定义配置并指定--xconf=true以生成支持xconf需求的标签.
  • 自定义结构,指定xconf需求的标签
type Server struct {
	Timeouts map[string]time.Duration `xconf:"timeouts"`
}

type SubTest struct {
	HTTPAddress string            `xconf:"http_address"`
	MapNotLeaf  map[string]int    `xconf:"map_not_leaf,notleaf"`
	Map2        map[string]int    `xconf:"map2"`
	Map3        map[string]int    `xconf:"map3"`
	Slice2      []int64           `xconf:"slice2"`
	Servers     map[string]Server `xconf:"servers,notleaf"`
}

type Config struct {
	HttpAddress     string          `xconf:"http_address"`
	Map1            map[string]int  `xconf:"map1"`
	MapNotLeaf      map[string]int  `xconf:"map_not_leaf,notleaf"`
	TimeDurations   []time.Duration `xconf:"time_durations"`
	Int64Slice      []int64         `xconf:"int64_slice"`
	Float64Slice    []float64       `xconf:"float64_slice"`
	Uin64Slice      []uint64        `xconf:"uin64_slice"`
	StringSlice     []string        `xconf:"string_slice"`
	ReadTimeout     time.Duration   `xconf:"read_timeout"`
	SubTest         SubTest         `xconf:"sub_test"`
}

从文件载入配置

yaml格式为例(tests/)

http_address: :3002
read_timeout: 100s
default_empty_map:
  test1: 1
map1:
  test1: 1000000
map_not_leaf:
  test1: 1000000
int64_slice:
- 1
- 2
sub_test:
  map2:
    ${IP_ADDRESS}: 2222
  map_not_leaf:
    test2222: 2222
  servers:
    s1:
      timeouts:
        read: ${READ_TIMEOUT|5s} 

参考:tests/main/main.go,文件间的继承通过xconf_inherit_files指定,参考tests/main/c2.toml

cc := NewTestConfig(
	xconf.WithFiles("c2.toml"), // 由指定的文件加载配置
	xconf.WithReaders(bytes.NewBuffer(yamlContents),bytes.NewBuffer(tomlContents),xconf.NewRemoteReader("http://127.0.0.1:9001/test.json", time.Duration(5)*time.Second)), // 由指定的reader加载配置
	xconf.WithFlagSet(flag.CommandLine), // 指定解析flag.CommandLine,默认值
	xconf.WithEnviron(os.Environ()), // 指定解析os.Environ(),默认值
)
xconf.Parse(cc)

文件级继承

通过 xconf_inherit_files 可以完整继承其他文件的配置内容,并在当前配置文件内覆盖差异化内容。
支持不同文件格式的继承(如 yaml / toml / json 等),被继承文件路径为相对于当前文件的路径,例如:./conf/base.yaml

xconf_inherit_files = ["c1.yaml"]

使用场景

  • 环境差异配置:如 dev / test / prod 共享基础配置,仅覆盖少量差异字段
  • 配置复用:多个服务复用同一基础配置,避免重复维护
  • 模块化配置管理:将通用配置拆分为独立文件,提升可读性与维护性

继承规则

  1. 加载顺序

    • xconf_inherit_files 中的顺序依次加载
    • 后加载的文件会覆盖前面的同名字段
  2. 当前文件优先级最高

    • 当前文件中的配置会覆盖所有继承文件中的同名字段
  3. 字段合并规则 参考上述文当中的Leaf定义,Leaf为xconf中配置的最小单位,也是最小覆盖单位。


示例

base.yaml
server:
  host: 127.0.0.1
  port: 8080

db:
  user: root
  password: 123456
dev.yaml
xconf_inherit_files = ["./base.yaml"]

server:
  port: 8081

db:
  password: dev_pass
合并结果
server:
  host: 127.0.0.1
  port: 8081

db:
  user: root
  password: dev_pass

多文件继承示例

xconf_inherit_files = [
  "./base.yaml",
  "./feature.yaml"
]

加载顺序:

base.yaml → feature.yaml → 当前文件

注意事项

  • 避免循环继承(A 继承 B,B 又继承 A)
  • 路径必须正确,否则会导致加载失败
  • 不同格式文件需保证结构兼容(字段语义一致)
  • 建议将通用配置抽离到 base 文件,减少重复定义

配置存入文件

// SaveToFile 将内置解析的数据dump到文件,根据文件后缀选择codec
func SaveToFile(fileName string) error
// SaveToWriter 将内置解析的数据dump到writer,类型为ct
func SaveToWriter(ct ConfigType, writer io.Writer) error 

// SaveVarToFile 将外部传入的valPtr,写入到fileName中,根据文件后缀选择codec
func SaveVarToFile(valPtr interface{}, fileName string) error 

// SaveVarToWriter 将外部传入的valPtr,写入到writer中,类型为ct
func SaveVarToWriter(valPtr interface{}, ct ConfigType, writer io.Writer) error 

// MustSaveToFile 将内置解析的数据dump到文件,根据文件后缀选择codec,如发生错误会panic
func MustSaveToFile(f string) 
// MustSaveToWriter 将内置解析的数据dump到writer,需指定ConfigType,如发生错误会panic
func MustSaveToWriter(ct ConfigType, writer io.Writer) 

// MustSaveVarToFile 将外部传入的valPtr,写入到fileName中,根据文件后缀选择codec
func MustSaveVarToFile(v interface{}, f string) 

// MustSaveVarToWriter 将外部传入的valPtr,写入到writer中,类型为ct
func MustSaveVarToWriter(v interface{}, ct ConfigType, w io.Writer) 

// MustSaveToBytes 将内置解析的数据以字节流返回,需指定ConfigType
func MustSaveToBytes(ct ConfigType) []byte { return xx.MustSaveToBytes(ct) }

// SaveVarToWriterAsYAML 将内置解析的数据解析到yaml,带comment
func SaveVarToWriterAsYAML(valPtr interface{}, writer io.Writer) error 

可用选项

  • WithFiles : 指定加载的文件,配置覆盖顺序依赖传入的文件顺序
  • WithReaders: 指定加载的io.Reader,配置覆盖顺序依赖传入的io.Reader顺序。
  • WithFlagSet: 指定解析的FlagSet,默认是全局的flag.CommandLine,如指定为nil则不会将参数自动创建到FlagSet,同样也不会解析FlagSet内数据。
  • WithFlagArgs: 指定FlagSet解析的参数数据,默认为os.Args[1:]
  • WithFlagValueProvider: FlagSet支持的类型有限,xconf/xflag/vars中扩展了部分类型,参考[Flag 与 Env支持]
  • WithEnviron: 指定环境变量值
  • WithErrorHandling:指定错误处理方式,同flag.CommandLine处理方式
  • WithLogDebug: 指定debug日志输出
  • WithLogWarning: 指定warn日志输出
  • WithFieldTagConvertor: 当无法通过TagName获取FieldTag时,通过该方法转换,默认SnakeCase.
  • WithTagName: FieldTag字段来源的Tag名,默认xconf
  • WithTagNameDefaultValue: 默认值使用的Tag名称 ,默认default
  • WithParseDefault:是否解析默认值,默认true,推荐使用optiongen生成默认配置数据
  • WithDebug: 调试模式,会输出详细的解析流程日志
  • WithDecoderConfigOption: 调整mapstructure参数,xconf使用mapstructure进行类型转换
  • FieldPathDeprecated: 弃用的配置,解析时不会报错,但会打印warning日志
  • ErrEnvBindNotExistWithoutDefault: EnvBind时如果Env中不存在指定的key而且没有指定默认值时报错
  • FieldFlagSetCreateIgnore: 指定的FieldPath或者类型名在没有Flag Provider的时候,不打印报警日志

Flag 与 Env支持

  • 支持Flag中通过xconf_files指定配置文件
  • xconf/xflag/vars中扩展了部分类型如下:
    • float32,float64
    • int,int8,int16,int32,int64
    • uint,uint8,uint16,uint32,uint64
    • []float32,[]float64
    • []int,[]int8,[]int16,[]int32,[]int64
    • []uint,[]uint8,[]uint16,[]uint32,[]uint64
    • []string
    • []Duration
    • map[stirng]string,map[int]int,map[int64]int64,map[int64]string,map[stirng]int,map[stirng]int64,map[stirng]Duration
  • 扩展类型Slice与Map配置
    • slcie的定义方式为元素通过vars.StringValueDelim分割,默认为,,如:--time_durations=5s,10s,100s
    • map的定位方式为K、V通过vars.StringValueDelim分割,默认为,,如:--sub_test.map_not_leaf=k1,1,k2,2,k3,3
  • 自定义扩展
    • 扩展需要实现flag.Getter接口,可以通过实现Usage() string实现自定义的Usage信息。
      const JsnoPrefix = "json@"
      
      type serverProvider struct {
          s    string
          set  bool
          data *map[string]Server
      }
      
      func (sp *serverProvider) String() string {
          return sp.s
      }
      func (sp *serverProvider) Set(s string) error {
          sp.s = s
          if sp.set == false {
              *sp.data = make(map[string]Server)
          }
          if !strings.HasPrefix(s, JsnoPrefix) {
              return errors.New("server map need json data with prefix:" + JsnoPrefix)
          }
          s = strings.TrimPrefix(s, JsnoPrefix)
          return json.Unmarshal([]byte(s), sp.data)
      }
      func (sp *serverProvider) Get() interface{} {
          ret := make(map[string]interface{})
          for k, v := range *sp.data {
              ret[k] = v
          }
          return ret
      }
      func (sp *serverProvider) Usage() string {
          return fmt.Sprintf("server map, json format")
      }
      func newServerProvider(v interface{}) flag.Getter {
          return &serverProvider{data: v.(*map[string]Server)}
      }
      
      
    • 注册扩展
      • vars.SetProviderByFieldPath通过FieldPath设定扩展
      • vars.SetProviderByFieldType通过字段类型名称设定扩展
        cc := &Config{}
        jsonServer := `json@{"s1":{"timeouts":{"read":5000000000},"timeouts_not_leaf":{"write":5000000000}}}`
        x := xconf.New(
            xconf.WithFlagSet(flag.NewFlagSet("xconf-test", flag.ContinueOnError)),
       
    
View on GitHub
GitHub Stars24
CategoryDevelopment
Updated16d ago
Forks2

Languages

Go

Security Score

95/100

Audited on Mar 24, 2026

No findings