Skip to content

Commit

Permalink
DEV: rebuild
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael O'Brien committed May 30, 2024
1 parent ad7cab5 commit f11ed06
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 18 deletions.
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@ CustomMetrics is a NodeJS library to emit and query custom metrics for AWS apps.

## Background

AWS CloudWatch offers metrics to monitor specific aspects of your apps that are not covered by the default AWS infrastructure metrics.
AWS CloudWatch offers metrics to monitor AWS services and your apps. Unfortunately, custom AWS CloudWatch metrics can be very expensive. If updated or queried regularly, each each custom AWS CloudWatch metric may cost up to $3.60 per metric per year with additional costs for querying. If you have many metrics or high dimensionality on your metrics, this can lead to a very large CloudWatch Metrics bill. This high cost prevents using metrics liberally in your app at scale.

Unfortunately, AWS CloudWatch metrics can be very expensive. If updated or queried regularly, each each custom AWS CloudWatch metric may cost up to $3.60 per metric per year with additional costs for querying. If you have many metrics or high dimensionality on your metrics, this can lead to a very large CloudWatch Metrics bill.
> **CustomMetrics** provides low cost metrics that are much cheaper and faster than standard AWS CloudWatch metrics.
> **CustomMetrics** provides cost effective metrics that are much cheaper and faster than standard CloudWatch metrics.
CustomMetrics achieves dramatic savings by supporting only **latest** period metrics. i.e. last day, last month, last hour, last 5 minutes etc. This enables each metric to be saved, stored and queried with minimal cost.
CustomMetrics achieves dramatic savings by supporting **latest** period metrics. i.e. last day, last month, last hour, last 5 minutes etc. This enables each metric to be saved, stored and queried with minimal cost.

CustomMetrics stores metrics to a DynamoDB table of your choosing that can coexist with existing application data.

Expand All @@ -34,6 +32,7 @@ CustomMetrics stores metrics to a DynamoDB table of your choosing that can coexi
- Computes statistics for: average, min, max, count and sum.
- Computes P value statistics with configurable P value resolution.
- Supports a default metric intervals of: last 5 mins, hour, day, week, month and year.
- Supports querying from arbitrary start dates.
- Configurable custom intervals for higher or different metric intervals.
- Fast and flexible metric query API.
- Query API can return data points or aggregate metric data to a single statistic.
Expand Down Expand Up @@ -265,9 +264,11 @@ const metrics = new CustomMetrics({
})
```

CustomMetric spans define how each metric is processed and aged. The spans are an ordered list of metric interval periods. For example, the default spans calculate statistics for the periods: 5 minutes, 1 hour, 1 day, 1 week, 1 month and 1 year.
CustomMetric spans define how each metric is processed and aged. The spans are an ordered list of metric interval periods.

The default spans store statistics for the periods: 5 minutes, 1 hour, 1 day, 1 week, 1 month and 1 year.

Via the `spans` constructor option you can provide an alternate list of spans for higher, lower or more granular resolution.
Via the `spans` CustomMetrics constructor you can provide an alternate list of spans for higher, lower or more granular resolution.

The default CustomMetrics spans are:

Expand Down Expand Up @@ -298,6 +299,26 @@ const metrics = new CustomMetrics({
})
```

#### Upgrading Metric Spans

If you want to change your spans in the future, you can upgrade your metric data with new spans. To do this, you can use the `upgrade` method. This will apportion data points from the old spans to the new spans. If the number of samples in a span is increased, the data points will be apportioned across the new span.

WARNING: if you define new spans via the constructor and do not call upgrade, the results may be unpredictable. If you do upgrade your metrics, ensure you supply the new span definition to all your constructor calls going forward. Otherwise, the default spans will be used.

```typescript
const metrics = new CustomMetrics({
table: 'mytable',
// New spans
spans: [
{period: 24 * 60 * 60, samples: 24},
{period: 7 * 24 * 60 * 60, samples: 28},
{period: 28 * 24 * 60 * 60, samples: 28},
{period: 365 * 24 * 60 * 60, samples: 48},
]
})
await metrics.upgrade()
```

## Logging

