fix: enforce log level, validate config values, ensure dedup dir, wire main to config and logging

This commit is contained in:
Sirin Puenggun 2025-10-13 23:37:31 +07:00
parent ec17691cb4
commit b5d4526d91
3 changed files with 55 additions and 16 deletions

View File

@ -2,34 +2,34 @@ package main
import ( import (
"context" "context"
"flag"
"fmt" "fmt"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
"time" "time"
"github.com/yourname/reddit-scraper/internal/config"
"github.com/yourname/reddit-scraper/internal/logging"
) )
func main() { func main() {
keyword := flag.String("keyword", "", "Search keyword (required)") cfg, err := config.Load()
limit := flag.Int("limit", 100, "Max posts to fetch") if err != nil {
flag.Parse() fmt.Fprintln(os.Stderr, "config load:", err)
if *keyword == "" {
fmt.Fprintln(os.Stderr, "-keyword is required")
os.Exit(2) os.Exit(2)
} }
logger := logging.Init(cfg.LogLevel)
logger.Info("starting", "keyword", cfg.Keyword, "limit", cfg.Limit, "concurrency", cfg.Concurrency)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel() defer cancel()
// placeholder: in future wire up controller // placeholder: in future wire up controller
fmt.Printf("Starting crawl for keyword=%s limit=%d\n", *keyword, *limit)
select { select {
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
fmt.Println("Done (placeholder)") logger.Info("done (placeholder)")
case <-ctx.Done(): case <-ctx.Done():
fmt.Println("Cancelled") logger.Info("cancelled")
} }
} }

View File

@ -62,6 +62,28 @@ func Load() (*Config, error) {
return nil, fmt.Errorf("creating output dir: %w", err) return nil, fmt.Errorf("creating output dir: %w", err)
} }
// Basic validation
if cfg.Limit <= 0 {
return nil, fmt.Errorf("limit must be > 0")
}
if cfg.Concurrency <= 0 {
return nil, fmt.Errorf("concurrency must be > 0")
}
if cfg.RetryLimit < 0 {
return nil, fmt.Errorf("retry-limit must be >= 0")
}
if cfg.RateLimitDelay <= 0 {
return nil, fmt.Errorf("rate-limit delay must be > 0")
}
// Ensure dedup cache parent dir exists
dedupDir := filepath.Dir(cfg.DedupCachePath)
if dedupDir != "" && dedupDir != "." {
if err := os.MkdirAll(filepath.Clean(dedupDir), 0o755); err != nil {
return nil, fmt.Errorf("creating dedup cache dir: %w", err)
}
}
return cfg, nil return cfg, nil
} }

View File

@ -3,13 +3,30 @@ package logging
import ( import (
"log/slog" "log/slog"
"os" "os"
"strings"
) )
func parseLevel(s string) slog.Level {
s = strings.ToUpper(strings.TrimSpace(s))
switch s {
case "TRACE":
return slog.LevelDebug
case "DEBUG":
return slog.LevelDebug
case "INFO":
return slog.LevelInfo
case "WARN", "WARNING":
return slog.LevelWarn
case "ERROR":
return slog.LevelError
default:
return slog.LevelInfo
}
}
// Init initializes a global logger and returns it. Level is a string like "INFO" or "DEBUG". // Init initializes a global logger and returns it. Level is a string like "INFO" or "DEBUG".
func Init(level string) *slog.Logger { func Init(level string) *slog.Logger {
handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{AddSource: false}) lvl := parseLevel(level)
logger := slog.New(handler) handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{AddSource: false, Level: lvl})
// Note: slog's level filtering is configured per Handler via HandlerOptions in Go 1.25; return slog.New(handler)
// here we rely on consumer code to not spam at debug when level is INFO.
return logger
} }