golang环境变量加载到结构体的轻量级插件库env的使用

Golang环境变量加载到结构体的轻量级插件库env的使用

logo

关于

这个包是为那些将配置存储在环境变量中的应用设计的。它的目的是用单一的结构体定义替换main.go中分散的os.Getenv调用,从而简化配置管理并提高代码可读性。

功能特性

安装

Go 1.20+

go get go-simpler.org/env

使用

Load是包的主要函数,它将环境变量加载到给定的结构体中。

结构体字段必须有env:"VAR"标签,其中VAR是对应的环境变量名。未导出的字段会被忽略。

os.Setenv("PORT", "8080")

var cfg struct {
    Port int `env:"PORT"`
}
if err := env.Load(&cfg, nil); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.Port) // 8080

支持的类型

  • int (任意种类)
  • float (任意种类)
  • bool
  • string
  • time.Duration
  • encoding.TextUnmarshaler
  • 上述任意类型的切片
  • 任意深度的嵌套结构体

解析规则参考strconv.Parse*函数。用户定义类型可以通过实现encoding.TextUnmarshaler接口来使用。

嵌套结构体

支持任意深度的嵌套结构体,允许对相关的环境变量进行分组。

os.Setenv("DB_HOST", "localhost")
os.Setenv("DB_PORT", "5432")

var cfg struct {
    DB struct {
        Host string `env:"DB_HOST"`
        Port int    `env:"DB_PORT"`
    }
}
if err := env.Load(&cfg, nil); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.DB.Host) // localhost
fmt.Println(cfg.DB.Port) // 5432

如果嵌套结构体有可选的env:"PREFIX"标签,则其字段声明的环境变量会加上PREFIX前缀。

os.Setenv("DB_HOST", "localhost")
os.Setenv("DB_PORT", "5432")

var cfg struct {
    DB struct {
        Host string `env:"HOST"`
        Port int    `env:"PORT"`
    } `env:"DB_"`
}
if err := env.Load(&cfg, nil); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.DB.Host) // localhost
fmt.Println(cfg.DB.Port) // 5432

默认值

可以使用default:"VALUE"结构体标签指定默认值。

os.Unsetenv("PORT")

var cfg struct {
    Port int `env:"PORT" default:"8080"`
}
if err := env.Load(&cfg, nil); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.Port) // 8080

必填项

使用required选项将环境变量标记为必填。如果未设置,则返回类型为NotSetError的错误。

os.Unsetenv("PORT")

var cfg struct {
    Port int `env:"PORT,required"`
}
if err := env.Load(&cfg, nil); err != nil {
    var notSetErr *env.NotSetError
    if errors.As(err, &notSetErr) {
        fmt.Println(notSetErr) // env: PORT is required but not set
    }
}

扩展

使用expand选项使用os.Expand自动扩展环境变量的值。

os.Setenv("PORT", "8080")
os.Setenv("ADDR", "localhost:${PORT}")

var cfg struct {
    Addr string `env:"ADDR,expand"`
}
if err := env.Load(&cfg, nil); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.Addr) // localhost:8080

切片分隔符

空格是解析切片值的默认分隔符。可以使用Options.SliceSep更改。

os.Setenv("PORTS", "8080,8081,8082")

var cfg struct {
    Ports []int `env:"PORTS"`
}
if err := env.Load(&cfg, &env.Options{SliceSep: ","}); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.Ports) // [8080 8081 8082]

名称分隔符

默认情况下,环境变量名称是从嵌套结构体标签原样拼接的。如果Options.NameSep不为空,则用作分隔符。

os.Setenv("DB_HOST", "localhost")
os.Setenv("DB_PORT", "5432")

var cfg struct {
    DB struct {
        Host string `env:"HOST"`
        Port int    `env:"PORT"`
    } `env:"DB"`
}
if err := env.Load(&cfg, &env.Options{NameSep: "_"}); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.DB.Host) // localhost
fmt.Println(cfg.DB.Port) // 5432

来源

默认情况下,Load直接从操作系统获取环境变量。要使用不同的来源,通过Options.Source传递Source接口的实现。

type Source interface {
    LookupEnv(key string) (value string, ok bool)
}

这是一个使用Map的示例,Map是一个在测试中很有用的Source实现。

m := env.Map{"PORT": "8080"}

var cfg struct {
    Port int `env:"PORT"`
}
if err := env.Load(&cfg, &env.Options{Source: m}); err != nil {
    fmt.Println(err)
}

fmt.Println(cfg.Port) // 8080

使用信息

