Log the config after loading

This commit is contained in:
2024-12-18 22:16:20 +01:00
parent 7ccd36f0e4
commit f6de4fb839
4 changed files with 34 additions and 40 deletions

View File

@@ -5,6 +5,7 @@ import (
"log" "log"
"novamd/internal/app" "novamd/internal/app"
"novamd/internal/logging"
) )
// @title NovaMD API // @title NovaMD API
@@ -23,6 +24,10 @@ func main() {
log.Fatal("Failed to load configuration:", err) log.Fatal("Failed to load configuration:", err)
} }
// Setup logging
logging.Setup(cfg.LogLevel)
logging.Debug("Configuration loaded", "config", logging.Redact(cfg))
// Initialize and start server // Initialize and start server
options, err := app.DefaultOptions(cfg) options, err := app.DefaultOptions(cfg)
if err != nil { if err != nil {
@@ -32,7 +37,7 @@ func main() {
server := app.NewServer(options) server := app.NewServer(options)
defer func() { defer func() {
if err := server.Close(); err != nil { if err := server.Close(); err != nil {
log.Println("Error closing server:", err) logging.Error("Failed to close server:", err)
} }
}() }()

View File

@@ -19,10 +19,10 @@ type Config struct {
RootURL string RootURL string
Domain string Domain string
CORSOrigins []string CORSOrigins []string
AdminEmail string AdminEmail string `log:"redact"`
AdminPassword string AdminPassword string `log:"redact"`
EncryptionKey string EncryptionKey string `log:"redact"`
JWTSigningKey string JWTSigningKey string `log:"redact"`
RateLimitRequests int RateLimitRequests int
RateLimitWindow time.Duration RateLimitWindow time.Duration
IsDevelopment bool IsDevelopment bool
@@ -58,46 +58,37 @@ func (c *Config) validate() error {
// LoadConfig creates a new Config instance with values from environment variables // LoadConfig creates a new Config instance with values from environment variables
func LoadConfig() (*Config, error) { func LoadConfig() (*Config, error) {
logging.Info("loading configuration from environment variables")
config := DefaultConfig() config := DefaultConfig()
if env := os.Getenv("NOVAMD_ENV"); env != "" { if env := os.Getenv("NOVAMD_ENV"); env != "" {
logging.Debug("loading config for environment", "env", env)
config.IsDevelopment = env == "development" config.IsDevelopment = env == "development"
} }
if dbPath := os.Getenv("NOVAMD_DB_PATH"); dbPath != "" { if dbPath := os.Getenv("NOVAMD_DB_PATH"); dbPath != "" {
logging.Debug("loading config for database path", "path", dbPath)
config.DBPath = dbPath config.DBPath = dbPath
} }
if workDir := os.Getenv("NOVAMD_WORKDIR"); workDir != "" { if workDir := os.Getenv("NOVAMD_WORKDIR"); workDir != "" {
logging.Debug("loading config for work directory", "dir", workDir)
config.WorkDir = workDir config.WorkDir = workDir
} }
if staticPath := os.Getenv("NOVAMD_STATIC_PATH"); staticPath != "" { if staticPath := os.Getenv("NOVAMD_STATIC_PATH"); staticPath != "" {
logging.Debug("loading config for static path", "path", staticPath)
config.StaticPath = staticPath config.StaticPath = staticPath
} }
if port := os.Getenv("NOVAMD_PORT"); port != "" { if port := os.Getenv("NOVAMD_PORT"); port != "" {
logging.Debug("loading config for port", "port", port)
config.Port = port config.Port = port
} }
if rootURL := os.Getenv("NOVAMD_ROOT_URL"); rootURL != "" { if rootURL := os.Getenv("NOVAMD_ROOT_URL"); rootURL != "" {
logging.Debug("loading config for root URL", "url", rootURL)
config.RootURL = rootURL config.RootURL = rootURL
} }
if domain := os.Getenv("NOVAMD_DOMAIN"); domain != "" { if domain := os.Getenv("NOVAMD_DOMAIN"); domain != "" {
logging.Debug("loading config for domain", "domain", domain)
config.Domain = domain config.Domain = domain
} }
if corsOrigins := os.Getenv("NOVAMD_CORS_ORIGINS"); corsOrigins != "" { if corsOrigins := os.Getenv("NOVAMD_CORS_ORIGINS"); corsOrigins != "" {
logging.Debug("loading config for CORS origins", "origins", corsOrigins)
config.CORSOrigins = strings.Split(corsOrigins, ",") config.CORSOrigins = strings.Split(corsOrigins, ",")
} }
@@ -106,35 +97,17 @@ func LoadConfig() (*Config, error) {
config.EncryptionKey = os.Getenv("NOVAMD_ENCRYPTION_KEY") config.EncryptionKey = os.Getenv("NOVAMD_ENCRYPTION_KEY")
config.JWTSigningKey = os.Getenv("NOVAMD_JWT_SIGNING_KEY") config.JWTSigningKey = os.Getenv("NOVAMD_JWT_SIGNING_KEY")
logging.Debug("sensitive configuration loaded",
"adminEmailSet", config.AdminEmail != "",
"adminPasswordSet", config.AdminPassword != "",
"encryptionKeySet", config.EncryptionKey != "",
"jwtSigningKeySet", config.JWTSigningKey != "")
// Configure rate limiting // Configure rate limiting
if reqStr := os.Getenv("NOVAMD_RATE_LIMIT_REQUESTS"); reqStr != "" { if reqStr := os.Getenv("NOVAMD_RATE_LIMIT_REQUESTS"); reqStr != "" {
parsed, err := strconv.Atoi(reqStr) parsed, err := strconv.Atoi(reqStr)
if err != nil { if err == nil {
logging.Warn("invalid rate limit requests value, using default",
"value", reqStr,
"default", config.RateLimitRequests,
"error", err)
} else {
logging.Debug("loading config for rate limit requests", "requests", parsed)
config.RateLimitRequests = parsed config.RateLimitRequests = parsed
} }
} }
if windowStr := os.Getenv("NOVAMD_RATE_LIMIT_WINDOW"); windowStr != "" { if windowStr := os.Getenv("NOVAMD_RATE_LIMIT_WINDOW"); windowStr != "" {
parsed, err := time.ParseDuration(windowStr) parsed, err := time.ParseDuration(windowStr)
if err != nil { if err == nil {
logging.Warn("invalid rate limit window value, using default",
"value", windowStr,
"default", config.RateLimitWindow,
"error", err)
} else {
logging.Debug("loading config for rate limit window", "window", parsed)
config.RateLimitWindow = parsed config.RateLimitWindow = parsed
} }
} }
@@ -142,13 +115,10 @@ func LoadConfig() (*Config, error) {
// Configure log level, if isDevelopment is set, default to debug // Configure log level, if isDevelopment is set, default to debug
if logLevel := os.Getenv("NOVAMD_LOG_LEVEL"); logLevel != "" { if logLevel := os.Getenv("NOVAMD_LOG_LEVEL"); logLevel != "" {
parsed := logging.ParseLogLevel(logLevel) parsed := logging.ParseLogLevel(logLevel)
logging.Debug("loading config for log level", "level", parsed)
config.LogLevel = parsed config.LogLevel = parsed
} else if config.IsDevelopment { } else if config.IsDevelopment {
logging.Debug("setting log level to debug for development")
config.LogLevel = logging.DEBUG config.LogLevel = logging.DEBUG
} else { } else {
logging.Debug("setting log level to info for production")
config.LogLevel = logging.INFO config.LogLevel = logging.INFO
} }
@@ -157,6 +127,5 @@ func LoadConfig() (*Config, error) {
return nil, err return nil, err
} }
logging.Info("configuration loaded successfully")
return config, nil return config, nil
} }

View File

@@ -4,6 +4,7 @@ package logging
import ( import (
"log/slog" "log/slog"
"os" "os"
"reflect"
) )
// Logger represents the interface for logging operations // Logger represents the interface for logging operations
@@ -60,6 +61,27 @@ func ParseLogLevel(level string) LogLevel {
} }
} }
// Redact redacts sensitive fields from a struct based on the `log` struct tag
// if the tag is set to "redact" the field value is replaced with "[REDACTED]"
func Redact(v any) map[string]any {
result := make(map[string]any)
val := reflect.ValueOf(v)
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
if tag := field.Tag.Get("log"); tag != "" {
switch tag {
case "redact":
result[field.Name] = "[REDACTED]"
default:
result[field.Name] = val.Field(i).Interface()
}
}
}
return result
}
// Implementation of Logger interface methods // Implementation of Logger interface methods
func (l *logger) Debug(msg string, args ...any) { func (l *logger) Debug(msg string, args ...any) {
l.logger.Debug(msg, args...) l.logger.Debug(msg, args...)

View File

@@ -33,8 +33,6 @@ func getLogger() logging.Logger {
// ValidateKey checks if the provided base64-encoded key is suitable for AES-256 // ValidateKey checks if the provided base64-encoded key is suitable for AES-256
func ValidateKey(key string) error { func ValidateKey(key string) error {
log := getLogger()
log.Debug("validating encryption key")
_, err := decodeAndValidateKey(key) _, err := decodeAndValidateKey(key)
return err return err
} }