confutil

package
v0.0.0-...-c4985a1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2024 License: MIT Imports: 11 Imported by: 0

README

confutil - 配置加载

import path: tencent.com/libs/go/confutil

核心功能

  • 所有配置通过一个结构体定义,配置读取后加载到该结构体的一个实例中,通过该实例使用配置
  • 每个配置项的环境变量、默认值都在字段上直接声明,不需要额外的代码
  • 配置项环境变量的前缀可以通过层级定义
  • 基于 viper ,且兼容 viper 的其它所有功能

示例

使用示例代码: apps/demo/go/srcs/load-config

通过 bazel run //apps/demo/go/srcs/load-config 即可运行该示例,该示例会打印其加载的配置内容,可以通过通过指定环境变量或配置文件看看其配置加载结果

比如:

# 修改 http 端口为 8080
SERVE_HTTP_ADDR=8080 bazel run //apps/demo/go/srcs/load-config

# 从配置文件加载
bazel run //apps/demo/go/srcs/load-config -- -config $(pwd)/apps/demo/go/srcs/load-config/samples/config.yaml

使用流程

1. 定义配置

服务配置应该被定义为一个结构体,可以多个结构体组合:

// Package config
// apps/fc/sample-app/srcs/something/config/config.go
package config

type Config struct {
	Field1 string `env:"FIELD1" default:"value"`
	Field2 int `env:"FIELD2"`
	Extra ExtraConfig `envPrefix:"EXTRA_"`
}

type ExtraConfig struct {
	Field3 float64 `env:"FIELD3"`
}

以上定义等价于以下在 viper 中的配置项:

  • key: field1 env: FIELD1 default: "value"
  • key: field2 env: FIELD2
  • key: extra.field3 env: EXTRA_FIELD3

配置定义详细用法参考 配置定义说明

2. 注册配置、定义配置加载方法

因为定义的是某服务实例化后的配置,仅对某个服务有效,因此这部分代码建议放到私有包中避免被其它模块错误引用

// Package conf
// apps/fc/sample-app/srcs/something/internal/conf/conf.go
package conf

import (
	"fmt"
	"os"

	"tencent.com/apps/demo/go/srcs/load-config/config"
	"tencent.com/libs/go/confutil"
)

// Config 加载的全局配置
// 从该对象可读取加载后的配置
var Config config.Config

// Manager 配置管理器
var Manager confutil.Manager

func init() {
	// 初始化配置管理器
	Manager = confutil.NewManager()
	// 注册配置
	if err := Manager.RegisterStruct(&config.Config{}); err != nil {
    panic(fmt.Errorf("register config error: %w", err))
	}
}

// LoadConfig 加载配置
func LoadConfig(configPath string) error {
	// 读取配置文件(可选)
	if configPath != "" {
    // 从指定路径读取
    if err := Manager.ReadInFile(configPath, "yaml"); err != nil {
      return fmt.Errorf("read config from file error: %w", err)
    }
	} else {
    // 从默认目录搜索
    // - ./config.yaml
    // - ~/.codematrix/demo/config.yaml
    // 找不到也不会报错
    if err := Manager.SearchInFile(
      "config", "yaml",
      ".",
      os.ExpandEnv("${HOME}/.codematrix/demo"),
    ); err != nil {
      return fmt.Errorf("read config from file error: %w", err)
    }
	}

	// 加载到对象
	if err := Manager.Unmarshal(&Config); err != nil {
    return fmt.Errorf("load config error: %w", err)
	}

	return nil
}

3. 加载配置、使用配置

调用上述 LoadConfig 方法后配置才可用,因此尽量在程序运行比较早期时调用,比如在 main 函数中。

该示例加载配置后直接打印其值:

// Package main
// apps/fc/sample-app/srcs/something/main.go
package main

import (
	"encoding/json"
	"flag"
	"log"

	"tencent.com/apps/fc/sample-app/srcs/something/internal/conf"
)

var (
	configPath = flag.String("config", "", "Config file path")
)

func main() {
	flag.Parse()

	// 加载配置
	if err := conf.LoadConfig(*configPath); err != nil {
		log.Fatalf("load config error: %v", err)
	}

	configJSONRaw, err := json.MarshalIndent(conf.Config, "", "  ")
	if err != nil {
		log.Fatalf("marshal config to json error: %v", err)
	}
	log.Printf("Config:\n%s\n", string(configJSONRaw))
}
配置定义说明

参考示例: apps/demo/go/srcs/load-config

