{"id":13811872,"url":"https://github.com/daangn/autopprof","last_synced_at":"2026-06-02T09:01:09.009Z","repository":{"id":62874017,"uuid":"553970941","full_name":"daangn/autopprof","owner":"daangn","description":"Automatically profile the Go applications when CPU or memory utilization crosses threshold","archived":false,"fork":false,"pushed_at":"2026-04-24T05:37:28.000Z","size":226,"stargazers_count":216,"open_issues_count":0,"forks_count":10,"subscribers_count":29,"default_branch":"main","last_synced_at":"2026-04-24T07:29:26.241Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daangn.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-10-19T03:37:16.000Z","updated_at":"2026-04-24T05:37:00.000Z","dependencies_parsed_at":"2024-01-13T15:37:24.692Z","dependency_job_id":"a7cfa2a5-d357-4b79-80c6-d79b769968be","html_url":"https://github.com/daangn/autopprof","commit_stats":{"total_commits":19,"total_committers":3,"mean_commits":6.333333333333333,"dds":0.1578947368421053,"last_synced_commit":"be7f157b566478bc63474520431c827196d5170d"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/daangn/autopprof","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daangn%2Fautopprof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daangn%2Fautopprof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daangn%2Fautopprof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daangn%2Fautopprof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daangn","download_url":"https://codeload.github.com/daangn/autopprof/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daangn%2Fautopprof/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33814312,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"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":[],"created_at":"2024-08-04T04:00:37.390Z","updated_at":"2026-06-02T09:01:08.994Z","avatar_url":"https://github.com/daangn.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# autopprof\n\n![Run tests](https://github.com/daangn/autopprof/workflows/Run%20tests/badge.svg) [![Release](https://img.shields.io/github/v/tag/daangn/autopprof?label=Release)](https://github.com/daangn/autopprof/releases)\n\nAutomatically profile the Go applications when CPU or memory utilization crosses specific\nthreshold levels against the Linux container CPU quota and memory limit.\n\nOnce you start the autopprof, the autopprof process will periodically check the CPU and\nmemory utilization of the Go applications. If the resource utilization crosses the\nspecified threshold for each type of resource, the autopprof will automatically profile\nthe application (heap or cpu) and report the profiling report to the specific reporter (\ne.g. Slack).\n\n| CPU Profile Report Example                                 | Memory Profile Report Example                              |\n|------------------------------------------------------------|------------------------------------------------------------|\n| ![profiling example cpu](images/profiling_example_cpu.png) | ![profiling example mem](images/profiling_example_mem.png) |\n\n## Installation\n\n```bash\ngo get -u github.com/daangn/autopprof/v2\n```\n\n## Usage\n\n\u003e If your application is running on non-linux systems, you should check the\n\u003e ErrUnsupportedPlatform error returned from `autopprof.Start()` and handle it properly.\n\n```go\npackage main\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/daangn/autopprof/v2\"\n\t\"github.com/daangn/autopprof/v2/report\"\n)\n\nfunc main() {\n\terr := autopprof.Start(autopprof.Option{\n\t\tApp:            \"YOUR_APP_NAME\",\n\t\tCPUThreshold:   0.8,              // Default: 0.75.\n\t\tMemThreshold:   0.8,              // Default: 0.75.\n\t\tWatchInterval:  10 * time.Second, // Default: 5s. How often each metric is sampled.\n\t\tReportCooldown: 5 * time.Minute,  // Default: 1m. Min gap before re-reporting the same metric.\n\t\tReporter: report.NewSlackReporter(\n\t\t\t\u0026report.SlackReporterOption{\n\t\t\t\tToken:     \"YOUR_TOKEN_HERE\",\n\t\t\t\tChannelID: \"REPORT_CHANNEL_ID\",\n\t\t\t},\n\t\t),\n\t})\n\tif errors.Is(err, autopprof.ErrUnsupportedPlatform) {\n\t\t// You can just skip the autopprof.\n\t\tlog.Println(err)\n\t} else if err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\tdefer autopprof.Stop()\n\n\t// Your code here.\n}\n```\n\n\u003e You can create a custom reporter by implementing the `report.Reporter` interface.\n\n## Custom metrics\n\nBeyond the built-in CPU / memory / goroutine watchers, you can register your own\n`Metric` — useful for domain signals that only the owning struct knows about\n(connection-pool usage, queue backlog, cache hit ratio, …).\n\n```go\ntype Pool struct { /* ... */ }\n\nfunc (p *Pool) Name() string            { return \"db_pool\" }\nfunc (p *Pool) Threshold() float64      { return 0.9 }\nfunc (p *Pool) Interval() time.Duration { return 10 * time.Second } // 0 → global watchInterval\nfunc (p *Pool) Query() (float64, error) { return p.Usage(), nil }\nfunc (p *Pool) Collect(v float64) (autopprof.CollectResult, error) {\n    snap, err := p.Snapshot()\n    if err != nil {\n        return autopprof.CollectResult{}, err\n    }\n    return autopprof.CollectResult{\n        Reader:   bytes.NewReader(snap),\n        Filename: fmt.Sprintf(\"db_pool_%d.dump\", time.Now().Unix()),\n        Comment:  fmt.Sprintf(\":rotating_light:[pool] %.2f ≥ 0.90\", v),\n    }, nil\n}\n\n// After autopprof.Start(...) — typically inside the struct's constructor:\n_ = autopprof.Register(pool)\ndefer autopprof.Unregister(\"db_pool\")\n```\n\nFor one-off hooks you can skip the custom type entirely:\n\n```go\n_ = autopprof.Register(autopprof.NewMetric(\n    \"goroutine_blocked\", 100, 5*time.Second,\n    func() (float64, error)                        { return float64(runtime.NumGoroutine()), nil },\n    func(v float64) (autopprof.CollectResult, error) { /* ... */ },\n))\n```\n\nThe names `cpu`, `mem`, and `goroutine` are reserved for the built-in metrics.\nUser metrics do **not** participate in the built-in cascade.\n\nA built-in breach reports every other enabled built-in in addition to the\ntriggering one. Set `DisableCPUProf`, `DisableMemProf`, or\n`DisableGoroutineProf` to opt a built-in out — it leaves the watcher and\nthe cascade in one step.\n\n## Migrating from v1 to v2\n\nv2 unifies CPU / Mem / Goroutine / Custom under a single `Metric` interface\nand narrows the `Reporter` surface. This section lists every change a v1\ncaller needs to make.\n\n### 1. Update the module path\n\n```diff\n- import \"github.com/daangn/autopprof\"\n+ import \"github.com/daangn/autopprof/v2\"\n```\n\nUpdate `go.mod`:\n\n```bash\ngo get github.com/daangn/autopprof/v2\n```\n\n### 2. `Option` changes\n\n| v1 | v2 |\n|---|---|\n| `ReportBoth bool` | **Removed.** Cascade is always on for enabled built-ins. |\n| `ReportAll bool`  | **Removed.** Cascade is always on for enabled built-ins. |\n| *(n/a)* | `App string` — the `\"\u003capp\u003e\"` segment of built-in filenames. Defaults to `\"autopprof\"` when empty. |\n| *(n/a)* | `Metrics []Metric` — user-defined metrics to register at `Start`. |\n\nAll other fields (`CPUThreshold`, `MemThreshold`, `GoroutineThreshold`,\n`Disable*Prof`, `Reporter`) are unchanged. Disable individual built-ins\nvia `Disable*Prof` — they're excluded from the cascade as well.\n\n```diff\n autopprof.Start(autopprof.Option{\n+    App:          \"YOUR_APP_NAME\",\n     CPUThreshold: 0.8,\n-    ReportBoth:   true,\n     Reporter:     myReporter,\n })\n```\n\n### 3. `SlackReporterOption` changes\n\n```diff\n report.NewSlackReporter(\u0026report.SlackReporterOption{\n-    App:       \"YOUR_APP_NAME\",   // moved to Option.App\n     Token:     \"YOUR_TOKEN_HERE\",\n-    Channel:   \"old-channel-name\", // removed; Slack API dropped channel-name uploads\n     ChannelID: \"REPORT_CHANNEL_ID\",\n })\n```\n\n### 4. `Reporter` interface (4 methods → 1)\n\n```diff\n-type Reporter interface {\n-    ReportCPUProfile(ctx context.Context, r io.Reader, ci CPUInfo) error\n-    ReportHeapProfile(ctx context.Context, r io.Reader, mi MemInfo) error\n-    ReportGoroutineProfile(ctx context.Context, r io.Reader, gi GoroutineInfo) error\n-}\n+type Reporter interface {\n+    Report(ctx context.Context, r io.Reader, info ReportInfo) error\n+}\n+\n+type ReportInfo struct {\n+    MetricName string  // \"cpu\", \"mem\", \"goroutine\", or user-defined name\n+    Filename   string\n+    Comment    string\n+    Value      float64\n+    Threshold  float64\n+}\n```\n\nCustom `Reporter` implementations should route on `info.MetricName`:\n\n```go\nfunc (r *MyReporter) Report(ctx context.Context, reader io.Reader, info report.ReportInfo) error {\n    switch info.MetricName {\n    case \"cpu\":\n        return r.sendCPU(ctx, reader, info.Value*100, info.Threshold*100)\n    case \"mem\":\n        return r.sendMem(ctx, reader, info.Value*100, info.Threshold*100)\n    case \"goroutine\":\n        return r.sendGoroutine(ctx, reader, int(info.Value), int(info.Threshold))\n    default:\n        return r.sendCustom(ctx, reader, info)\n    }\n}\n```\n\nRemoved types from the `report` package: `CPUInfo`, `MemInfo`, `GoroutineInfo`,\n`CPUProfileFilenameFmt`, `HeapProfileFilenameFmt`, `GoroutineProfileFilenameFmt`.\n\n### 5. Bug fixes carried in v2\n\n- `Option.DisableGoroutineProf` was silently ignored in v1 (the value\n  wasn't assigned to the internal struct at `Start` time). It now takes\n  effect correctly, which may change observed behavior for callers\n  relying on the flag.\n- Cascade (the v1 `ReportAll: true` behavior) is now unconditional for\n  enabled built-ins; use `Disable*Prof` to opt specific metrics out.\n\n### 6. New: custom metrics\n\nv2 lets you register your own `Metric`. See the **Custom metrics** section\nabove — this is the main reason to migrate.\n\n## Benchmark\n\nBenchmark the overhead of watching the CPU and memory utilization. The overhead is very\nsmall, so we don't have to worry about the performance degradation.\n\n\u003e You can run the benchmark test with this command.\n\u003e\n\u003e ```bash\n\u003e ./benchmark.sh\n\u003e ```\n\u003e\n\n```\nBenchmarkLightJob-12                                \t98078731\t       134.5 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkLightJobWithWatchCPUUsage-12               \t92799849\t       133.0 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkLightJobWithWatchMemUsage-12               \t96778594\t       128.3 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkHeavyJob-12                                \t  105848\t    106606 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkHeavyJobWithWatchCPUUsage-12               \t  113047\t    112734 ns/op\t       1 B/op\t       0 allocs/op\nBenchmarkHeavyJobWithWatchMemUsage-12               \t  101102\t    133426 ns/op\t       1 B/op\t       0 allocs/op\nBenchmarkLightAsyncJob-12                           \t  399696\t     29953 ns/op\t    7040 B/op\t     352 allocs/op\nBenchmarkLightAsyncJobWithWatchGoroutineCount-12    \t  347266\t     34259 ns/op\t    7040 B/op\t     352 allocs/op\nBenchmarkHeavyAsyncJob-12                           \t     452\t  26689023 ns/op\t 6002064 B/op\t  300097 allocs/op\nBenchmarkHeavyAsyncJobWithWatchGoroutineCount-12    \t     414\t  27350318 ns/op\t 5973015 B/op\t  298647 allocs/op\n```\n\n## License\n\n[Apache 2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaangn%2Fautopprof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaangn%2Fautopprof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaangn%2Fautopprof/lists"}