@emartech/aggregate-metric-logger 2.2.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # aggregate-metric-logger
2
- aggregate metrics in memory and log them every minute
2
+ Aggregate metrics in memory and log them every minute
3
3
 
4
4
  ## Setup
5
5
 
@@ -11,11 +11,11 @@ To change the default `aggregate-metric-logger` namespace used for logs, set
11
11
 
12
12
  ## Setup to send metrics to GCP monitoring
13
13
 
14
- (currently only the `metricLogger.count`, `metricLogger.measureGcp` method sends metrics to GCP)
14
+ (currently only the `metricLogger.count` and `metricLogger.measureGcp` methods send metrics to GCP)
15
15
 
16
16
  Set `METRIC_LOGGER_ENABLED` just us for LaaS based metrics.
17
17
 
18
- Set `GCP_PROJECT_ID` and `GCP_CREDENTIALS` env variables. `HOSTNAME` env variable also has to be
18
+ Set `MONITORING_PROJECT_ID` and `GCP_CREDENTIALS` env variables. `HOSTNAME` env variable also has to be
19
19
  set, but for a pod running on kubernetes this should already be set.
20
20
 
21
21
 
package/env/.env CHANGED
@@ -1,3 +1,3 @@
1
1
  HOSTNAME=local-machine
2
- GCP_PROJECT_ID=dummy-project
2
+ MONITORING_PROJECT_ID=dummy-project
3
3
  METRIC_LOGGER_ENABLED=true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emartech/aggregate-metric-logger",
3
- "version": "2.2.0",
3
+ "version": "3.0.1",
4
4
  "description": "aggregate metrics in memory for logging",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -5,8 +5,9 @@ const {
5
5
  stubCreateTimeSeries,
6
6
  getPublishedMetrics,
7
7
  flushPromises,
8
- stubCreateTimeSeriesWithCallbackDuringPublish
8
+ resolvablePromise
9
9
  } = require('../../test-helpers/test-helpers');
10
+ const { MetricServiceClient } = require('@google-cloud/monitoring');
10
11
 
11
12
  beforeEach(() => {
12
13
  jest.useFakeTimers();
@@ -123,9 +124,9 @@ describe('gcpMetricCollector', () => {
123
124
  mean: 7,
124
125
  sumOfSquaredDeviation: 0,
125
126
  bucketOptions: {
126
- exponentialBuckets: { numFiniteBuckets: 20, growthFactor: 2, scale: 1 }
127
+ exponentialBuckets: { numFiniteBuckets: 25, growthFactor: 2, scale: 1 }
127
128
  },
128
- bucketCounts: [0, 0, 0, 1, ...Array(18).fill(0)]
129
+ bucketCounts: [0, 0, 0, 1, ...Array(23).fill(0)]
129
130
  }
130
131
  },
131
132
  metricKind: 'GAUGE',
@@ -151,7 +152,7 @@ describe('gcpMetricCollector', () => {
151
152
  count: 2,
152
153
  mean: 9.5,
153
154
  sumOfSquaredDeviation: 12.5,
154
- bucketCounts: [0, 0, 0, 1, 1, ...Array(17).fill(0)]
155
+ bucketCounts: [0, 0, 0, 1, 1, ...Array(22).fill(0)]
155
156
  }
156
157
  }
157
158
  }
@@ -182,7 +183,7 @@ describe('gcpMetricCollector', () => {
182
183
  count: 1,
183
184
  mean: 5,
184
185
  sumOfSquaredDeviation: 0,
185
- bucketCounts: [0, 0, 0, 1, ...Array(18).fill(0)]
186
+ bucketCounts: [0, 0, 0, 1, ...Array(23).fill(0)]
186
187
  }
187
188
  }
188
189
  }
@@ -190,16 +191,27 @@ describe('gcpMetricCollector', () => {
190
191
  ]);
191
192
  });
192
193
 