If the `log` constructor option is set to true, CustomMetrics will log errors to the console. If set to "verbose", CustomMetrics will also trace metric database accesses to the console.
Expand Down
1 change: 1 addition & 0 deletions dist/cjs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export type MetricQueryOptions = {
id?: string;
log?: boolean;
owner?: string;
start?: number;
timestamp?: number;
};
type BufferElt = {
Expand Down
25 changes: 21 additions & 4 deletions dist/cjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,31 @@ class CustomMetrics {
if (!metric) {
return { dimensions, id: options.id, metric: metricName, namespace, period, points: [], owner, samples: 0 };
}
let span = metric.spans.find((s) => period <= s.period);
if (!span) {
span = metric.spans[metric.spans.length - 1];
period = span.period;
let span;
if (options.start) {
span = metric.spans.find((s) => (s.end - s.period) <= options.start / 1000);
if (!span) {
span = metric.spans[metric.spans.length - 1];
period = span.period;
}
}
else {
span = metric.spans.find((s) => period <= s.period);
if (!span) {
span = metric.spans[metric.spans.length - 1];
period = span.period;
}
}
this.addValue(metric, timestamp, { count: 0, sum: 0 }, 0, period);
let result;
if (metric && span) {
if (options.start) {
let interval = span.period / span.samples;
let end = span.points.length - Math.ceil((span.end - (options.start / 1000 + period)) / interval);
let front = end - Math.round(period / interval);
span.end -= (span.points.length - end) * interval;
span.points = span.points.slice(front, end);
}
if (options.accumulate) {
result = this.accumulateMetric(metric, span, statistic, owner, timestamp);
}
Expand Down
1 change: 1 addition & 0 deletions dist/mjs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export type MetricQueryOptions = {
id?: string;
log?: boolean;
owner?: string;
start?: number;
timestamp?: number;
};
type BufferElt = {
Expand Down
25 changes: 21 additions & 4 deletions dist/mjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,14 +251,31 @@ export class CustomMetrics {
if (!metric) {
return { dimensions, id: options.id, metric: metricName, namespace, period, points: [], owner, samples: 0 };
}
let span = metric.spans.find((s) => period <= s.period);
if (!span) {
span = metric.spans[metric.spans.length - 1];
period = span.period;
let span;
if (options.start) {
span = metric.spans.find((s) => (s.end - s.period) <= options.start / 1000);
if (!span) {
span = metric.spans[metric.spans.length - 1];
period = span.period;
}
}
else {
span = metric.spans.find((s) => period <= s.period);
if (!span) {
span = metric.spans[metric.spans.length - 1];
period = span.period;
}
}
this.addValue(metric, timestamp, { count: 0, sum: 0 }, 0, period);
let result;
if (metric && span) {
if (options.start) {
let interval = span.period / span.samples;
let end = span.points.length - Math.ceil((span.end - (options.start / 1000 + period)) / interval);
let front = end - Math.round(period / interval);
span.end -= (span.points.length - end) * interval;
span.points = span.points.slice(front, end);
}
if (options.accumulate) {
result = this.accumulateMetric(metric, span, statistic, owner, timestamp);
}
Expand Down
6 changes: 3 additions & 3 deletions test/utils/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Setup -- setup for the test run
*/
import {DynamoDBClient} from '@aws-sdk/client-dynamodb'
import {CreateTableCommand, DescribeTableCommand} from '@aws-sdk/client-dynamodb'
import DynamoDbLocal from 'dynamo-db-local'
import {CreateTableCommand, CreateTableCommandInput, DescribeTableCommand} from '@aws-sdk/client-dynamodb'
import * as DynamoDbLocal from 'dynamo-db-local'
import waitPort from 'wait-port'

const PORT = parseInt(process.env.PORT || '4765')
Expand Down Expand Up @@ -41,7 +41,7 @@ module.exports = async () => {
}

async function createTable(client, table) {
let def = {
let def: CreateTableCommandInput = {
AttributeDefinitions: [
{AttributeName: 'pk', AttributeType: 'S'},
{AttributeName: 'sk', AttributeType: 'S'},
Expand Down

0 comments on commit f11ed06

Please sign in to comment.