diff --git a/.github/workflows/staticcheck.yml b/.github/workflows/staticcheck.yml index b18d4f0..9c87c02 100644 --- a/.github/workflows/staticcheck.yml +++ b/.github/workflows/staticcheck.yml @@ -22,10 +22,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v4.3.0 with: - go-version: 1.25 - - - name: Install staticcheck - run: make install + go-version: 1.26 - name: Run staticcheck run: make lint diff --git a/.gitignore b/.gitignore index 6fce158..6f5b92b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ .vscode glide.lock vendor +CLAUDE.md +sample +sample-ctx diff --git a/MODERNIZATION.md b/MODERNIZATION.md index 0f667d4..2a0ddb1 100644 --- a/MODERNIZATION.md +++ b/MODERNIZATION.md @@ -49,7 +49,7 @@ This removes the `jonbodner/stackerr` dependency entirely. --- -## 4. Fix `slog` Usage — Structured Logging Done Right +## ~~4. Fix `slog` Usage — Structured Logging Done Right~~ (DONE) The recent migration to `slog` (commit c52e1f8) left behind anti-patterns. The code uses `slog.Log` with `fmt.Sprintln`/`fmt.Sprintf` to pre-format messages, which defeats the entire purpose of structured logging. @@ -392,7 +392,7 @@ If `Build` returns an error, `productDao` will have nil function fields. Subsequ - ~~#1 — `interface{}` to `any`~~ *(DONE)* - ~~#2 — Replace `multierr` with `errors.Join`~~ *(DONE)* - #3 — Replace `stackerr` with stdlib error handling -- #4 — Fix slog usage for proper structured logging +- ~~#4 — Fix slog usage for proper structured logging~~ *(DONE)* - #11 — Update dependencies **Lower priority (cleanup):** diff --git a/Makefile b/Makefile index 2b0fa2d..d7a1d25 100644 --- a/Makefile +++ b/Makefile @@ -27,17 +27,13 @@ fmt: .PHONY:fmt lint: fmt - staticcheck ./... + go tool staticcheck ./... .PHONY:lint vet: fmt go vet ./... .PHONY:vet -build: vet - go build github.com/jonbodner/proteus/cmd/sample +build: vet lint + go build github.com/jonbodner/proteus/cmd/sample-ctx .PHONY:build - -install: - go install honnef.co/go/tools/cmd/staticcheck@latest -.PHONY:install diff --git a/builder.go b/builder.go index e1bb626..092dc0d 100644 --- a/builder.go +++ b/builder.go @@ -251,7 +251,7 @@ func validIdentifier(ctx context.Context, curVar string) (string, error) { loop: for { pos, tok, lit := s.Scan() - slog.Log(ctx, slog.LevelDebug, fmt.Sprintf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)) + slog.DebugContext(ctx, "token scan", "position", fset.Position(pos), "token", tok, "literal", lit) switch tok { case token.EOF: if first || lastPeriod { diff --git a/cmd/null/main.go b/cmd/null/main.go index 2fe59dd..5ffb904 100644 --- a/cmd/null/main.go +++ b/cmd/null/main.go @@ -59,16 +59,21 @@ func run(productDao Product2Dao) { populate(ctx, tx, productDao) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDao.FindByID(ctx, tx, 10))) + product, err := productDao.FindByID(ctx, tx, 10) + slog.DebugContext(ctx, "FindByID", "product", product, "err", err) cost := sql.NullFloat64{ Float64: 56.23, Valid: true, } p := Product2{10, "Thingie", cost} - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDao.Update(ctx, tx, p))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDao.FindByID(ctx, tx, 10))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDao.FindByNameAndCost(ctx, tx, "fred", 54.10))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDao.FindByNameAndCost(ctx, tx, "Thingie", 56.23))) + rowsAffected, err := productDao.Update(ctx, tx, p) + slog.DebugContext(ctx, "Update", "rowsAffected", rowsAffected, "err", err) + product, err = productDao.FindByID(ctx, tx, 10) + slog.DebugContext(ctx, "FindByID after update", "product", product, "err", err) + products, err := productDao.FindByNameAndCost(ctx, tx, "fred", 54.10) + slog.DebugContext(ctx, "FindByNameAndCost fred", "products", products, "err", err) + products, err = productDao.FindByNameAndCost(ctx, tx, "Thingie", 56.23) + slog.DebugContext(ctx, "FindByNameAndCost Thingie", "products", products, "err", err) } func defineSchema(ctx context.Context, tx proteus.ContextWrapper) error { @@ -78,7 +83,7 @@ func defineSchema(ctx context.Context, tx proteus.ContextWrapper) error { ` _, err := tx.ExecContext(ctx, sqlStmt) if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintf("%q: %s\n", err, sqlStmt)) + slog.ErrorContext(ctx, "exec failed", "err", err, "statement", sqlStmt) return err } return nil @@ -88,13 +93,13 @@ func setupDBPostgres(ctx context.Context) (proteus.ContextWrapper, closer) { db, err := sql.Open("postgres", "postgres://pro_user:pro_pwd@localhost/proteus?sslmode=disable") if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "db open failed", "err", err) os.Exit(1) } tx, err := db.Begin() if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "begin tx failed", "err", err) os.Exit(1) } @@ -114,9 +119,9 @@ func populate(ctx context.Context, w proteus.ContextWrapper, productDao Product2 } rowCount, err := productDao.Insert(ctx, w, i, fmt.Sprintf("person%d", i), cost) if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "insert failed", "err", err) os.Exit(1) } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(rowCount)) + slog.DebugContext(ctx, "inserted row", "rowCount", rowCount) } } diff --git a/cmd/sample-ctx/main.go b/cmd/sample-ctx/main.go index 637a3ef..d357bdd 100644 --- a/cmd/sample-ctx/main.go +++ b/cmd/sample-ctx/main.go @@ -67,39 +67,64 @@ func run(setupDb setupDb, productDAO ProductDAO) { } defer func() { _ = tx.Rollback() }() - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByID(ctx, tx, 10))) + product, err := productDAO.FindByID(ctx, tx, 10) + slog.DebugContext(ctx, "FindByID", "product", product, "err", err) + cost := new(float64) *cost = 56.23 p := Product{10, "Thingie", cost} - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.Update(ctx, tx, p))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByID(ctx, tx, 10))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByNameAndCost(ctx, tx, "fred", 54.10))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByNameAndCost(ctx, tx, "Thingie", 56.23))) + rowsAffected, err := productDAO.Update(ctx, tx, p) + slog.DebugContext(ctx, "Update", "rowsAffected", rowsAffected, "err", err) + + product, err = productDAO.FindByID(ctx, tx, 10) + slog.DebugContext(ctx, "FindByID after update", "product", product, "err", err) + + products, err := productDAO.FindByNameAndCost(ctx, tx, "fred", 54.10) + slog.DebugContext(ctx, "FindByNameAndCost fred", "products", products, "err", err) + + products, err = productDAO.FindByNameAndCost(ctx, tx, "Thingie", 56.23) + slog.DebugContext(ctx, "FindByNameAndCost Thingie", "products", products, "err", err) //using a map of [string]any works too! - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDMap(ctx, tx, 10)))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByNameAndCostMap(ctx, tx, "Thingie", 56.23)))) + m2, err := productDAO.FindByIDMap(ctx, tx, 10) + slog.DebugContext(ctx, "FindByIDMap", "result", m2, "err", err) + + maps, err := productDAO.FindByNameAndCostMap(ctx, tx, "Thingie", 56.23) + slog.DebugContext(ctx, "FindByNameAndCostMap", "results", maps, "err", err) + + product, err = productDAO.FindByID(ctx, tx, 11) + slog.DebugContext(ctx, "FindByID 11", "product", product, "err", err) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByID(ctx, tx, 11)))) m := map[string]any{ "Id": 11, "Name": "bobbo", "Cost": 12.94, } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.UpdateMap(ctx, tx, m)))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByID(ctx, tx, 11)))) + rowsAffected, err = productDAO.UpdateMap(ctx, tx, m) + slog.DebugContext(ctx, "UpdateMap", "rowsAffected", rowsAffected, "err", err) + + product, err = productDAO.FindByID(ctx, tx, 11) + slog.DebugContext(ctx, "FindByID after UpdateMap", "product", product, "err", err) //searching using a slice - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSlice(ctx, tx, []int{1, 3, 5})))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSliceAndName(ctx, tx, []int{1, 3, 5}, "person1")))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSliceNameAndCost(ctx, tx, []int{1, 3, 5}, "person3", nil)))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSliceCostAndNameSlice(ctx, tx, []int{1, 3, 5}, []string{"person3", "person5"}, nil)))) + products, err = productDAO.FindByIDSlice(ctx, tx, []int{1, 3, 5}) + slog.DebugContext(ctx, "FindByIDSlice", "products", products, "err", err) + + products, err = productDAO.FindByIDSliceAndName(ctx, tx, []int{1, 3, 5}, "person1") + slog.DebugContext(ctx, "FindByIDSliceAndName", "products", products, "err", err) + + products, err = productDAO.FindByIDSliceNameAndCost(ctx, tx, []int{1, 3, 5}, "person3", nil) + slog.DebugContext(ctx, "FindByIDSliceNameAndCost", "products", products, "err", err) + + products, err = productDAO.FindByIDSliceCostAndNameSlice(ctx, tx, []int{1, 3, 5}, []string{"person3", "person5"}, nil) + slog.DebugContext(ctx, "FindByIDSliceCostAndNameSlice", "products", products, "err", err) //using positional parameters instead of names - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByNameAndCostUnlabeled(ctx, tx, "Thingie", 56.23)))) + products, err = productDAO.FindByNameAndCostUnlabeled(ctx, tx, "Thingie", 56.23) + slog.DebugContext(ctx, "FindByNameAndCostUnlabeled", "products", products, "err", err) if err := tx.Commit(); err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "commit failed", "err", err) } } @@ -108,7 +133,7 @@ func setupDbPostgres(ctx context.Context, productDAO ProductDAO) *sql.DB { db, err := sql.Open("timer", "postgres postgres://pro_user:pro_pwd@localhost/proteus?sslmode=disable") if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "db open failed", "err", err) } sqlStmt := ` drop table if exists product; @@ -116,7 +141,7 @@ func setupDbPostgres(ctx context.Context, productDAO ProductDAO) *sql.DB { ` _, err = db.Exec(sqlStmt) if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintf("%q: %s\n", err, sqlStmt)) + slog.ErrorContext(ctx, "exec failed", "err", err, "statement", sqlStmt) return nil } populate(ctx, db, productDAO) @@ -126,7 +151,7 @@ func setupDbPostgres(ctx context.Context, productDAO ProductDAO) *sql.DB { func populate(ctx context.Context, db *sql.DB, productDao ProductDAO) { tx, err := db.Begin() if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "begin tx failed", "err", err) return } defer func() { _ = tx.Rollback() }() @@ -139,12 +164,12 @@ func populate(ctx context.Context, db *sql.DB, productDao ProductDAO) { } rowCount, err := productDao.Insert(ctx, tx, i, fmt.Sprintf("person%d", i), cost) if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "insert failed", "err", err) } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(rowCount)) + slog.DebugContext(ctx, "inserted row", "rowCount", rowCount) } if err := tx.Commit(); err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "commit failed", "err", err) } } diff --git a/cmd/sample/main.go b/cmd/sample/main.go index b99311c..330fbe2 100644 --- a/cmd/sample/main.go +++ b/cmd/sample/main.go @@ -66,39 +66,64 @@ func run(setupDb setupDb, productDAO ProductDAO) { } defer func() { _ = tx.Rollback() }() - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByID(tx, 10))) + product, err := productDAO.FindByID(tx, 10) + slog.DebugContext(ctx, "FindByID", "result", product, "error", err) + cost := new(float64) *cost = 56.23 p := Product{10, "Thingie", cost} - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.Update(tx, p))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByID(tx, 10))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByNameAndCost(tx, "fred", 54.10))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(productDAO.FindByNameAndCost(tx, "Thingie", 56.23))) + rowsAffected, err := productDAO.Update(tx, p) + slog.DebugContext(ctx, "Update", "result", rowsAffected, "error", err) + + product, err = productDAO.FindByID(tx, 10) + slog.DebugContext(ctx, "FindByID after update", "result", product, "error", err) + + results, err := productDAO.FindByNameAndCost(tx, "fred", 54.10) + slog.DebugContext(ctx, "FindByNameAndCost fred", "result", results, "error", err) + + results, err = productDAO.FindByNameAndCost(tx, "Thingie", 56.23) + slog.DebugContext(ctx, "FindByNameAndCost Thingie", "result", results, "error", err) //using a map of [string]any works too! - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDMap(tx, 10)))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByNameAndCostMap(tx, "Thingie", 56.23)))) + mapResult, err := productDAO.FindByIDMap(tx, 10) + slog.DebugContext(ctx, "FindByIDMap", "result", mapResult, "error", err) + + mapResults, err := productDAO.FindByNameAndCostMap(tx, "Thingie", 56.23) + slog.DebugContext(ctx, "FindByNameAndCostMap", "result", mapResults, "error", err) + + product, err = productDAO.FindByID(tx, 11) + slog.DebugContext(ctx, "FindByID 11", "result", product, "error", err) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByID(tx, 11)))) m := map[string]any{ "Id": 11, "Name": "bobbo", "Cost": 12.94, } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.UpdateMap(tx, m)))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByID(tx, 11)))) + rowsAffected, err = productDAO.UpdateMap(tx, m) + slog.DebugContext(ctx, "UpdateMap", "result", rowsAffected, "error", err) + + product, err = productDAO.FindByID(tx, 11) + slog.DebugContext(ctx, "FindByID after UpdateMap", "result", product, "error", err) //searching using a slice - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSlice(tx, []int{1, 3, 5})))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSliceAndName(tx, []int{1, 3, 5}, "person1")))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSliceNameAndCost(tx, []int{1, 3, 5}, "person3", nil)))) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByIDSliceCostAndNameSlice(tx, []int{1, 3, 5}, []string{"person3", "person5"}, nil)))) + results, err = productDAO.FindByIDSlice(tx, []int{1, 3, 5}) + slog.DebugContext(ctx, "FindByIDSlice", "result", results, "error", err) + + results, err = productDAO.FindByIDSliceAndName(tx, []int{1, 3, 5}, "person1") + slog.DebugContext(ctx, "FindByIDSliceAndName", "result", results, "error", err) + + results, err = productDAO.FindByIDSliceNameAndCost(tx, []int{1, 3, 5}, "person3", nil) + slog.DebugContext(ctx, "FindByIDSliceNameAndCost", "result", results, "error", err) + + results, err = productDAO.FindByIDSliceCostAndNameSlice(tx, []int{1, 3, 5}, []string{"person3", "person5"}, nil) + slog.DebugContext(ctx, "FindByIDSliceCostAndNameSlice", "result", results, "error", err) //using positional parameters instead of names - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln((productDAO.FindByNameAndCostUnlabeled(tx, "Thingie", 56.23)))) + results, err = productDAO.FindByNameAndCostUnlabeled(tx, "Thingie", 56.23) + slog.DebugContext(ctx, "FindByNameAndCostUnlabeled", "result", results, "error", err) if err := tx.Commit(); err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "commit failed", "err", err) } } @@ -107,7 +132,7 @@ func setupDbPostgres(ctx context.Context, productDAO ProductDAO) *sql.DB { db, err := sql.Open("timer", "postgres postgres://pro_user:pro_pwd@localhost/proteus?sslmode=disable") if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "db open failed", "err", err) } sqlStmt := ` drop table if exists product; @@ -115,7 +140,7 @@ func setupDbPostgres(ctx context.Context, productDAO ProductDAO) *sql.DB { ` _, err = db.Exec(sqlStmt) if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintf("%q: %s\n", err, sqlStmt)) + slog.ErrorContext(ctx, "exec failed", "err", err, "statement", sqlStmt) return nil } populate(ctx, db, productDAO) @@ -125,7 +150,7 @@ func setupDbPostgres(ctx context.Context, productDAO ProductDAO) *sql.DB { func populate(ctx context.Context, db *sql.DB, productDao ProductDAO) { tx, err := db.Begin() if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "begin tx failed", "err", err) return } defer func() { _ = tx.Rollback() }() @@ -138,12 +163,12 @@ func populate(ctx context.Context, db *sql.DB, productDao ProductDAO) { } rowCount, err := productDao.Insert(tx, i, fmt.Sprintf("person%d", i), cost) if err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "insert failed", "err", err) } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(rowCount)) + slog.DebugContext(ctx, "inserted row", "rowCount", rowCount) } if err := tx.Commit(); err != nil { - slog.Log(ctx, slog.LevelError, fmt.Sprintln(err)) + slog.ErrorContext(ctx, "commit failed", "err", err) } } diff --git a/go.mod b/go.mod index e4d47c2..f75d035 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/jonbodner/proteus -go 1.25 +go 1.25.0 require ( github.com/go-sql-driver/mysql v1.5.0 - github.com/google/go-cmp v0.4.0 + github.com/google/go-cmp v0.6.0 github.com/jonbodner/dbtimer v0.0.0-20170410163237-7002f3758ae1 github.com/jonbodner/stackerr v1.0.0 github.com/lib/pq v1.10.9 @@ -13,6 +13,14 @@ require ( ) require ( + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/google/pprof v0.0.0-20230429030804-905365eefe3e // indirect + golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054 // indirect + honnef.co/go/tools v0.7.0 // indirect ) + +tool honnef.co/go/tools/cmd/staticcheck diff --git a/go.sum b/go.sum index b91678b..4325c6a 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -8,8 +10,9 @@ github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20230429030804-905365eefe3e h1:yuPVjc55Y343adYwQLA29DR8KrA0yuLJLlN5LxqlsgQ= github.com/google/pprof v0.0.0-20230429030804-905365eefe3e/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= @@ -31,12 +34,23 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ= +golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054 h1:CHVDrNHx9ZoOrNN9kKWYIbT5Rj+WF2rlwPkhbQQ5V4U= +golang.org/x/tools v0.40.1-0.20260108161641-ca281cf95054/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= +honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= diff --git a/mapper/extract.go b/mapper/extract.go index 1915552..346be27 100644 --- a/mapper/extract.go +++ b/mapper/extract.go @@ -3,7 +3,6 @@ package mapper import ( "context" "database/sql/driver" - "fmt" "log/slog" "reflect" "strconv" @@ -68,10 +67,9 @@ func Extract(ctx context.Context, s any, path []string) (any, error) { if sv.Type().Key().Kind() != reflect.String { return nil, stackerr.New("cannot extract value; map does not have a string key") } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(path[1])) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(sv.MapKeys())) + slog.DebugContext(ctx, "map extract", "key", path[1], "availableKeys", sv.MapKeys()) v := sv.MapIndex(reflect.ValueOf(path[1])) - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln(v)) + slog.DebugContext(ctx, "map extract result", "value", v) if !v.IsValid() { return nil, stackerr.New("cannot extract value; no such map key " + path[1]) } diff --git a/mapper/mapper.go b/mapper/mapper.go index 079c125..5c77bcd 100644 --- a/mapper/mapper.go +++ b/mapper/mapper.go @@ -3,7 +3,6 @@ package mapper import ( "context" "database/sql" - "fmt" "log/slog" "reflect" "strings" @@ -18,7 +17,7 @@ func ptrConverter(ctx context.Context, isPtr bool, sType reflect.Type, out refle if isPtr { out2 := reflect.New(sType) k := out.Type().Kind() - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("kind of out", k)) + slog.DebugContext(ctx, "ptrConverter", "kind", k) if (k == reflect.Interface || k == reflect.Pointer) && out.IsNil() { out2 = reflect.Zero(reflect.PointerTo(sType)) } else { @@ -147,7 +146,7 @@ var ( ) func buildStruct(ctx context.Context, sType reflect.Type, cols []string, vals []any, colFieldMap map[string]fieldInfo) (reflect.Value, error) { - slog.Log(ctx, slog.LevelDebug, fmt.Sprintf("sType: %s cols: %v vals: %+v colFieldMap: %+v", sType, cols, vals, colFieldMap)) + slog.DebugContext(ctx, "buildStruct", "sType", sType, "cols", cols, "vals", vals, "colFieldMap", colFieldMap) out := reflect.New(sType).Elem() for k, v := range cols { if sf, ok := colFieldMap[v]; ok { @@ -166,12 +165,12 @@ func buildStructInner(ctx context.Context, sType reflect.Type, out reflect.Value field := out.Field(sf.pos[depth]) curFieldType := sf.fieldType[depth] if curFieldType.Kind() == reflect.Pointer { - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("isPtr", sf, rv, rv.Type(), rv.Elem(), curVal, sType)) + slog.DebugContext(ctx, "isPtr", "field", sf, "rv", rv, "rvType", rv.Type(), "rvElem", rv.Elem(), "curVal", curVal, "sType", sType) if rv.Elem().IsNil() { - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("nil", sf, rv, curVal, sType)) + slog.DebugContext(ctx, "nil pointer field", "field", sf, "rv", rv, "curVal", curVal, "sType", sType) return nil } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("isPtr Not Nil", rv.Elem().Type())) + slog.DebugContext(ctx, "isPtr not nil", "elemType", rv.Elem().Type()) if curFieldType.Implements(scannerType) { toScan := (reflect.New(curFieldType).Elem().Interface()).(sql.Scanner) err := toScan.Scan(rv.Elem().Elem().Interface()) @@ -183,7 +182,7 @@ func buildStructInner(ctx context.Context, sType reflect.Type, out reflect.Value field.Set(reflect.New(curFieldType.Elem())) field.Elem().Set(rv.Elem().Elem().Convert(curFieldType.Elem())) } else { - slog.Log(ctx, slog.LevelError, fmt.Sprintln("can't find the field")) + slog.ErrorContext(ctx, "can't find the field") return stackerr.Errorf("unable to assign pointer to value %v of type %v to struct field %s of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sf.name[depth], curFieldType) } } else { @@ -200,12 +199,12 @@ func buildStructInner(ctx context.Context, sType reflect.Type, out reflect.Value return err } } else if rv.Elem().IsNil() { - slog.Log(ctx, slog.LevelError, fmt.Sprintln("Attempting to assign a nil to a non-pointer field")) + slog.ErrorContext(ctx, "attempting to assign nil to non-pointer field") return stackerr.Errorf("unable to assign nil value to non-pointer struct field %s of type %v", sf.name[depth], curFieldType) } else if rv.Elem().Elem().Type().ConvertibleTo(curFieldType) { field.Set(rv.Elem().Elem().Convert(curFieldType)) } else { - slog.Log(ctx, slog.LevelError, fmt.Sprintln("can't find the field")) + slog.ErrorContext(ctx, "can't find the field") return stackerr.Errorf("unable to assign value %v of type %v to struct field %s of type %v", rv.Elem().Elem(), rv.Elem().Elem().Type(), sf.name[depth], curFieldType) } } diff --git a/mapper_test.go b/mapper_test.go index eae8232..e670115 100644 --- a/mapper_test.go +++ b/mapper_test.go @@ -36,7 +36,7 @@ func setupDb(t *testing.T) *sql.DB { db, err := sql.Open("postgres", "postgres://pro_user:pro_pwd@localhost/proteus?sslmode=disable") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } sqlStmt := ` @@ -45,19 +45,19 @@ func setupDb(t *testing.T) *sql.DB { ` _, err = db.Exec(sqlStmt) if err != nil { - slog.Log(ctx, slog.LevelError, "err", "error", slog.AnyValue(err), "query", slog.StringValue(sqlStmt)) + slog.ErrorContext(ctx, "error", "err", err, "query", sqlStmt) t.FailNow() } tx, err := db.Begin() if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer func() { _ = tx.Rollback() }() stmt, err := tx.Prepare("insert into product(id, name, cost) values($1, $2, $3)") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer stmt.Close() @@ -69,12 +69,12 @@ func setupDb(t *testing.T) *sql.DB { } _, err = stmt.Exec(i, name, 1.1*float64(i)) if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } } if err := tx.Commit(); err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } return db @@ -93,7 +93,7 @@ func TestBuildStruct(t *testing.T) { rows, err := db.Query("select id, name, cost from product") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer rows.Close() @@ -147,14 +147,14 @@ func TestBuildPrimitive(t *testing.T) { //primitive stmt, err := db.Prepare("select name from product where id = $1") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer stmt.Close() rows, err := stmt.Query("4") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } sType := reflect.TypeOf("") @@ -182,7 +182,7 @@ func TestBuildPrimitive(t *testing.T) { _, err = db.Exec("delete from product") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } } @@ -194,14 +194,14 @@ func TestBuildPrimitiveNilFail(t *testing.T) { //primitive stmt, err := db.Prepare("select name from product where id = $1") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer stmt.Close() rows, err := stmt.Query("3") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } sType := reflect.TypeOf("") @@ -222,7 +222,7 @@ func TestBuildPrimitiveNilFail(t *testing.T) { _, err = db.Exec("delete from product") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } } @@ -234,14 +234,14 @@ func TestBuildPrimitivePtr(t *testing.T) { //primitive stmt, err := db.Prepare("select name from product where id = $1") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer stmt.Close() rows, err := stmt.Query("4") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } sType := reflect.TypeOf((*string)(nil)) @@ -270,7 +270,7 @@ func TestBuildPrimitivePtr(t *testing.T) { _, err = db.Exec("delete from product") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } } @@ -282,14 +282,14 @@ func TestBuildPrimitivePtrNil(t *testing.T) { //primitive stmt, err := db.Prepare("select name from product where id = $1") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer stmt.Close() rows, err := stmt.Query("3") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } sType := reflect.TypeFor[*string]() @@ -315,7 +315,7 @@ func TestBuildPrimitivePtrNil(t *testing.T) { _, err = db.Exec("delete from product") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } } @@ -326,7 +326,7 @@ func TestBuildMap(t *testing.T) { rows, err := db.Query("select id, name, cost from product") if err != nil { - slog.Error("err", "error", slog.AnyValue(err)) + slog.Error("err", "error", err) t.FailNow() } defer rows.Close() diff --git a/proteus.go b/proteus.go index 9ee140a..c0b4997 100644 --- a/proteus.go +++ b/proteus.go @@ -199,7 +199,7 @@ func Build(dao any, paramAdapter ParamAdapter, mappers ...QueryMapper) error { //validate to make sure that the function matches what we expect hasCtx, err := validateFunction(funcType) if err != nil { - slog.Log(ctx, slog.LevelWarn, fmt.Sprintln("skipping function", curField.Name, "due to error:", err.Error())) + slog.WarnContext(ctx, "skipping function", "function", curField.Name, "error", err) outErr = errors.Join(outErr, err) continue } @@ -219,14 +219,14 @@ func Build(dao any, paramAdapter ParamAdapter, mappers ...QueryMapper) error { //check to see if the query is in a QueryMapper query, err = lookupQuery(query, mappers) if err != nil { - slog.Log(ctx, slog.LevelWarn, fmt.Sprintln("skipping function", curField.Name, "due to error:", err.Error())) + slog.WarnContext(ctx, "skipping function", "function", curField.Name, "error", err) outErr = errors.Join(outErr, err) continue } implementation, err := makeImplementation(ctx, funcType, query, paramAdapter, nameOrderMap) if err != nil { - slog.Log(ctx, slog.LevelWarn, fmt.Sprintln("skipping function", curField.Name, "due to error:", err.Error())) + slog.WarnContext(ctx, "skipping function", "function", curField.Name, "error", err) outErr = errors.Join(outErr, err) continue } diff --git a/proteus_function.go b/proteus_function.go index 1713bc4..5da01ee 100644 --- a/proteus_function.go +++ b/proteus_function.go @@ -3,7 +3,6 @@ package proteus import ( "context" "database/sql" - "fmt" "log/slog" "reflect" "strings" @@ -99,7 +98,7 @@ func (fb Builder) ExecResult(ctx context.Context, e ContextExecutor, query strin return nil, err } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("calling", finalQuery, "with params", queryArgs)) + slog.DebugContext(ctx, "calling query", "query", finalQuery, "params", queryArgs) result, err := e.ExecContext(ctx, finalQuery, queryArgs...) return result, err } @@ -116,7 +115,7 @@ func (fb Builder) Query(ctx context.Context, q ContextQuerier, query string, par return err } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("calling", finalQuery, "with params", queryArgs)) + slog.DebugContext(ctx, "calling query", "query", finalQuery, "params", queryArgs) rows, err := q.QueryContext(ctx, finalQuery, queryArgs...) if err != nil { return err diff --git a/runner.go b/runner.go index bee90ab..91126a1 100644 --- a/runner.go +++ b/runner.go @@ -2,7 +2,6 @@ package proteus import ( "context" - "fmt" "log/slog" "reflect" "strings" @@ -67,7 +66,7 @@ func makeContextExecutorImplementation(ctx context.Context, funcType reflect.Typ return buildRetVals(result, err) } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("calling", finalQuery, "with params", queryArgs)) + slog.DebugContext(ctx, "calling query", "query", finalQuery, "params", queryArgs) result, err = executor.ExecContext(ctx, finalQuery, queryArgs...) return buildRetVals(result, err) @@ -93,7 +92,7 @@ func makeExecutorImplementation(ctx context.Context, funcType reflect.Type, quer return buildRetVals(result, err) } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("calling", finalQuery, "with params", queryArgs)) + slog.DebugContext(ctx, "calling query", "query", finalQuery, "params", queryArgs) result, err = executor.Exec(finalQuery, queryArgs...) return buildRetVals(result, err) @@ -185,7 +184,7 @@ func makeContextQuerierImplementation(ctx context.Context, funcType reflect.Type return buildRetVals(rows, err) } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("calling", finalQuery, "with params", queryArgs)) + slog.DebugContext(ctx, "calling query", "query", finalQuery, "params", queryArgs) // going to work around the defective Go MySQL driver, which refuses to convert the text protocol properly. // It is used when doing a query without parameters. if len(queryArgs) == 0 { @@ -233,7 +232,7 @@ func makeQuerierImplementation(ctx context.Context, funcType reflect.Type, query return buildRetVals(rows, err) } - slog.Log(ctx, slog.LevelDebug, fmt.Sprintln("calling", finalQuery, "with params", queryArgs)) + slog.DebugContext(ctx, "calling query", "query", finalQuery, "params", queryArgs) // going to work around the defective Go MySQL driver, which refuses to convert the text protocol properly. // It is used when doing a query without parameters. if len(queryArgs) == 0 { @@ -380,7 +379,7 @@ func mapRows(ctx context.Context, rows *sql.Rows, builder mapper.Builder) (any, err = rows.Scan(vals...) if err != nil { - slog.Log(ctx, slog.LevelWarn, "scan failed") + slog.WarnContext(ctx, "scan failed") return nil, err } diff --git a/speed/speed.go b/speed/speed.go index 146f217..878a7e6 100644 --- a/speed/speed.go +++ b/speed/speed.go @@ -123,7 +123,7 @@ func setupDbPostgres(ctx context.Context) *sql.DB { db, err := sql.Open("postgres", "postgres://pro_user:pro_pwd@localhost/proteus?sslmode=disable") if err != nil { - slog.Error("error", "err", slog.AnyValue(err)) + slog.Error("error", "err", err) os.Exit(1) } sqlStmt := ` @@ -132,7 +132,7 @@ func setupDbPostgres(ctx context.Context) *sql.DB { ` _, err = db.Exec(sqlStmt) if err != nil { - slog.Error("error", "err", slog.AnyValue(err), "statement", slog.StringValue(sqlStmt)) + slog.Error("error", "err", err, "statement", sqlStmt) return nil } populate(ctx, db) @@ -144,7 +144,7 @@ func populate(ctx context.Context, db *sql.DB) { proteus.Build(&productDao, proteus.Postgres) tx, err := db.Begin() if err != nil { - slog.Error("error", "err", slog.AnyValue(err)) + slog.Error("error", "err", err) os.Exit(1) } defer func() { _ = tx.Rollback() }() @@ -157,14 +157,14 @@ func populate(ctx context.Context, db *sql.DB) { } rowCount, err := productDao.Insert(ctx, tx, i, fmt.Sprintf("person%d", i), cost) if err != nil { - slog.Error("error", "err", slog.AnyValue(err)) + slog.Error("error", "err", err) os.Exit(1) } slog.Debug("rowCount", "rowCount", rowCount) } if err := tx.Commit(); err != nil { - slog.Error("error", "err", slog.AnyValue(err)) + slog.Error("error", "err", err) os.Exit(1) } }