Usage函数打印一个使用信息,记录所有定义的环境变量。可以使用usage:"STRING"结构体标签为环境变量添加可选的用法字符串。

os.Unsetenv("DB_HOST")
os.Unsetenv("DB_PORT")

var cfg struct {
    DB struct {
        Host string `env:"DB_HOST,required" usage:"database host"`
        Port int    `env:"DB_PORT,required" usage:"database port"`
    }
    HTTPPort int `env:"HTTP_PORT" default:"8080" usage:"http server port"`
}
if err := env.Load(&cfg, nil); err != nil {
    fmt.Println(err)
    fmt.Println("Usage:")
    env.Usage(&cfg, os.Stdout, nil)
}

输出:

Usage:
  DB_HOST    string  required      database host
  DB_PORT    int     required      database port
  HTTP_PORT  int     default 8080  http server port

可以通过实现Usage([]env.Var, io.Writer, *env.Options)方法来自定义消息的格式。

type Config struct{ ... }

func (Config) Usage(vars []env.Var, w io.Writer, opts *env.Options) {
    for v := range vars {
        // write to w.
    }
}

更多关于golang环境变量加载到结构体的轻量级插件库env的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang环境变量加载到结构体的轻量级插件库env的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang环境变量加载到结构体的轻量级插件库 - env

env是一个轻量级的Go库,用于将环境变量加载到结构体中。它简单易用,支持各种数据类型和自定义解析器。下面我将介绍env的基本用法和示例代码。

安装

go get github.com/caarlos0/env/v8

基本用法

简单示例

package main

import (
	"fmt"
	"time"

	"github.com/caarlos0/env/v8"
)

type config struct {
	Home         string        `env:"HOME"`
	Port         int           `env:"PORT" envDefault:"3000"`
	Password     string        `env:"PASSWORD,unset"`
	IsProduction bool          `env:"PRODUCTION"`
	Duration     time.Duration `env:"DURATION"`
	Hosts        []string      `env:"HOSTS" envSeparator:":"`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		fmt.Printf("%+v\n", err)
	}

	fmt.Printf("%+v\n", cfg)
}

标签说明

  • env:"VARNAME" - 指定环境变量名
  • envDefault:"value" - 设置默认值
  • unset - 加载后从环境中删除该变量
  • envSeparator:"|" - 指定切片类型的分隔符(默认为逗号)
  • required - 标记字段为必填

高级用法

嵌套结构体

type config struct {
	Database struct {
		User     string `env:"DB_USER" envDefault:"postgres"`
		Password string `env:"DB_PASSWORD"`
		Host     string `env:"DB_HOST" envDefault:"localhost"`
		Port     int    `env:"DB_PORT" envDefault:"5432"`
	}
	Server struct {
		Port int `env:"SERVER_PORT" envDefault:"8080"`
	}
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", cfg)
}

自定义解析器

type customType string

func (c *customType) UnmarshalText(text []byte) error {
	*c = customType(fmt.Sprintf("custom-%s", string(text)))
	return nil
}

type config struct {
	Custom customType `env:"CUSTOM"`
}

func main() {
	os.Setenv("CUSTOM", "value")
	
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		panic(err)
	}
	
	fmt.Println(cfg.Custom) // 输出: custom-value
}

必填字段

type config struct {
	SecretKey string `env:"SECRET_KEY,required"`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		fmt.Println(err) // 会返回错误,因为SECRET_KEY未设置
	}
}

从文件加载

type config struct {
	SecretKey string `env:"SECRET_KEY,file"`
}

func main() {
	// 假设有一个文件路径存储在SECRET_KEY_FILE环境变量中
	os.Setenv("SECRET_KEY_FILE", "/path/to/secret_key.txt")
	
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		panic(err)
	}
	
	fmt.Println(cfg.SecretKey) // 输出文件内容
}

最佳实践

  1. 在main函数或init函数中尽早加载配置
  2. 为所有配置项提供合理的默认值
  3. 对生产环境必需的配置使用required标签
  4. 敏感信息考虑使用file标签从文件加载
  5. 使用嵌套结构体组织相关配置项

与其他库的比较

env库相比其他配置库(如viper)有以下特点:

  • 更轻量级,只专注于环境变量到结构体的映射
  • 更简单的API,只有一个主要函数Parse
  • 支持自定义解析器
  • 支持从文件加载敏感信息

env库适合简单的配置需求,特别是12-factor应用。如果需要更复杂的配置来源(如JSON/YAML文件、命令行参数等),可以考虑使用viper等更全面的配置库。

希望这个介绍对你有帮助!env库简单易用,是处理环境变量配置的不错选择。

回到顶部