Skip to content

Commit 1b853d1

Browse files
author
Alan Churley
committed
Adding some more info in the readme
1 parent dd2b56d commit 1b853d1

9 files changed

+354
-11
lines changed

Diff for: packages/metrics/README.md

+264-4
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
npm run test
88

99
npm run example:hello-world
10+
npm run example:constructor-options
1011
npm run example:manual-flushing
12+
npm run example:programatic-access
1113
npm run example:dimensions
1214
npm run example:default-dimensions
15+
npm run example:default-dimensions-constructor
1316
npm run example:empty-metrics
1417
npm run example:single-metric
1518
npm run example:cold-start
@@ -117,16 +120,15 @@ const metrics = new Metrics();
117120

118121
class Lambda implements LambdaInterface {
119122

120-
@metrics.logMetrics({ captureColdStartMetric: true }))
123+
@metrics.logMetrics({ captureColdStartMetric: true })
121124
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
122-
metrics.addDimension('OuterDimension', 'true');
123125
metrics.addMetric('test-metric', MetricUnits.Count, 10);
124126
}
125127

126128
}
127129

128130
```
129-
> Please note, we do not emit a 0 value for the ColdStart metric, for cost reasons
131+
130132
<details>
131133
<summary>Click to expand and see the logs outputs</summary>
132134

@@ -180,9 +182,267 @@ class Lambda implements LambdaInterface {
180182

181183
```
182184
</details>
185+
186+
> Please note, we do not emit a 0 value for the ColdStart metric, for cost reasons
187+
183188
If it's a cold start invocation, this feature will:
184189

185190
- Create a separate EMF blob solely containing a metric named ColdStart
186191
- Add function_name and service dimensions
187192

