{"id":13507783,"url":"https://github.com/hahuang65/beaker","last_synced_at":"2026-02-21T17:33:25.634Z","repository":{"id":33077944,"uuid":"36715066","full_name":"hahuang65/beaker","owner":"hahuang65","description":"A mirror for https://git.sr.ht/~hwrd/beaker","archived":false,"fork":false,"pushed_at":"2022-01-08T00:21:31.000Z","size":1468,"stargazers_count":278,"open_issues_count":8,"forks_count":19,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-12-13T01:58:30.087Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hahuang65.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-06-02T07:12:46.000Z","updated_at":"2025-10-24T23:37:39.000Z","dependencies_parsed_at":"2022-09-18T05:45:40.540Z","dependency_job_id":null,"html_url":"https://github.com/hahuang65/beaker","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hahuang65/beaker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hahuang65%2Fbeaker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hahuang65%2Fbeaker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hahuang65%2Fbeaker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hahuang65%2Fbeaker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hahuang65","download_url":"https://codeload.github.com/hahuang65/beaker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hahuang65%2Fbeaker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29688263,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T15:51:39.154Z","status":"ssl_error","status_checked_at":"2026-02-21T15:49:03.425Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-01T02:00:39.007Z","updated_at":"2026-02-21T17:33:20.625Z","avatar_url":"https://github.com/hahuang65.png","language":"Elixir","funding_links":[],"categories":["Debugging"],"sub_categories":[],"readme":"Beaker\n======\n\n[![Build Status](https://travis-ci.org/hahuang65/beaker.svg?branch=master)](https://travis-ci.org/hahuang65/beaker)\n[![Inline docs](http://inch-ci.org/github/hahuang65/beaker.svg?branch=master\u0026style=shields)](http://inch-ci.org/github/hahuang65/beaker)\n\nBeaker is a tool that can be used to keep track of metrics for your Elixir project. It aims to provide an easy way to register statistics as well as an easy way to visualize them.\n\nNote: Beaker metrics are currently ephemeral and are scoped to the app (or more specifically Beaker) being stopped. Metrics are not persisted across restarts.\n\nFor more information, see the [online documentation](http://hexdocs.pm/beaker).\n\n## Usage\n\nTo include Beaker in your application, add it to your `mix.exs` file:\n\n```elixir\ndefp deps do\n  [\n    {:beaker, \"\u003e= 1.2.0\"}\n  ]\n```\n\nAnd add it to the list of applications started with yours:\n\n```elixir\ndef application do\n  [applications: [:beaker]]\nend\n```\n\n## Integration with Phoenix\nBeaker provides a way to visualize your metrics through [Phoenix](http://www.phoenixframework.org).\n\nIt'll end up looking something like:\n\n[![Beaker](/beaker_sample.png)](/beaker_sample.png)\n\nThe source code for this frontend can be found at [Beaker-Frontend](https://github.com/mfeckie/beaker-frontend)!\nThe API Documentation which the frontend consumes can be found at [API Documentation](API_doc.md)\n\n1) Add `beaker` and `phoenix` to the dependencies in your Mixfile:\n```elixir\ndefp deps do\n  [\n    {:phoenix, \"\u003e= 1.1\"},\n    {:phoenix_html, \"\u003e= 2.3\"}\n  ]\nend\n```\n\n2) Add `beaker` and `phoenix_html` to the started applications in your Mixfile:\n```elixir\ndef application do\n  applications: [:phoenix, :phoenix_html, :beaker]\nend\n```\n\n3) Forward requests to `Beaker.Web` in your router:\n```elixir\nforward \"/beaker\", Beaker.Web\n```\n\nThis will add a page at `/beaker` with all your metrics visualized on the page.\nGauges and Counters will display a box with their name and value.\nTime Series will display a chart with the last 120 minutes worth of aggregated data.\n\nIf you'd like to track your Phoenix performance, you can add that to your Phoenix Endpoint:\n\n```elixir\ndefmodule MyApp.Endpoint do\n  use Phoenix.Endpoint, otp_app: :my_app\n\n  plug Beaker.Integrations.Phoenix\n\n  plug ...\nend\n```\n\nThis will keep track ALL requests (including requests for static assets) passing through Phoenix, and keep track of their response time and keep a counter of how many requests were made.\n**NOTE**: It is **EXTREMELY** important that this is plugged before anything else in order to get the most accurate response timings.\n\nCurrently, these are not in-depth to split apart requests going to specific controllers, but provides a good overview to see how your overall Phoenix performance is.\nHopefully in-depth tracking of requests can be implemented in a future release. Also, it'll be nice to be able to provide an option to ignore serving of static assets.\n\n## Integration with Ecto\n\nBeaker provides a simple way to integrate with Ecto to track the performance of your queries.\n\nTo use it, in your Repo, just:\n\n```elixir\ndefmodule MyApp.Repo do\n  use Ecto.Repo, otp_app: :my_app\n  use Beaker.Integrations.Ecto\nend\n```\n\nDoing so will keep track of your queries times, queue times, and query counts for all your Ecto queries automatically.\nThese will show up in Beaker's web interface.\n\nCurrently, these are not in-depth to split apart different types of queries, but provides a good overview to see how your overall Ecto performance is.\nHopefully in-depth tracking of queries can be implemented in a future release.\n\n## Metrics\n\nBeaker provides a variety of different metric types:\n\n### Gauge\n\nThe gauge is a simple gauge. It's a metric where a value can be set and retrieved.\n\nIt is commonly used for metrics that return a single value.\nExamples are:\n  * Average response time\n  * Uptime (Availability)\n  * Latency / Ping\n\nYou can set and retrieve the value of a gauge like so:\n\n```elixir\niex\u003e Beaker.Gauge.set(\"foo\", 50)\n:ok\n\niex\u003e Beaker.Gauge.get(\"foo\")\n50\n```\n\nYou can also set a minimum and maximum value for the gauge which can be used by the frontend for displaying your data.\n\n```elixir\niex\u003e Beaker.Gauge.set(\"foo-min-max\", 50, 1, 100)\n:ok\n\niex\u003e Beaker.Gauge.get(\"foo-min-max\")\n%{max: 100, min: 1, name: \"foo-min-max\", value: 50}\n```\n\nSometimes you'll want to time something, and set that duration to a gauge. We provide a convenience for that.\n\n```elixir\niex\u003e Beaker.Gauge.time(\"foo\", fn -\u003e 2 + 2 end)\n4\n```\n\n```elixir\n# Or if you prefer `do` syntax:\nBeaker.Gauge.time(\"foo\") do\n  2 + 2\nend\n4\n```\n\nYou can get all of your gauges in the form of a map if you need:\n\n```elixir\niex\u003e Beaker.Gauge.all\n%{\"foo\" =\u003e 10, \"bar\" =\u003e 45}\n```\n\nYou can remove all your gauges and start from scratch:\n\n```elixir\niex\u003e Beaker.Gauge.clear\n:ok\n```\n\nOr just clear out a single gauge:\n\n```elixir\niex\u003e Beaker.Gauge.clear(\"foo\")\n:ok\n```\n\n### Counter\n\nThe counter is a signed bi-directional integer counter. It can keep track of integers and increment and decrement them.\n\nIt is commonly used for metrics that keep track of some cumulative value.\nExamples are:\n  * Total number of downloads\n  * Number of queued jobs\n  * Quotas\n\nYou can set and retrieve the value of a counter like so:\n\n```elixir\niex\u003e Beaker.Counter.set(\"foo\", 10)\n:ok\n\niex\u003e Beaker.Counter.get(\"foo\")\n10\n```\n\nYou can also use a counter more traditionally, via incrementing and decrementing:\n\n```elixir\niex\u003e Beaker.Counter.incr(\"foo\")\n:ok\n\niex\u003e Beaker.Counter.get(\"foo\")\n11\n\n\niex\u003e Beaker.Counter.decr(\"foo\")\n:ok\n\niex\u003e Beaker.Counter.get(\"foo\")\n10\n```\n\nIf incrementing and decrementing by 1 is not a big enough step for you:\n\n```elixir\niex\u003e Beaker.Counter.incr_by(\"foo\", 5)\n:ok\n\niex\u003e Beaker.Counter.get(\"foo\")\n15\n\niex\u003e Beaker.Counter.decr_by(\"foo\", 10)\n:ok\n\niex\u003e Beaker.Counter.get(\"foo\")\n5\n```\n\nYou can get all of your counters in the form of a map if you need:\n\n```elixir\niex\u003e Beaker.Counter.all\n%{\"foo\" =\u003e 10, \"bar\" =\u003e 45}\n```\n\nYou can remove all your counters and start from scratch:\n\n```elixir\niex\u003e Beaker.Counter.clear\n:ok\n```\n\nOr just clear out a single counter:\n\n```elixir\niex\u003e Beaker.Counter.clear(\"foo\")\n:ok\n```\n\n### Time Series\n\nThe time series is basically a series of values with a time (epoch) attached to each value at the time the value was recorded.\n\nIt is commonly used to keep track of the change in value of some metric across a period of time.\nExamples are:\n  * Response time across a period of time\n  * Error rates across a period of time\n  * Download count across a period of time\n\nTo sample (record a value) a time series:\n\n```elixir\niex\u003e Beaker.TimeSeries.sample(\"foo\", 50)\n:ok\niex\u003e Beaker.TimeSeries.sample(\"foo\", 66)\n:ok\niex\u003e Beaker.TimeSeries.sample(\"foo\", 30)\n:ok\niex\u003e Beaker.TimeSeries.sample(\"bar\", 10)\n:ok\niex\u003e Beaker.TimeSeries.sample(\"bar\", 50)\n:ok\n```\n\nSometimes you'll want to time something, and sample that duration to a time series. We provide a convenience for that.\n\n```elixir\niex\u003e Beaker.TimeSeries.time(\"baz\", fn -\u003e 2 + 2 end)\n4\n```\n\n```elixir\n# Or if you prefer `do` block syntax:\nBeaker.TimeSeries.time(\"baz\") do\n  2 + 2\nend\n```\n\nAnytime a time series is retrieved, it will be in the format of a list of pairs.\nEach pair consists of a timestamp in epoch and the value sampled, i.e. `{timestamp, value}`.\nThe list will be guaranteed to be in reverse chronological order, that is, the latest sample will be the first in the list.\n\nTo get the time series that have been recorded for a key:\n\n```elixir\niex\u003e Beaker.TimeSeries.get(\"foo\")\n[{1434738115306786, 30}, {1434738112851607, 66}, {1434738107132294, 50}]\n```\n\nTo retrieve all time series:\n\n```elixir\niex\u003e Beaker.TimeSeries.all\n%{\"bar\" =\u003e [{1434738203344586, 50}, {1434738201507329, 10}],\n  \"foo\" =\u003e [{1434738115306786, 32}, {1434738112851607, 87}, {1434738107132294, 50}]}\n```\n\nAnd to clear all time series:\n\n```elixir\niex\u003e Beaker.TimeSeries.clear\n:ok\n```\n\nOr clear a single time series:\n\n```elixir\niex\u003e Beaker.TimeSeries.clear(\"foo\")\n:ok\n```\n\n### Time Series Aggregation\n\nTime series will be aggregated once every 60 seconds, for the last full minute (e.g. if aggregation is run at 01:22:32, it will run for the minute of 01:21 to 01:22).\n\nFor each minute, aggregation will calculate the minimum, maximum, and average values as well as the number of values for that minute and store it in `Beaker.TimeSeries.Aggregated`.\n\n*Note*: Aggregation is not destructive. Raw data will remain in `Beaker.TimeSeries`, and the calculated values from aggregation will be stored in `Beaker.TimeSeries.Aggregated`.\n\nTo inspect and use aggregated data, the `Beaker.TimeSeries.Aggregated.get/1` and `Beaker.TimeSeries.Aggregated.all/0` functions are available and function exactly as their `Beaker.TimeSeries` counterparts. However, please be aware that although aggregated data is also returned as a list of `{time, value}` pairs, `value` is actually a tuple that looks like `{average, minimum, maximum, count}` for it's paired minute.\n\n```elixir\niex\u003e Beaker.TimeSeries.Aggregated.get(\"foo\")\n[{1434738060000000, {48.666666666666664, 30, 66, 3}}] # 48.666 is the average, 30 is the minimum, 66 is the maximum, and 3 is the count of entries for the minute of 1434738060000000 in epoch time\n```\n\nTo check the last time aggregation was successfully run:\n\n```elixir\niex\u003e Beaker.TimeSeries.Aggregator.last_aggregated_at\n1442989860000000 # Epoch time\n```\n\n*Performance*: Currently, the aggregation algorithm is not very optimized. I did some basic performance testing on a 2014 MacBook Pro with 16GB of RAM and a 3 Ghz i7 processor.\n\n600 data points within a minute (~10 samples per second) will take each aggregation roughly between 3 to 6 milliseconds.\n\n600 data points within a minute for 10 time series (6000 data points total) will take each aggregation roughly between 5 and 15 milliseconds.\n\nI hope to improve this algorithm to be faster in the next few releases.\n\n## Important Links\n\n  * [Documentation](http://hexdocs.pm/beaker)\n  * [Beaker Frontend Code](https://github.com/mfeckie/beaker-frontend)\n  * [API Documentation](API_doc.md)\n\n## Devloping\n\n`DOC=1 mix test` will create a markdown file representing the request and response for the API.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhahuang65%2Fbeaker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhahuang65%2Fbeaker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhahuang65%2Fbeaker/lists"}