cfg

package
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

cfg - Go 配置管理包

基于 spf13/viper 的简化封装,提供统一的配置管理接口,支持配置文件、环境变量、.env 文件等多种配置源。

设计理念

  • 零配置:大多数情况下无需配置,自动发现配置文件
  • 简洁 API:只有 New()NewWithPrefix() 两个构造函数
  • 显式错误处理:所有 Get 方法返回 (value, error),业务代码自行决定如何处理错误
  • 可选默认值:通过变长参数支持默认值,不修改内部状态

快速开始

1. 创建 Provider(推荐)
// 自动查找配置文件(从当前目录向上查找3层,查找 config.yml/yaml/json/toml/env)
provider, err := cfg.New()
if err != nil {
    log.Fatal(err)
}

// 使用显式路径
provider, err := cfg.New("./config/app.yaml")

// 带环境变量前缀(环境变量如 MYAPP_DB_HOST 会覆盖配置文件的 db.host)
provider, err := cfg.NewWithPrefix("MYAPP")
provider, err := cfg.NewWithPrefix("MYAPP", "./config/app.yaml")
2. 使用全局实例(便捷方式)
// 初始化(只需一次,通常在 main 函数中)
if err := cfg.Init(); err != nil {
    log.Fatal(err)
}
// 或指定路径
if err := cfg.Init("./config.yaml"); err != nil {
    log.Fatal(err)
}
// 或带前缀
if err := cfg.InitWithPrefix("MYAPP"); err != nil {
    log.Fatal(err)
}

// 全局读取
name, err := cfg.GetString("app.name")
if err != nil {
    // 处理错误:键不存在且未提供默认值
}

port, _ := cfg.GetInt("server.port", 8080)  // 提供默认值,错误可忽略
debug, _ := cfg.GetBool("app.debug", false)
3. 读取配置值
// 基本类型 - 返回 (value, error)
str, err := provider.GetString("db.host")
if err != nil {
    log.Fatal(err)
}

// 使用默认值(键不存在时返回默认值,不报错)
port, _ := provider.GetInt("db.port", 5432)
debug, _ := provider.GetBool("app.debug", false)
timeout, _ := provider.GetDuration("server.timeout", 30*time.Second)

// 检查是否存在
if provider.Exists("db.password") {
    // ...
}

// 获取原始值(需要类型断言)
rawValue := provider.Get("db.port")
if port, ok := rawValue.(int); ok {
    fmt.Println(port)
}

// 获取子配置(支持嵌套)
dbConfig := provider.Sub("db")
host, _ := dbConfig.GetString("host")  // 等同于 provider.GetString("db.host")
4. 反序列化到结构体
// 定义配置结构体
type Config struct {
    App struct {
        Name    string `mapstructure:"name"`
        Version string `mapstructure:"version"`
        Debug   bool   `mapstructure:"debug"`
    } `mapstructure:"app"`
    DB struct {
        Host     string `mapstructure:"host"`
        Port     int    `mapstructure:"port"`
        Username string `mapstructure:"username"`
        Password string `mapstructure:"password"`
        Pool     struct {
            MaxOpen int `mapstructure:"max_open"`
            MaxIdle int `mapstructure:"max_idle"`
        } `mapstructure:"pool"`
    } `mapstructure:"db"`
    Server struct {
        Port    int           `mapstructure:"port"`
        Timeout time.Duration `mapstructure:"timeout"`
    } `mapstructure:"server"`
}

// 反序列化整个配置
var cfg Config
if err := provider.Unmarshal(&cfg); err != nil {
    log.Fatal(err)
}

// 使用配置
fmt.Println(cfg.App.Name)
fmt.Println(cfg.DB.Host)
fmt.Println(cfg.DB.Pool.MaxOpen)

// 也可以只反序列化子配置
dbProvider := provider.Sub("db")
var dbConfig DB  // 只包含 db 相关字段的结构体
if err := dbProvider.Unmarshal(&dbConfig); err != nil {
    log.Fatal(err)
}
5. Mapstructure Tag 详解

mapstructure tag 控制配置键与结构体字段的映射关系:

基本映射
type Config struct {
    // 配置键名与字段名不同时使用
    HostName string `mapstructure:"host"`      // 对应配置中的 host
    DBPort   int    `mapstructure:"db_port"`   // 对应配置中的 db_port
}
忽略字段
type Config struct {
    Name     string `mapstructure:"name"`
    Password string `mapstructure:"-"`  // 忽略此字段,不参与反序列化
}
嵌入结构体(扁平化)
// 配置文件: host: localhost, port: 5432
type CommonConfig struct {
    Host string `mapstructure:"host"`
    Port int    `mapstructure:"port"`
}

