feat(架构): 完善基础架构设计
This commit is contained in:
@@ -12,6 +12,7 @@ type Config struct {
|
||||
Cache CacheConfig `mapstructure:"cache"`
|
||||
Logger LoggerConfig `mapstructure:"logger"`
|
||||
JWT JWTConfig `mapstructure:"jwt"`
|
||||
SMS SMSConfig `mapstructure:"sms"`
|
||||
RateLimit RateLimitConfig `mapstructure:"ratelimit"`
|
||||
Monitoring MonitoringConfig `mapstructure:"monitoring"`
|
||||
Health HealthConfig `mapstructure:"health"`
|
||||
@@ -42,6 +43,7 @@ type DatabaseConfig struct {
|
||||
MaxOpenConns int `mapstructure:"max_open_conns"`
|
||||
MaxIdleConns int `mapstructure:"max_idle_conns"`
|
||||
ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
|
||||
AutoMigrate bool `mapstructure:"auto_migrate"`
|
||||
}
|
||||
|
||||
// RedisConfig Redis配置
|
||||
@@ -67,14 +69,15 @@ type CacheConfig struct {
|
||||
|
||||
// LoggerConfig 日志配置
|
||||
type LoggerConfig struct {
|
||||
Level string `mapstructure:"level"`
|
||||
Format string `mapstructure:"format"`
|
||||
Output string `mapstructure:"output"`
|
||||
FilePath string `mapstructure:"file_path"`
|
||||
MaxSize int `mapstructure:"max_size"`
|
||||
MaxBackups int `mapstructure:"max_backups"`
|
||||
MaxAge int `mapstructure:"max_age"`
|
||||
Compress bool `mapstructure:"compress"`
|
||||
Level string `mapstructure:"level"`
|
||||
Format string `mapstructure:"format"`
|
||||
Output string `mapstructure:"output"`
|
||||
FilePath string `mapstructure:"file_path"`
|
||||
MaxSize int `mapstructure:"max_size"`
|
||||
MaxBackups int `mapstructure:"max_backups"`
|
||||
MaxAge int `mapstructure:"max_age"`
|
||||
Compress bool `mapstructure:"compress"`
|
||||
UseColor bool `mapstructure:"use_color"`
|
||||
}
|
||||
|
||||
// JWTConfig JWT配置
|
||||
@@ -119,12 +122,12 @@ type ResilienceConfig struct {
|
||||
|
||||
// DevelopmentConfig 开发配置
|
||||
type DevelopmentConfig struct {
|
||||
Debug bool `mapstructure:"debug"`
|
||||
EnableProfiler bool `mapstructure:"enable_profiler"`
|
||||
EnableCors bool `mapstructure:"enable_cors"`
|
||||
CorsOrigins string `mapstructure:"cors_allowed_origins"`
|
||||
CorsMethods string `mapstructure:"cors_allowed_methods"`
|
||||
CorsHeaders string `mapstructure:"cors_allowed_headers"`
|
||||
Debug bool `mapstructure:"debug"`
|
||||
EnableProfiler bool `mapstructure:"enable_profiler"`
|
||||
EnableCors bool `mapstructure:"enable_cors"`
|
||||
CorsOrigins string `mapstructure:"cors_allowed_origins"`
|
||||
CorsMethods string `mapstructure:"cors_allowed_methods"`
|
||||
CorsHeaders string `mapstructure:"cors_allowed_headers"`
|
||||
}
|
||||
|
||||
// AppConfig 应用程序配置
|
||||
@@ -134,6 +137,26 @@ type AppConfig struct {
|
||||
Env string `mapstructure:"env"`
|
||||
}
|
||||
|
||||
// SMSConfig 短信配置
|
||||
type SMSConfig struct {
|
||||
AccessKeyID string `mapstructure:"access_key_id"`
|
||||
AccessKeySecret string `mapstructure:"access_key_secret"`
|
||||
EndpointURL string `mapstructure:"endpoint_url"`
|
||||
SignName string `mapstructure:"sign_name"`
|
||||
TemplateCode string `mapstructure:"template_code"`
|
||||
CodeLength int `mapstructure:"code_length"`
|
||||
ExpireTime time.Duration `mapstructure:"expire_time"`
|
||||
RateLimit SMSRateLimit `mapstructure:"rate_limit"`
|
||||
MockEnabled bool `mapstructure:"mock_enabled"` // 是否启用模拟短信服务
|
||||
}
|
||||
|
||||
// SMSRateLimit 短信限流配置
|
||||
type SMSRateLimit struct {
|
||||
DailyLimit int `mapstructure:"daily_limit"` // 每日发送限制
|
||||
HourlyLimit int `mapstructure:"hourly_limit"` // 每小时发送限制
|
||||
MinInterval time.Duration `mapstructure:"min_interval"` // 最小发送间隔
|
||||
}
|
||||
|
||||
// GetDSN 获取数据库DSN连接字符串
|
||||
func (d DatabaseConfig) GetDSN() string {
|
||||
return "host=" + d.Host +
|
||||
@@ -163,4 +186,4 @@ func (a AppConfig) IsDevelopment() bool {
|
||||
// IsStaging 检查是否为测试环境
|
||||
func (a AppConfig) IsStaging() bool {
|
||||
return a.Env == "staging"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -11,228 +12,173 @@ import (
|
||||
|
||||
// LoadConfig 加载应用程序配置
|
||||
func LoadConfig() (*Config, error) {
|
||||
// 设置配置文件名和路径
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(".")
|
||||
viper.AddConfigPath("./configs")
|
||||
viper.AddConfigPath("$HOME/.tyapi")
|
||||
// 1️⃣ 获取环境变量决定配置文件
|
||||
env := getEnvironment()
|
||||
fmt.Printf("🔧 当前运行环境: %s\n", env)
|
||||
|
||||
// 设置环境变量前缀
|
||||
viper.SetEnvPrefix("")
|
||||
viper.AutomaticEnv()
|
||||
// 2️⃣ 加载基础配置文件
|
||||
baseConfig := viper.New()
|
||||
baseConfig.SetConfigName("config")
|
||||
baseConfig.SetConfigType("yaml")
|
||||
baseConfig.AddConfigPath(".")
|
||||
baseConfig.AddConfigPath("./configs")
|
||||
baseConfig.AddConfigPath("$HOME/.tyapi")
|
||||
|
||||
// 配置环境变量键名映射
|
||||
setupEnvKeyMapping()
|
||||
|
||||
// 设置默认值
|
||||
setDefaults()
|
||||
|
||||
// 尝试读取配置文件(可选)
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
// 读取基础配置文件
|
||||
if err := baseConfig.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||
return nil, fmt.Errorf("读取配置文件失败: %w", err)
|
||||
return nil, fmt.Errorf("读取基础配置文件失败: %w", err)
|
||||
}
|
||||
// 配置文件不存在时使用环境变量和默认值
|
||||
return nil, fmt.Errorf("未找到 config.yaml 文件,请确保配置文件存在")
|
||||
}
|
||||
fmt.Printf("✅ 已加载配置文件: %s\n", baseConfig.ConfigFileUsed())
|
||||
|
||||
// 3️⃣ 加载环境特定配置文件
|
||||
envConfigFile := findEnvConfigFile(env)
|
||||
if envConfigFile != "" {
|
||||
// 创建一个新的viper实例来读取环境配置
|
||||
envConfig := viper.New()
|
||||
envConfig.SetConfigFile(envConfigFile)
|
||||
|
||||
if err := envConfig.ReadInConfig(); err != nil {
|
||||
fmt.Printf("⚠️ 环境配置文件加载警告: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("✅ 已加载环境配置: %s\n", envConfigFile)
|
||||
|
||||
// 将环境配置合并到基础配置中
|
||||
if err := mergeConfigs(baseConfig, envConfig.AllSettings()); err != nil {
|
||||
return nil, fmt.Errorf("合并配置失败: %w", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("⚠️ 未找到环境配置文件 env.%s.yaml\n", env)
|
||||
}
|
||||
|
||||
// 4️⃣ 设置环境变量前缀和自动读取
|
||||
baseConfig.SetEnvPrefix("")
|
||||
baseConfig.AutomaticEnv()
|
||||
baseConfig.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
|
||||
// 5️⃣ 解析配置到结构体
|
||||
var config Config
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
if err := baseConfig.Unmarshal(&config); err != nil {
|
||||
return nil, fmt.Errorf("解析配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 验证配置
|
||||
// 6️⃣ 验证配置
|
||||
if err := validateConfig(&config); err != nil {
|
||||
return nil, fmt.Errorf("配置验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 7️⃣ 输出配置摘要
|
||||
printConfigSummary(&config, env)
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// setupEnvKeyMapping 设置环境变量到配置键的映射
|
||||
func setupEnvKeyMapping() {
|
||||
// 服务器配置
|
||||
viper.BindEnv("server.port", "SERVER_PORT")
|
||||
viper.BindEnv("server.mode", "SERVER_MODE")
|
||||
viper.BindEnv("server.host", "SERVER_HOST")
|
||||
viper.BindEnv("server.read_timeout", "SERVER_READ_TIMEOUT")
|
||||
viper.BindEnv("server.write_timeout", "SERVER_WRITE_TIMEOUT")
|
||||
viper.BindEnv("server.idle_timeout", "SERVER_IDLE_TIMEOUT")
|
||||
// mergeConfigs 递归合并配置
|
||||
func mergeConfigs(baseConfig *viper.Viper, overrideSettings map[string]interface{}) error {
|
||||
for key, val := range overrideSettings {
|
||||
// 如果值是一个嵌套的map,则递归合并
|
||||
if subMap, ok := val.(map[string]interface{}); ok {
|
||||
// 创建子键路径
|
||||
subKey := key
|
||||
|
||||
// 数据库配置
|
||||
viper.BindEnv("database.host", "DB_HOST")
|
||||
viper.BindEnv("database.port", "DB_PORT")
|
||||
viper.BindEnv("database.user", "DB_USER")
|
||||
viper.BindEnv("database.password", "DB_PASSWORD")
|
||||
viper.BindEnv("database.name", "DB_NAME")
|
||||
viper.BindEnv("database.sslmode", "DB_SSLMODE")
|
||||
viper.BindEnv("database.timezone", "DB_TIMEZONE")
|
||||
viper.BindEnv("database.max_open_conns", "DB_MAX_OPEN_CONNS")
|
||||
viper.BindEnv("database.max_idle_conns", "DB_MAX_IDLE_CONNS")
|
||||
viper.BindEnv("database.conn_max_lifetime", "DB_CONN_MAX_LIFETIME")
|
||||
|
||||
// Redis配置
|
||||
viper.BindEnv("redis.host", "REDIS_HOST")
|
||||
viper.BindEnv("redis.port", "REDIS_PORT")
|
||||
viper.BindEnv("redis.password", "REDIS_PASSWORD")
|
||||
viper.BindEnv("redis.db", "REDIS_DB")
|
||||
viper.BindEnv("redis.pool_size", "REDIS_POOL_SIZE")
|
||||
viper.BindEnv("redis.min_idle_conns", "REDIS_MIN_IDLE_CONNS")
|
||||
viper.BindEnv("redis.max_retries", "REDIS_MAX_RETRIES")
|
||||
viper.BindEnv("redis.dial_timeout", "REDIS_DIAL_TIMEOUT")
|
||||
viper.BindEnv("redis.read_timeout", "REDIS_READ_TIMEOUT")
|
||||
viper.BindEnv("redis.write_timeout", "REDIS_WRITE_TIMEOUT")
|
||||
|
||||
// 缓存配置
|
||||
viper.BindEnv("cache.default_ttl", "CACHE_DEFAULT_TTL")
|
||||
viper.BindEnv("cache.cleanup_interval", "CACHE_CLEANUP_INTERVAL")
|
||||
viper.BindEnv("cache.max_size", "CACHE_MAX_SIZE")
|
||||
|
||||
// 日志配置
|
||||
viper.BindEnv("logger.level", "LOG_LEVEL")
|
||||
viper.BindEnv("logger.format", "LOG_FORMAT")
|
||||
viper.BindEnv("logger.output", "LOG_OUTPUT")
|
||||
viper.BindEnv("logger.file_path", "LOG_FILE_PATH")
|
||||
viper.BindEnv("logger.max_size", "LOG_MAX_SIZE")
|
||||
viper.BindEnv("logger.max_backups", "LOG_MAX_BACKUPS")
|
||||
viper.BindEnv("logger.max_age", "LOG_MAX_AGE")
|
||||
viper.BindEnv("logger.compress", "LOG_COMPRESS")
|
||||
|
||||
// JWT配置
|
||||
viper.BindEnv("jwt.secret", "JWT_SECRET")
|
||||
viper.BindEnv("jwt.expires_in", "JWT_EXPIRES_IN")
|
||||
viper.BindEnv("jwt.refresh_expires_in", "JWT_REFRESH_EXPIRES_IN")
|
||||
|
||||
// 限流配置
|
||||
viper.BindEnv("ratelimit.requests", "RATE_LIMIT_REQUESTS")
|
||||
viper.BindEnv("ratelimit.window", "RATE_LIMIT_WINDOW")
|
||||
viper.BindEnv("ratelimit.burst", "RATE_LIMIT_BURST")
|
||||
|
||||
// 监控配置
|
||||
viper.BindEnv("monitoring.metrics_enabled", "METRICS_ENABLED")
|
||||
viper.BindEnv("monitoring.metrics_port", "METRICS_PORT")
|
||||
viper.BindEnv("monitoring.tracing_enabled", "TRACING_ENABLED")
|
||||
viper.BindEnv("monitoring.tracing_endpoint", "TRACING_ENDPOINT")
|
||||
viper.BindEnv("monitoring.sample_rate", "TRACING_SAMPLE_RATE")
|
||||
|
||||
// 健康检查配置
|
||||
viper.BindEnv("health.enabled", "HEALTH_CHECK_ENABLED")
|
||||
viper.BindEnv("health.interval", "HEALTH_CHECK_INTERVAL")
|
||||
viper.BindEnv("health.timeout", "HEALTH_CHECK_TIMEOUT")
|
||||
|
||||
// 容错配置
|
||||
viper.BindEnv("resilience.circuit_breaker_enabled", "CIRCUIT_BREAKER_ENABLED")
|
||||
viper.BindEnv("resilience.circuit_breaker_threshold", "CIRCUIT_BREAKER_THRESHOLD")
|
||||
viper.BindEnv("resilience.circuit_breaker_timeout", "CIRCUIT_BREAKER_TIMEOUT")
|
||||
viper.BindEnv("resilience.retry_max_attempts", "RETRY_MAX_ATTEMPTS")
|
||||
viper.BindEnv("resilience.retry_initial_delay", "RETRY_INITIAL_DELAY")
|
||||
viper.BindEnv("resilience.retry_max_delay", "RETRY_MAX_DELAY")
|
||||
|
||||
// 开发配置
|
||||
viper.BindEnv("development.debug", "DEBUG")
|
||||
viper.BindEnv("development.enable_profiler", "ENABLE_PROFILER")
|
||||
viper.BindEnv("development.enable_cors", "ENABLE_CORS")
|
||||
viper.BindEnv("development.cors_allowed_origins", "CORS_ALLOWED_ORIGINS")
|
||||
viper.BindEnv("development.cors_allowed_methods", "CORS_ALLOWED_METHODS")
|
||||
viper.BindEnv("development.cors_allowed_headers", "CORS_ALLOWED_HEADERS")
|
||||
|
||||
// 应用程序配置
|
||||
viper.BindEnv("app.name", "APP_NAME")
|
||||
viper.BindEnv("app.version", "APP_VERSION")
|
||||
viper.BindEnv("app.env", "ENV")
|
||||
// 递归合并子配置
|
||||
for subK, subV := range subMap {
|
||||
fullKey := fmt.Sprintf("%s.%s", subKey, subK)
|
||||
baseConfig.Set(fullKey, subV)
|
||||
}
|
||||
} else {
|
||||
// 直接设置值
|
||||
baseConfig.Set(key, val)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setDefaults 设置默认配置值
|
||||
func setDefaults() {
|
||||
// 服务器默认值
|
||||
viper.SetDefault("server.port", "8080")
|
||||
viper.SetDefault("server.mode", "debug")
|
||||
viper.SetDefault("server.host", "0.0.0.0")
|
||||
viper.SetDefault("server.read_timeout", "30s")
|
||||
viper.SetDefault("server.write_timeout", "30s")
|
||||
viper.SetDefault("server.idle_timeout", "120s")
|
||||
// findEnvConfigFile 查找环境特定的配置文件
|
||||
func findEnvConfigFile(env string) string {
|
||||
// 尝试查找的配置文件路径
|
||||
possiblePaths := []string{
|
||||
fmt.Sprintf("configs/env.%s.yaml", env),
|
||||
fmt.Sprintf("configs/env.%s.yml", env),
|
||||
fmt.Sprintf("configs/env.%s", env),
|
||||
fmt.Sprintf("env.%s.yaml", env),
|
||||
fmt.Sprintf("env.%s.yml", env),
|
||||
fmt.Sprintf("env.%s", env),
|
||||
}
|
||||
|
||||
// 数据库默认值
|
||||
viper.SetDefault("database.host", "localhost")
|
||||
viper.SetDefault("database.port", "5432")
|
||||
viper.SetDefault("database.user", "postgres")
|
||||
viper.SetDefault("database.password", "password")
|
||||
viper.SetDefault("database.name", "tyapi_db")
|
||||
viper.SetDefault("database.sslmode", "disable")
|
||||
viper.SetDefault("database.timezone", "Asia/Shanghai")
|
||||
viper.SetDefault("database.max_open_conns", 100)
|
||||
viper.SetDefault("database.max_idle_conns", 10)
|
||||
viper.SetDefault("database.conn_max_lifetime", "300s")
|
||||
// 如果有自定义环境文件路径
|
||||
if customEnvFile := os.Getenv("ENV_FILE"); customEnvFile != "" {
|
||||
possiblePaths = append([]string{customEnvFile}, possiblePaths...)
|
||||
}
|
||||
|
||||
// Redis默认值
|
||||
viper.SetDefault("redis.host", "localhost")
|
||||
viper.SetDefault("redis.port", "6379")
|
||||
viper.SetDefault("redis.password", "")
|
||||
viper.SetDefault("redis.db", 0)
|
||||
viper.SetDefault("redis.pool_size", 10)
|
||||
viper.SetDefault("redis.min_idle_conns", 5)
|
||||
viper.SetDefault("redis.max_retries", 3)
|
||||
viper.SetDefault("redis.dial_timeout", "5s")
|
||||
viper.SetDefault("redis.read_timeout", "3s")
|
||||
viper.SetDefault("redis.write_timeout", "3s")
|
||||
for _, path := range possiblePaths {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
absPath, _ := filepath.Abs(path)
|
||||
return absPath
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存默认值
|
||||
viper.SetDefault("cache.default_ttl", "300s")
|
||||
viper.SetDefault("cache.cleanup_interval", "600s")
|
||||
viper.SetDefault("cache.max_size", 1000)
|
||||
return ""
|
||||
}
|
||||
|
||||
// 日志默认值
|
||||
viper.SetDefault("logger.level", "info")
|
||||
viper.SetDefault("logger.format", "json")
|
||||
viper.SetDefault("logger.output", "stdout")
|
||||
viper.SetDefault("logger.file_path", "logs/app.log")
|
||||
viper.SetDefault("logger.max_size", 100)
|
||||
viper.SetDefault("logger.max_backups", 5)
|
||||
viper.SetDefault("logger.max_age", 30)
|
||||
viper.SetDefault("logger.compress", true)
|
||||
// getEnvironment 获取当前环境
|
||||
func getEnvironment() string {
|
||||
var env string
|
||||
var source string
|
||||
|
||||
// JWT默认值
|
||||
viper.SetDefault("jwt.secret", "your-super-secret-jwt-key-change-this-in-production")
|
||||
viper.SetDefault("jwt.expires_in", "24h")
|
||||
viper.SetDefault("jwt.refresh_expires_in", "168h")
|
||||
// 优先级:CONFIG_ENV > ENV > APP_ENV > 默认值
|
||||
if env = os.Getenv("CONFIG_ENV"); env != "" {
|
||||
source = "CONFIG_ENV"
|
||||
} else if env = os.Getenv("ENV"); env != "" {
|
||||
source = "ENV"
|
||||
} else if env = os.Getenv("APP_ENV"); env != "" {
|
||||
source = "APP_ENV"
|
||||
} else {
|
||||
env = "development"
|
||||
source = "默认值"
|
||||
}
|
||||
|
||||
// 限流默认值
|
||||
viper.SetDefault("ratelimit.requests", 100)
|
||||
viper.SetDefault("ratelimit.window", "60s")
|
||||
viper.SetDefault("ratelimit.burst", 10)
|
||||
fmt.Printf("🌍 环境检测: %s (来源: %s)\n", env, source)
|
||||
|
||||
// 监控默认值
|
||||
viper.SetDefault("monitoring.metrics_enabled", true)
|
||||
viper.SetDefault("monitoring.metrics_port", "9090")
|
||||
viper.SetDefault("monitoring.tracing_enabled", false)
|
||||
viper.SetDefault("monitoring.tracing_endpoint", "http://localhost:14268/api/traces")
|
||||
viper.SetDefault("monitoring.sample_rate", 0.1)
|
||||
// 验证环境值
|
||||
validEnvs := []string{"development", "production", "testing"}
|
||||
isValid := false
|
||||
for _, validEnv := range validEnvs {
|
||||
if env == validEnv {
|
||||
isValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 健康检查默认值
|
||||
viper.SetDefault("health.enabled", true)
|
||||
viper.SetDefault("health.interval", "30s")
|
||||
viper.SetDefault("health.timeout", "5s")
|
||||
if !isValid {
|
||||
fmt.Printf("⚠️ 警告: 未识别的环境 '%s',将使用默认环境 'development'\n", env)
|
||||
return "development"
|
||||
}
|
||||
|
||||
// 容错默认值
|
||||
viper.SetDefault("resilience.circuit_breaker_enabled", true)
|
||||
viper.SetDefault("resilience.circuit_breaker_threshold", 5)
|
||||
viper.SetDefault("resilience.circuit_breaker_timeout", "60s")
|
||||
viper.SetDefault("resilience.retry_max_attempts", 3)
|
||||
viper.SetDefault("resilience.retry_initial_delay", "100ms")
|
||||
viper.SetDefault("resilience.retry_max_delay", "2s")
|
||||
return env
|
||||
}
|
||||
|
||||
// 开发默认值
|
||||
viper.SetDefault("development.debug", true)
|
||||
viper.SetDefault("development.enable_profiler", false)
|
||||
viper.SetDefault("development.enable_cors", true)
|
||||
viper.SetDefault("development.cors_allowed_origins", "*")
|
||||
viper.SetDefault("development.cors_allowed_methods", "GET,POST,PUT,DELETE,OPTIONS")
|
||||
viper.SetDefault("development.cors_allowed_headers", "*")
|
||||
|
||||
// 应用程序默认值
|
||||
viper.SetDefault("app.name", "tyapi-server")
|
||||
viper.SetDefault("app.version", "1.0.0")
|
||||
viper.SetDefault("app.env", "development")
|
||||
// printConfigSummary 打印配置摘要
|
||||
func printConfigSummary(config *Config, env string) {
|
||||
fmt.Printf("\n🔧 配置摘要:\n")
|
||||
fmt.Printf(" 🌍 环境: %s\n", env)
|
||||
fmt.Printf(" 📄 配置模板: config.yaml\n")
|
||||
fmt.Printf(" 📱 应用名称: %s\n", config.App.Name)
|
||||
fmt.Printf(" 🔖 版本: %s\n", config.App.Version)
|
||||
fmt.Printf(" 🌐 服务端口: %s\n", config.Server.Port)
|
||||
fmt.Printf(" 🗄️ 数据库: %s@%s:%s/%s\n",
|
||||
config.Database.User,
|
||||
config.Database.Host,
|
||||
config.Database.Port,
|
||||
config.Database.Name)
|
||||
fmt.Printf(" 📊 追踪状态: %v (端点: %s)\n",
|
||||
config.Monitoring.TracingEnabled,
|
||||
config.Monitoring.TracingEndpoint)
|
||||
fmt.Printf(" 📈 采样率: %.1f%%\n", config.Monitoring.SampleRate*100)
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
// validateConfig 验证配置
|
||||
|
||||
Reference in New Issue
Block a user