6.0 KiB
6.0 KiB
Configuration Files
The config package supports TOML configuration files with automatic loading, discovery, and atomic saving.
TOML Format
TOML (Tom's Obvious, Minimal Language) is the supported configuration format:
# Basic values
host = "localhost"
port = 8080
debug = false
# Nested sections
[server]
host = "0.0.0.0"
port = 9090
timeout = "30s"
[database]
url = "postgres://localhost/mydb"
max_conns = 25
timeout = "5s"
# Arrays
[features]
enabled = ["auth", "api", "metrics"]
# Inline tables
tls = { enabled = true, cert = "/path/to/cert", key = "/path/to/key" }
Loading Configuration Files
Basic Loading
cfg := config.New()
cfg.RegisterStruct("", &Config{})
if err := cfg.LoadFile("config.toml"); err != nil {
if errors.Is(err, config.ErrConfigNotFound) {
log.Println("Config file not found, using defaults")
} else {
log.Fatal("Failed to load config:", err)
}
}
With Builder
cfg, err := config.NewBuilder().
WithDefaults(&Config{}).
WithFile("/etc/myapp/config.toml").
Build()
Multiple File Attempts
// Try multiple locations
locations := []string{
"./config.toml",
"~/.config/myapp/config.toml",
"/etc/myapp/config.toml",
}
var cfg *config.Config
var err error
for _, path := range locations {
cfg, err = config.NewBuilder().
WithDefaults(&Config{}).
WithFile(path).
Build()
if err == nil || !errors.Is(err, config.ErrConfigNotFound) {
break
}
}
Automatic File Discovery
Use file discovery to find configuration automatically:
cfg, _ := config.NewBuilder().
WithDefaults(&Config{}).
WithFileDiscovery(config.FileDiscoveryOptions{
Name: "myapp",
Extensions: []string{".toml", ".conf"},
EnvVar: "MYAPP_CONFIG",
CLIFlag: "--config",
UseXDG: true,
UseCurrentDir: true,
Paths: []string{"/opt/myapp"},
}).
Build()
Search order:
- CLI flag:
--config=/path/to/config.toml - Environment variable:
$MYAPP_CONFIG - Current directory:
./myapp.toml,./myapp.conf - XDG config:
~/.config/myapp/myapp.toml - System paths:
/etc/myapp/myapp.toml - Custom paths:
/opt/myapp/myapp.toml
Saving Configuration
Save Current State
// Save all current values atomically
if err := cfg.Save("config.toml"); err != nil {
log.Fatal("Failed to save config:", err)
}
The save operation is atomic - it writes to a temporary file then renames it.
Save Specific Source
// Save only values from environment variables
if err := cfg.SaveSource("env-config.toml", config.SourceEnv); err != nil {
log.Fatal(err)
}
// Save only file-loaded values
if err := cfg.SaveSource("file-only.toml", config.SourceFile); err != nil {
log.Fatal(err)
}
Generate Default Configuration
// Create a default config file
defaults := &Config{}
// ... set default values ...
cfg, _ := config.NewBuilder().
WithDefaults(defaults).
Build()
// Save defaults as config template
if err := cfg.SaveSource("config.toml.example", config.SourceDefault); err != nil {
log.Fatal(err)
}
File Structure Mapping
TOML structure maps directly to dot-notation paths:
# Maps to "debug"
debug = true
[server]
# Maps to "server.host"
host = "localhost"
# Maps to "server.port"
port = 8080
[server.tls]
# Maps to "server.tls.enabled"
enabled = true
# Maps to "server.tls.cert"
cert = "/path/to/cert"
[[users]]
# Array elements: "users.0.name", "users.0.role"
name = "admin"
role = "administrator"
[[users]]
# Array elements: "users.1.name", "users.1.role"
name = "user"
role = "standard"
Type Handling
TOML types map to Go types:
# Strings
name = "myapp"
multiline = """
Line one
Line two
"""
# Numbers
port = 8080 # int64
timeout = 30 # int64
ratio = 0.95 # float64
max_size = 1_000_000 # int64 (underscores allowed)
# Booleans
enabled = true
debug = false
# Dates/Times (RFC 3339)
created_at = 2024-01-15T09:30:00Z
expires = 2024-12-31
# Arrays
ports = [8080, 8081, 8082]
tags = ["production", "stable"]
# Tables (objects)
[database]
host = "localhost"
port = 5432
# Array of tables
[[servers]]
name = "web1"
host = "10.0.0.1"
[[servers]]
name = "web2"
host = "10.0.0.2"
Error Handling
File loading can produce several error types:
err := cfg.LoadFile("config.toml")
if err != nil {
switch {
case errors.Is(err, config.ErrConfigNotFound):
// File doesn't exist - often not fatal
log.Println("No config file, using defaults")
case strings.Contains(err.Error(), "failed to parse TOML"):
// TOML syntax error
log.Fatal("Invalid TOML syntax:", err)
case strings.Contains(err.Error(), "failed to read"):
// Permission or I/O error
log.Fatal("Cannot read config file:", err)
default:
log.Fatal("Config error:", err)
}
}
Security Considerations
File Permissions
// After saving, verify permissions
info, err := os.Stat("config.toml")
if err == nil {
mode := info.Mode()
if mode&0077 != 0 {
log.Warn("Config file is world/group readable")
// Fix permissions
os.Chmod("config.toml", 0600)
}
}
Size Limits
Files and values have size limits:
- Maximum file size: ~10MB (10 * MaxValueSize)
- Maximum value size: 1MB
Partial Loading
Load only specific sections:
var serverCfg ServerConfig
if err := cfg.Scan("server", &serverCfg); err != nil {
log.Fatal(err)
}
var dbCfg DatabaseConfig
if err := cfg.Scan("database", &dbCfg); err != nil {
log.Fatal(err)
}
Best Practices
- Use Example Files: Generate
.examplefiles with defaults - Check Permissions: Ensure config files aren't world-readable
- Validate After Load: Add validators to check loaded values
- Handle Missing Files: Missing config files often aren't fatal
- Use Atomic Saves: The built-in Save method is atomic
- Document Structure: Comment your TOML files thoroughly