type DBConfig struct {
    CommonConfig `mapstructure:",squash"`  // 扁平化嵌入,配置中直接写 host, port
    Database     string `mapstructure:"database"`
}
// 结果: DBConfig.Host = "localhost", DBConfig.Port = 5432
捕获剩余字段
type Config struct {
    Name    string                 `mapstructure:"name"`
    Extra   map[string]interface{} `mapstructure:",remain"`  // 捕获未映射的字段
}
// 配置文件: name: myapp, debug: true, version: 1.0
// 结果: Extra = {"debug": true, "version": "1.0"}
嵌套切片
type Config struct {
    Servers []struct {
        Host string `mapstructure:"host"`
        Port int    `mapstructure:"port"`
    } `mapstructure:"servers"`
}
// 配置文件:
// servers:
//   - host: srv1
//     port: 8080
//   - host: srv2
//     port: 8081
弱类型解析
type Config struct {
    Port int `mapstructure:"port"`  // 配置中是字符串 "5432" 也能解析为 int
}
// 注意:viper 默认启用弱类型解析,字符串 "5432" 会自动转为 5432

支持的配置文件格式

YAML (推荐)
app:
  name: myapp
  version: 1.0.0
  debug: true

db:
  host: localhost
  port: 5432
  pool:
    max_open: 10
    max_idle: 5

server:
  port: 8080
  timeout: 30s
JSON
{
  "app": {
    "name": "myapp",
    "debug": true
  },
  "db": {
    "host": "localhost",
    "port": 5432
  }
}
TOML
[app]
name = "myapp"
debug = true

[db]
host = "localhost"
port = 5432

[db.pool]
max_open = 10
.env
APP_NAME=myapp
APP_DEBUG=true
DB_HOST=localhost
DB_PORT=5432
DB_POOL_MAX_OPEN=10

配置文件查找顺序

  1. 如果调用 New("./path/to/config.yaml"),直接使用该路径
  2. 否则,从当前目录开始,向上查找最多3层父目录
  3. 查找名为 config 的文件,支持以下扩展名(按优先级):
    • .yml
    • .yaml
    • .json
    • .toml
    • .env

环境变量映射

配置文件中的键通过以下规则映射到环境变量:

  • app.nameMYAPP_APP_NAME
  • db.hostMYAPP_DB_HOST
  • db.pool.max_openMYAPP_DB_POOL_MAX_OPEN
# 设置环境变量会覆盖配置文件中的值
export MYAPP_DB_HOST=production.db.com
export MYAPP_DB_PORT=3306

