Skip to content

快速开始 ​

5 分钟上手 env 库,从安装到实际使用。

安装 ​

bash
go get github.com/cybergodev/env

要求

Go 1.25+

创建 .env 文件 ​

在项目根目录创建 .env 文件:

bash
# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=secret

# 应用配置
DEBUG=true
APP_NAME=myapp
LOG_LEVEL=info

# 多值(逗号分隔)
ALLOWED_HOSTS=localhost,example.com,api.example.com

完整语法参考

.env 文件的引号、转义、多行值等完整语法规则,参见文件格式。

最简用法 ​

go
package main

import (
	"fmt"
	"github.com/cybergodev/env"
)

func main() {
	// 加载 .env 文件并应用到系统环境
	if err := env.Load(".env"); err != nil {
		panic(err)
	}

	// 获取环境变量
	host := env.GetString("DB_HOST", "localhost")
	port := env.GetInt("DB_PORT", 5432)

	fmt.Printf("Server: %s:%d\n", host, port)
	// 输出:Server: localhost:5432
}

两种使用模式

env 提供两种使用方式:

模式用法适用场景
全局模式env.Load() + env.GetString()简单应用、脚本、快速原型
实例模式env.New() + loader.GetString()多实例、测试隔离、精细控制生命周期

全局模式使用包级函数,内部维护一个默认 Loader 单例——调用 env.Load() 后,所有 env.GetXxx() 自动使用该实例。实例模式通过 env.New() 创建独立 Loader,适合需要隔离或同时管理多套配置的场景。

本文档示例默认使用全局模式,实例模式的完整用法见多环境配置章节。

注意:忘记调用 Load() 并不会报错——未初始化时 Get*/Lookup 会静默返回默认值或零值,而 Set/Delete/Validate/ParseInto 会返回 ErrNotInitialized。详见包函数。

读取值 - 所有类型 ​

基本类型 ​

go
// === 带默认值 ===

// 字符串 - 未找到时返回默认值 "localhost"
host := env.GetString("HOST", "localhost")

// 整数 (int64) - 未找到时返回默认值 8080
port := env.GetInt("PORT", 8080)

// 浮点数 (float64) - 未找到时返回默认值 0.99
rate := env.GetFloat64("RATE", 0.99)

// 无符号整数 (uint64) - 未找到时返回默认值 4096
size := env.GetUint64("MAX_SIZE", 4096)

// 布尔值 - 未找到时返回默认值 false
debug := env.GetBool("DEBUG", false)

// 时间间隔 - 未找到时返回默认值 30s
timeout := env.GetDuration("TIMEOUT", 30*time.Second)
go
// === 不带默认值 ===

// 字符串 - 未找到时返回空字符串 ""
host := env.GetString("HOST")

// 整数 (int64) - 未找到时返回 0
port := env.GetInt("PORT")

// 布尔值 - 未找到时返回 false
debug := env.GetBool("DEBUG")

// 时间间隔 - 未找到时返回 0
timeout := env.GetDuration("TIMEOUT")

键名解析

库支持多种键名访问方式:

go
// JSON: {"app": {"name": "myapp"}}
// 存储为:APP_NAME=myapp

// 以下方式都能访问到该值
name := env.GetString("APP_NAME")      // 扁平化键名(推荐)
name = env.GetString("app.name")       // 点号路径(自动转换)
name = env.GetString("APP.NAME")       // 大写点号路径

解析规则:

  1. 精确匹配:优先查找精确键名 KEY
  2. 大写转换:小写键名尝试大写版本 key → KEY
  3. 路径解析:点号路径转换为下划线 app.name → APP_NAME

布尔值支持 ​

GetBool 支持以下值(不区分大小写):

真值假值
true, 1, yes, on, enabledfalse, 0, no, off, disabled

切片类型 ​

go
// 字符串切片
hosts := env.GetSlice[string]("HOSTS", []string{"localhost"})

