diff --git a/packages/commons/src/config/EnvironmentVariablesService.ts b/packages/commons/src/config/EnvironmentVariablesService.ts index f75ac127d0..d9d45d8f88 100644 --- a/packages/commons/src/config/EnvironmentVariablesService.ts +++ b/packages/commons/src/config/EnvironmentVariablesService.ts @@ -93,6 +93,17 @@ class EnvironmentVariablesService implements ConfigServiceInterface { return truthyValues.includes(value.toLowerCase()); } + /** + * Helper function to determine if a value is considered falsy. + * + * @param value The value to check for falsiness. + */ + public isValueFalse(value: string): boolean { + const falsyValues: string[] = ['0', 'n', 'no', 'f', 'false', 'off']; + + return falsyValues.includes(value.toLowerCase()); + } + /** * Get the AWS X-Ray Trace data from the environment variable. * diff --git a/packages/commons/tests/unit/EnvironmentVariablesService.test.ts b/packages/commons/tests/unit/EnvironmentVariablesService.test.ts index f941dbf6cb..fcf9974527 100644 --- a/packages/commons/tests/unit/EnvironmentVariablesService.test.ts +++ b/packages/commons/tests/unit/EnvironmentVariablesService.test.ts @@ -159,6 +159,37 @@ describe('Class: EnvironmentVariablesService', () => { ); }); + describe('Method: isValueFalse', () => { + const valuesToTest: Array> = [ + ['0', true], + ['n', true], + ['no', true], + ['f', true], + ['FALSE', true], + ['off', true], + ['1', false], + ['y', false], + ['yes', false], + ['t', false], + ['TRUE', false], + ['on', false], + ['', false], + ['somethingsilly', false], + ]; + + it.each(valuesToTest)( + 'takes string "%s" and returns %s', + (input, output) => { + // Prepare + const service = new EnvironmentVariablesService(); + // Act + const value = service.isValueFalse(input as string); + // Assess + expect(value).toBe(output); + } + ); + }); + describe('Method: isDevMode', () => { it('returns true if the environment variable POWERTOOLS_DEV is "true"', () => { // Prepare diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index b1a56e7f0b..cd3b77f22d 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -201,6 +201,11 @@ class Metrics extends Utility implements MetricsInterface { */ private storedMetrics: StoredMetrics = {}; + /** + * Whether to disable metrics + */ + private disabled = false; + /** * Custom timestamp for the metrics */ @@ -472,6 +477,13 @@ class Metrics extends Utility implements MetricsInterface { return Object.keys(this.storedMetrics).length > 0; } + /** + * Whether metrics are disabled. + */ + protected isDisabled(): boolean { + return this.disabled; + } + /** * A class method decorator to automatically log metrics after the method returns or throws an error. * @@ -579,8 +591,12 @@ class Metrics extends Utility implements MetricsInterface { 'If application metrics should never be empty, consider using `throwOnEmptyMetrics`' ); } - const emfOutput = this.serializeMetrics(); - hasMetrics && this.console.log(JSON.stringify(emfOutput)); + + if (!this.disabled) { + const emfOutput = this.serializeMetrics(); + hasMetrics && this.console.log(JSON.stringify(emfOutput)); + } + this.clearMetrics(); this.clearDimensions(); this.clearMetadata(); @@ -913,6 +929,15 @@ class Metrics extends Utility implements MetricsInterface { this.getEnvVarsService().getNamespace()) as string; } + /** + * Set the disbaled flag based on the environment variables `POWERTOOLS_METRICS_DISABLED` and `POWERTOOLS_DEV`. + * + * The `POWERTOOLS_METRICS_DISABLED` environment variable takes precedence over `POWERTOOLS_DEV`. + */ + private setDisabled(): void { + this.disabled = this.getEnvVarsService().getMetricsDisabled(); + } + /** * Set the options to be used by the Metrics instance. * @@ -932,6 +957,7 @@ class Metrics extends Utility implements MetricsInterface { this.setEnvVarsService(); this.setConsole(); this.setCustomConfigService(customConfigService); + this.setDisabled(); this.setNamespace(namespace); this.setService(serviceName); this.setDefaultDimensions(defaultDimensions); diff --git a/packages/metrics/src/config/EnvironmentVariablesService.ts b/packages/metrics/src/config/EnvironmentVariablesService.ts index 5f277e3010..a611308649 100644 --- a/packages/metrics/src/config/EnvironmentVariablesService.ts +++ b/packages/metrics/src/config/EnvironmentVariablesService.ts @@ -13,12 +13,29 @@ class EnvironmentVariablesService { private namespaceVariable = 'POWERTOOLS_METRICS_NAMESPACE'; + private readonly disabledVariable = 'POWERTOOLS_METRICS_DISABLED'; + /** * Get the value of the `POWERTOOLS_METRICS_NAMESPACE` environment variable. */ public getNamespace(): string { return this.get(this.namespaceVariable); } + + /** + * Get the value of the `POWERTOOLS_METRICS_DISABLED` or `POWERTOOLS_DEV` environment variables. + * + * The `POWERTOOLS_METRICS_DISABLED` environment variable takes precedence over `POWERTOOLS_DEV`. + */ + public getMetricsDisabled(): boolean { + const value = this.get(this.disabledVariable); + + if (this.isValueFalse(value)) return false; + if (this.isValueTrue(value)) return true; + if (this.isDevMode()) return true; + + return false; + } } export { EnvironmentVariablesService }; diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index 3097042aa7..298a40561e 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -1430,6 +1430,29 @@ describe('Class: Metrics', () => { expect(consoleLogSpy).toBeCalledWith(JSON.stringify(mockData)); }); + test('it should not log anything if metrics are disabled', () => { + // Prepare + process.env.POWERTOOLS_METRICS_DISABLED = 'true'; + const customLogger = { + log: jest.fn(), + warn: jest.fn(), + debug: jest.fn(), + error: jest.fn(), + info: jest.fn(), + }; + const metrics: Metrics = new Metrics({ + namespace: TEST_NAMESPACE, + logger: customLogger, + }); + const consoleLogSpy = jest.spyOn(customLogger, 'log'); + + // Act + metrics.publishStoredMetrics(); + + // Assess + expect(consoleLogSpy).toHaveBeenCalledTimes(0); + }); + test('it should call clearMetrics function', () => { // Prepare const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); @@ -2355,6 +2378,54 @@ describe('Class: Metrics', () => { }); }); + describe('Method: isDisabled', () => { + it('should be enabled by default', () => { + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + + // Act & Assess + // biome-ignore lint/complexity/useLiteralKeys: accessing protected method + expect(metrics['isDisabled']()).toBe(false); + }); + + it('should be disabled if POWERTOOLS_DEV is set to true', () => { + process.env.POWERTOOLS_DEV = 'true'; + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + + // Act & Assess + // biome-ignore lint/complexity/useLiteralKeys: accessing protected method + expect(metrics['isDisabled']()).toBe(true); + }); + + it('should be disabled if POWERTOOLS_METRICS_DISABLED is set to true', () => { + // Prepare + process.env.POWERTOOLS_METRICS_DISABLED = 'true'; + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + + // Act & Assess + // biome-ignore lint/complexity/useLiteralKeys: accessing protected method + expect(metrics['isDisabled']()).toBe(true); + }); + + it('should be enabled if POWERTOOLS_METRICS_DISABLED is set to false', () => { + process.env.POWERTOOLS_METRICS_DISABLED = 'false'; + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + + // Act & Assess + // biome-ignore lint/complexity/useLiteralKeys: accessing protected method + expect(metrics['isDisabled']()).toBe(false); + }); + + it('should be enabled if POWERTOOLS_DEV is set to true and POWERTOOLS_METRICS_DISABLED is set to false', () => { + process.env.POWERTOOLS_DEV = 'true'; + process.env.POWERTOOLS_METRICS_DISABLED = 'false'; + const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); + + // Act & Assess + // biome-ignore lint/complexity/useLiteralKeys: accessing protected method + expect(metrics['isDisabled']()).toBe(false); + }); + }); + describe('Method: setTimestamp', () => { const testCases = [ {