From 57c29db9b92119a2603b0efcf56d73f492eace1c Mon Sep 17 00:00:00 2001 From: Shane Brunson Date: Tue, 13 Feb 2024 09:41:31 -0600 Subject: [PATCH] feat: add new metric helpers for counters and gauges (#80) --- README.md | 44 ++++++++++++++++++++++++++++-- src/logger.js | 31 ++++++++++++++++++++- src/logger.test.js | 68 ++++++++++++++++++++++++++++++++++++++++++++++ src/types.js | 24 ++++++++++++++++ 4 files changed, 164 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a6ee671..380a8e7 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,38 @@ For example: Call this to attach general tracking information to the current page. This is useful if the data is not associated with a specific event, and will be sent to the server the next time the logs are flushed. -### `$logger.metric(, );` +### `$logger.metricCounter(, );` -Queues a metric +Queues a counter metric, helper wrapping `logger.metric` + +``` +logger.metricCounter({ + namespace: "pp.team.product.feature", + event: "button_click", + dimensions: { + type: "paypal" + } +}) +``` + +### `$logger.metricGauge(, );` + +Queues a gauge metric, helper wrapping `logger.metric` + +``` +logger.metricGauge({ + namespace: "pp.team.product.feature", + event: "request_latency", + value: 100, + dimensions: { + method: "GET" + } +}) +``` + +### Deprecated - `$logger.metric(, );` + +Queues a metric. We suggest using the `metricCount` or `metricGauge` interface for better type safety and clearer intention in your code. ## Advanced @@ -60,6 +89,17 @@ $logger.addMetaBuilder(function () { }); ``` +### `$logger.addMetricDimensionBuilder();` + +Attach a method which is called and will attach values to **each metric's dimensions** whenever the logs are flushed + +```javascript +$logger.addMetricDimensionBuilder(() => ({ + token_used: true, + type: "user_id_token", +})); +``` + ### `$logger.addPayloadBuilder();` Attach a method which is called and will attach values to **each individual log's payload** whenever the logs are flushed diff --git a/src/logger.js b/src/logger.js index 76b5a59..ad35cc2 100644 --- a/src/logger.js +++ b/src/logger.js @@ -18,7 +18,12 @@ import { import { LOG_LEVEL, PROTOCOL } from "./constants"; import { extendIfDefined } from "./util"; import { type Transport, getHTTPTransport } from "./http"; -import type { MetricPayload, Payload } from "./types"; +import type { + MetricPayload, + Payload, + MetricPayloadCounter, + MetricPayloadGauge, +} from "./types"; type LoggerOptions = {| url?: string, @@ -50,6 +55,8 @@ export type LoggerType = {| track: Track, metric: LogMetric, + metricCounter: (payload: MetricPayloadCounter) => LoggerType, + metricGauge: (payload: MetricPayloadGauge) => LoggerType, flush: () => ZalgoPromise, immediateFlush: () => ZalgoPromise, @@ -298,6 +305,26 @@ export function Logger({ return logger; // eslint-disable-line no-use-before-define } + function metricCounter(metricPayload: MetricPayloadCounter): LoggerType { + return metric({ + metricNamespace: metricPayload.namespace, + metricEventName: metricPayload.event, + metricValue: metricPayload.value ?? 1, + metricType: "counter", + dimensions: metricPayload.dimensions, + }); + } + + function metricGauge(metricPayload: MetricPayloadGauge): LoggerType { + return metric({ + metricNamespace: metricPayload.namespace, + metricEventName: metricPayload.event, + metricValue: metricPayload.value, + metricType: "gauge", + dimensions: metricPayload.dimensions, + }); + } + function setTransport(newTransport: Transport): LoggerType { transport = newTransport; return logger; // eslint-disable-line no-use-before-define @@ -356,6 +383,8 @@ export function Logger({ error, track, metric, + metricCounter, + metricGauge, flush, immediateFlush, addPayloadBuilder, diff --git a/src/logger.test.js b/src/logger.test.js index 2684c9f..ddfd20b 100644 --- a/src/logger.test.js +++ b/src/logger.test.js @@ -265,6 +265,74 @@ describe("beaver logger provides flushing methods that send events to the server }); }); +describe("metricCounter", () => { + test("should add metrics of counter type", () => { + const testLogger = initLogger(); + + testLogger.metricCounter({ + namespace: "namespace", + event: "no_value", + dimensions: { + one: "1", + }, + }); + + testLogger.metricCounter({ + namespace: "namespace", + event: "value", + value: 3, + dimensions: { + one: "1", + }, + }); + + expect(getLoggerBuffer(testLogger).metrics[0]).toEqual({ + metricNamespace: "namespace", + metricEventName: "no_value", + metricValue: 1, + metricType: "counter", + dimensions: { + one: "1", + }, + }); + + expect(getLoggerBuffer(testLogger).metrics[1]).toEqual({ + metricNamespace: "namespace", + metricEventName: "value", + metricValue: 3, + metricType: "counter", + dimensions: { + one: "1", + }, + }); + }); +}); + +describe("metricGauge", () => { + test("should add metrics of gauge type", () => { + const testLogger = initLogger(); + + testLogger.metricGauge({ + namespace: "namespace", + event: "load", + value: 100, + dimensions: { + one: "1", + }, + }); + + expect(getLoggerBuffer(testLogger).metrics[0]).toEqual({ + metricNamespace: "namespace", + metricEventName: "load", + metricValue: 100, + metricType: "gauge", + dimensions: { + one: "1", + }, + }); + }); +}); + describe("addMetricDimensionBuilder", () => { test("should add dimensions from builder", () => { const testLogger = initLogger(); diff --git a/src/types.js b/src/types.js index 30844ec..6de10dc 100644 --- a/src/types.js +++ b/src/types.js @@ -14,3 +14,27 @@ export type MetricPayload = {| */ dimensions?: { [string]: mixed }, |}; + +export type MetricPayloadCounter = {| + namespace: string, // the name of the metric that's used for charting / finding in signalFx + event: string, // assigned to event_name dimension in signalFx + value?: number, // in most cases this will be 1 if we want to count 1 instance of an event happening. + /** + * For proper usage & best practices guidance around dimensions please read: --------------------> + * - https://engineering.paypalcorp.com/confluence/pages/viewpage.action?pageId=981633893 + * - https://engineering.paypalcorp.com/confluence/display/Checkout/Checkout+Observability+Overview + */ + dimensions?: { [string]: mixed }, +|}; + +export type MetricPayloadGauge = {| + namespace: string, // the name of the metric that's used for charting / finding in signalFx + event: string, // assigned to event_name dimension in signalFx + value: number, // in most cases this will be 1 if we want to count 1 instance of an event happening. + /** + * For proper usage & best practices guidance around dimensions please read: --------------------> + * - https://engineering.paypalcorp.com/confluence/pages/viewpage.action?pageId=981633893 + * - https://engineering.paypalcorp.com/confluence/display/Checkout/Checkout+Observability+Overview + */ + dimensions?: { [string]: mixed }, +|};