配置优先级(由高到低)

  1. 环境变量(如 MYAPP_DB_HOST
  2. 配置文件(config.yml / config.json / .env 等)
  3. 默认值(代码中设置的默认值)

时间格式说明

GetDuration

支持 Go 的 duration 格式:

// 支持的格式
"1h30m"      // 1小时30分钟
"90m"        // 90分钟
"2s"         // 2秒
"500ms"      // 500毫秒
"1h30m10s"   // 1小时30分10秒

// 使用示例
timeout, err := provider.GetDuration("server.timeout")
// 或使用默认值
timeout, _ := provider.GetDuration("server.timeout", 30*time.Second)
GetSizeInBytes

解析人类可读的大小格式:

// 支持的格式
"1GB"      // 1 GB
"100MB"    // 100 MB
"512KB"    // 512 KB
"1024B"    // 1024 字节

// 使用示例
maxSize, err := provider.GetSizeInBytes("upload.max_size")
// 或使用默认值
maxSize, _ := provider.GetSizeInBytes("upload.max_size", 100*1024*1024)  // 默认 100MB

特殊 Provider

Map Provider(测试用)

创建一个独立的、不受环境变量影响的 Provider:

data := map[string]any{
    "app": map[string]any{
        "name": "test-app",
    },
    "db": map[string]any{
        "host": "localhost",
        "port": 5432,
    },
}
provider := cfg.NewFromMap(data)
Empty Provider

返回零值的空 Provider,用于安全链式调用:

empty := cfg.EmptyProvider()
// 所有 Get 方法返回 ErrNotFound(或默认值如果提供)
// Exists 返回 false,Sub 返回自身

host, err := empty.GetString("host")  // 返回 ("", ErrNotFound)
port, _ := empty.GetInt("port", 3306) // 返回 (3306, nil)

链式调用安全

所有 Provider 都支持安全的链式调用:

// 即使键不存在也不会 panic
value, _ := provider.Sub("nonexistent").Sub("nested").GetInt("key")  // 返回 (0, ErrNotFound)

// 链式调用中使用默认值
timeout, _ := provider.Sub("server").GetDuration("timeout", 30*time.Second)

完整示例

package main

import (
    "errors"
    "log"
    "github.com/your-org/go-kit/cfg"
)

func main() {
    // 方式1:使用全局实例(推荐)
    if err := cfg.InitWithPrefix("MYAPP"); err != nil {
        log.Fatal(err)
    }

    // 关键配置:严格处理错误
    dbHost, err := cfg.GetString("db.host")
    if err != nil {
        log.Fatal("db.host is required")
    }

    // 可选配置:使用默认值
    dbPort, _ := cfg.GetInt("db.port", 5432)

    // 方式2:创建独立 Provider
    provider, err := cfg.New("./config.yaml")
    if err != nil {
        log.Fatal(err)
    }

    // 读取子配置
    db := provider.Sub("database")
    maxConns, _ := db.GetInt("pool.max_open", 10)

    // 检查特定错误类型
    timeout, err := cfg.GetDuration("server.timeout")
    if errors.Is(err, cfg.ErrNotFound) {
        log.Println("using default timeout")
        timeout = 30 * time.Second
    }
}

API 参考

构造函数
// 自动查找配置文件
cfg.New()
cfg.New("./path/to/config.yaml")

// 带环境变量前缀
cfg.NewWithPrefix("MYAPP")
cfg.NewWithPrefix("MYAPP", "./path/to/config.yaml")

// 从 map 创建(测试用)
cfg.NewFromMap(map[string]any{...})

// 空 Provider
cfg.EmptyProvider()
全局函数
// 初始化
cfg.Init()
cfg.Init("./config.yaml")
cfg.InitWithPrefix("MYAPP")
cfg.InitWithPrefix("MYAPP", "./config.yaml")

// 获取全局 Provider
cfg.Default()
cfg.IsInitialized()

// 读取配置 - 都返回 (value, error)
cfg.GetString(key string, defaultValue ...string) (string, error)
cfg.GetInt(key string, defaultValue ...int) (int, error)
cfg.GetBool(key string, defaultValue ...bool) (bool, error)
cfg.GetDuration(key string, defaultValue ...time.Duration) (time.Duration, error)

// 更多数值类型
cfg.GetInt32(key string, defaultValue ...int32) (int32, error)
cfg.GetInt64(key string, defaultValue ...int64) (int64, error)
cfg.GetUint(key string, defaultValue ...uint) (uint, error)
cfg.GetUint8(key string, defaultValue ...uint8) (uint8, error)
cfg.GetUint16(key string, defaultValue ...uint16) (uint16, error)
cfg.GetUint32(key string, defaultValue ...uint32) (uint32, error)
cfg.GetUint64(key string, defaultValue ...uint64) (uint64, error)
cfg.GetFloat64(key string, defaultValue ...float64) (float64, error)

// 时间和大小
cfg.GetTime(key string, defaultValue ...time.Time) (time.Time, error)
cfg.GetSizeInBytes(key string, defaultValue ...uint64) (uint64, error)  // 解析 "1GB", "100MB"

// 切片类型 - 只返回 error,不支持默认值
cfg.GetStringSlice(key string) ([]string, error)
cfg.GetIntSlice(key string) ([]int, error)

// Map 类型
cfg.GetStringMap(key string) (map[string]any, error)

// 其他
cfg.Get(key string) any
cfg.Exists(key string) bool
cfg.Unmarshal(dst any) error
cfg.Sub(key string) Provider

错误处理

所有 Get 方法都返回 (value, error),错误类型如下:

var (
    ErrNotFound       = errors.New("cfg: key not found")       // 键不存在且未提供默认值
    ErrTypeMismatch   = errors.New("cfg: type mismatch")       // 默认值类型不匹配或传了多个默认值
    ErrNotInitialized = errors.New("cfg: provider not initialized") // 保留错误类型,当前全局函数默认不会返回
)
使用模式
// 模式1:严格处理错误(推荐用于关键配置)
host, err := cfg.GetString("db.host")
if err != nil {
    return fmt.Errorf("db.host is required: %w", err)
}

// 模式2:使用默认值(配置项可选时)
port, _ := cfg.GetInt("db.port", 5432)  // 不存在则使用 5432

// 模式3:检查特定错误
timeout, err := cfg.GetDuration("server.timeout")
if errors.Is(err, cfg.ErrNotFound) {
    // 使用自定义逻辑设置默认值
    timeout = calculateDefaultTimeout()
}

注意事项

  1. Init 是可选的: 未调用 cfg.Init() 时,全局函数会回退到 EmptyProvider,返回 ErrNotFound 或默认值
  2. Init 只执行一次: 多次调用 Init 只有第一次生效
  3. 错误处理: NewInit 返回的错误必须处理
  4. 线程安全: Provider 是线程安全的,可以在多个 goroutine 中并发使用
  5. 默认值不修改内部状态: 传入的默认值仅在当前调用中返回,不会写入配置
  6. 公开 API 契约稳定: Get*/Sub/Unmarshal 等导出方法是兼容边界;内部去重实现不应改变这些方法语义

常见陷阱

Unmarshal 不会清空已有字段
var cfg Config
cfg.Name = "default"

// 如果配置文件中没有 name 字段,cfg.Name 仍保持 "default"
provider.Unmarshal(&cfg)
环境变量字符串解析
// .env 文件: PORT=8080
// 环境变量: export MYAPP_PORT=9090

// GetInt 可以正确解析
port := provider.GetInt("port")  // 8080 或 9090(取决于优先级)

// 但 GetString 会返回原始字符串
portStr := provider.GetString("port")  // "8080" 或 "9090"
Sub 后 Unmarshal 的区别
// 方式1:反序列化整个配置(包含层级)
var fullConfig Config  // 包含 app, db, server
provider.Unmarshal(&fullConfig)

// 方式2:只反序列化子配置(扁平化)
var dbConfig DB  // 只包含 db 下的字段
dbProvider := provider.Sub("db")
dbProvider.Unmarshal(&dbConfig)
// 注意:此时 dbConfig 的字段直接对应 db.*,不是 db.db.*
配置键不存在时的行为
// Get 方法返回 error,需要用默认值或处理错误
host, err := provider.GetString("db.nonexistent")
if errors.Is(err, cfg.ErrNotFound) {
    // 键不存在且未提供默认值
}

// 使用默认值时不会返回错误
port, _ := provider.GetInt("db.nonexistent", 3306)  // 返回 3306

// Exists 用于检查键是否存在
if provider.Exists("db.password") {
    // 键存在(即使值为空字符串)
}

// 嵌套键不存在时,Sub 返回 EmptyProvider(安全链式调用)
db := provider.Sub("nonexistent")
host, _ := db.GetString("host")  // 返回 ("", ErrNotFound),不会 panic

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFound 表示配置键不存在
	ErrNotFound = errors.New("cfg: key not found")
	// ErrTypeMismatch 表示类型不匹配
	ErrTypeMismatch = errors.New("cfg: type mismatch")
	// ErrNotInitialized 表示 Provider 未初始化
	ErrNotInitialized = errors.New("cfg: provider not initialized")
)

