Skip to content

Commit 412cfb3

Browse files
Merge pull request #63 from vishnuvisnu/support-custom-timestamp
Added logic to support custom timestamp
2 parents aa5df0e + 3d5bc9f commit 412cfb3

File tree

7 files changed

+81
-12
lines changed

7 files changed

+81
-12
lines changed

src/index.spec.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ import http from "http";
22
import nock from "nock";
33

44
import { Context, Handler } from "aws-lambda";
5-
import { datadog, getTraceHeaders, sendDistributionMetric, TraceHeaders } from "./index";
5+
import {
6+
datadog,
7+
getTraceHeaders,
8+
sendDistributionMetric,
9+
TraceHeaders,
10+
sendDistributionMetricWithDate,
11+
} from "./index";
612
import { incrementErrorsMetric, incrementInvocationsMetric } from "./metrics/enhanced-metrics";
713
import { MetricsListener } from "./metrics/listener";
814
import { LogLevel, setLogLevel } from "./utils";
@@ -163,6 +169,28 @@ describe("datadog", () => {
163169
expect(nock.isDone()).toBeTruthy();
164170
});
165171

172+
it("reads site keys from the environment using custom timestamp", async () => {
173+
const site = "datadoghq.com";
174+
const siteEnvVar = "DD_SITE";
175+
const apiKey = "12345";
176+
process.env[siteEnvVar] = site;
177+
178+
nock("https://api.datadoghq.com")
179+
.post(`/api/v1/distribution_points?api_key=${apiKey}`, (request: any) => request.series[0].metric === "my-dist")
180+
.reply(200, {});
181+
182+
const wrapped = datadog(
183+
async () => {
184+
sendDistributionMetricWithDate("my-dist", 100, new Date(), "first-tag", "second-tag");
185+
return "";
186+
},
187+
{ apiKey, forceWrap: true },
188+
);
189+
await wrapped({}, {} as any, () => {});
190+
191+
expect(nock.isDone()).toBeTruthy();
192+
});
193+
166194
it("makes the current trace headers available", async () => {
167195
let traceHeaders: Partial<TraceHeaders> = {};
168196
const event = {

src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,23 @@ export function datadog<TEvent, TResult>(
117117
return wrappedFunc;
118118
}
119119

120+
/**
121+
* Sends a Distribution metric asynchronously to the Datadog API.
122+
* @param name The name of the metric to send.
123+
* @param value The value of the metric
124+
* @param metricTime The timesamp associated with this metric data point.
125+
* @param tags The tags associated with the metric. Should be of the format "tag:value".
126+
*/
127+
export function sendDistributionMetricWithDate(name: string, value: number, metricTime: Date, ...tags: string[]) {
128+
tags = [...tags, getRuntimeTag()];
129+
130+
if (currentMetricsListener !== undefined) {
131+
currentMetricsListener.sendDistributionMetricWithDate(name, value, metricTime, ...tags);
132+
} else {
133+
logError("handler not initialized");
134+
}
135+
}
136+
120137
/**
121138
* Sends a Distribution metric asynchronously to the Datadog API.
122139
* @param name The name of the metric to send.

src/metrics/enhanced-metrics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function getEnhancedMetricTags(context: Context): string[] {
6565
*/
6666
function incrementEnhancedMetric(metricName: string, context: Context) {
6767
// Always write enhanced metrics to standard out
68-
writeMetricToStdout(`${ENHANCED_LAMBDA_METRICS_NAMESPACE}.${metricName}`, 1, getEnhancedMetricTags(context));
68+
writeMetricToStdout(`${ENHANCED_LAMBDA_METRICS_NAMESPACE}.${metricName}`, 1, new Date(), getEnhancedMetricTags(context));
6969
}
7070

7171
export function incrementInvocationsMetric(context: Context): void {

src/metrics/listener.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,25 @@ describe("MetricsListener", () => {
100100

101101
expect(spy).toHaveBeenCalledWith(`{"e":1487076708,"m":"my-metric","t":["tag:a","tag:b"],"v":10}\n`);
102102
});
103+
104+
it("logs metrics when logForwarding is enabled with custom timestamp", async () => {
105+
const spy = jest.spyOn(process.stdout, "write");
106+
// jest.spyOn(Date, "now").mockImplementation(() => 1487076708000);
107+
const kms = new MockKMS("kms-api-key-decrypted");
108+
const listener = new MetricsListener(kms as any, {
109+
apiKey: "api-key",
110+
apiKeyKMS: "kms-api-key-encrypted",
111+
enhancedMetrics: false,
112+
logForwarding: true,
113+
shouldRetryMetrics: false,
114+
siteURL,
115+
});
116+
// jest.useFakeTimers();
117+
118+
listener.onStartInvocation({});
119+
listener.sendDistributionMetricWithDate("my-metric", 10, new Date(1584983836 * 1000), "tag:a", "tag:b");
120+
await listener.onCompleteInvocation();
121+
122+
expect(spy).toHaveBeenCalledWith(`{"e":1584983836,"m":"my-metric","t":["tag:a","tag:b"],"v":10}\n`);
123+
});
103124
});

src/metrics/listener.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,12 @@ export class MetricsListener {
8383
this.currentProcessor = undefined;
8484
}
8585

86-
public sendDistributionMetric(name: string, value: number, ...tags: string[]) {
86+
public sendDistributionMetricWithDate(name: string, value: number, metricTime: Date, ...tags: string[]) {
8787
if (this.config.logForwarding) {
88-
writeMetricToStdout(name, value, tags);
88+
writeMetricToStdout(name, value, metricTime, tags);
8989
return;
9090
}
91-
const dist = new Distribution(name, [{ timestamp: new Date(), value }], ...tags);
91+
const dist = new Distribution(name, [{ timestamp: metricTime, value }], ...tags);
9292

9393
if (this.currentProcessor !== undefined) {
9494
this.currentProcessor.then((processor) => {
@@ -99,6 +99,10 @@ export class MetricsListener {
9999
}
100100
}
101101

102+
public sendDistributionMetric(name: string, value: number, ...tags: string[]) {
103+
this.sendDistributionMetricWithDate(name, value, new Date(Date.now()), ...tags);
104+
}
105+
102106
private async createProcessor(config: MetricsConfig, apiKey: Promise<string>) {
103107
const key = await apiKey;
104108
const url = `https://api.${config.siteURL}`;

src/metrics/metric-log.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { buildMetricLog } from "./metric-log";
22

33
describe("buildMetricLog", () => {
4-
jest.spyOn(Date, "now").mockImplementation(() => 1487076708123);
54
it("handles empty tag list", () => {
6-
expect(buildMetricLog("my.test.metric", 1337, [])).toStrictEqual(
5+
expect(buildMetricLog("my.test.metric", 1337, new Date(1487076708123), [])).toStrictEqual(
76
'{"e":1487076708.123,"m":"my.test.metric","t":[],"v":1337}\n',
87
);
98
});
109
it("writes timestamp in Unix seconds", () => {
11-
expect(buildMetricLog("my.test.metric", 1337, ["region:us", "account:dev", "team:serverless"])).toStrictEqual(
10+
expect(buildMetricLog("my.test.metric", 1337, new Date(1487076708123), ["region:us", "account:dev", "team:serverless"])).toStrictEqual(
1211
'{"e":1487076708.123,"m":"my.test.metric","t":["region:us","account:dev","team:serverless"],"v":1337}\n',
1312
);
1413
});

src/metrics/metric-log.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Builds the string representation of the metric that will be written to logs
2-
export function buildMetricLog(name: string, value: number, tags: string[]) {
2+
export function buildMetricLog(name: string, value: number, metricTime: Date, tags: string[]) {
33
return `${JSON.stringify({
44
// Date.now() returns Unix time in milliseconds, we convert to seconds for DD API submission
5-
e: Date.now() / 1000,
5+
e: metricTime.getTime() / 1000,
66
m: name,
77
t: tags,
88
v: value,
@@ -15,8 +15,8 @@ export function buildMetricLog(name: string, value: number, tags: string[]) {
1515
* @param value Metric datapoint's value
1616
* @param tags Tags to apply to the metric
1717
*/
18-
export function writeMetricToStdout(name: string, value: number, tags: string[]) {
18+
export function writeMetricToStdout(name: string, value: number, metricTime: Date, tags: string[]) {
1919
// We use process.stdout.write, because console.log will prepend metadata to the start
2020
// of the log that log forwarder doesn't know how to read.
21-
process.stdout.write(buildMetricLog(name, value, tags));
21+
process.stdout.write(buildMetricLog(name, value, metricTime, tags));
2222
}

0 commit comments

Comments
 (0)