diff --git a/packages/commons/src/config/EnvironmentVariablesService.ts b/packages/commons/src/config/EnvironmentVariablesService.ts index 2f2d0c7165..63bc9f6561 100644 --- a/packages/commons/src/config/EnvironmentVariablesService.ts +++ b/packages/commons/src/config/EnvironmentVariablesService.ts @@ -53,11 +53,23 @@ class EnvironmentVariablesService extends ConfigService { * @returns {string} */ public getXrayTraceId(): string | undefined { - const xRayTraceId = this.get(this.xRayTraceIdVariable); + const xRayTraceData = this.getXrayTraceData(); - if (xRayTraceId === '') return undefined; + return xRayTraceData?.Root; + } - return xRayTraceId.split(';')[0].replace('Root=', ''); + /** + * It returns true if the Sampled flag is set in the _X_AMZN_TRACE_ID environment variable. + * + * The AWS X-Ray Trace data available in the environment variable has this format: + * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, + * + * @returns {boolean} + */ + public getXrayTraceSampled(): boolean { + const xRayTraceData = this.getXrayTraceData(); + + return xRayTraceData?.Sampled === '1'; } /** @@ -72,6 +84,28 @@ class EnvironmentVariablesService extends ConfigService { return truthyValues.includes(value.toLowerCase()); } + /** + * It parses the key/value data present in the _X_AMZN_TRACE_ID environment variable + * and returns it as an object when available. + */ + private getXrayTraceData(): Record | undefined { + const xRayTraceEnv = this.get(this.xRayTraceIdVariable); + + if (xRayTraceEnv === '') return undefined; + + if (!xRayTraceEnv.includes('=')) return { Root: xRayTraceEnv }; + + const xRayTraceData: Record = {}; + + xRayTraceEnv.split(';').forEach((field) => { + + const [ key, value ] = field.split('='); + + xRayTraceData[key] = value; + }); + + return xRayTraceData; + } } export { diff --git a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts index c9b56b22ef..06e458282b 100644 --- a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts @@ -110,6 +110,48 @@ describe('Class: EnvironmentVariablesService', () => { }); + describe('Method: getXrayTraceSampled', () => { + + test('It returns true if the Sampled flag is set in the _X_AMZN_TRACE_ID environment variable', () => { + + // Prepare + process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1'; + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getXrayTraceSampled(); + + // Assess + expect(value).toEqual(true); + }); + + test('It returns false if the Sampled flag is not set in the _X_AMZN_TRACE_ID environment variable', () => { + + // Prepare + process.env._X_AMZN_TRACE_ID = 'Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047'; + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getXrayTraceSampled(); + + // Assess + expect(value).toEqual(false); + }); + + it('It returns false when no _X_AMZN_TRACE_ID environment variable is present', () => { + + // Prepare + delete process.env._X_AMZN_TRACE_ID; + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getXrayTraceSampled(); + + // Assess + expect(value).toEqual(false); + }); + }); + describe('Method: isValueTrue', () => { const valuesToTest: Array> = [ diff --git a/packages/tracer/src/Tracer.ts b/packages/tracer/src/Tracer.ts index b4fe242395..6b8cff3009 100644 --- a/packages/tracer/src/Tracer.ts +++ b/packages/tracer/src/Tracer.ts @@ -508,6 +508,8 @@ class Tracer extends Utility implements TracerInterface { * @returns string - The root X-Ray trace id. */ public getRootXrayTraceId(): string | undefined { + if (!this.isTracingEnabled()) return undefined; + return this.envVarsService.getXrayTraceId(); } @@ -547,6 +549,21 @@ class Tracer extends Utility implements TracerInterface { return segment; } + /** + * Get the current value of the AWS X-Ray Sampled flag. + * + * Utility method that returns the current AWS X-Ray Sampled flag. + * + * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-traces + * + * @returns boolean - `true` if the trace is sampled, `false` if tracing is disabled or the trace is not sampled. + */ + public isTraceSampled(): boolean { + if (!this.isTracingEnabled()) return false; + + return this.envVarsService.getXrayTraceSampled(); + } + /** * Get the current value of the `tracingEnabled` property. * diff --git a/packages/tracer/src/TracerInterface.ts b/packages/tracer/src/TracerInterface.ts index 344e44177e..30a78d86cc 100644 --- a/packages/tracer/src/TracerInterface.ts +++ b/packages/tracer/src/TracerInterface.ts @@ -13,6 +13,7 @@ interface TracerInterface { captureMethod(options?: CaptureMethodOptions): MethodDecorator getSegment(): Segment | Subsegment | undefined getRootXrayTraceId(): string | undefined + isTraceSampled(): boolean isTracingEnabled(): boolean putAnnotation: (key: string, value: string | number | boolean) => void putMetadata: (key: string, value: unknown, namespace?: string | undefined) => void diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts index 997258bd5a..28ae7ffa4d 100644 --- a/packages/tracer/tests/unit/Tracer.test.ts +++ b/packages/tracer/tests/unit/Tracer.test.ts @@ -277,6 +277,48 @@ describe('Class: Tracer', () => { }); + test('when called and Tracer is disabled, it returns undefined', () => { + + // Prepare + const tracer: Tracer = new Tracer({ enabled: false }); + + // Act + const xRayTraceId = tracer.getRootXrayTraceId(); + + // Assess + expect(xRayTraceId).toBe(undefined); + }); + + }); + + describe('Method: isTraceSampled', () => { + + test('when called, it returns true if the Sampled flag is set', () => { + + // Prepare + const tracer: Tracer = new Tracer(); + + // Act + const xRayTraceSampled = tracer.isTraceSampled(); + + // Assess + expect(xRayTraceSampled).toBe(false); + + }); + + test('when called and Trace is disabled, it returns false', () => { + + // Prepare + const tracer: Tracer = new Tracer({ enabled: false }); + + // Act + const xRayTraceSampled = tracer.isTraceSampled(); + + // Assess + expect(xRayTraceSampled).toBe(false); + + }); + }); describe('Method: getSegment', () => {