diff --git a/src/metrics/listener.spec.ts b/src/metrics/listener.spec.ts index f1c4bd06..244b0197 100644 --- a/src/metrics/listener.spec.ts +++ b/src/metrics/listener.spec.ts @@ -138,6 +138,51 @@ describe("MetricsListener", () => { expect(distributionMock).toHaveBeenCalledWith("my-metric", 10, undefined, ["tag:a", "tag:b"]); }); + it("only sends metrics with timestamps to the API when the extension is enabled", async () => { + const flushScope = nock(EXTENSION_URL).post("/lambda/flush", JSON.stringify({})).reply(200); + mock({ + "/opt/extensions/datadog-agent": Buffer.from([0]), + }); + nock("https://api.example.com").post("/api/v1/distribution_points?api_key=api-key").reply(200, {}); + + const distributionMock = jest.fn(); + (StatsDClient as any).mockImplementation(() => { + return { + distribution: distributionMock, + close: (callback: any) => callback(undefined), + }; + }); + + jest.spyOn(Date, "now").mockImplementation(() => 1487076708000); + + const metricTimeOneMinuteAgo = new Date(Date.now() - 60000); + const kms = new MockKMS("kms-api-key-decrypted"); + const listener = new MetricsListener(kms as any, { + apiKey: "api-key", + apiKeyKMS: "", + enhancedMetrics: false, + logForwarding: false, + shouldRetryMetrics: false, + localTesting: true, + siteURL, + }); + + await listener.onStartInvocation({}); + listener.sendDistributionMetricWithDate( + "my-metric-with-a-timestamp", + 10, + metricTimeOneMinuteAgo, + false, + "tag:a", + "tag:b", + ); + listener.sendDistributionMetric("my-metric-without-a-timestamp", 10, false, "tag:a", "tag:b"); + await listener.onCompleteInvocation(); + expect(flushScope.isDone()).toBeTruthy(); + expect(nock.isDone()).toBeTruthy(); + expect(distributionMock).toHaveBeenCalledWith("my-metric-without-a-timestamp", 10, undefined, ["tag:a", "tag:b"]); + }); + it("logs metrics when logForwarding is enabled with custom timestamp", async () => { const spy = jest.spyOn(process.stdout, "write"); // jest.spyOn(Date, "now").mockImplementation(() => 1487076708000); diff --git a/src/metrics/listener.ts b/src/metrics/listener.ts index 9175503a..c8e4100c 100644 --- a/src/metrics/listener.ts +++ b/src/metrics/listener.ts @@ -131,13 +131,20 @@ export class MetricsListener { public sendDistributionMetricWithDate( name: string, value: number, - metricTime: Date, + metricTime: Date, // TODO: Next breaking change to update to optional or 'Date | undefined'? forceAsync: boolean, ...tags: string[] ) { if (this.isExtensionRunning) { - this.statsDClient?.distribution(name, value, undefined, tags); - return; + const isMetricTimeValid = Date.parse(metricTime.toString()) > 0; + if (isMetricTimeValid) { + // Only create the processor to submit metrics to the API when a user provides a valid timestamp as + // Dogstatsd does not support timestamps for distributions. + this.currentProcessor = this.createProcessor(this.config, this.apiKey); + } else { + this.statsDClient?.distribution(name, value, undefined, tags); + return; + } } if (this.config.logForwarding || forceAsync) { writeMetricToStdout(name, value, metricTime, tags); @@ -162,11 +169,13 @@ export class MetricsListener { } public sendDistributionMetric(name: string, value: number, forceAsync: boolean, ...tags: string[]) { - this.sendDistributionMetricWithDate(name, value, new Date(Date.now()), forceAsync, ...tags); + // The Extension doesn't support distribution metrics with timestamps. Use sendDistributionMetricWithDate instead. + const metricTime = this.isExtensionRunning ? new Date(0) : new Date(Date.now()); + this.sendDistributionMetricWithDate(name, value, metricTime, forceAsync, ...tags); } private async createProcessor(config: MetricsConfig, apiKey: Promise) { - if (!this.isExtensionRunning && !this.config.logForwarding) { + if (!this.config.logForwarding) { const { APIClient } = require("./api"); const { Processor } = require("./processor");