预定义错误

Functions

func Exists

func Exists(key string) bool

Exists 检查全局配置中键是否存在

func Get

func Get(key string) any

Get 从全局配置获取原始值

func GetBool

func GetBool(key string, defaultValue ...bool) (bool, error)

GetBool 从全局配置获取布尔值

func GetDuration

func GetDuration(key string, defaultValue ...time.Duration) (time.Duration, error)

GetDuration 从全局配置获取时间间隔值

func GetFloat64

func GetFloat64(key string, defaultValue ...float64) (float64, error)

GetFloat64 从全局配置获取 float64 值

func GetInt

func GetInt(key string, defaultValue ...int) (int, error)

GetInt 从全局配置获取整数值

func GetInt32

func GetInt32(key string, defaultValue ...int32) (int32, error)

GetInt32 从全局配置获取 int32 值

func GetInt64

func GetInt64(key string, defaultValue ...int64) (int64, error)

GetInt64 从全局配置获取 int64 值

func GetIntSlice

func GetIntSlice(key string) ([]int, error)

GetIntSlice 从全局配置获取整数切片

func GetSizeInBytes

func GetSizeInBytes(key string, defaultValue ...uint64) (uint64, error)

GetSizeInBytes 从全局配置获取字节大小

func GetString

func GetString(key string, defaultValue ...string) (string, error)

GetString 从全局配置获取字符串值

func GetStringMap

func GetStringMap(key string) (map[string]any, error)

GetStringMap 从全局配置获取字符串到任意类型的映射

func GetStringSlice

func GetStringSlice(key string) ([]string, error)

GetStringSlice 从全局配置获取字符串切片

func GetTime

func GetTime(key string, defaultValue ...time.Time) (time.Time, error)

GetTime 从全局配置获取时间值

func GetUint

func GetUint(key string, defaultValue ...uint) (uint, error)

GetUint 从全局配置获取无符号整数值

func GetUint8

