{"id":26546860,"url":"https://github.com/changsongl/gorm-plugin","last_synced_at":"2025-03-22T05:28:09.108Z","repository":{"id":57650374,"uuid":"330338706","full_name":"changsongl/gorm-plugin","owner":"changsongl","description":"Gorm v2 插件包，慢查询统计包。Gorm v2 slow query plugin. Monitoring slow query and log details.","archived":false,"fork":false,"pushed_at":"2021-11-11T09:08:19.000Z","size":46,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-18T08:39:34.382Z","etag":null,"topics":["golang","gorm","gorm-plugin","metrics","prometheus","slow-query"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/changsongl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-17T07:36:19.000Z","updated_at":"2024-07-30T02:56:32.000Z","dependencies_parsed_at":"2022-09-10T19:22:46.975Z","dependency_job_id":null,"html_url":"https://github.com/changsongl/gorm-plugin","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changsongl%2Fgorm-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changsongl%2Fgorm-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changsongl%2Fgorm-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/changsongl%2Fgorm-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/changsongl","download_url":"https://codeload.github.com/changsongl/gorm-plugin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244912802,"owners_count":20530764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","gorm","gorm-plugin","metrics","prometheus","slow-query"],"created_at":"2025-03-22T05:28:08.547Z","updated_at":"2025-03-22T05:28:09.094Z","avatar_url":"https://github.com/changsongl.png","language":"Go","readme":"# gorm-plugins\nA set of gorm plugins for providing more features. It is using prometheus\nmetrics to monitor your SQLs.\n\nI will frequently write gorm plugins in this project. Please star it if you like it.\n\n#### Download package with go mod:\n`go get github.com/changsongl/gorm-plugin`\n\n### 1. Query\nIt is a plugin to monitor query execution time and slow query count \nfor tables. We can use prometheus client to expose metrics api, and\nhaving a prometheus server to scrape data from it. Eventually, you can\nuse `Alertmanager` or `Grafana` to monitor your slow query and query running \ntime.\n\n\n````golang\npackage main\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/changsongl/gorm-plugin/query\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\ntype test struct {\n\tId   int64  `gorm:\"column:id\" json:\"id\"`\n\tTest string `gorm:\"column:test\" json:\"test\"`\n}\n\nfunc (test) TableName() string {\n\treturn \"test\"\n}\n\nfunc main() {\n\t// create gorm v2 db\n\tdsn := \"root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4\u0026parseTime=True\u0026loc=Local\"\n\tdb, err := gorm.Open(mysql.Open(dsn), \u0026gorm.Config{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// create query plugin\n\tplugin := query.New(\n\t\tquery.SlowQueryCallback(query.Config{\n\t\t\tDBName:        \"my_test_db\",     // can be empty\n\t\t\tNamePrefix:    \"myprefix\",       // can be empty\n\t\t\tNamespace:     \"mynamespace\",    // can be empty\n\t\t\tSlowThreshold: time.Millisecond, // slow query time threshold\n\t\t}),\n\t\tquery.ErrorQueryCallback(query.Config{\n\t\t\tDBName:     \"my_test_db\",  // can be empty\n\t\t\tNamePrefix: \"myprefix\",    // can be empty\n\t\t\tNamespace:  \"mynamespace\", // can be empty\n\t\t}),\n\t)\n\t// using plugin\n\tif err := db.Use(plugin); err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\t// running sqls\n\tdb.Raw(\"SELECT id FROM test WHERE id = ?\", 3).Scan(\u0026test{})\n\tdb.Create(\u0026test{Test: \"hahaha\"})\n\tdb.Where(\"id = 123232132\").First(\u0026test{})                    // record not found\n\tdb.Raw(\"SELECT id FROM test2 WHERE id = ?\", 3).Scan(\u0026test{}) // error\n\n\t// register query plugin collectors to prometheus\n\tprometheus.MustRegister(plugin.MetricsCollectors()...)\n\n\t// run prometheus server\n\thttp.Handle(\"/metrics\", promhttp.Handler())\n\tsrv := \u0026http.Server{\n\t\tAddr: \":8080\",\n\t}\n\tif err := srv.ListenAndServe(); err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n\n\n\n// http://127.0.0.1:8080/metrics\n\n# HELP mynamespace_myprefix_error_count gorm-plugin: error counter\n# TYPE mynamespace_myprefix_error_count counter\nmynamespace_myprefix_error_count{callback=\"gorm:row\",db_name=\"my_test_db\",table_name=\"\"} 1\n# HELP mynamespace_myprefix_query_time gorm-plugin: slow query timeQuery histogram (unit: second)\n# TYPE mynamespace_myprefix_query_time histogram\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"0.05\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"0.1\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"0.25\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"0.5\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"1\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"2.5\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"5\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"10\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"\",le=\"+Inf\"} 2\nmynamespace_myprefix_query_time_sum{db_name=\"my_test_db\",table_name=\"\"} 0.018511481\nmynamespace_myprefix_query_time_count{db_name=\"my_test_db\",table_name=\"\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"0.05\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"0.1\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"0.25\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"0.5\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"1\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"2.5\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"5\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"10\"} 2\nmynamespace_myprefix_query_time_bucket{db_name=\"my_test_db\",table_name=\"test\",le=\"+Inf\"} 2\nmynamespace_myprefix_query_time_sum{db_name=\"my_test_db\",table_name=\"test\"} 0.016793621\nmynamespace_myprefix_query_time_count{db_name=\"my_test_db\",table_name=\"test\"} 2\n# HELP mynamespace_myprefix_slow_query_count gorm-plugin: slow query counter\n# TYPE mynamespace_myprefix_slow_query_count counter\nmynamespace_myprefix_slow_query_count{callback=\"gorm:create\",db_name=\"my_test_db\",table_name=\"test\"} 1\nmynamespace_myprefix_slow_query_count{callback=\"gorm:query\",db_name=\"my_test_db\",table_name=\"test\"} 1\nmynamespace_myprefix_slow_query_count{callback=\"gorm:row\",db_name=\"my_test_db\",table_name=\"\"} 2\n````\n\n### 2. Explain SQL\nIt is a plugin for explain the sql you ran. You can set the requirement of explain result.\n\n````sql\nCREATE TABLE `test`.`explain_table` (\n`id` INT NOT NULL AUTO_INCREMENT,\n`room_id` INT UNSIGNED NOT NULL,\n`room_name` VARCHAR(45) NOT NULL,\nPRIMARY KEY (`id`),\nINDEX `idx_room_id` (`room_id` ASC));\n\nINSERT INTO `test`.`explain_table` (`id`, `room_id`, `room_name`) VALUES (NULL, '1', 'room_name_1');\nINSERT INTO `test`.`explain_table` (`id`, `room_id`, `room_name`) VALUES (NULL, '2', 'room_name2');\nINSERT INTO `test`.`explain_table` (`room_id`, `room_name`) VALUES ('3', 'room_name2');\nINSERT INTO `test`.`explain_table` (`room_id`, `room_name`) VALUES ('4', 'room_name2');\nINSERT INTO `test`.`explain_table` (`room_id`, `room_name`) VALUES ('5', 'room_name3');\nINSERT INTO `test`.`explain_table` (`room_id`, `room_name`) VALUES ('5', 'room_name5');\nINSERT INTO `test`.`explain_table` (`room_id`, `room_name`) VALUES ('6', 'room_name9');\nINSERT INTO `test`.`explain_table` (`room_id`, `room_name`) VALUES ('7', 'room_name1');\n````\n\n````go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/changsongl/gorm-plugin/explain\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\ntype test struct {\n\tID       int64  `gorm:\"column:id\" json:\"id\"`\n\tRoomID   int64  `gorm:\"column:room_id\" json:\"room_id\"`\n\tRoomName string `gorm:\"column:room_name\" json:\"room_name\"`\n}\n\nfunc (test) TableName() string {\n\treturn \"explain_table\"\n}\n\nfunc main() {\n\t// create gorm v2 db\n\tdsn := \"root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4\u0026parseTime=True\u0026loc=Local\"\n\tdb, err := gorm.Open(mysql.Open(dsn), \u0026gorm.Config{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// create query plugin\n\tplugin := explain.New(\n\t\texplain.CallBackFuncOption(func(result explain.CallBackResult) {\n\t\t\tfmt.Printf(\"%+v\\n\", result)\n\t\t}),\n\t\texplain.TypeLevelOption(explain.ResultTypeRange),\n\t)\n\n\t// using plugin\n\tif err := db.Use(plugin); err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tdb.Where(\"room_name = 'haha'\").First(\u0026test{})\n}\n````\n\n````shell\n{Err:index not pass type requirement (range)  Results:[{Id:1 SelectType:SIMPLE Table:explain_table Type:index PossibleKey: Key:PRIMARY KeyLen:4 Ref: Rows:1 Extra:Using where}] SQL:SELECT * FROM `explain_table` WHERE room_name = 'haha' ORDER BY `explain_table`.`id` LIMIT 1}\n````\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchangsongl%2Fgorm-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchangsongl%2Fgorm-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchangsongl%2Fgorm-plugin/lists"}