{"id":31052709,"url":"https://github.com/getconversio/redis-metrics","last_synced_at":"2025-09-15T01:05:45.208Z","repository":{"id":30280745,"uuid":"33832287","full_name":"getconversio/redis-metrics","owner":"getconversio","description":"Easy metric tracking and aggregation using Redis","archived":false,"fork":false,"pushed_at":"2021-03-30T14:56:47.000Z","size":775,"stargazers_count":27,"open_issues_count":8,"forks_count":8,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-08-16T02:32:00.536Z","etag":null,"topics":["counter","javascript","nodejs","redis","redis-metrics"],"latest_commit_sha":null,"homepage":"http://getconversio.github.io/redis-metrics/","language":"JavaScript","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/getconversio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-04-12T20:18:47.000Z","updated_at":"2025-01-13T06:00:20.000Z","dependencies_parsed_at":"2022-09-15T15:23:13.788Z","dependency_job_id":null,"html_url":"https://github.com/getconversio/redis-metrics","commit_stats":null,"previous_names":["receiptful/redis-metrics"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/getconversio/redis-metrics","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getconversio%2Fredis-metrics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getconversio%2Fredis-metrics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getconversio%2Fredis-metrics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getconversio%2Fredis-metrics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/getconversio","download_url":"https://codeload.github.com/getconversio/redis-metrics/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getconversio%2Fredis-metrics/sbom","scorecard":{"id":424153,"data":{"date":"2025-08-11","repo":{"name":"github.com/getconversio/redis-metrics","commit":"f37feeef3fe154c8efa1b2dcfd717183093e297c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.1,"checks":[{"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":"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":"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":"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":"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":"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":"Code-Review","score":3,"reason":"Found 8/26 approved changesets -- score normalized to 3","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":"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":"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 17 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"}},{"name":"Vulnerabilities","score":0,"reason":"37 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-3gx7-xhv7-5mx3","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-675m-85rw-j3w4","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-xf5p-87ch-gxw2","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-8hfj-j24r-96c4","Warn: Project is vulnerable to: GHSA-wc69-rhjr-hc9g","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-35q2-47q7-3pc3","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-mxhp-79qh-mcx6","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-cf4h-3jhx-xvhq","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T01:54:37.420Z","repository_id":30280745,"created_at":"2025-08-19T01:54:37.420Z","updated_at":"2025-08-19T01:54:37.420Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275189915,"owners_count":25420672,"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","status":"online","status_checked_at":"2025-09-14T02:00:10.474Z","response_time":75,"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":["counter","javascript","nodejs","redis","redis-metrics"],"created_at":"2025-09-15T01:05:40.180Z","updated_at":"2025-09-15T01:05:45.200Z","avatar_url":"https://github.com/getconversio.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redis-metrics 📈\n\nEasy metric tracking and aggregation using Redis.\n\nThis module was originally created to provide an easy way of storing and\nviewing aggregated counter and trend statistics.\n\nIn a sense, the libary tries to provide sugar-coated method calls for storing\nand fetching Redis data to report counts and trends. The first design goal is to\nmake counting simpler.\n\nRead on below or read more on the [documentation site](http://getconversio.github.io/redis-metrics/).\n\n[![Build Status](https://travis-ci.org/getconversio/redis-metrics.svg?branch=master)](https://travis-ci.org/getconversio/redis-metrics)\n[![codecov](https://codecov.io/gh/getconversio/redis-metrics/branch/master/graph/badge.svg)](https://codecov.io/gh/getconversio/redis-metrics)\n\n## Install\n\n```console\n$ npm install --save redis-metrics\n```\n\n## API\n\n#### `RedisMetrics`\n\n##### `new RedisMetrics({ client, host, port, redisOptions, counterOptions })`\n\nInstances a new RedisMetrics instance with the given redis configuration. You can pass your own redis client or a host \u0026 port for a lib-managed connection.\n\n##### `metrics.counter(eventName, { timeGranularity = 'none', expireKeys = true })`\n\nCreates a new `TimestampedCounter`. The default options create a simple counter with no time granularity (basically, a total count only) that expires its keys automatically.\n\n**`timeGranularity`:** can be one of 'none', 'year', 'month', 'day', 'hour', 'minute', 'second'. This indicates the minimum granularity for which to store individual counters. The more granular, the more memory used, and the quicker the expiration (if enabled).\n\n#### `TimestampedCounter`\n\n##### `counter.incr({ eventObj = null } = { })`\n\nIncrements a single counter by 1. An optional `eventObj` can be passed to increment a counter\nfor a specific event attribute. This enables using the `top` and `topRange` functions.\n\n##### `counter.incrby(amount, { eventObj = null } = { })`\n\nSame as `counter.incr` for \u003e 1 increments.\n\n##### `counter.count({ timeGranularity = 'total', eventObj = null })`\n\nReturns the current count for this counter. Returns the total by default, but can return results for the current 'year', 'month', etc. Can optionally return results for a single `eventObj`.\n\n##### `counter.countRange(timeGranularity, { startDate, endDate = new Date() }, { eventObj = null })`\n\nReturns a timeseries of the current counts in the given granularity. Requires a `startDate` from which to start counting.\n\n##### `counter.top({ timeGranularity = 'total', direction = 'desc', startingAt = 0, limit = -1 })`\n\nReturns the list of top `eventObj` and their counts. By default, an all-time toplist is returned in descending order, and all event objects are included.\n\n**`startingAt`:** specify a number of top scorers to skip\n**`limit`:** specify to limit how many event objects \u0026 counts to return\n\n##### `counter.topRange({ startDate, endDate }, { timeGranularity = 'total', direction = 'desc', startingAt = 0, limit = -1 })`\n\nWhen given the `total` granularity, acts as `counter.top` but limits the results to the given time range. When given any other granularity, returns a timeseries of toplists for that granularity.\n\n##### `counter.trimEvents({ direction = 'desc', limit = 1000 })`\n\nKeeps only the top `limit` event objects for a given counter.\n\n##### `counter.zero({ eventObj = null })`\n\nWipes all counters going back 5 years.\n\n## Examples\n\n### Basic counter\n\n```javascript\n// Create an instance\nconst RedisMetrics = require('redis-metrics');\nconst metrics = new RedisMetrics();\n\n// If you need to catch uncaught exceptions, add an error handler to the client.\nmetrics.client.on('error', function(err) { /* ... */ });\n\n// Create a counter for a \"pageview\" event and increment it three times.\nconst myCounter = metrics.counter('pageview');\nmyCounter.incr();\nmyCounter.incr();\nmyCounter.incr();\n\n// Fetch the count for myCounter, using promise.\nmyCounter.count().then(function(cnt) {\n  console.log(cnt); // Outputs 3 to the console.\n});\n```\n\n### Time-aware counter\n\n```javascript\nconst RedisMetrics = require('redis-metrics');\nconst metrics = new RedisMetrics();\n\n// Use the timeGranularity option to specify how specific the counter should be\n// when incrementing.\nconst myCounter = metrics.counter('pageview', { timeGranularity: 'hour' });\nmyCounter.incr();\nmyCounter.incr();\nmyCounter.incr();\n\n// Fetch the count for myCounter for the current year.\nmyCounter.count({ timeGranularity: 'year' })\n  .then(function(cnt) {\n    console.log(cnt); // Outputs 3 to the console.\n  });\n\n// Fetch the count for each of the last two hours.\n// We are using moment here for convenience.\nconst moment = require('moment');\nconst now = moment();\nconst lastHour = moment(now).subtract(1, 'hours');\n\nmyCounter.countRange('hour', { startDate: lastHour })\n  .then(function(obj) {\n    // \"obj\" is an object with timestamps as keys and counts as values.\n    // For example something like this:\n    // {\n    //   '2015-04-15T11:00:00+00:00': 2,\n    //   '2015-04-15T12:00:00+00:00': 3\n    // }\n  });\n\n// Fetch the count for each day in the last 30 days\nconst thirtyDaysAgo = moment(now).subtract(30, 'days');\nmyCounter.countRange('day', { startDate: thirtyDaysAgo })\n  .then(function(obj) {\n    // \"obj\" contains counter information for each of the last thirty days.\n    // For example something like this:\n    // {\n    //   '2015-03-16T00:00:00+00:00': 2,\n    //   '2015-03-17T00:00:00+00:00': 3,\n    //   ...\n    //   '2015-04-15T00:00:00+00:00': 1\n    // }\n  });\n\n// Fetch the count for the last 60 seconds...\n// ... Sorry, you can't do that because the counter is only set up to track by\n// the hour.\n```\n\n### Event Objects\n\n```javascript\nconst myCounter = metrics.counter('pageview', { timeGranularity: 'hour' });\n\n// You may want to get specific when counting an event. Use the `eventObj` option\n// to store individual counters for different event attributes:\nmyCounter.incrby(2, { eventObj: '/page1.html' });\nmyCounter.incrby(5, { eventObj: '/page2.html' });\nmyCounter.incrby(8, { eventObj: '/page3.html' });\n\nmyCounter.count({ eventObj: '/page2.html' })\n  .then(count =\u003e {\n    console.log(count); // Outputs 5 to the console.\n  });\n```\n\n### Top Event Object Counts\n\n```javascript\nconst myCounter = metrics.counter('pageview', { timeGranularity: 'hour' });\n\n// You may want to get specific when counting an event. Use the `eventObj` option\n// to store individual counters for different event attributes:\nmyCounter.incrby(2, { eventObj: '/page1.html' });\nmyCounter.incrby(5, { eventObj: '/page2.html' });\nmyCounter.incrby(8, { eventObj: '/page3.html' });\n\nmyCounter.top()\n  .then(toplist =\u003e {\n    // toplist:\n    // [\n    //   { '/page3.html': 8 },\n    //   { '/page2.html': 5 },\n    //   { '/page1.html': 2 }\n    // ]\n  });\n\n// a few months later...\nmyCounter.incrby(5, { eventObj: '/page1.html' });\nmyCounter.incrby(3, { eventObj: '/page2.html' });\n\nmyCounter.topRange({ startDate: moment().subtract(1, 'month') })\n  .then(toplist =\u003e {\n    // toplist:\n    // [\n    //   { '/page1.html': 5 },\n    //   { '/page2.html': 3 }\n    // ]\n  });\n\n```\n\n## Redis Namespace\n\nBy default keys are stored in Redis as `c:{name}:{period}`. If you prefer to use a different Redis namespace than `c`, you can pass this in as an option:\n\n```\nconst myCounter = metrics.counter('pageview', { timeGranularity: 'hour', namespace: 'stats' });`\n```\n\n## V2\n\nSee the [changelog](CHANGELOG.md).\n\n## Test\n\nRun tests including code coverage:\n\n    $ npm test\n\n## Documentation\n\nThe internal module documentation is based on [jsdoc](http://usejsdoc.org) and\ncan be generated with:\n\n    $ npm run docs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetconversio%2Fredis-metrics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgetconversio%2Fredis-metrics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetconversio%2Fredis-metrics/lists"}