Skip to content
Merged
12 changes: 12 additions & 0 deletions docs/features/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ You can create metrics using the `addMetric` method, and you can create dimensio
--8<-- "examples/snippets/metrics/customDimensions.ts"
```

### Creating dimension sets

You can create separate dimension sets for your metrics using the `addDimensions` method. This allows you to group metrics by different dimension combinations.

When you call `addDimensions()`, it creates a new dimension set rather than adding to the existing dimensions. This is useful when you want to track the same metric across different dimension combinations.

=== "handler.ts"

```typescript hl_lines="9 12-15 18-21"
--8<-- "examples/snippets/metrics/dimensionSets.ts"
```

!!! tip "Autocomplete Metric Units"
Use the `MetricUnit` enum to easily find a supported metric unit by CloudWatch. Alternatively, you can pass the value as a string if you already know them e.g. "Count".

Expand Down
30 changes: 30 additions & 0 deletions examples/snippets/metrics/dimensionSets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MetricUnit, Metrics } from '@aws-lambda-powertools/metrics';

const metrics = new Metrics({
namespace: 'serverlessAirline',
serviceName: 'orders',
});

export const handler = async (
_event: unknown,
_context: unknown
): Promise<void> => {
// Add a single dimension
metrics.addDimension('environment', 'prod');

// Add a new dimension set
metrics.addDimensions({
dimension1: '1',
dimension2: '2',
});

// Add another dimension set
metrics.addDimensions({
region: 'us-east-1',
category: 'books',
});

// Add metrics
metrics.addMetric('successfulBooking', MetricUnit.Count, 1);
metrics.publishStoredMetrics();
};
76 changes: 67 additions & 9 deletions packages/metrics/src/Metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ class Metrics extends Utility implements MetricsInterface {
*/
private dimensions: Dimensions = {};

/**
* Additional dimension sets for the current metrics context
* @default []
*/
private dimensionSets: Dimensions[] = [];

/**
* Service for accessing environment variables
*/
Expand Down Expand Up @@ -267,9 +273,35 @@ class Metrics extends Utility implements MetricsInterface {
* @param dimensions - An object with key-value pairs of dimensions
*/
public addDimensions(dimensions: Dimensions): void {
for (const [name, value] of Object.entries(dimensions)) {
this.addDimension(name, value);
const newDimensionSet: Dimensions = {};
for (const [key, value] of Object.entries(dimensions)) {
if (!value) {
this.#logger.warn(
`The dimension ${key} doesn't meet the requirements and won't be added. Ensure the dimension name and value are non empty strings`
);
continue;
}
if (
Object.hasOwn(this.dimensions, key) ||
Object.hasOwn(this.defaultDimensions, key) ||
Object.hasOwn(newDimensionSet, key)
) {
this.#logger.warn(
`Dimension "${key}" has already been added. The previous value will be overwritten.`
);
}
newDimensionSet[key] = value;
}

const currentCount = this.getCurrentDimensionsCount();
const newSetCount = Object.keys(newDimensionSet).length;
if (currentCount + newSetCount >= MAX_DIMENSION_COUNT) {
throw new RangeError(
`The number of metric dimensions must be lower than ${MAX_DIMENSION_COUNT}`
);
}

this.dimensionSets.push(newDimensionSet);
}

/**
Expand Down Expand Up @@ -447,6 +479,7 @@ class Metrics extends Utility implements MetricsInterface {
*/
public clearDimensions(): void {
this.dimensions = {};
this.dimensionSets = [];
}

/**
Expand Down Expand Up @@ -692,26 +725,46 @@ class Metrics extends Utility implements MetricsInterface {
{}
);

const dimensionNames = [
...new Set([
const dimensionNames = [];

const allDimensionKeys = new Set([
...Object.keys(this.defaultDimensions),
...Object.keys(this.dimensions),
]);

if (Object.keys(this.dimensions).length > 0) {
dimensionNames.push([...allDimensionKeys]);
}

for (const dimensionSet of this.dimensionSets) {
const dimensionSetKeys = new Set([
...Object.keys(this.defaultDimensions),
...Object.keys(this.dimensions),
]),
];
...Object.keys(dimensionSet),
]);
dimensionNames.push([...dimensionSetKeys]);
}

if (
dimensionNames.length === 0 &&
Object.keys(this.defaultDimensions).length > 0
) {
dimensionNames.push([...Object.keys(this.defaultDimensions)]);
}

return {
_aws: {
Timestamp: this.#timestamp ?? new Date().getTime(),
CloudWatchMetrics: [
{
Namespace: this.namespace || DEFAULT_NAMESPACE,
Dimensions: [dimensionNames],
Dimensions: dimensionNames as [string[]],
Metrics: metricDefinitions,
},
],
},
...this.defaultDimensions,
...this.dimensions,
...this.dimensionSets.reduce((acc, dims) => Object.assign(acc, dims), {}),
...metricValues,
...this.metadata,
};
Expand Down Expand Up @@ -824,9 +877,14 @@ class Metrics extends Utility implements MetricsInterface {
* Gets the current number of dimensions count.
*/
private getCurrentDimensionsCount(): number {
const dimensionSetsCount = this.dimensionSets.reduce(
(total, dimensionSet) => total + Object.keys(dimensionSet).length,
0
);
return (
Object.keys(this.dimensions).length +
Object.keys(this.defaultDimensions).length
Object.keys(this.defaultDimensions).length +
dimensionSetsCount
);
}

Expand Down
Loading
Loading