环境变量
  • 结构体字段通过 envPrefix 标签定义该层级以下字段的环境变量的前缀(可嵌套多层)
  • 叶子字段通过 env 标签定义其绑定的环境变量
    • 同一个字段绑定多个环境变量使用 , 分隔(可用于配置调整后兼容旧的环境变量)
    • ^ 开头的环境变量表示不带 envPrefix 定义的前缀

比如

package config

type Config struct {
	Middleware MiddlewareConfig `envPrefix:"MIDDLEWARE_"`
}

type MiddlewareConfig struct {
	MySQL MySQLConfig `envPrefix:"MYSQL_"`
}

type MySQLConfig struct {
	Host     string `env:"HOST,^DB_HOST"`
	Port     int    `env:"PORT"`
	User     string `env:"USER,USERNAME"`
	Password string `env:"PASSWORD,PASSWD"`
	Database string `env:"DATABASE,DB"`
}

该示例声明了如下环境变量

  • MIDDLEWARE_MYSQL_HOSTDB_HOST MySQL 主机名
  • MIDDLEWARE_MYSQL_PORT MySQL 端口号
  • MIDDLEWARE_MYSQL_USERMIDDLEWARE_MYSQL_USERNAME MySQL 用户名
  • MIDDLEWARE_MYSQL_PASSWORDMIDDLEWARE_MYSQL_PASSWD MySQL 密码
  • MIDDLEWARE_MYSQL_DATABASEMIDDLEWARE_MYSQL_DB MySQL 数据库名
默认值
  • 叶子字段通过 default 标签定义其默认值
  • 默认的默认值是类型的“零值”,所以如果期望的默认值就是“零值”可以不用定义
  • 默认值声明是使用 viper 默认的反序列化方法进行反序列化的

比如:

package config

type Config struct {
	Debug bool `default:"true"`
	LogLevel string `default:"info"`
	FloatField float64 `default:"1.2"`
}
匿名字段

对于不想多一级嵌套的匿名结构体字段,可以加标签 mapstructure:",squash"

比如

package config

import (
	"tencent.com/libs/go/confutil"
)

// Config 配置
type Config struct {
	BaseConfig `mapstructure:",squash"`
	Field string
}

type BaseConfig struct {
	// 是否调试模式
	Debug bool
	// 日志级别
	// debug / info / warning / error
	LogLevel string
}

最终解析的 viper 字段为:

  • debug
  • loglevel
  • field

而不会包含 baseconfig. 前缀,在从配置文件加载时也不需要多一级:

# YAML 示例
debug: false
logLevel: "info"
field: "whatever"
JSON 字段类型

[]interface{}map[string]interface{} 类型的字段通过 JSON 进行反序列化

比如:

package config

type Config struct {
	Field []string `env:"FIELD"`
}

可以通过环境变量 FIELD=["value1","value2"] 指定该字段值

但是在从文件读取时也可以直接使用 YAML 或 JSON 的 list 来表示:

# YAML 示例:
field:
  - value1
  - value2

也支持表示为 JSON 格式的字符串

# YAML 示例:
field: '["value1","value2"]'
敏感字符串字段类型

对于包含敏感内容的字符串字段,建议使用 tencent.com/libs/go/confutil.SecretString 类型。该类型修改了 string 默认的序列化方法,使其在一般情况下不会直接打印出原文。

package config

import (
	"tencent.com/libs/go/confutil"
)

type MySQLConfig struct {
	Host     string                `json:"host" env:"HOST"`
	Port     int                   `json:"port" env:"PORT" default:"3306"`
	User     string                `json:"user" env:"USER,USERNAME" default:"root"`
	Password confutil.SecretString `json:"password" env:"PASSWORD,PASSWD"`
	Database string                `json:"database" env:"DATABASE,DB"`
}

该示例中, Password 字段在打印时一般会显示为 ****** (sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855) 。不会显示原文,提供的哈希也足以校验配置是否正确。

在程序中要使用的时候可以通过 .Raw() 方法获取其原始内容

Duration 字段类型

time.Duration 类型的字段反序列化时使用其字符串格式表示,比如 1s 2m 3h

忽略字段

默认情况下 confutil 会解析所有字段,遇到不支持的字段类型会报错,如果不希望 confutil 处理某些字段,可以加 tag confutil:"-"

比如:

package config

type Config struct {
	Whatever interface{} `confutil:"-"`
}

实例中 Whatever 字段将会被忽略

直接操作 Viper 对象

如果需要直接操作管理配置的 Viper 对象(比如需要将配置项绑定到 flag ),可以调用实例化的 tencent.com/libs/go/confutil.Manager.Viper() 方法。比如:

package conf

import (
	"fmt"
	"os"

	"tencent.com/apps/demo/go/srcs/load-config/config"
	"tencent.com/libs/go/confutil"
)

