package main import ( "context" "errors" "flag" "log/slog" "net/http" "os" "os/signal" "path/filepath" "syscall" "time" "github.com/wzray/dns/internal/resolv" "github.com/wzray/dns/internal/server" ) func main() { addr := flag.String("addr", ":8080", "HTTP listen address") logLevel := flag.String("log-level", "info", "Log level: debug, info, warn, error") logFormat := flag.String("log-format", "text", "Log format: text or json") flag.Parse() log := newLogger(*logLevel, *logFormat) resolvPath := resolv.DefaultPath if err := os.MkdirAll(filepath.Dir(resolvPath), 0o755); err != nil { log.Error("prepare resolv directory", "err", err) os.Exit(1) } mgr := resolv.New(resolvPath) srv := server.New(mgr, log) httpSrv := &http.Server{ Addr: *addr, Handler: srv.Handler(), ReadHeaderTimeout: 5 * time.Second, } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() errCh := make(chan error, 1) go func() { log.Info("starting server", "addr", *addr) if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { errCh <- err } }() select { case <-ctx.Done(): log.Info("shutting down") case err := <-errCh: log.Error("server error", "err", err) os.Exit(1) } shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := httpSrv.Shutdown(shutdownCtx); err != nil { log.Error("graceful shutdown failed", "err", err) os.Exit(1) } } func newLogger(level, format string) *slog.Logger { var lvl slog.Level switch level { case "debug": lvl = slog.LevelDebug case "warn": lvl = slog.LevelWarn case "error": lvl = slog.LevelError default: lvl = slog.LevelInfo } opts := &slog.HandlerOptions{Level: lvl} var h slog.Handler if format == "json" { h = slog.NewJSONHandler(os.Stderr, opts) } else { h = slog.NewTextHandler(os.Stderr, opts) } return slog.New(h) }