Skip to content
Merged
36 changes: 31 additions & 5 deletions packages/metrics/src/Metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,10 @@ class Metrics extends Utility implements MetricsInterface {
super();

this.dimensions = {};
this.setOptions(options);
this.setEnvConfig();
this.setConsole();
this.#logger = options.logger || this.console;
this.setOptions(options);
}

/**
Expand Down Expand Up @@ -824,13 +826,39 @@ class Metrics extends Utility implements MetricsInterface {
* @param dimensions - The dimensions to be added to the default dimensions object
*/
public setDefaultDimensions(dimensions: Dimensions | undefined): void {
if (!dimensions) return;

const cleanedDimensions: Dimensions = {};

for (const [key, value] of Object.entries(dimensions)) {
if (
isStringUndefinedNullEmpty(key) ||
isStringUndefinedNullEmpty(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.defaultDimensions, key)) {
this.#logger.warn(
`Dimension "${key}" has already been added. The previous value will be overwritten.`
);
}

cleanedDimensions[key] = value;
}

const targetDimensions = {
...this.defaultDimensions,
...dimensions,
...cleanedDimensions,
};
if (MAX_DIMENSION_COUNT <= Object.keys(targetDimensions).length) {

if (Object.keys(targetDimensions).length >= MAX_DIMENSION_COUNT) {
throw new Error('Max dimension count hit');
}

this.defaultDimensions = targetDimensions;
}

Expand Down Expand Up @@ -1058,8 +1086,6 @@ class Metrics extends Utility implements MetricsInterface {
functionName,
} = options;

this.setEnvConfig();
this.setConsole();
this.setCustomConfigService(customConfigService);
this.setDisabled();
this.setNamespace(namespace);
Expand Down
75 changes: 75 additions & 0 deletions packages/metrics/tests/unit/dimensions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,4 +552,79 @@ describe('Working with dimensions', () => {
})
);
});

it('warns when setDefaultDimensions overwrites existing dimensions', () => {
// Prepare
const metrics = new Metrics({
namespace: DEFAULT_NAMESPACE,
defaultDimensions: { environment: 'prod' },
});

// Act
metrics.setDefaultDimensions({ region: 'us-east-1' });
metrics.setDefaultDimensions({
environment: 'staging', // overwrites default dimension
});

// Assess
expect(console.warn).toHaveBeenCalledOnce();
expect(console.warn).toHaveBeenCalledWith(
'Dimension "environment" has already been added. The previous value will be overwritten.'
);
});

it('returns immediately if dimensions is undefined', () => {
// Prepare
const metrics = new Metrics({
singleMetric: true,
namespace: DEFAULT_NAMESPACE,
});
// Act
metrics.setDefaultDimensions(undefined);
metrics.addMetric('myMetric', MetricUnit.Count, 1);
// Assess: No warning, only default dimensions/service
expect(console.warn).not.toHaveBeenCalled();
expect(console.log).toHaveEmittedEMFWith(
expect.objectContaining({
service: 'hello-world',
})
);
});
it.each([
{ value: undefined, name: 'valid-name' },
{ value: null, name: 'valid-name' },
{ value: '', name: 'valid-name' },
{ value: 'valid-value', name: '' },
])(
'skips invalid default dimension values in setDefaultDimensions ($name)',
({ value, name }) => {
// Arrange
const metrics = new Metrics({
singleMetric: true,
namespace: DEFAULT_NAMESPACE,
});

// Act
metrics.setDefaultDimensions({
validDimension: 'valid',
[name as string]: value as string,
});

metrics.addMetric('test', MetricUnit.Count, 1);
metrics.publishStoredMetrics();

// Assert
expect(console.warn).toHaveBeenCalledWith(
`The dimension ${name} doesn't meet the requirements and won't be added. Ensure the dimension name and value are non empty strings`
);

expect(console.log).toHaveEmittedEMFWith(
expect.objectContaining({ validDimension: 'valid' })
);

expect(console.log).toHaveEmittedEMFWith(
expect.not.objectContaining({ [name]: value })
);
}
);
});