188-
This has the advantage of keeping cold start metric separate from your application metrics, where you might have unrelated dimensions.
193+
This has the advantage of keeping cold start metric separate from your application metrics, where you might have unrelated dimensions.
194+
195+
### Creating Metrics
196+
197+
You can create metrics using addMetric, and you can create dimensions for all your aggregate metrics using addDimension method.
198+
199+
```typescript
200+
201+
import { Metrics, MetricUnits } from '../src';
202+
203+
process.env.POWERTOOLS_METRICS_NAMESPACE = 'hello-world';
204+
process.env.POWERTOOLS_SERVICE_NAME = 'hello-world-service';
205+
206+
// Instantiate the Logger with default configuration
207+
const metrics = new Metrics();
208+
209+
class Lambda implements LambdaInterface {
210+
211+
@metrics.logMetrics({ captureColdStartMetric: true })
212+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
213+
metrics.addDimension('OuterDimension', 'true'); //Optional - add custom dimensions
214+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
215+
}
216+
217+
}
218+
219+
```
220+
<details>
221+
<summary>Click to expand and see the logs outputs</summary>
222+
223+
```bash
224+
225+
{
226+
"_aws":{
227+
"Timestamp":1625587915572,
228+
"CloudWatchMetrics":
229+
[
230+
{
231+
"Namespace":"hello-world",
232+
"Dimensions":[[
233+
"service",
234+
"OuterDimension"
235+
]],
236+
"Metrics":
237+
[
238+
{
239+
"Name":"test-metric",
240+
"Unit":"Count"
241+
}
242+
]
243+
}
244+
]
245+
},
246+
"OuterDimension": "true",
247+
"service":"hello-world-service",
248+
"test-metric": 10
249+
}
250+
251+
```
252+
</details>
253+
254+
> ### Autocomplete Metic Units
255+
> MetricUnits will facilitate finding a supported metric unit by CloudWatch. Alternatively, you can pass the value as a string if you already know them e.g. "Count".
256+
257+
> ### Metrics Overflow
258+
> CloudWatch EMF supports a max of 100 metrics per batch. Metrics utility will flush all metrics when adding the 100th metric. Subsequent metrics, e.g. 101th, will be aggregated into a new EMF object, for your convenience.
259+
260+
>### ! Do not create metrics or dimensions outside the handler
261+
> Metrics or dimensions added in the global scope will only be added during cold start. Disregard if you that's the intended behaviour.
262+
263+
### Adding default dimensions
264+
You can use either setDefaultDimensions method or by passing a defaultDimensions object to either the decorator or to the constructor
265+
266+
If you'd like to remove them at some point, you can use clearDefaultDimensions method.
267+
```typescript
268+
import { Metrics, MetricUnits } from '../src';
269+
270+
const metrics = new Metrics();
271+
metrics.setDefaultDimensions({ 'application': 'hello-world' });
272+
class Lambda implements LambdaInterface {
273+
@metrics.logMetrics()
274+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
275+
metrics.addMetric('new-test-metric', MetricUnits.Count, 5);
276+
}
277+
278+
}
279+
```
280+
281+
With decorators:
282+
```typescript
283+
import { Metrics, MetricUnits } from '../src';
284+
285+
const metrics = new Metrics();
286+
287+
class Lambda implements LambdaInterface {
288+
@metrics.logMetrics({ defaultDimensions:{ 'application': 'hello-world' } })
289+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
290+
metrics.addMetric('new-test-metric', MetricUnits.Count, 5);
291+
}
292+
293+
}
294+
```
295+
<details>
296+
<summary>Click to expand and see the logs outputs</summary>
297+
298+
```bash
299+
300+
{
301+
"_aws":{
302+
"Timestamp":1625587915572,
303+
"CloudWatchMetrics":
304+
[
305+
{
306+
"Namespace":"hello-world",
307+
"Dimensions":[[
308+
"application"
309+
]],
310+
"Metrics":
311+
[
312+
{
313+
"Name":"new-test-metric",
314+
"Unit":"Count"
315+
}
316+
]
317+
}
318+
]
319+
},
320+
"application":"hello-world",
321+
"new-test-metric": 5
322+
}
323+
324+
```
325+
</details>
326+
327+
### Flushing Metrics
328+
329+
As you finish adding all your metrics, you need to serialize and flush them to standard output. You can do that automatically with the logMetrics decorator.
330+
331+
This decorator also serializes, and flushes all your metrics. During metrics validation, if no metrics are provided then a warning will be logged, but no exception will be raised.
332+
333+
```typescript
334+
class Lambda implements LambdaInterface {
335+
336+
@metrics.logMetrics()
337+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
338+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
339+
340+
}
341+
342+
}
343+
```
344+
345+
If you need to log the metrics from within your code or if you do not wish to use the decorator, you can do this by calling the purgeStoredMetrics method.
346+
347+
This will serialize, and flush all your metrics.
348+
```typescript
349+
const metrics = new Metrics();
350+
351+
const lambdaHandler: Handler = async () => {
352+
353+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
354+
const metricsObject = metrics.serializeMetrics();
355+
metrics.purgeStoredMetrics();
356+
console.log(JSON.stringify(metricsObject));
357+
358+
return {
359+
foo: 'bar'
360+
};
361+
362+
};
363+
```
364+
365+
366+
If you just need to clear the metrics manually you can do this by calling the clearMetrics method
367+
```typescript
368+
class Lambda implements LambdaInterface {
369+
370+
@metrics.logMetrics()
371+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
372+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
373+
metrics.clearMetrics();
374+
375+
}
376+
377+
}
378+
```
379+
380+
#### Getting programmatic access to stored metrics
381+
If you need to get programmatic access to the current stored metrics you can do this by calling the serializeMetrics method.
382+
383+
This will not clear any metrics that are currently stored, if you need to do this, you can use the clearMetrics method as described above.
384+
```typescript
385+
class Lambda implements LambdaInterface {
386+
387+
@metrics.logMetrics()
388+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
389+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
390+
const metricsObject = metrics.clearMetrics();
391+
console.log(JSON.stringify(metricsObject));
392+
}
393+
394+
}
395+
```
396+
### Raising SchemaValidationError on empty metrics
397+
398+
If you want to ensure that at least one metric is emitted, you can pass raiseOnEmptyMetrics to the logMetrics decorator:
399+
400+
```typescript
401+
class Lambda implements LambdaInterface {
402+
403+
@metrics.logMetrics({raiseOnEmptyMetrics: true})
404+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
405+
// This will throw an error unless at least one metric is added
406+
}
407+
408+
}
409+
```
410+
411+
### Adding metadata
412+
You can add high-cardinality data as part of your Metrics log with addMetadata method. This is useful when you want to search highly contextual information along with your metrics in your logs.
413+
>### Info
414+
> This will not be available during metrics visualization - Use dimensions for this purpose
415+
```typescript
416+
class Lambda implements LambdaInterface {
417+
418+
@metrics.logMetrics()
419+
metrics.addMetadata('metadata_name', 'metadata_value')
420+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
421+
//Your Logic
422+
}
423+
424+
}
425+
```
426+
427+
### Single metric with a different dimension
428+
429+
CloudWatch EMF uses the same dimensions across all your metrics. Use the ```singleMetric``` method if you have a metric that should have different dimensions.
430+
431+
```typescript
432+
class Lambda implements LambdaInterface {
433+
434+
@metrics.logMetrics()
435+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
436+
const singleMetric = metrics.singleMetric();
437+
metrics.addDimension('OuterDimension', 'true');
438+
singleMetric.addDimension('InnerDimension', 'true');
439+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
440+
singleMetric.addMetric('single-metric', MetricUnits.Percent, 50);
441+
}
442+
443+
}
444+
```
445+
>### Info
446+
> Generally, this would be an edge case since you pay for unique metric. Keep the following formula in mind:
447+
>
448+
> **unique metric = (metric_name + dimension_name + dimension_value)**