func GetUint8(key string, defaultValue ...uint8) (uint8, error)

GetUint8 从全局配置获取 uint8 值

func GetUint16

func GetUint16(key string, defaultValue ...uint16) (uint16, error)

GetUint16 从全局配置获取 uint16 值

func GetUint32

func GetUint32(key string, defaultValue ...uint32) (uint32, error)

GetUint32 从全局配置获取 uint32 值

func GetUint64

func GetUint64(key string, defaultValue ...uint64) (uint64, error)

GetUint64 从全局配置获取 uint64 值

func Init

func Init(path ...string) error

Init 初始化全局配置实例 如果未调用 Init,包级便捷函数会回退到 EmptyProvider(返回 ErrNotFound 或默认值) 第一次调用后,后续调用将被忽略(返回第一次的错误)

使用示例:

cfg.Init()                           // 自动查找配置
cfg.Init("./config.yaml")            // 指定路径

func InitWithPrefix

func InitWithPrefix(prefix string, path ...string) error

InitWithPrefix 使用环境变量前缀初始化全局配置

使用示例:

cfg.InitWithPrefix("MYAPP")                    // 自动查找,带前缀
cfg.InitWithPrefix("MYAPP", "./config.yaml")   // 指定路径,带前缀

func IsInitialized

func IsInitialized() bool

IsInitialized 检查全局配置是否已初始化

func Unmarshal

func Unmarshal(dst any) error

Unmarshal 将全局配置反序列化到目标结构体

Types

type Provider

type Provider interface {
	// Get 获取原始值
	Get(key string) any

	// 基础类型获取,支持可选默认值,返回 (值, 错误)
	// 不传默认值时,key 不存在返回 ErrNotFound
	// 传默认值时,key 不存在返回默认值
	GetString(key string, defaultValue ...string) (string, error)
	GetBool(key string, defaultValue ...bool) (bool, error)
	GetInt(key string, defaultValue ...int) (int, error)
	GetInt32(key string, defaultValue ...int32) (int32, error)
	GetInt64(key string, defaultValue ...int64) (int64, error)
	GetUint(key string, defaultValue ...uint) (uint, error)
	GetUint8(key string, defaultValue ...uint8) (uint8, error)
	GetUint16(key string, defaultValue ...uint16) (uint16, error)
	GetUint32(key string, defaultValue ...uint32) (uint32, error)
	GetUint64(key string, defaultValue ...uint64) (uint64, error)
	GetFloat64(key string, defaultValue ...float64) (float64, error)
	GetDuration(key string, defaultValue ...time.Duration) (time.Duration, error)
	GetTime(key string, defaultValue ...time.Time) (time.Time, error)

	// 特殊类型
	GetSizeInBytes(key string, defaultValue ...uint64) (uint64, error)

	// 切片类型(不支持默认值参数,因为 API 难用,通过 Exists 判断)
	GetStringSlice(key string) ([]string, error)
	GetIntSlice(key string) ([]int, error)

	// Map 类型
	GetStringMap(key string) (map[string]any, error)

	// 检查与操作
	Exists(key string) bool
	Unmarshal(dst any) error
	Sub(key string) Provider
}

Provider 定义配置提供者的接口

func Default

func Default() Provider

Default 获取全局 Provider 实例 如果 Init 尚未调用,返回空 Provider(emptyProvider)

func EmptyProvider

func EmptyProvider() Provider

EmptyProvider 返回空 Provider 实例 用于测试或需要空实现的场景

func New

func New(path ...string) (Provider, error)

New 创建一个新的 Provider 实例 自动按以下顺序查找配置: 1. 如果传入了 path 参数,直接使用该路径 2. 自动查找当前目录及父目录中的 config.*(支持 .yml/.yaml/.json/.toml/.env) 3. 环境变量(优先级最高,覆盖配置文件的值)

使用示例:

cfg.New()                                    // 自动查找配置
cfg.New("./config/app.yaml")                 // 指定路径

func NewFromMap

func NewFromMap(m map[string]any) Provider

NewFromMap 从 map 创建 Provider(主要用于测试) 创建一个独立的、不受环境变量影响的 Provider 实例

func NewWithPrefix

func NewWithPrefix(prefix string, path ...string) (Provider, error)

NewWithPrefix 创建带环境变量前缀的 Provider

使用示例:

cfg.NewWithPrefix("MYAPP")                   // 自动查找,带前缀
cfg.NewWithPrefix("MYAPP", "./config.yaml")  // 指定路径,带前缀

func Sub

func Sub(key string) Provider

Sub 从全局配置获取子配置

Jump to

Keyboard shortcuts

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