// 整数切片(支持 int, int64, uint, uint64)
ports := env.GetSlice[int64]("PORTS", []int64{80, 443})
portsInt := env.GetSlice[int]("PORTS")  // 也支持 int 类型

// 浮点数切片
rates := env.GetSlice[float64]("RATES", []float64{0.1, 0.2})

// 布尔切片
flags := env.GetSlice[bool]("FLAGS", []bool{true, false})

// Duration 切片
timeouts := env.GetSlice[time.Duration]("TIMEOUTS")

解析顺序:

  1. 优先查找索引键 KEY_0, KEY_1, KEY_2...
  2. 若无索引键,则按逗号分隔解析 KEY 的值
go
// 方式一:索引键(推荐)
// HOSTS_0=localhost
// HOSTS_1=example.com
hosts := env.GetSlice[string]("HOSTS")  // ["localhost", "example.com"]

// 方式二:逗号分隔
// PORTS=80,443,8080
ports := env.GetSlice[int64]("PORTS")  // [80, 443, 8080]

检查与查找 ​

go
// 检查键是否存在
value, exists := env.Lookup("API_KEY")
if !exists {
    // 键不存在
}

// 获取所有键
keys := env.Keys()

// 获取所有键值对
all := env.All()

// 获取变量数量
count := env.Len()

安全值 ​

go
secret := env.GetSecure("API_KEY")
if secret != nil {
    defer secret.Release()

    // 获取原始值(仅在需要明文时调用,如加解密、API 调用)
    value := secret.Reveal()

    // 日志使用掩码(防止泄露)
    log.Printf("API Key: %s", secret.Masked())  // 输出:[SECURE:32 bytes]
}

结构体映射 ​

使用标签将环境变量映射到结构体:

go
package main

import (
	"fmt"
	"time"

	"github.com/cybergodev/env"
)

type Config struct {
	Host     string        `env:"DB_HOST" envDefault:"localhost"`
	Port     int64         `env:"DB_PORT" envDefault:"5432"`
	Password string        `env:"DB_PASSWORD"`
	Debug    bool          `env:"DEBUG" envDefault:"false"`
	Timeout  time.Duration `env:"TIMEOUT" envDefault:"30s"`
	Hosts    []string      `env:"ALLOWED_HOSTS"`
}

func main() {
	env.Load(".env")

	var cfg Config
	if err := env.ParseInto(&cfg); err != nil {
		panic(err)
	}

	fmt.Printf("%+v\n", cfg)
	// 输出:{Host:localhost Port:5432 Password:secret Debug:true Timeout:30s Hosts:[localhost example.com api.example.com]}
}
详见

结构体映射 指南。

配置预设 ​

库提供四种预设配置,适用于不同场景:

预设用途特点
DefaultConfig()通用场景安全默认值,适合大多数情况
DevelopmentConfig()开发环境宽松限制,允许覆盖
TestingConfig()测试环境紧凑限制,允许覆盖,适合单元测试
ProductionConfig()生产环境严格验证 + 审计日志
go
// 开发环境 - 宽松限制
cfg := env.DevelopmentConfig()

// 测试环境 - 紧凑限制
cfg := env.TestingConfig()

// 生产环境 - 严格验证 + 审计日志
cfg := env.ProductionConfig()

预设详细对比 ​

功能DefaultDevelopmentTestingProduction
覆盖已存在变量✗✓✓✗
文件不存在时报错✗✗✗✓
审计日志✗✗✗✓
YAML 语法✗✓✗✗
文件大小限制2MB10MB64KB64KB
最大变量数5005005050
禁止键检查✓✓✓✓
值验证✓✓✓✓

选择建议

  • 开发环境:使用 DevelopmentConfig(),宽松限制便于快速迭代
  • 测试环境:使用 TestingConfig(),允许覆盖便于测试隔离
  • 生产环境:使用 ProductionConfig(),启用审计和严格验证

多环境配置 ​

按环境加载 ​

