fix: improve logging and error visibility, move Story 1.7 to Epic 2

Fixes:
- Added database connection logging with masked DSN
- Added migration progress logging
- Added HTTP server startup logging with address
- Fixed database provider to accept logger parameter
- Improved error visibility throughout initialization

Documentation:
- Moved Story 1.7 (Service Client Interfaces) to Epic 2 as Story 2.7
- Updated Epic 1 and Epic 2 READMEs
- Updated COMPLETE_TASK_LIST.md
- Updated story metadata (ID, Epic, Dependencies)

These changes will help diagnose startup issues and provide better visibility into what the application is doing.
This commit is contained in:
2025-11-05 19:39:25 +01:00
parent fde01bfc73
commit d1d0b170ce
6 changed files with 54 additions and 13 deletions

View File

@@ -67,7 +67,7 @@ func ProvideLogger() fx.Option {
// ProvideDatabase creates an FX option that provides the database client.
func ProvideDatabase() fx.Option {
return fx.Provide(func(cfg config.ConfigProvider, lc fx.Lifecycle) (*database.Client, error) {
return fx.Provide(func(cfg config.ConfigProvider, log logger.Logger, lc fx.Lifecycle) (*database.Client, error) {
dsn := cfg.GetString("database.dsn")
if dsn == "" {
return nil, fmt.Errorf("database DSN is not configured")
@@ -108,9 +108,11 @@ func ProvideDatabase() fx.Option {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
// Run migrations on startup
log.Info("Running database migrations...")
if err := dbClient.Migrate(ctx); err != nil {
return fmt.Errorf("failed to run database migrations: %w", err)
}
log.Info("Database migrations completed successfully")
return nil
},
OnStop: func(ctx context.Context) error {
@@ -225,8 +227,22 @@ func ProvideHTTPServer() fx.Option {
// Register lifecycle hooks
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
// Get server address from config
port := cfg.GetInt("server.port")
if port == 0 {
port = 8080
}
host := cfg.GetString("server.host")
if host == "" {
host = "0.0.0.0"
}
addr := fmt.Sprintf("%s:%d", host, port)
// Start server in a goroutine
go func() {
log.Info("HTTP server starting",
logger.String("addr", addr),
)
if err := srv.Start(); err != nil && err != http.ErrServerClosed {
log.Error("HTTP server error",
logger.String("error", err.Error()),
@@ -259,6 +275,32 @@ func CoreModule() fx.Option {
)
}
// maskDSN masks sensitive information in DSN for logging.
func maskDSN(dsn string) string {
// Simple masking: replace password with ***
// Format: postgres://user:password@host:port/db
if len(dsn) < 20 {
return "***"
}
// Find @ symbol and replace password part
if idx := indexOf(dsn, '@'); idx > 0 {
if colonIdx := indexOf(dsn[:idx], ':'); colonIdx > 0 {
return dsn[:colonIdx+1] + "***" + dsn[idx:]
}
}
return "***"
}
// indexOf finds the index of a character in a string.
func indexOf(s string, c byte) int {
for i := 0; i < len(s); i++ {
if s[i] == c {
return i
}
}
return -1
}
// RegisterLifecycleHooks registers lifecycle hooks for logging.
func RegisterLifecycleHooks(lc fx.Lifecycle, l logger.Logger) {
lc.Append(fx.Hook{