{"id":13794184,"url":"https://github.com/szuecs/gin-gomonitor","last_synced_at":"2026-01-18T08:44:02.842Z","repository":{"id":34545581,"uuid":"38490127","full_name":"szuecs/gin-gomonitor","owner":"szuecs","description":"Gin middleware for monitoring","archived":false,"fork":false,"pushed_at":"2018-01-07T17:24:31.000Z","size":46,"stargazers_count":65,"open_issues_count":1,"forks_count":6,"subscribers_count":9,"default_branch":"master","last_synced_at":"2026-01-14T13:41:13.904Z","etag":null,"topics":["gin","gin-middleware","golang","metrics","metrics-endpoint","monitoring"],"latest_commit_sha":null,"homepage":null,"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/szuecs.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":"2015-07-03T11:46:12.000Z","updated_at":"2025-05-20T17:33:11.000Z","dependencies_parsed_at":"2022-09-08T12:01:21.722Z","dependency_job_id":null,"html_url":"https://github.com/szuecs/gin-gomonitor","commit_stats":null,"previous_names":["zalando-techmonkeys/gin-gomonitor","zalando/gin-gomonitor"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/szuecs/gin-gomonitor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szuecs%2Fgin-gomonitor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szuecs%2Fgin-gomonitor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szuecs%2Fgin-gomonitor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szuecs%2Fgin-gomonitor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/szuecs","download_url":"https://codeload.github.com/szuecs/gin-gomonitor/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szuecs%2Fgin-gomonitor/sbom","scorecard":{"id":864404,"data":{"date":"2025-08-11","repo":{"name":"github.com/szuecs/gin-gomonitor","commit":"52a62f5215c5f8058fd3745158b10db84f795d32"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":1,"reason":"Found 1/10 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-24T02:17:02.083Z","repository_id":34545581,"created_at":"2025-08-24T02:17:02.083Z","updated_at":"2025-08-24T02:17:02.083Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28534148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["gin","gin-middleware","golang","metrics","metrics-endpoint","monitoring"],"created_at":"2024-08-03T23:00:36.888Z","updated_at":"2026-01-18T08:44:02.807Z","avatar_url":"https://github.com/szuecs.png","language":"Go","funding_links":[],"categories":["metrics","Go"],"sub_categories":[],"readme":"# Gin-Gomonitor\n\n[![Build Status](https://travis-ci.org/szuecs/gin-gomonitor.svg?branch=master)](https://travis-ci.org/szuecs/gin-gomonitor)\n[![Coverage Status](https://coveralls.io/repos/szuecs/gin-gomonitor/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/szuecs/gin-gomonitor?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/szuecs/gin-gomonitor)](https://goreportcard.com/report/szuecs/gin-gomonitor)\n\nGin-Gomonitor is made specially for [Gin Framework](https://github.com/gin-gonic/gin) users who also want to use [Go-Monitor](https://github.com/mcuadros/go-monitor). It was created by Go developers who needed Gin middleware for exposing metrics with Go-Monitor, which provides a simple and extensible way to build monitorizable long-term execution processes or daemons via HTTP. Gin-Gomonitor supports customized aspects and implements a simple counter aspect within the\npackage ginmon.\n\n## Project Context and Features\n\nWhen it comes to choosing a Go framework, there's a lot of confusion\nabout what to use. The scene is very fragmented, and detailed\ncomparisons of different frameworks are still somewhat rare. Meantime,\nhow to handle dependencies and structure projects are big topics in\nthe Go community.\n\nWe've liked using Gin for its speed, accessibility, and usefulness in\ndeveloping microservice architectures. In creating Gin-Gomonitor, we\nwanted to take fuller advantage of\n[Gin](https://github.com/gin-gonic/gin)'s capabilities and help other\ndevs do likewise.\n\nWe implemented the following custom\n[Aspects](https://github.com/szuecs/gin-gomonitor/tree/master/aspects):\n\nCounterAspect implements a counter for request per time.Duration,\ncounting the sum of all and for each path independent counters.\n\nRequestTimeAspect implements the measurement of request times\nincluding values for min, max, mean, stdev, p90, p95, p99.\n\nGenericChannelAspect implements a generic method to send key value\npairs to this monitoring facility. This aspect will calculate min,\nmax, mean, stdev, p90, p95, p99 for all values of a key send to the\nchannel in a given time frame. It can be used without Gin if you like.\n\nSee also our [full example](https://github.com/szuecs/gin-gomonitor/blob/master/example/main.go).\n\n#### How Go-Monitor Is Different from Other Metric Libraries\n\nGo-Monitor is easily extendable, does not need type casts to create\nJSON, and has useful metrics already defined. It exposes metrics as\nJSON to a metrics endpoint using a different TCP port.\n\n## Requirements\n\nGin-Gomonitor uses the following [Go](https://golang.org/) packages as dependencies:\n\n- [Gin](github.com/gin-gonic/gin)\n- [Go-Monitor](gopkg.in/mcuadros/go-monitor.v1)\n\n## Installation\n\nAssuming you've installed [Go](https://golang.org/dl) and\n[Gin](https://github.com/gin-gonic/gin), run this:\n\n    % go get -u github.com/szuecs/gin-gomonitor\n\n## Usage\n\n[This example](https://github.com/szuecs/gin-gomonitor/blob/master/example/main.go) shows you how to use Gin-Gomonitor. To try it out, use:\n\n      % go run example/main.go\n\n### Default Monitor with Custom Aspect\n\nFirst define your Custom Aspect, so that it will be exposed in a special path /Custom:\n\n```go\ntype CustomAspect struct {\n\tCustomValue int\n}\n\nfunc (a *CustomAspect) GetStats() interface{} {\n\treturn a.CustomValue\n}\n\nfunc (a *CustomAspect) Name() string {\n\treturn \"Custom\"\n}\n\nfunc (a *CustomAspect) InRoot() bool {\n\treturn false\n}\n```\n\nNext, initialize the CounterAspect defined by Gin-Gomonitor and your own CustomAspect (defined above):\n\n```go\n    router := gin.New()\n\n    counterAspect := ginmon.NewCounterAspect()\n    counterAspect.StartTimer(1 * time.Minute)\n\n    anotherAspect := \u0026CustomAspect{3}\n    asps := []aspects.Aspect{counterAspect, anotherAspect}\n    router.Use(ginmon.CounterHandler(counterAspect))\n```\n\nFinally, register the middleware to expose all metrics on TCP port 9000:\n\n```go\n    gomonitor.Start(9000, asps)\n```\n\n#### Testing\n\nThe page's counter metric will increment if you hit the page:\n\n    % curl http://localhost:9000/Counter\n    {\n        \"Counter\": {\n            \"request_sum_per_minute\": 0,\n            \"requests_per_minute\": {},\n            \"request_codes_per_minute\": {}\n        }\n    }\n    % for i in {1..20}; do curl localhost:8080/ \u0026\u003e/dev/null ; curl localhost:8080/foo \u0026\u003e/dev/null ; done; sleep 3; curl http://localhost:9000/Counter\n    {\n        \"Counter\": {\n            \"request_sum_per_minute\": 40,\n            \"requests_per_minute\": {\n                \"/\": 20,\n                \"/foo\": 20\n            },\n            \"request_codes_per_minute\": {\n                \"200\": 20,\n                \"404\": 20\n            }\n        }\n    }\n\nThe page custom metric will show three as defined:\n\n    % curl http://localhost:9000/Custom\n    {\n      \"Custom: 3\n    }\n\nThe regular metrics from go-monitor exposes Go process and build information:\n\n    % curl http://localhost:9000/\n    {\n      \"MemStats\": {\n        \"Alloc\": 1285680,\n        \"TotalAlloc\": 1285680,\n        \"Sys\": 4458744,\n        \"Lookups\": 36,\n        \"Mallocs\": 6216,\n        \"Frees\": 0,\n        \"HeapAlloc\": 1285680,\n       ...\n      \"Runtime\": {\n        \"GoVersion\": \"go1.5.1\",\n        \"GoOs\": \"linux\",\n        \"GoArch\": \"amd64\",\n        \"CpuNum\": 4,\n        \"GoroutineNum\": 7,\n        \"Gomaxprocs\": 4,\n        \"CgoCallNum\": 1\n      },\n      \"Time\": {\n        \"StartTime\": \"2016-03-06T17:33:59.440088805+01:00\",\n        \"RequestTime\": \"2016-03-06T17:48:55.245710327+01:00\"\n      }\n    }\n\nYou can also filter by sub-category:\n\n    % curl localhost:9000/Runtime\n    {\n      \"Runtime\": {\n        \"GoVersion\": \"go1.5.1\",\n        \"GoOs\": \"linux\",\n        \"GoArch\": \"amd64\",\n        \"CpuNum\": 4,\n        \"GoroutineNum\": 7,\n        \"Gomaxprocs\": 4,\n        \"CgoCallNum\": 1\n      }\n    }\n\n### CounterAspect\n\nCounterAspect measures requests per configured time.Duration.  It\nshows requests as sum, per path and per HTTP code, such that you can\nmonitor increasing user traffic, changing access patterns of user\ntraffic and http errors.\n\n```go\nfunc main() {\n        // initialize CounterAspect and reset every minute\n        counterAspect := ginmon.NewCounterAspect()\n        counterAspect.StartTimer(1 * time.Minute)\n\n        asps := []aspects.Aspect{counterAspect}\n\trouter := gin.New()\n        // register CounterAspect middleware\n\t// test: curl http://localhost:9000/Counter\n        router.Use(ginmon.CounterHandler(counterAspect))\n\n\t// start metrics endpoint\n\tgomonitor.Start(9000, asps)\n\t// last middleware\n\trouter.Use(gin.Recovery())\n\n\trouter.GET(\"/\", func(ctx *gin.Context) {\n\t\tctx.JSON(http.StatusOK, gin.H{ \"hello\": \"world\"})\n\t})\n\n\tlog.Fatal(router.Run(\":8080\"))\n}\n```\n\nThe page's counter metric will increment if you hit the page:\n\n    % curl http://localhost:9000/Counter\n    {\n        \"Counter\": {\n            \"request_sum_per_minute\": 0,\n            \"requests_per_minute\": {},\n            \"request_codes_per_minute\": {}\n        }\n    }\n    % for i in {1..20}; do curl localhost:8080/ \u0026\u003e/dev/null ; curl localhost:8080/foo \u0026\u003e/dev/null ; done; sleep 3; curl http://localhost:9000/Counter\n    {\n        \"Counter\": {\n            \"request_sum_per_minute\": 40,\n            \"requests_per_minute\": {\n                \"/\": 20,\n                \"/foo\": 20\n            },\n            \"request_codes_per_minute\": {\n                \"200\": 20,\n                \"404\": 20\n            }\n        }\n    }\n\n\n### RequestTimeAspect\n\nRequestTimeAspect measures processing time in the middleware\nchain. The request will start in the outermost middleware, in this\nexample it is the RequestTimeHandler. The request will be passed through all\nother middleware handlers and at last to your router endpoint. Your\nresponse from your handler will be passed again to all middleware\nhandlers. RequestTimeAspect will save all measured time.Duration in a\nslice and calculate the next metrics with it each timeDuration you\nconfigured with the parameter to StartTimer(d time.Duration). The\nmetrics endpoint is configured to be\nhttp://localhost:9000/RequestTime, which will be calculated in this\nexample code every 5 seconds:\n\n```go\nfunc main() {\n        // initialize RequestTimeAspect and calculate every 5 seconds\n\trequestAspect := ginmon.NewRequestTimeAspect()\n\trequestAspect.StartTimer(5 * time.Second)\n\tasps := []aspects.Aspect{requestAspect}\n\n\trouter := gin.New()\n        // register RequestTimeAspect middleware\n\t// test: curl http://localhost:9000/RequestTime\n\trouter.Use(ginmon.RequestTimeHandler(requestAspect))\n\t// start metrics endpoint\n\tgomonitor.Start(9000, asps)\n\t// last middleware\n\trouter.Use(gin.Recovery())\n\n\trouter.GET(\"/\", func(ctx *gin.Context) {\n\t\tctx.JSON(http.StatusOK, gin.H{ \"hello\": \"world\"})\n\t})\n\n\tlog.Fatal(router.Run(\":8080\"))\n}\n```\n\nRequestTimeAspect will calculate min, max, mean, standard deviation,\nP90, P95, P99 of all measured time.Duration for all your endpoints in\nthis router group. It also creates a time stamp, such that you\nknow when the calculation happened.\n\n```bash\n% for i in {1..20}; do curl localhost:8080/ \u0026\u003e/dev/null; done; sleep 5; curl localhost:9000/RequestTime\n{\n  \"RequestTime\": {\n    \"count\": 20,\n    \"min\": 47098,\n    \"max\": 94502,\n    \"mean\": 62199.75,\n    \"stdev\": 13823.2430381624,\n    \"p90\": 91248,\n    \"p95\": 94502,\n    \"p99\": 94502,\n    \"timestamp\": \"2017-01-22T19:59:48.164355177+01:00\"\n  }\n}\n```\n\n### GenericChannelAspect\n\nGenericChannelAspect enables you to send arbitrary ginmon.DataChannel\ndata through a channel to gin-gomonitor, which will calculate min,\nmax, mean, standard deviation, P90, P95, P99 grouped by\nginmon.DataChannel.Name for every every configured time.Duration. The\nmetrics endpoint is configured to be http://localhost:9000/generic,\nwhich will be calculated in this example code every 3 seconds:\n\n```go\nfunc main() {\n        // initialize GenericChannelAspect and calculate every 3 seconds\n\tgenericAspect := ginmon.NewGenericChannelAspect(\"generic\")\n\tgenericAspect.StartTimer(3 * time.Second)\n\tgenericCH := genericAspect.SetupGenericChannelAspect()\n\tasps := []aspects.Aspect{genericAspect}\n\n\trouter := gin.New()\n        // register GenericChannelAspect middleware\n\t// test: curl http://localhost:9000/generic\n\t// start metrics endpoint\n\tgomonitor.Start(9000, asps)\n\t// catch panics as last middleware\n\trouter.Use(gin.Recovery())\n\n        // send a lot of data concurrently to the monitoring data channel\n\ti := 0\n\tgo func() {\n\t\tfor {\n\t\t\ti++\n\t\t\tgenericCH \u003c- ginmon.DataChannel{Name: \"foo\", Value: float64(i)}\n\t\t}\n\t}()\n\tj := 0\n\tgo func() {\n\t\tfor {\n\t\t\tj++\n\t\t\tgenericCH \u003c- ginmon.DataChannel{Name: \"bar\", Value: float64(j % 5)}\n\t\t}\n\t}()\n\n\trouter.Run(\":8080\"))\n}\n```\n\n```bash\n% curl http://localhost:9000/generic\n{\n  \"generic\": {\n    \"bar\": {\n      \"count\": 2110190,\n      \"min\": 0,\n      \"max\": 4,\n      \"mean\": 2,\n      \"stdev\": 1.4142138974647371,\n      \"p90\": 4,\n      \"p95\": 4,\n      \"p99\": 4,\n      \"timestamp\": \"2017-01-24T14:40:20.970407737+01:00\"\n    },\n    \"foo\": {\n      \"count\": 2445672,\n      \"min\": 5.128943e+06,\n      \"max\": 7.574614e+06,\n      \"mean\": 6.3517785e+06,\n      \"stdev\": 706004.8381128459,\n      \"p90\": 7.330047e+06,\n      \"p95\": 7.452331e+06,\n      \"p99\": 7.550158e+06,\n      \"timestamp\": \"2017-01-24T14:40:21.299909533+01:00\"\n    }\n  }\n}\n```\n\n## Contributing/TODO\n\nWe welcome contributions from the community—just submit a pull\nrequest. To help you get started, here are some items that we'd love\nhelp with:\n\n- Adding more custom metrics\n  - add more tests\n  - time per request: path, httpverb\n  - number of requests: httpverb\n  - review and maybe refactor lock usage in generic_channel.go\n  - reduce goroutine usage: We could use one goroutine for all myAspect.StartTimer()\n  - add logging and enable user to choose logging, see [Dave Cheney's post](https://dave.cheney.net/2017/01/23/the-package-level-logger-anti-pattern)\n  - \u0026lt;your idea\u0026gt;\n- the code base\n\nPlease use GitHub issues as your starting point for contributions, new ideas or bug reports.\n\n## Contact\n\n* E-Mail: [MAINTAINERS](MAINTAINERS)\n\n## Contributors\n\nThanks to:\n\n- \u0026lt;your name\u0026gt;\n\n\n## License\n\nSee [LICENSE](LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszuecs%2Fgin-gomonitor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fszuecs%2Fgin-gomonitor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszuecs%2Fgin-gomonitor/lists"}