{"id":38247185,"url":"https://github.com/sensedeep/dynamodb-metrics","last_synced_at":"2026-01-17T01:24:33.284Z","repository":{"id":57217824,"uuid":"404616351","full_name":"sensedeep/dynamodb-metrics","owner":"sensedeep","description":"DynamoDB Metrics for Single Table Designs","archived":false,"fork":false,"pushed_at":"2023-02-15T05:21:41.000Z","size":238,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2023-08-28T12:14:52.554Z","etag":null,"topics":["aws","dynamodb","metrics","monitoring","nosql","serverless"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/sensedeep.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-09T06:49:30.000Z","updated_at":"2022-05-09T19:02:39.000Z","dependencies_parsed_at":"2022-08-28T21:01:05.757Z","dependency_job_id":null,"html_url":"https://github.com/sensedeep/dynamodb-metrics","commit_stats":null,"previous_names":[],"tags_count":6,"template":null,"template_full_name":null,"purl":"pkg:github/sensedeep/dynamodb-metrics","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sensedeep%2Fdynamodb-metrics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sensedeep%2Fdynamodb-metrics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sensedeep%2Fdynamodb-metrics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sensedeep%2Fdynamodb-metrics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sensedeep","download_url":"https://codeload.github.com/sensedeep/dynamodb-metrics/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sensedeep%2Fdynamodb-metrics/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28491432,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T00:50:05.742Z","status":"ssl_error","status_checked_at":"2026-01-17T00:43:11.982Z","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":["aws","dynamodb","metrics","monitoring","nosql","serverless"],"created_at":"2026-01-17T01:24:33.219Z","updated_at":"2026-01-17T01:24:33.277Z","avatar_url":"https://github.com/sensedeep.png","language":"TypeScript","readme":"![DynamoDB Metrics](https://www.sensedeep.com/images/metrics-logo.png)\n\n## Metrics for your DynamoDB Single Table designs!\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/sensedeep/dynamodb-metrics/build.yml?branch=main)](https://img.shields.io/github/actions/workflow/status/sensedeep/dynamodb-metrics/build.yml?branch=main)\n[![npm](https://img.shields.io/npm/v/dynamodb-metrics.svg)](https://www.npmjs.com/package/dynamodb-metrics)\n[![npm](https://img.shields.io/npm/l/dynamodb-metrics.svg)](https://www.npmjs.com/package/dynamodb-metrics)\n[![Coverage Status](https://coveralls.io/repos/github/sensedeep/dynamodb-metrics/badge.svg?branch=main)](https://coveralls.io/github/sensedeep/dynamodb-metrics?branch=main)\n\n\nDynamoDB Metrics calculates detailed DynamoDB metrics for single table design patterns.\n\nThe standard DynamoDB metrics provide basic table and index level metrics. However, when using single-table design patterns, a more detailed set of performance metrics are required.\n\nIf you've wondered:\n\n* Which customer tenant is causing the most load and consuming the most RCU/WCU.\n* Which app or function is causing the most load and consuming the most RCU/WCU.\n* Which single-table entity/model is most loaded and consuming RCU/WCU.\n* Which operations are being used the most.\n* Which queries are the most inefficient (items vs scanned).\n* Who is doing scans (app, function, model).\n\nDynamoDB metrics was created for those with single-table DynamoDB designs who need to understand how their application data entities are performing.\n\n## DynamoDB Metrics Features\n\n* Creates detailed CloudWatch metrics for Tables, Tenants, Indexes, Apps/Functions, Entities and DynamoDB operations\n* Emits metrics using [CloudWatch EMF](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html) for zero-latency metric creation.\n* Control which dimensions and additional properties are emitted in the EMF log data.\n* Supports AWS V2 and V3 SDKs.\n* Easy few line integration.\n* Very low CPU and memory impact.\n* Supported by the free [SenseDeep Developer Plan](https://www.sensedeep.com/) for graphical DynamoDB single-table monitoring.\n* No dependencies.\n* Optionally integrates with [SenseLogs](https://www.npmjs.com/settings/sensedeep/packages) for dynamic control of metrics.\n* Clean, readable small code base (\u003c400 lines).\n* Full TypeScript support.\n\n## Quick Tour\n\nInstall the library using npm or yarn.\n\n    npm i dynamodb-metrics\n\nImport the DynamoDB Metrics library. If you are not using ES modules or TypeScript, use `require` to import the library.\n\n```javascript\nimport Metrics from 'dynamodb-metrics'\n```\n\nThen create your `Metrics` instance and pass your DynamoDB client as a parameter.\n\n```javascript\nconst metrics = new Metrics({\n    client: client,\n    indexes: {primary: {hash: 'pk', sort: 'sk'}},\n    separator: '#',\n})\n```\n\nThe `client` should be a DynamoDB client instance. The `indexes` parameter describes the names of your primary and secondary keys. Metrics uses this key description to decode your single-table items.\n\nRead [Single Table Configuration](#single-table-configuration) below for options on how to tell Metrics about your key design.\n\nMetrics will flush metrics by default every 30 seconds or after 100 requests, but you can tailor these defaults via constructor parameters. You can also force out the metrics via `metrics.flush` at any time.\n\n```javascript\nmetrics.flush()\n```\n\n\n## Initializing the AWS SDK V2, V3 with/without DocumentClient\n\nIf using the AWS V2 SDK with the DocumentClient, create your DynamoDB client.\n\n```javascript\nimport DynamoDB from 'aws-sdk/clients/dynamodb'\nclient: new DynamoDB.DocumentClient({})\n\n// or if using the AWS V2 SDK with the low level API\n\nconst client = new DynamoDB({})\n```\n\nIf using the AWS V3 SDK, first create the low level client and then create the V3 DocumentClient instance.\n\n```javascript\nimport { DynamoDBClient } from '@aws-sdk/client-dynamodb'\nimport { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'\n\nconst low = new DynamoDBClient({})\nconst client = DynamoDBDocumentClient.from(low)\n```\n\n## Metrics\n\nDynamoDB Metrics creates the following metrics\n\n* read \u0026mdash; Read capacity units consumed\n* write \u0026mdash; Write capacity units consumed\n* latency \u0026mdash; Aggregated request latency in milliseconds\n* count \u0026mdash; Count of items returned\n* scanned \u0026mdash; Number of items scanned\n* requests \u0026mdash; Number of API requests issued\n\nDynamoDB Metrics will create these metrics for the following dimensions:\n\n* Table\n* Tenant\n* Source\n* Index\n* Model\n* Operation\n\nThe enabled dimensions by default are: Table, Source, Index, Model and Operation. You can vary the enabled dimensions via the `dimensions` constructor property.\n\nThe Table dimension is set to the table Name.\n\nThe Tenant dimension is defined via the `Metric` constructor `tenant` parameter. You can set this to any identifying string you like. It is typically set to your customer or tenant ID or name. If unset, it will not be used. Be careful using Tenant with high cardinality data sets. See [Under the Hood](#under-the-hood) for managing CloudWatch metric costs.\n\nThe Source dimension is defined via the `Metric` constructor `source` parameter. You can set this to any identifying string you like. It is typically set to your application or function name. If unset, it will default to the name of the executing Lambda function.\n\nThe Index dimension is set to `primary` for the primary index and to the name of a Global Secondary Index (GSI) is that is being used.\n\nThe Model is the single-table entity name. Read [DynamoDB Single Table Design](https://www.sensedeep.com/blog/posts/2021/dynamodb-singletable-design.html) for background on single table design patterns. The model name is determined based on the keys used or returned in the request. See below for Single Table Configuration.\n\nThe operation dimension is set to the DynamoDB operation: getItem, putItem etc.\n\nYou can tailor the set of dimensions via the `dimensions` constructor parameter.\n\n## Single Table Configuration\n\nDynamoDB Metrics needs to determine the single-table model/entity for each request so that it can attribute the request to the appropriate entity. And so, Metrics needs to be able to interpret your key attributes in requests and responses. To do this, when constructing the `Metrics` instance you describe your indexes and the hash/sort key separator you are using.\n\n## Model via Separators\n\nIf you construct your hash/sort keys with unique prefixes for your single-table entity models, then the separator approach is ideal. Simply set the separator property in the Metric constructor. By default this is set to '#'.\n\nFor example, if your hash key format was `MODEL_NAME:ID` then you would set the separator to ':'.\n\n```javascript\nconst metrics = new Metrics({\n    indexes,\n    separator: ':'\n})\n```\n\n## Model via Callback\n\nIf you are using a more complex scheme to encode your single-table entities, then set the `model` callback so you can determine the model name yourself. For example:\n\n```javascript\nconst metrics = new Metrics({\n    indexes,\n    model(operation, params, result) =\u003e {\n        //  Custom logic to return the model name. For example:\n        return Object.values(params.Item[hash])[0].split('#')[0]\n    }\n})\n```\n\n### SenseDeep\n\n[SenseDeep](https://www.sensedeep.com/) offers a free subscription for developers that monitors and graphs the DynamoDB metrics.\n\nHere is a screen shot:\n\n![SenseDeep Single Table Monitoring](https://www.sensedeep.com/images/sensedeep/table-single.png)\n\n\n### Metrics Class API\n\nThe Metrics class provides the public API.\n\n### Constructor\n\n```javascript\nnew Metrics(options)\n```\n\nThe Metrics constructor takes an options map parameter with the following properties.\n\n| Property | Type | Description |\n| -------- | :--: | ----------- |\n| chan | `string` | If using SenseLogs, this will define the SenseLogs channel to use for the output.|\n| client | `DynamoDB client` | Set to an AWS V2 or V3 DynamoDB DocumentClient instance.|\n| dimensions | `array` | Ordered array of dimensions to emit. Defaults to [Table, Tenant, Source, Index, Model, Operation].|\n| enable | `boolean` | Set to true to enable metrics. Defaults to true.|\n| env | `boolean` | Set to true to enable dynamic control via the LOG_FILTER environment variable. Defaults to false.|\n| indexes | `map` | Map of indexes supported by the table. The map keys are the names of the indexes. The values are a map of 'hash' and 'sort' attribute names. Must always contain a `primary` element.|\n| max | `number` | Maximum number of metric events to buffer before flushing to stdout and on to CloudWatch EMF. Defaults to 100.|\n| model | `function` | Set to a function to be invoked to determine the entity model name. Invoked as: `model(operation, params, result)`. Defaults to null.|\n| namespace | `string` | Namespace to use for the emitted metrics. Defaults to `SingleTable/Metrics.1`.|\n| period | `number` | Number of seconds to buffer metric events before flushing to stdout. Defaults to 30 seconds.|\n| properties | `map\\|function` | Set to a map of additional properties to be included in EMF log record. These are not metrics. Set to a function that will be invoked as `properties(operation, params, result)` and should return a map of properties. Defaults to null.|\n| queries | `boolean` | Set to true to enable per-query profile metrics. Defaults to true.|\n| separator | `string` | Separator used between entity/model names in the hash and sort keys. Defaults to '#'.|\n| senselogs | `instance` | SenseLogs instance to use to emit the metrics. This permits dynamic control of metrics.|\n| source | `string` | Set to an identifying string for the application or function calling DynamoDB. Defaults to the Lambda function name.|\n| tenant | `string` | Set to an identifying string for the customer or tenant. Defaults to null.|\n\n\u003c!--\n| hot | `boolean` | Set to true to enable hot-partition tracking. WARNING: this can lead to high CloudWatch costs. Defaults to false.|\n    hot: true,\n--\u003e\n\nFor example, every parameter in use:\n\n```javascript\nconst metrics = new Metrics({\n    client,\n    dimensions: ['Table', 'Source', 'Index', 'Model', 'Operations'],\n    chan: 'metrics',\n    enable: true,\n    env: true,\n    indexes: {\n        primary: { hash: 'pk', sort: 'sk' },\n        gs1: { hash: 'gs1pk', sort: 'gs1sk' },\n        gs2: { hash: 'gs2pk', sort: 'gs2sk' }\n    },\n    max: 99,\n    model: (operation, params, result) =\u003e {\n        //  Determine the entity model from the API params\n        return Object.values(params.Item['pk'])[0].split('#')[0]\n    },\n    namespace: 'Acme/Launches',\n    period: 15 * 1000,\n    properties: (operation, params, result) =\u003e {\n        //  Additional properties to add to the EMF record\n        return {color: 'red'}\n    },\n    queries: true,\n    separator: '#',\n    source: 'BigRocket',\n    tenant: 'Customer-42',\n})\n```\n\nMetrics can be dynamically controlled by the LOG_FILTER environment variable. If this environment variable contains the string `dbmetrics` and the `env` params is set to true, then Metrics will be enabled. If the `env` parameter is unset, LOG_FILTER will be ignored.\n\n## Profiling Queries\n\nYou can also profile queries and scans by setting the `queries` constructor parameter to true and passing a `profile` property to the relevant DynamoDB query or scan command. You can set the profile value to any identifying string to describe the query or scan.\n\nThese profile metrics are created under the Profile dimension.\n\nNote: the Profile dimension is separate and is not listed in the `dimensions` constructor parameter.\n\nFor example:\n\n```javascript\nlet items = await client.query({\n    TableName,\n    KeyConditionExpression: `pk = :pk`,\n    ExpressionAttributeValues: {':pk': 'User#42'},\n    profile: 'test-query',\n}).promise()\n\n// or V3\nlet items = await client.send(new QueryCommand({\n    TableName,\n    KeyConditionExpression: `pk = :pk`,\n    ExpressionAttributeValues: {':pk': 'User#42'},\n    profile: 'test-query',\n}))\n```\n\n## Under the Hood\n\nThe metric are emitted using the [CloudWatch EMF](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html) via the `metrics` method. This permits zero-latency creation of metrics without impacting the performance of you Lambdas.\n\nMetrics will only be emitted for dimension combinations that are active. If you have many application entities and indexes, you may end up with a large number of metrics. If your site uses all these dimensions actively, your CloudWatch Metric costs may be high. You will be charged by AWS CloudWatch for the total number of metrics that are active each hour at the rate of $0.30 cents per hour per metric.\n\nIf that is the case, you can minimize your cloud watch charges, by reducing the number of dimensions via the `dimensions` property. You could consider disabling the `source` or `operation` dimensions. Alternatively, you should consider using the LOG_FILTER environment variable or [SenseLogs](https://www.npmjs.com/package/senselogs) to dynamically control your metrics.\n\nDynamoDB Metrics are buffered and aggregated to minimize the load on your system. If a Lambda function is reclaimed by AWS Lambda, there may be a few metric requests that are not emitted before the function is reclaimed. This should be a very small percentage and should not significantly impact the quality of the metrics. You can control this buffering via the `max` and `period` parameters.\n\n### Methods\n\n#### flush()\n\nFlush any buffered metrics to stdout. By default, Metrics will flush buffered metrics every 30 seconds or after 100 requests. This parameters are controlled by the Metrics `period` and `max` constructor parameters.\n\n### References\n\n- [DynamoDB Metrics Sample](https://github.com/sensedeep/dynamodb-metrics/tree/main/samples/overview)\n- [SenseDeep Blog](https://www.sensedeep.com/blog/)\n- [SenseDeep Web Site](https://www.sensedeep.com/)\n- [SenseDeep Developer Studio](https://app.sensedeep.com/)\n\n### Participate\n\nAll feedback, discussion, contributions and bug reports are very welcome.\n\n* [discussions](https://github.com/sensedeep/dynamodb-metrics/discussions)\n* [issues](https://github.com/sensedeep/dynamodb-metrics/issues)\n\n### Contact\n\nYou can contact me (Michael O'Brien) on Twitter at: [@mobstream](https://twitter.com/mobstream), or [email](mob-pub-18@sensedeep.com) and ready my [Blog](https://www.sensedeep.com/blog).\n\n### SenseDeep\n\nThe best way to monitor DynamoDB is via the Serverless Developer Studio [SenseDeep](https://www.sensedeep.com/).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsensedeep%2Fdynamodb-metrics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsensedeep%2Fdynamodb-metrics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsensedeep%2Fdynamodb-metrics/lists"}