From 680079dee0cc67e08b5f49dcc299891b12300cdb Mon Sep 17 00:00:00 2001 From: takaaa220 Date: Tue, 7 Jan 2025 13:52:44 +0900 Subject: [PATCH 1/2] feat: add queryConvertFunc option to bundebug --- extra/bundebug/debug.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/extra/bundebug/debug.go b/extra/bundebug/debug.go index 70519b685..97a5eb035 100644 --- a/extra/bundebug/debug.go +++ b/extra/bundebug/debug.go @@ -39,6 +39,18 @@ func WithWriter(w io.Writer) Option { } } +// WithQueryConvertFunc sets a custom function to transform query strings +// before they are logged. +// +// This function allows users to define how query strings should be modified +// for logging purposes. For example, it can be used to truncate long queries, +// mask sensitive information, or format queries for better readability. +func WithQueryConvertFunc(queryConvertFunc func(query string) string) Option { + return func(h *QueryHook) { + h.queryConvertFunc = queryConvertFunc + } +} + // FromEnv configures the hook using the environment variable value. // For example, WithEnv("BUNDEBUG"): // - BUNDEBUG=0 - disables the hook. @@ -62,7 +74,11 @@ func FromEnv(keys ...string) Option { type QueryHook struct { enabled bool verbose bool - writer io.Writer + // queryConvertFunc is an optional function that transforms the query string + // before it is logged. For example, this can be used to truncate long queries, + // mask sensitive data, or apply custom formatting. + queryConvertFunc func(query string) string + writer io.Writer } var _ bun.QueryHook = (*QueryHook)(nil) @@ -99,12 +115,17 @@ func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) { now := time.Now() dur := now.Sub(event.StartTime) + query := event.Query + if h.queryConvertFunc != nil { + query = h.queryConvertFunc(query) + } + args := []interface{}{ "[bun]", now.Format(" 15:04:05.000 "), formatOperation(event), fmt.Sprintf(" %10s ", dur.Round(time.Microsecond)), - event.Query, + query, } if event.Err != nil { From dfdb8ddb8ef03aaa057c01c448b8c57793ee2788 Mon Sep 17 00:00:00 2001 From: takaaa220 Date: Tue, 7 Jan 2025 13:53:57 +0900 Subject: [PATCH 2/2] docs: add an example for queryConvertFunc --- example/debug-convert-func/README.md | 15 ++++++ example/debug-convert-func/go.mod | 46 ++++++++++++++++ example/debug-convert-func/go.sum | 77 ++++++++++++++++++++++++++ example/debug-convert-func/main.go | 80 ++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 example/debug-convert-func/README.md create mode 100644 example/debug-convert-func/go.mod create mode 100644 example/debug-convert-func/go.sum create mode 100644 example/debug-convert-func/main.go diff --git a/example/debug-convert-func/README.md b/example/debug-convert-func/README.md new file mode 100644 index 000000000..2155a8be4 --- /dev/null +++ b/example/debug-convert-func/README.md @@ -0,0 +1,15 @@ +# Basic example + +To run this example: + +```shell +go run . +``` + +To disable query logging: + +```shell +BUNDEBUG=0 go run . +``` + +See [docs](https://bun.uptrace.dev/) for details. diff --git a/example/debug-convert-func/go.mod b/example/debug-convert-func/go.mod new file mode 100644 index 000000000..1c18a932c --- /dev/null +++ b/example/debug-convert-func/go.mod @@ -0,0 +1,46 @@ +module github.com/uptrace/bun/example/debug-convert-func + +go 1.22.0 + +replace github.com/uptrace/bun => ../.. + +replace github.com/uptrace/bun/dbfixture => ../../dbfixture + +replace github.com/uptrace/bun/extra/bundebug => ../../extra/bundebug + +replace github.com/uptrace/bun/dialect/sqlitedialect => ../../dialect/sqlitedialect + +replace github.com/uptrace/bun/driver/sqliteshim => ../../driver/sqliteshim + +require ( + github.com/uptrace/bun v1.2.8 + github.com/uptrace/bun/dialect/sqlitedialect v1.2.8 + github.com/uptrace/bun/driver/sqliteshim v1.2.8 + github.com/uptrace/bun/extra/bundebug v1.2.8 +) + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.24 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect + golang.org/x/sys v0.29.0 // indirect + modernc.org/gc/v3 v3.0.0-20250105121824-520be1a3aee6 // indirect + modernc.org/libc v1.61.6 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.1 // indirect + modernc.org/sqlite v1.34.4 // indirect + modernc.org/strutil v1.2.1 // indirect + modernc.org/token v1.1.0 // indirect +) diff --git a/example/debug-convert-func/go.sum b/example/debug-convert-func/go.sum new file mode 100644 index 000000000..ae0cd9c0d --- /dev/null +++ b/example/debug-convert-func/go.sum @@ -0,0 +1,77 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= +github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= +github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588= +golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.24.2 h1:uektamHbSXU7egelXcyVpMaaAsrRH4/+uMKUQAQUdOw= +modernc.org/cc/v4 v4.24.2/go.mod h1:T1lKJZhXIi2VSqGBiB4LIbKs9NsKTbUXj4IDrmGqtTI= +modernc.org/ccgo/v4 v4.23.5 h1:6uAwu8u3pnla3l/+UVUrDDO1HIGxHTYmFH6w+X9nsyw= +modernc.org/ccgo/v4 v4.23.5/go.mod h1:FogrWfBdzqLWm1ku6cfr4IzEFouq2fSAPf6aSAHdAJQ= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.6.0 h1:Tiw3pezQj7PfV8k4Dzyu/vhRHR2e92kOXtTFU8pbCl4= +modernc.org/gc/v2 v2.6.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20250105121824-520be1a3aee6 h1:JoKwHjIFumiKrjMbp1cNbC5E9UyCgA/ZcID0xOWQ2N8= +modernc.org/gc/v3 v3.0.0-20250105121824-520be1a3aee6/go.mod h1:LG5UO1Ran4OO0JRKz2oNiXhR5nNrgz0PzH7UKhz0aMU= +modernc.org/libc v1.61.6 h1:L2jW0wxHPCyHK0YSHaGaVlY0WxjpG/TTVdg6gRJOPqw= +modernc.org/libc v1.61.6/go.mod h1:G+DzuaCcReUYYg4nNSfigIfTDCENdj9EByglvaRx53A= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.1 h1:HS1HRg1jEohnuONobEq2WrLEhLyw8+J42yLFTnllm2A= +modernc.org/memory v1.8.1/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8= +modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/example/debug-convert-func/main.go b/example/debug-convert-func/main.go new file mode 100644 index 000000000..6157075b3 --- /dev/null +++ b/example/debug-convert-func/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "context" + "database/sql" + "regexp" + "strings" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/sqlitedialect" + "github.com/uptrace/bun/driver/sqliteshim" + "github.com/uptrace/bun/extra/bundebug" +) + +func main() { + ctx := context.Background() + + sqlite, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared") + if err != nil { + panic(err) + } + sqlite.SetMaxOpenConns(1) + + db := bun.NewDB(sqlite, sqlitedialect.New()) + db.AddQueryHook(bundebug.NewQueryHook( + bundebug.WithVerbose(true), + bundebug.FromEnv("BUNDEBUG"), + bundebug.WithQueryConvertFunc(func(query string) string { + // truncate long query values + + maxLength := 20 + + return regexp.MustCompile(`'(?:[^'\\]|\\.)*'`).ReplaceAllStringFunc(query, func(match string) string { + content := match[1 : len(match)-1] + if len(content) > maxLength { + return "'" + content[:maxLength] + "... [TRUNCATED]'" + } + return match + }) + }), + )) + + if err := resetSchema(ctx, db); err != nil { + panic(err) + } + + users := make([]User, 0) + // log `WHERE (name = abcabcabcabcabcabcab... [TRUNCATED]')` + if err := db.NewSelect().Model(&users).Where("name = ?", strings.Repeat("abc", 100)).Scan(ctx); err != nil { + panic(err) + } +} + +type User struct { + ID int64 `bun:",pk,autoincrement"` + Name string + Emails []string +} + +func resetSchema(ctx context.Context, db *bun.DB) error { + if err := db.ResetModel(ctx, (*User)(nil)); err != nil { + return err + } + + users := []User{ + { + Name: "admin", + Emails: []string{"admin1@admin", "admin2@admin"}, + }, + { + Name: "root", + Emails: []string{"root1@root", "root2@root"}, + }, + } + if _, err := db.NewInsert().Model(&users).Exec(ctx); err != nil { + return err + } + + return nil +}