// Config 加载的全局配置
// 从该对象可读取加载后的配置
var Config config.Config

// Manager 配置管理器
var Manager confutil.Manager

func init() {
	// 初始化配置管理器
	Manager = confutil.NewManager()
	// 注册配置
	if err := Manager.RegisterStruct(&config.Config{}); err != nil {
		panic(fmt.Errorf("register config error: %w", err))
	}
	// 打印注册的字段
	fmt.Println(Manager.Viper().AllKeys())  // <--- 通过 .Viper 可以获取管理该配置的 Viper 对象
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrInitMultiTimes = errors.New("config manager initialized multiple times")

ErrInitMultiTimes 配置管理器被初始化了多次

Functions

func DefaultViperDecoderConfigOption

func DefaultViperDecoderConfigOption(config *mapstructure.DecoderConfig)

DefaultViperDecoderConfigOption 默认 Viper 解析配置

func StringToMapHookFunc

func StringToMapHookFunc() mapstructure.DecodeHookFuncKind

StringToMapHookFunc 添加将字符串解析到 map[string]interface{} 的钩子

func StringToSliceHookFunc

func StringToSliceHookFunc() mapstructure.DecodeHookFuncKind

StringToSliceHookFunc 添加将字符串解析到 []interface{} 的钩子

Types

type ConfigItem

type ConfigItem struct {
	Name       string
	BindingEnv string
	DefaultVal interface{}
}

ConfigItem 配置项

type ConfigManager

type ConfigManager struct {

	// 注册的配置项
	Items []ConfigItem

	// 配置文件名(不含文件扩展名)
	ConfigFileName string

	// 配置文件类型
	ConfigFileType string

	// 配置文件搜索目录
	ConfigFileSearchPaths []string
	// contains filtered or unexported fields
}

ConfigManager 配置管理器

func (*ConfigManager) Init

func (mgr *ConfigManager) Init() error

Init 初始化配置管理器

func (*ConfigManager) Unmarshal

func (mgr *ConfigManager) Unmarshal(rawVal interface{}, opts ...viper.DecoderConfigOption) error

Unmarshal 将配置反序列化到结构体

func (*ConfigManager) Viper

func (mgr *ConfigManager) Viper() *viper.Viper

Viper 获取管理的 Viper 对象

type Field

type Field interface {
	// Name 返回字段名
	Name() string
	// Env 返回字段绑定的环境变量
	Env() []string
	// DefaultValue 返回字段默认值
	DefaultValue() interface{}
}

Field 配置字段

func ExpendConfigStruct

func ExpendConfigStruct(ins interface{}) ([]Field, error)

ExpendConfigStruct 展开配置结构体

func NewField

func NewField(name string, defaultValue interface{}, env ...string) Field

NewField 创建一个 Field

type Manager

type Manager interface {
	// Viper 获取管理配置的 Viper 实例
	Viper() *viper.Viper
	// Register 注册字段
	Register(fields ...Field) error
	// RegisterStruct 注册结构体
	RegisterStruct(ins interface{}) error
	// SearchInFile 搜索并读取配置文件
	SearchInFile(name, format string, searchPaths ...string) error
	// ReadInFile 从指定文件读取
	ReadInFile(configPath, format string) error
	// Unmarshal 将配置反序列化到配置对象中
	Unmarshal(obj interface{}, opts ...viper.DecoderConfigOption) error
}

Manager 配置管理器

func NewManager

func NewManager() Manager

NewManager 创建一个 Manager

type SecretString

type SecretString string

SecretString 加密字符串

func (SecretString) MarshalJSON

func (ss SecretString) MarshalJSON() ([]byte, error)

MarshalJSON 序列化为 JSON

func (SecretString) MarshalYAML

func (ss SecretString) MarshalYAML() (interface{}, error)

MarshalYAML 序列化为 YAML

func (SecretString) Raw

func (ss SecretString) Raw() string

Raw 原文

func (SecretString) String

func (ss SecretString) String() string

String 打印的字符串

type SentryConfig

type SentryConfig struct {
	// Sentry DSN
	DSN SecretString `json:"dsn" env:"DSN"`
	// 是否调试模式
	Debug bool `json:"debug,omitempty" env:"DEBUG"`
	// 环境
	Environment string `json:"environment,omitempty" env:"ENVIRONMENT"`
	// 采样率 0 - 1
	TracesSampleRate float64 `json:"tracesSampleRate,omitempty" env:"TRACES_SAMPLE_RATE" default:"1.0"`
}

SentryConfig Sentry 配置

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL