feat: configurable component-level logging (#575)

* Refactor logging to use centralized logger utility

- Removed direct usage of zerolog in multiple files and replaced it with a centralized logging utility in the `utils` package.
- Introduced `Loggers` struct to manage different loggers (Audit, HTTP, App) with configurable levels and outputs.
- Updated all relevant files to utilize the new logging structure, ensuring consistent logging practices across the application.
- Enhanced error handling and logging messages for better traceability and debugging.

* refactor: update logging implementation to use new logger structure

* Refactor logging to use tlog package

- Replaced instances of utils logging with tlog in various controllers, services, and middleware.
- Introduced audit logging for login success, login failure, and logout events.
- Created tlog package with structured logging capabilities using zerolog.
- Added tests for the new tlog logger functionality.

* refactor: update logging configuration in environment files

* fix: adding coderabbit suggestions

* fix: ensure correct audit caller

* fix: include reason in audit login failure logs
This commit is contained in:
Pushpinder Singh
2026-01-15 08:57:19 -05:00
committed by GitHub
parent ba2d732415
commit 53bd413046
28 changed files with 486 additions and 214 deletions

View File

@@ -16,8 +16,7 @@ import (
"github.com/steveiliop56/tinyauth/internal/controller"
"github.com/steveiliop56/tinyauth/internal/repository"
"github.com/steveiliop56/tinyauth/internal/utils"
"github.com/rs/zerolog/log"
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
)
type BootstrapApp struct {
@@ -103,13 +102,13 @@ func (app *BootstrapApp) Setup() error {
app.context.redirectCookieName = fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
// Dumps
log.Trace().Interface("config", app.config).Msg("Config dump")
log.Trace().Interface("users", app.context.users).Msg("Users dump")
log.Trace().Interface("oauthProviders", app.context.oauthProviders).Msg("OAuth providers dump")
log.Trace().Str("cookieDomain", app.context.cookieDomain).Msg("Cookie domain")
log.Trace().Str("sessionCookieName", app.context.sessionCookieName).Msg("Session cookie name")
log.Trace().Str("csrfCookieName", app.context.csrfCookieName).Msg("CSRF cookie name")
log.Trace().Str("redirectCookieName", app.context.redirectCookieName).Msg("Redirect cookie name")
tlog.App.Trace().Interface("config", app.config).Msg("Config dump")
tlog.App.Trace().Interface("users", app.context.users).Msg("Users dump")
tlog.App.Trace().Interface("oauthProviders", app.context.oauthProviders).Msg("OAuth providers dump")
tlog.App.Trace().Str("cookieDomain", app.context.cookieDomain).Msg("Cookie domain")
tlog.App.Trace().Str("sessionCookieName", app.context.sessionCookieName).Msg("Session cookie name")
tlog.App.Trace().Str("csrfCookieName", app.context.csrfCookieName).Msg("CSRF cookie name")
tlog.App.Trace().Str("redirectCookieName", app.context.redirectCookieName).Msg("Redirect cookie name")
// Database
db, err := app.SetupDatabase(app.config.DatabasePath)
@@ -153,7 +152,7 @@ func (app *BootstrapApp) Setup() error {
})
}
log.Debug().Interface("providers", configuredProviders).Msg("Authentication providers")
tlog.App.Debug().Interface("providers", configuredProviders).Msg("Authentication providers")
if len(configuredProviders) == 0 {
return fmt.Errorf("no authentication providers configured")
@@ -169,28 +168,28 @@ func (app *BootstrapApp) Setup() error {
}
// Start db cleanup routine
log.Debug().Msg("Starting database cleanup routine")
tlog.App.Debug().Msg("Starting database cleanup routine")
go app.dbCleanup(queries)
// If analytics are not disabled, start heartbeat
if !app.config.DisableAnalytics {
log.Debug().Msg("Starting heartbeat routine")
tlog.App.Debug().Msg("Starting heartbeat routine")
go app.heartbeat()
}
// If we have an socket path, bind to it
if app.config.Server.SocketPath != "" {
if _, err := os.Stat(app.config.Server.SocketPath); err == nil {
log.Info().Msgf("Removing existing socket file %s", app.config.Server.SocketPath)
tlog.App.Info().Msgf("Removing existing socket file %s", app.config.Server.SocketPath)
err := os.Remove(app.config.Server.SocketPath)
if err != nil {
return fmt.Errorf("failed to remove existing socket file: %w", err)
}
}
log.Info().Msgf("Starting server on unix socket %s", app.config.Server.SocketPath)
tlog.App.Info().Msgf("Starting server on unix socket %s", app.config.Server.SocketPath)
if err := router.RunUnix(app.config.Server.SocketPath); err != nil {
log.Fatal().Err(err).Msg("Failed to start server")
tlog.App.Fatal().Err(err).Msg("Failed to start server")
}
return nil
@@ -198,9 +197,9 @@ func (app *BootstrapApp) Setup() error {
// Start server
address := fmt.Sprintf("%s:%d", app.config.Server.Address, app.config.Server.Port)
log.Info().Msgf("Starting server on %s", address)
tlog.App.Info().Msgf("Starting server on %s", address)
if err := router.Run(address); err != nil {
log.Fatal().Err(err).Msg("Failed to start server")
tlog.App.Fatal().Err(err).Msg("Failed to start server")
}
return nil
@@ -223,7 +222,7 @@ func (app *BootstrapApp) heartbeat() {
bodyJson, err := json.Marshal(body)
if err != nil {
log.Error().Err(err).Msg("Failed to marshal heartbeat body")
tlog.App.Error().Err(err).Msg("Failed to marshal heartbeat body")
return
}
@@ -234,12 +233,12 @@ func (app *BootstrapApp) heartbeat() {
heartbeatURL := config.ApiServer + "/v1/instances/heartbeat"
for ; true; <-ticker.C {
log.Debug().Msg("Sending heartbeat")
tlog.App.Debug().Msg("Sending heartbeat")
req, err := http.NewRequest(http.MethodPost, heartbeatURL, bytes.NewReader(bodyJson))
if err != nil {
log.Error().Err(err).Msg("Failed to create heartbeat request")
tlog.App.Error().Err(err).Msg("Failed to create heartbeat request")
continue
}
@@ -248,14 +247,14 @@ func (app *BootstrapApp) heartbeat() {
res, err := client.Do(req)
if err != nil {
log.Error().Err(err).Msg("Failed to send heartbeat")
tlog.App.Error().Err(err).Msg("Failed to send heartbeat")
continue
}
res.Body.Close()
if res.StatusCode != 200 && res.StatusCode != 201 {
log.Debug().Str("status", res.Status).Msg("Heartbeat returned non-200/201 status")
tlog.App.Debug().Str("status", res.Status).Msg("Heartbeat returned non-200/201 status")
}
}
}
@@ -266,10 +265,10 @@ func (app *BootstrapApp) dbCleanup(queries *repository.Queries) {
ctx := context.Background()
for ; true; <-ticker.C {
log.Debug().Msg("Cleaning up old database sessions")
tlog.App.Debug().Msg("Cleaning up old database sessions")
err := queries.DeleteExpiredSessions(ctx, time.Now().Unix())
if err != nil {
log.Error().Err(err).Msg("Failed to clean up old database sessions")
tlog.App.Error().Err(err).Msg("Failed to clean up old database sessions")
}
}
}