@@ -30,27 +30,29 @@ import (
3030 "golang.org/x/sync/errgroup"
3131)
3232
33+ const logError = "error"
34+
3335// Config holds the application configuration.
3436type Config struct {
3537 // Observability
36- MetricsPort int `env:"METRICS_PORT" envDefault:"9090"`
37- HealthPort int `env:"HEALTH_PORT" envDefault:"8080"`
38- LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
39- LogFormat string `env:"LOG_FORMAT" envDefault:"json"`
38+ MetricsPort int `env:"METRICS_PORT" envDefault:"9090"`
39+ HealthPort int `env:"HEALTH_PORT" envDefault:"8080"`
40+ LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
41+ LogFormat string `env:"LOG_FORMAT" envDefault:"json"`
4042
4143 // Resource Limits
42- MaxConnections int `env:"MAX_CONNECTIONS" envDefault:"10000"`
43- MaxGoroutines int `env:"MAX_GOROUTINES" envDefault:"50000"`
44+ MaxConnections int `env:"MAX_CONNECTIONS" envDefault:"10000"`
45+ MaxGoroutines int `env:"MAX_GOROUTINES" envDefault:"50000"`
4446
4547 // Connection Pooling
4648 PoolMaxIdle int `env:"POOL_MAX_IDLE" envDefault:"100"`
4749 PoolMaxActive int `env:"POOL_MAX_ACTIVE" envDefault:"1000"`
4850 PoolIdleTimeout time.Duration `env:"POOL_IDLE_TIMEOUT" envDefault:"5m"`
4951
5052 // Circuit Breaker
51- BreakerMaxFailures int `env:"BREAKER_MAX_FAILURES" envDefault:"5"`
52- BreakerResetTimeout time.Duration `env:"BREAKER_RESET_TIMEOUT" envDefault:"60s"`
53- BreakerTimeout time.Duration `env:"BREAKER_TIMEOUT" envDefault:"30s"`
53+ BreakerMaxFailures int `env:"BREAKER_MAX_FAILURES" envDefault:"5"`
54+ BreakerResetTimeout time.Duration `env:"BREAKER_RESET_TIMEOUT" envDefault:"60s"`
55+ BreakerTimeout time.Duration `env:"BREAKER_TIMEOUT" envDefault:"30s"`
5456
5557 // Rate Limiting
5658 RateLimitCapacity int64 `env:"RATE_LIMIT_CAPACITY" envDefault:"100"`
@@ -70,14 +72,20 @@ type Config struct {
7072}
7173
7274func main () {
75+ if err := run (); err != nil {
76+ fmt .Fprintln (os .Stderr , err )
77+ os .Exit (1 )
78+ }
79+ }
80+
81+ func run () error {
7382 // Load configuration
7483 cfg := Config {}
7584 if err := godotenv .Load (); err != nil {
7685 // .env file is optional
7786 }
7887 if err := env .Parse (& cfg ); err != nil {
79- fmt .Fprintf (os .Stderr , "Failed to parse config: %v\n " , err )
80- os .Exit (1 )
88+ return fmt .Errorf ("failed to parse config: %w" , err )
8189 }
8290
8391 // Setup logger
@@ -216,7 +224,7 @@ func main() {
216224
217225 mqttProxy , err := proxy .NewMQTT (mqttProxyConfig , instrumentedHandler )
218226 if err != nil {
219- logger .Error ("Failed to create MQTT proxy" , slog .String ("error" , err .Error ()))
227+ logger .Error ("Failed to create MQTT proxy" , slog .String (logError , err .Error ()))
220228 } else {
221229 g .Go (func () error {
222230 address := net .JoinHostPort (mqttProxyConfig .Host , mqttProxyConfig .Port )
@@ -246,21 +254,22 @@ func main() {
246254 shutdownCtx , shutdownCancel := context .WithTimeout (context .Background (), cfg .ShutdownTimeout )
247255 defer shutdownCancel ()
248256
249- done := make (chan error )
257+ done := make (chan error , 1 )
250258 go func () {
251259 done <- g .Wait ()
252260 }()
253261
254262 select {
255263 case err := <- done :
256264 if err != nil {
257- logger .Error ("Shutdown error" , slog .String ("error" , err .Error ()))
258- os . Exit ( 1 )
265+ logger .Error ("Shutdown error" , slog .String (logError , err .Error ()))
266+ return err
259267 }
260268 logger .Info ("Graceful shutdown completed" )
269+ return nil
261270 case <- shutdownCtx .Done ():
262271 logger .Warn ("Shutdown timeout exceeded, forcing exit" )
263- os . Exit ( 1 )
272+ return shutdownCtx . Err ( )
264273 }
265274}
266275
@@ -274,7 +283,7 @@ func setupLogger(level, format string) *slog.Logger {
274283 logLevel = slog .LevelInfo
275284 case "warn" :
276285 logLevel = slog .LevelWarn
277- case "error" :
286+ case logError :
278287 logLevel = slog .LevelError
279288 default :
280289 logLevel = slog .LevelInfo
@@ -311,7 +320,7 @@ func startMetricsServer(port int, logger *slog.Logger) {
311320 }
312321
313322 if err := srv .ListenAndServe (); err != nil && err != http .ErrServerClosed {
314- logger .Error ("Metrics server error" , slog .String ("error" , err .Error ()))
323+ logger .Error ("Metrics server error" , slog .String (logError , err .Error ()))
315324 }
316325}
317326
@@ -334,6 +343,6 @@ func startHealthServer(port int, checker *health.Checker, logger *slog.Logger) {
334343 }
335344
336345 if err := srv .ListenAndServe (); err != nil && err != http .ErrServerClosed {
337- logger .Error ("Health server error" , slog .String ("error" , err .Error ()))
346+ logger .Error ("Health server error" , slog .String (logError , err .Error ()))
338347 }
339348}
0 commit comments