193
- it('should not remove metrics data during publishing', async () => {
194
- stubCreateTimeSeriesWithCallbackDuringPublish(() => gcpMetricCollector.measure('jolo', { kacsa: 'A' }, 5));
194
+ it('should not lose metrics data logged during publishing', async () => {
195
+ const firstCreateTimeSeriesPromise = resolvablePromise();
196
+ const secondCreateTimeSeriesPromise = resolvablePromise();
197
+ jest.spyOn(MetricServiceClient.prototype, 'createTimeSeries')
198
+ .mockReturnValueOnce(firstCreateTimeSeriesPromise)
199
+ .mockReturnValueOnce(secondCreateTimeSeriesPromise);
195
200
 
196
201
  gcpMetricCollector.measure('jolo', { kacsa: 'A' }, 7);
197
202
 
198
203
  jest.advanceTimersByTime(60 * 1000 + 1);
204
+
205
+ gcpMetricCollector.measure('jolo', { kacsa: 'A' }, 5);
206
+ firstCreateTimeSeriesPromise.resolve();
207
+
199
208
  await flushPromises();
209
+
200
210
  jest.advanceTimersByTime(60 * 1000 + 1);
201
211
  await flushPromises();
202
212
 
213
+ secondCreateTimeSeriesPromise.resolve();
214
+
203
215
  expect(getPublishedMetrics()).toMatchObject([
204
216
  [{}],
205
217
  [
@@ -209,7 +221,7 @@ describe('gcpMetricCollector', () => {
209
221
  count: 1,
210
222
  mean: 5,
211
223
  sumOfSquaredDeviation: 0,
212
- bucketCounts: [0, 0, 0, 1, ...Array(18).fill(0)]
224
+ bucketCounts: [0, 0, 0, 1, ...Array(23).fill(0)]
213
225
  }
214
226
  }
215
227
  }
@@ -10,7 +10,7 @@ exports.buildTimeSeries = metrics =>
10
10
  },
11
11
  resource: {
12
12
  type: 'global',
13
- labels: { project_id: process.env.GCP_PROJECT_ID }
13
+ labels: { project_id: process.env.MONITORING_PROJECT_ID }
14
14
  },
15
15
  points: [
16
16
  {
@@ -2,14 +2,20 @@
2
2
 
3
3
  const logger = require('@emartech/json-logger')('aggregate-metric-logger');
4
4
  const monitoring = require('@google-cloud/monitoring');
5
+ const { chunk } = require('lodash');
6
+
7
+ const MAX_TIME_SERIES_COUNT = 200;
5
8
 
6
9
  exports.publishTimeSeries = async timeSeries => {
7
10
  try {
8
11
  const client = getMonitoringClient();
9
- await client.createTimeSeries({
10
- name: client.projectPath(process.env.GCP_PROJECT_ID),
11
- timeSeries
12
- });
12
+
13
+ for (const timeSeriesChunk of chunk(timeSeries, MAX_TIME_SERIES_COUNT)) {
14
+ await client.createTimeSeries({
15
+ name: client.projectPath(process.env.MONITORING_PROJECT_ID),
16
+ timeSeries: timeSeriesChunk
17
+ });
18
+ }
13
19
  } catch (error) {
14
20
  logger.warnFromError('pushing-metrics-failed', error, { do_not_alert: true });
15
21
  }
@@ -19,7 +25,7 @@ let monitoringClientInstance = null;
19
25
  const getMonitoringClient = () => {
20
26
  if (!monitoringClientInstance) {
21
27
  monitoringClientInstance = new monitoring.MetricServiceClient({
22
- projectId: process.env.GCP_PROJECT_ID
28
+ projectId: process.env.MONITORING_PROJECT_ID
23
29
  });
24
30
  }
25
31
  return monitoringClientInstance;
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { MetricServiceClient } = require('@google-cloud/monitoring');
4
+ const { range } = require('lodash');
4
5
  const { publishTimeSeries } = require('./gcp-time-series-publisher');
5
6
  const { stubCreateTimeSeries } = require('../../test-helpers/test-helpers');
6
7
 
@@ -8,10 +9,10 @@ jest.mock('@google-cloud/monitoring');
8
9
 
9
10
  describe('gcpMetricPublisher', () => {
10
11
  describe('publishTimeSeries', () => {
11
- it('should push a metrics to gcp in the correct format', async () => {
12
+ it('should push metrics to gcp to the correct project', async () => {
12
13
  stubCreateTimeSeries();
13
14
  process.env.HOSTNAME = 'test-host';
14
- process.env.GCP_PROJECT_ID = 'test-project';
15
+ process.env.MONITORING_PROJECT_ID = 'test-project';
15
16
  const timeSeries = [1, 2];
16
17
 
17
18
  await publishTimeSeries(timeSeries);
@@ -21,5 +22,23 @@ describe('gcpMetricPublisher', () => {
21
22
  timeSeries
22
23
  });
23
24
  });
25
+
26
+ it('should push metrics to gcp in batches', async () => {
27
+ stubCreateTimeSeries();
28
+ process.env.HOSTNAME = 'test-host';
29
+ process.env.MONITORING_PROJECT_ID = 'test-project';
30
+
31
+ await publishTimeSeries(range(1, 300));
32
+
33
+ expect(MetricServiceClient.prototype.createTimeSeries).toHaveBeenCalledWith({
34
+ name: 'projects/test-project',
35
+ timeSeries: range(1, 201)
36
+ });
37
+ expect(MetricServiceClient.prototype.createTimeSeries).toHaveBeenCalledWith({
38
+ name: 'projects/test-project',
39
+ timeSeries: range(201, 300)
40
+ });
41
+ });
42
+
24
43
  });
25
44
  });
@@ -2,7 +2,7 @@
2
2
 
3
3
  // exponential bucket rules: https://cloud.google.com/monitoring/api/ref_v3/rest/v3/TypedValue#exponential
4
4
 
5
- exports.createDistribution = ({ scale = 1, growthFactor = 2, numFiniteBuckets = 20 } = {}) => {
5
+ exports.createDistribution = ({ scale = 1, growthFactor = 2, numFiniteBuckets = 25 } = {}) => {
6
6
  const bucketUpperBounds = Array(numFiniteBuckets + 2)
7
7
  .fill(null)
8
8
  .map((value, index, array) =>
@@ -3,16 +3,6 @@
3
3
  const { MetricServiceClient } = require('@google-cloud/monitoring');
4
4
  const { omit, pick } = require('lodash');
5
5
 
6
- exports.stubCreateTimeSeriesWithCallbackDuringPublish = (createMetric) => {
7
- jest.spyOn(MetricServiceClient.prototype, 'createTimeSeries').mockImplementation(
8
- () =>
9
- new Promise(resolve => {
10
- createMetric();
11
- resolve();
12
- })
13
- );
14
- };
15
-
16
6
  exports.stubCreateTimeSeries = () => {
17
7
  jest.spyOn(MetricServiceClient.prototype, 'createTimeSeries').mockResolvedValue();
18
8
  };
@@ -35,3 +25,18 @@ exports.getPublishedMetrics = () => {
35
25
  };
36
26
 
37
27
  exports.flushPromises = async () => new Promise(jest.requireActual('timers').setImmediate);
28
+
29
+ exports.resolvablePromise = () => {
30
+ let resolvePromise;
31
+ let rejectPromise;
32
+
33
+ const promise = new Promise((resolve, reject) => {
34
+ resolvePromise = resolve;
35
+ rejectPromise = reject;
36
+ });
37
+
38
+ promise.resolve = resolvePromise;
39
+ promise.reject = rejectPromise;
40
+
41
+ return promise;
42
+ };