From c22cf9e7c8cfcb2c2fc12649700b990cd0b0bdd0 Mon Sep 17 00:00:00 2001 From: Andrew-71 Date: Sun, 13 Oct 2024 11:25:00 +0300 Subject: [PATCH] Add debug mode to log --- Makefile | 2 +- README.md | 10 ++++++---- cmd/main.go | 9 +++++++-- cmd/serve/main.go | 1 + config/config.go | 4 ++++ logging/log.go | 34 ++++++++++++++++++++++++++++++++++ storage/sqlite/sqlite.go | 5 +++-- 7 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 logging/log.go diff --git a/Makefile b/Makefile index 2cbf643..5661dae 100644 --- a/Makefile +++ b/Makefile @@ -5,4 +5,4 @@ serve: go build && ./pye serve dev: - go build && ./pye serve --db dev-data.db \ No newline at end of file + go build && ./pye serve --db dev-data.db --debug \ No newline at end of file diff --git a/README.md b/README.md index 6d48fbb..2dd4fa7 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,17 @@ in a state that proves I am competent Go developer. ## Commands -## `pye serve [--config] [--port] [--db]` +## JWT server -Serve a simple JWT auth system +Serve a simple JWT auth system +**Usage**: `pye serve [--config] [--port] [--db]` * `POST /register` - register a user with Basic Auth * `POST /login` - get a JWT token by Basic Auth * `GET /pem` - get PEM-encoded public RS256 key * Data and RS256 key persistently stored in an SQLite database and a PEM file -## `pye verify ` +## JWT verification -Verify a JWT with a public key from a PEM file \ No newline at end of file +Verify a JWT with a public key from a PEM file +**Usage**: `pye verify ` \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 1f2430c..0c4d22e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -9,16 +9,19 @@ import ( "git.a71.su/Andrew71/pye/cmd/serve" "git.a71.su/Andrew71/pye/cmd/verify" "git.a71.su/Andrew71/pye/config" + "git.a71.su/Andrew71/pye/logging" ) -func Run() { +func Run() { serveCmd := flag.NewFlagSet("serve", flag.ExitOnError) serveConfig := serveCmd.String("config", "", "override config file") servePort := serveCmd.Int("port", 0, "override port") serveDb := serveCmd.String("db", "", "override sqlite database") + serveDebug := serveCmd.Bool("debug", false, "debug logging") verifyCmd := flag.NewFlagSet("verify", flag.ExitOnError) + verifyDebug := verifyCmd.Bool("debug", false, "debug logging") if len(os.Args) < 2 { fmt.Println("expected 'serve' or 'verify' subcommands") @@ -28,6 +31,7 @@ func Run() { switch os.Args[1] { case "serve": serveCmd.Parse(os.Args[2:]) + logging.LogInit(*serveDebug) if *serveConfig != "" { err := config.LoadConfig(*serveConfig) if err != nil { @@ -43,8 +47,9 @@ func Run() { serve.Serve() case "verify": verifyCmd.Parse(os.Args[2:]) + logging.LogInit(*verifyDebug) if len(os.Args) != 4 { - fmt.Println("Usage: ") + fmt.Println("Usage: [--debug]") } verify.Verify(os.Args[2], os.Args[3]) default: diff --git a/cmd/serve/main.go b/cmd/serve/main.go index 978a9df..8f56ab2 100644 --- a/cmd/serve/main.go +++ b/cmd/serve/main.go @@ -28,5 +28,6 @@ func Serve() { router.HandleFunc("GET /login", func(w http.ResponseWriter, r *http.Request) { auth.Login(w, r, data) }) slog.Info("🪐 pye started", "port", config.Cfg.Port) + slog.Debug("debug mode active") http.ListenAndServe(":"+strconv.Itoa(config.Cfg.Port), router) } diff --git a/config/config.go b/config/config.go index 565c12a..a5a8957 100644 --- a/config/config.go +++ b/config/config.go @@ -10,12 +10,16 @@ type Config struct { Port int `json:"port"` KeyFile string `json:"key-file"` SQLiteFile string `json:"sqlite-file"` + LogToFile bool `json:"log-to-file"` + LogFile string `json:"log-file"` } var DefaultConfig = Config{ Port: 7102, KeyFile: "private.key", SQLiteFile: "data.db", + LogToFile: false, + LogFile: "pye.log", } var ( diff --git a/logging/log.go b/logging/log.go new file mode 100644 index 0000000..47bf473 --- /dev/null +++ b/logging/log.go @@ -0,0 +1,34 @@ +package logging + +import ( + "io" + "log" + "log/slog" + "os" + + "git.a71.su/Andrew71/pye/config" +) + +// LogInit makes slog output to both os.Stdout and a file if needed, and sets slog.LevelDebug if enabled. +func LogInit(debugMode bool) { + var w io.Writer + if config.Cfg.LogToFile { + f, err := os.OpenFile(config.Cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + slog.Error("error opening log file, logging to stdout only", "path", config.Cfg.LogFile, "error", err) + return + } + // No defer f.Close() because that breaks the MultiWriter + w = io.MultiWriter(f, os.Stdout) + } else { + w = os.Stdout + } + + // Make slog use intended format + var opts *slog.HandlerOptions + if debugMode { + opts = &slog.HandlerOptions{Level: slog.LevelDebug} + } + slog.SetDefault(slog.New(slog.NewTextHandler(w, opts))) + log.SetFlags(log.LstdFlags | log.Lshortfile) +} diff --git a/storage/sqlite/sqlite.go b/storage/sqlite/sqlite.go index 98926d7..d33d8c1 100644 --- a/storage/sqlite/sqlite.go +++ b/storage/sqlite/sqlite.go @@ -18,6 +18,7 @@ const create string = ` PRIMARY KEY("uuid") );` +// SQLiteStorage is a storage.Storage implementation with SQLite3 type SQLiteStorage struct { db *sql.DB } @@ -64,14 +65,14 @@ func MustLoadSQLite(dataFile string) SQLiteStorage { } db, err := sql.Open("sqlite3", dataFile) if err != nil { - slog.Error("error opening database", "error", err) + slog.Error("error opening sqlite3 database", "error", err) os.Exit(1) } statement, err := db.Prepare(create) if err != nil { if err.Error() != "table \"users\" already exists" { - slog.Info("error initialising database table", "error", err) + slog.Info("error initialising sqlite3 database table", "error", err) os.Exit(1) } } else {