go
// 根据环境确定配置文件
goEnv := os.Getenv("GO_ENV")
if goEnv == "" {
    goEnv = "development"
}

// 默认:先加载的文件优先(.env 中已存在的键不会被后续文件覆盖)
env.Load(".env", ".env."+goEnv, ".env.local")

// 需要「后加载覆盖」(如 .env.local 覆盖 .env)时:
cfg := env.DefaultConfig()
cfg.OverwriteExisting = true
cfg.Filenames = []string{".env", ".env." + goEnv, ".env.local"}
if err := env.LoadWithConfig(cfg); err != nil { log.Fatal(err) }

覆盖语义

默认配置(DefaultConfig())与 ProductionConfig() 均为「先加载的文件胜出」:后加载文件中与已加载同名的键会被跳过,防止运行时意外覆盖已加载的配置。DevelopmentConfig() 与 TestingConfig() 预设已内置 OverwriteExisting=true,仅在这些预设(或手动开启该选项)下,后加载的文件才会覆盖先加载的。

使用 Loader 实例 ​

需要更多控制时,使用 Loader 实例:

go
package main

import (
	"fmt"
	"github.com/cybergodev/env"
)

func main() {
	// 创建配置
	cfg := env.ProductionConfig()
	cfg.RequiredKeys = []string{"DB_HOST", "API_KEY"}

	// 创建加载器
	loader, err := env.New(cfg)
	if err != nil {
		panic(err)
	}
	defer loader.Close()

	// 加载文件(按顺序;ProductionConfig 下先加载的胜出,已存在的键不会被覆盖)
	if err := loader.LoadFiles(".env", ".env.production"); err != nil {
		panic(err)
	}

	// 验证必需键
	if err := loader.Validate(); err != nil {
		panic(err)
	}

	// 使用
	host := loader.GetString("DB_HOST")
	fmt.Println("Host:", host)
}

多文件与多格式 ​

多文件加载 ​

按顺序加载。默认(DefaultConfig())先加载的文件胜出:后加载文件中已存在的同名键会被跳过;需要「后加载覆盖」时设置 OverwriteExisting = true(DevelopmentConfig()/TestingConfig() 预设已内置):

go
env.Load(".env", "config.json", "config.yaml")
go
loader.LoadFiles(".env", ".env.local")

多格式支持 ​

自动检测文件格式:

go
loader.LoadFiles("config.env", "settings.json", "secrets.yaml")
支持的格式
格式扩展名检测方式
.env.env文件扩展名
JSON.json文件扩展名
YAML.yaml, .yml文件扩展名

错误处理 ​

go
import "errors"

err := loader.LoadFiles(".env")
if err != nil {
    switch {
    case errors.Is(err, env.ErrFileNotFound):
        // 文件不存在
    case errors.Is(err, env.ErrFileTooLarge):
        // 文件过大
    case errors.Is(err, env.ErrSecurityViolation):
        // 禁止键(实际返回 *SecurityError)
    default:
        // 其他错误
    }

    // 键格式非法:实际返回 *ValidationError,Field=="key"
    var valErr *env.ValidationError
    if errors.As(err, &valErr) && valErr.Field == "key" {
        // 无效键格式
    }
}
获取详细错误信息
go
// 解析错误详情
var parseErr *env.ParseError
if errors.As(err, &parseErr) {
    fmt.Printf("文件 %s 第 %d 行: %v\n", parseErr.File, parseErr.Line, parseErr.Err)
}

// 文件错误详情
var fileErr *env.FileError
if errors.As(err, &fileErr) {
    fmt.Printf("文件 %s 操作 %s 失败: %v\n", fileErr.Path, fileErr.Op, fileErr.Err)
}

// 安全错误详情
var secErr *env.SecurityError
if errors.As(err, &secErr) {
    fmt.Printf("安全错误: %s - %s\n", secErr.Action, secErr.Reason)
}

下一步 ​

深入学习 ​

API 参考 ​

安全 ​