Diff for: packages/metrics/examples/constructor-options.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as dummyEvent from '../../../tests/resources/events/custom/hello-world.json';
2+
import { context as dummyContext } from '../../../tests/resources/contexts/hello-world';
3+
import { Metrics, MetricUnits } from '../src';
4+
import { LambdaInterface } from "./utils/lambda";
5+
import { Callback, Context } from "aws-lambda/handler";
6+
7+
const metrics = new Metrics({
8+
namespace: 'hello-world-constructor',
9+
service: 'hello-world-service-constructor'
10+
});
11+
12+
class Lambda implements LambdaInterface {
13+
14+
@metrics.logMetrics()
15+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
16+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
17+
18+
}
19+
20+
}
21+
new Lambda().handler(dummyEvent, dummyContext, () => console.log('Lambda invoked!'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { populateEnvironmentVariables } from '../tests/helpers';
2+
3+
// Populate runtime
4+
populateEnvironmentVariables();
5+
// Additional runtime variables
6+
process.env.POWERTOOLS_METRICS_NAMESPACE = 'hello-world-constructor';
7+
process.env.POWERTOOLS_SERVICE_NAME = 'hello-world-service-constructor';
8+
9+
import * as dummyEvent from '../../../tests/resources/events/custom/hello-world.json';
10+
import { context as dummyContext } from '../../../tests/resources/contexts/hello-world';
11+
import { LambdaInterface } from './utils/lambda/LambdaInterface';
12+
import { Callback, Context } from 'aws-lambda/handler';
13+
import { Metrics, MetricUnits } from '../src';
14+
15+
const metrics = new Metrics({ defaultDimensions:{ 'application': 'hello-world' } });
16+
17+
class Lambda implements LambdaInterface {
18+
19+
@metrics.logMetrics()
20+
public handler<TEvent, TResult>(_event: TEvent, _context: Context, _callback: Callback<TResult>): void | Promise<TResult> {
21+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
22+
23+
}
24+
25+
}
26+
27+
new Lambda().handler(dummyEvent, dummyContext, () => console.log('Lambda invoked!'));

Diff for: packages/metrics/examples/manual-flushing.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ const metrics = new Metrics();
1515
const lambdaHandler: Handler = async () => {
1616

1717
metrics.addMetric('test-metric', MetricUnits.Count, 10);
18-
const metricsObject = metrics.serializeMetrics();
19-
metrics.clearMetrics();
20-
console.log(JSON.stringify(metricsObject));
18+
metrics.purgeStoredMetrics();
19+
//Metrics will be logged and cleared
2120

2221
return {
2322
foo: 'bar'

Diff for: packages/metrics/examples/programatic-access.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { populateEnvironmentVariables } from '../tests/helpers';
2+
3+
// Populate runtime
4+
populateEnvironmentVariables();
5+
// Additional runtime variables
6+
process.env.POWERTOOLS_METRICS_NAMESPACE = 'hello-world';
7+
8+
import * as dummyEvent from '../../../tests/resources/events/custom/hello-world.json';
9+
import { context as dummyContext } from '../../../tests/resources/contexts/hello-world';
10+
import { Handler } from 'aws-lambda';
11+
import { Metrics, MetricUnits } from '../src';
12+
13+
const metrics = new Metrics();
14+
15+
const lambdaHandler: Handler = async () => {
16+
17+
metrics.addMetric('test-metric', MetricUnits.Count, 10);
18+
const metricsObject = metrics.serializeMetrics();
19+
metrics.clearMetrics();
20+
console.log(JSON.stringify(metricsObject));
21+
22+
return {
23+
foo: 'bar'
24+
};
25+
26+
};
27+
28+
lambdaHandler(dummyEvent, dummyContext, () => console.log('Lambda invoked!'));

0 commit comments

Comments
 (0)