chore: expand debug logging + embeddable version

This commit is contained in:
Gabe Farrell 2025-06-13 18:29:08 -04:00
parent edbd7d506e
commit e92733823d
25 changed files with 563 additions and 303 deletions

View file

@ -29,26 +29,28 @@ import (
"github.com/rs/zerolog"
)
const Version = "dev"
func Run(
getenv func(string) string,
w io.Writer,
version string,
) error {
err := cfg.Load(getenv)
if err != nil {
return fmt.Errorf("failed to load configuration: %v", err)
panic("Engine: Failed to load configuration")
}
l := logger.Get()
l.Debug().Msg("Engine: Starting application initialization")
if cfg.StructuredLogging() {
l.Debug().Msg("Engine: Enabling structured logging")
*l = l.Output(w)
} else {
l.Debug().Msg("Engine: Enabling console logging")
*l = l.Output(zerolog.ConsoleWriter{
Out: w,
TimeFormat: time.RFC3339,
// FormatLevel: func(i interface{}) string {
// return strings.ToUpper(fmt.Sprintf("[%s]", i))
// },
FormatMessage: func(i interface{}) string {
return fmt.Sprintf("\u001b[30;1m>\u001b[0m %s |", i)
},
@ -57,64 +59,75 @@ func Run(
ctx := logger.NewContext(l)
l.Info().Msgf("Koito %s", Version)
l.Info().Msgf("Koito %s", version)
l.Debug().Msgf("Engine: Checking config directory: %s", cfg.ConfigDir())
_, err = os.Stat(cfg.ConfigDir())
if err != nil {
l.Info().Msgf("Creating config dir: %s", cfg.ConfigDir())
l.Info().Msgf("Engine: Creating config directory: %s", cfg.ConfigDir())
err = os.MkdirAll(cfg.ConfigDir(), 0744)
if err != nil {
l.Error().Err(err).Msg("Failed to create config directory")
l.Fatal().Err(err).Msg("Engine: Failed to create config directory")
return err
}
}
l.Info().Msgf("Using config dir: %s", cfg.ConfigDir())
l.Info().Msgf("Engine: Using config directory: %s", cfg.ConfigDir())
l.Debug().Msgf("Engine: Checking import directory: %s", path.Join(cfg.ConfigDir(), "import"))
_, err = os.Stat(path.Join(cfg.ConfigDir(), "import"))
if err != nil {
l.Debug().Msgf("Creating import dir: %s", path.Join(cfg.ConfigDir(), "import"))
l.Info().Msgf("Engine: Creating import directory: %s", path.Join(cfg.ConfigDir(), "import"))
err = os.Mkdir(path.Join(cfg.ConfigDir(), "import"), 0744)
if err != nil {
l.Error().Err(err).Msg("Failed to create import directory")
l.Fatal().Err(err).Msg("Engine: Failed to create import directory")
return err
}
}
l.Debug().Msg("Engine: Initializing database connection")
var store *psql.Psql
store, err = psql.New()
for err != nil {
l.Error().Err(err).Msg("Failed to connect to database; retrying in 5 seconds")
l.Error().Err(err).Msg("Engine: Failed to connect to database; retrying in 5 seconds")
time.Sleep(5 * time.Second)
store, err = psql.New()
}
defer store.Close(ctx)
l.Info().Msg("Engine: Database connection established")
l.Debug().Msg("Engine: Initializing MusicBrainz client")
var mbzC mbz.MusicBrainzCaller
if !cfg.MusicBrainzDisabled() {
mbzC = mbz.NewMusicBrainzClient()
l.Info().Msg("Engine: MusicBrainz client initialized")
} else {
mbzC = &mbz.MbzErrorCaller{}
l.Warn().Msg("Engine: MusicBrainz client disabled")
}
l.Debug().Msg("Engine: Initializing image sources")
images.Initialize(images.ImageSourceOpts{
UserAgent: cfg.UserAgent(),
EnableCAA: !cfg.CoverArtArchiveDisabled(),
EnableDeezer: !cfg.DeezerDisabled(),
})
l.Info().Msg("Engine: Image sources initialized")
l.Debug().Msg("Engine: Checking for default user")
userCount, _ := store.CountUsers(ctx)
if userCount < 1 {
l.Debug().Msg("Creating default user...")
l.Info().Msg("Engine: Creating default user")
user, err := store.SaveUser(ctx, db.SaveUserOpts{
Username: cfg.DefaultUsername(),
Password: cfg.DefaultPassword(),
Role: models.UserRoleAdmin,
})
if err != nil {
l.Fatal().AnErr("error", err).Msg("Failed to save default user in database")
l.Fatal().Err(err).Msg("Engine: Failed to save default user in database")
}
apikey, err := utils.GenerateRandomString(48)
if err != nil {
l.Fatal().AnErr("error", err).Msg("Failed to generate default api key")
l.Fatal().Err(err).Msg("Engine: Failed to generate default API key")
}
label := "Default"
_, err = store.SaveApiKey(ctx, db.SaveApiKeyOpts{
@ -123,28 +136,27 @@ func Run(
Label: label,
})
if err != nil {
l.Fatal().AnErr("error", err).Msg("Failed to save default api key in database")
l.Fatal().Err(err).Msg("Engine: Failed to save default API key in database")
}
l.Info().Msgf("Default user has been created. Login: %s : %s", cfg.DefaultUsername(), cfg.DefaultPassword())
l.Info().Msgf("Engine: Default user created. Login: %s : %s", cfg.DefaultUsername(), cfg.DefaultPassword())
}
l.Debug().Msg("Engine: Checking allowed hosts configuration")
if cfg.AllowAllHosts() {
l.Warn().Msg("Your configuration allows requests from all hosts. This is a potential security risk!")
l.Warn().Msg("Engine: Configuration allows requests from all hosts. This is a potential security risk!")
} else if len(cfg.AllowedHosts()) == 0 || cfg.AllowedHosts()[0] == "" {
l.Warn().Msgf("You are currently not allowing any hosts! Did you forget to set the %s variable?", cfg.ALLOWED_HOSTS_ENV)
l.Warn().Msgf("Engine: No hosts allowed! Did you forget to set the %s variable?", cfg.ALLOWED_HOSTS_ENV)
} else {
l.Debug().Msgf("Allowing hosts: %v", cfg.AllowedHosts())
l.Info().Msgf("Engine: Allowing hosts: %v", cfg.AllowedHosts())
}
l.Debug().Msg("Engine: Setting up HTTP server")
var ready atomic.Bool
mux := chi.NewRouter()
// bind general middleware to mux
mux.Use(middleware.WithRequestID)
mux.Use(middleware.Logger(l))
mux.Use(chimiddleware.Recoverer)
mux.Use(chimiddleware.RealIP)
// call router binds on mux
bindRoutes(mux, &ready, store, mbzC)
httpServer := &http.Server{
@ -153,36 +165,38 @@ func Run(
}
go func() {
ready.Store(true) // signal readiness
l.Info().Msg("listening on " + cfg.ListenAddr())
ready.Store(true)
l.Info().Msgf("Engine: Listening on %s", cfg.ListenAddr())
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
l.Fatal().AnErr("error", err).Msg("Error when running ListenAndServe")
l.Fatal().Err(err).Msg("Engine: Error when running ListenAndServe")
}
}()
// Import
l.Debug().Msg("Engine: Checking import configuration")
if !cfg.SkipImport() {
go func() {
RunImporter(l, store, mbzC)
}()
}
l.Info().Msg("Pruning orphaned images...")
l.Info().Msg("Engine: Pruning orphaned images")
go catalog.PruneOrphanedImages(logger.NewContext(l), store)
// Wait for interrupt signal to gracefully shutdown the server with a timeout of 10 seconds.
// Use a buffered channel to avoid missing signals as recommended for signal.Notify
l.Info().Msg("Engine: Initialization finished")
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
<-quit
l.Info().Msg("Received server shutdown notice")
l.Info().Msg("Engine: Received server shutdown notice")
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
l.Info().Msg("waiting for all processes to finish...")
l.Info().Msg("Engine: Waiting for all processes to finish")
mbzC.Shutdown()
if err := httpServer.Shutdown(ctx); err != nil {
l.Fatal().Err(err).Msg("Engine: Error during server shutdown")
return err
}
l.Info().Msg("shutdown successful")
l.Info().Msg("Engine: Shutdown successful")
return nil
}