vspd: Decouple config loading & log initialization

Loading config and preparing loggers are two separate concepts which
should be handled individually. Nesting one inside the other makes
little sense and complicates reusing the code.
This commit is contained in:
jholdstock 2024-05-21 08:59:49 +01:00 committed by Jamie Holdstock
parent 64ad28e19b
commit 6e558fb283
2 changed files with 36 additions and 26 deletions

View File

@ -18,7 +18,6 @@ import (
"github.com/decred/dcrd/dcrutil/v4" "github.com/decred/dcrd/dcrutil/v4"
"github.com/decred/dcrd/hdkeychain/v3" "github.com/decred/dcrd/hdkeychain/v3"
"github.com/decred/slog"
"github.com/decred/vspd/database" "github.com/decred/vspd/database"
"github.com/decred/vspd/internal/config" "github.com/decred/vspd/internal/config"
"github.com/decred/vspd/internal/version" "github.com/decred/vspd/internal/version"
@ -60,9 +59,7 @@ type vspdConfig struct {
HomeDir string `long:"homedir" no-ini:"true" description:"Path to application home directory. Used for storing VSP database and logs."` HomeDir string `long:"homedir" no-ini:"true" description:"Path to application home directory. Used for storing VSP database and logs."`
ConfigFile string `long:"configfile" no-ini:"true" description:"DEPRECATED: This behavior is no longer available and this option will be removed in a future version of the software."` ConfigFile string `long:"configfile" no-ini:"true" description:"DEPRECATED: This behavior is no longer available and this option will be removed in a future version of the software."`
logBackend *slog.Backend logPath string
logLevel slog.Level
dbPath string dbPath string
network *config.Network network *config.Network
dcrdCert []byte dcrdCert []byte
@ -379,18 +376,8 @@ func loadConfig() (*vspdConfig, error) {
return nil, fmt.Errorf("failed to create data directory: %w", err) return nil, fmt.Errorf("failed to create data directory: %w", err)
} }
// Initialize loggers and log rotation. // Set the log path.
logDir := filepath.Join(cfg.HomeDir, "logs", cfg.network.Name) cfg.logPath = filepath.Join(cfg.HomeDir, "logs", cfg.network.Name)
cfg.logBackend, err = newLogBackend(logDir, appName, cfg.MaxLogSize, cfg.LogsToKeep)
if err != nil {
return nil, fmt.Errorf("failed to initialize logger: %w", err)
}
var ok bool
cfg.logLevel, ok = slog.LevelFromString(cfg.LogLevel)
if !ok {
return nil, fmt.Errorf("unknown log level: %s", cfg.LogLevel)
}
// Set the database path. // Set the database path.
cfg.dbPath = filepath.Join(dataDir, dbFilename) cfg.dbPath = filepath.Join(dataDir, dbFilename)
@ -429,9 +416,3 @@ func loadConfig() (*vspdConfig, error) {
return &cfg, nil return &cfg, nil
} }
func (cfg *vspdConfig) logger(subsystem string) slog.Logger {
log := cfg.logBackend.Logger(subsystem)
log.SetLevel(cfg.logLevel)
return log
}

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/decred/dcrd/wire" "github.com/decred/dcrd/wire"
"github.com/decred/slog"
"github.com/decred/vspd/database" "github.com/decred/vspd/database"
"github.com/decred/vspd/internal/config" "github.com/decred/vspd/internal/config"
"github.com/decred/vspd/internal/signal" "github.com/decred/vspd/internal/signal"
@ -33,6 +34,28 @@ func main() {
os.Exit(run()) os.Exit(run())
} }
// initLogging uses the provided vspd config to create a logging backend, and
// returns a function which can be used to create ready-to-use subsystem
// loggers.
func initLogging(cfg *vspdConfig) (func(subsystem string) slog.Logger, error) {
backend, err := newLogBackend(cfg.logPath, "vspd", cfg.MaxLogSize, cfg.LogsToKeep)
if err != nil {
return nil, fmt.Errorf("failed to initialize logger: %w", err)
}
var ok bool
level, ok := slog.LevelFromString(cfg.LogLevel)
if !ok {
return nil, fmt.Errorf("unknown log level: %q", cfg.LogLevel)
}
return func(subsystem string) slog.Logger {
log := backend.Logger(subsystem)
log.SetLevel(level)
return log
}, nil
}
// run is the real main function for vspd. It is necessary to work around the // run is the real main function for vspd. It is necessary to work around the
// fact that deferred functions do not run when os.Exit() is called. // fact that deferred functions do not run when os.Exit() is called.
func run() int { func run() int {
@ -43,7 +66,13 @@ func run() int {
return 1 return 1
} }
log := cfg.logger("VSP") makeLogger, err := initLogging(cfg)
if err != nil {
fmt.Fprintf(os.Stderr, "initLogging error: %v\n", err)
return 1
}
log := makeLogger("VSP")
// Create a context that is canceled when a shutdown request is received // Create a context that is canceled when a shutdown request is received
// through an interrupt signal such as SIGINT (Ctrl+C). // through an interrupt signal such as SIGINT (Ctrl+C).
@ -72,7 +101,7 @@ func run() int {
} }
// Open database. // Open database.
db, err := database.Open(cfg.dbPath, cfg.logger(" DB"), maxVoteChangeRecords) db, err := database.Open(cfg.dbPath, makeLogger(" DB"), maxVoteChangeRecords)
if err != nil { if err != nil {
log.Errorf("Failed to open database: %v", err) log.Errorf("Failed to open database: %v", err)
return 1 return 1
@ -80,7 +109,7 @@ func run() int {
const writeBackup = true const writeBackup = true
defer db.Close(writeBackup) defer db.Close(writeBackup)
rpcLog := cfg.logger("RPC") rpcLog := makeLogger("RPC")
// Create a channel to receive blockConnected notifications from dcrd. // Create a channel to receive blockConnected notifications from dcrd.
blockNotifChan := make(chan *wire.BlockHeader) blockNotifChan := make(chan *wire.BlockHeader)
@ -108,7 +137,7 @@ func run() int {
MaxVoteChangeRecords: maxVoteChangeRecords, MaxVoteChangeRecords: maxVoteChangeRecords,
VspdVersion: version.String(), VspdVersion: version.String(),
} }
api, err := webapi.New(db, cfg.logger("API"), dcrd, wallets, apiCfg) api, err := webapi.New(db, makeLogger("API"), dcrd, wallets, apiCfg)
if err != nil { if err != nil {
log.Errorf("Failed to initialize webapi: %v", err) log.Errorf("Failed to initialize webapi: %v", err)
return 1 return 1