Skip to content

Commit

Permalink
feat(aws-logs): add extractMetric() helper (#676)
Browse files Browse the repository at this point in the history
This helper function makes it convenient to extract metrics
from structured records emitted to CloudWatch logs. For example:

    logGroup.extractMetric('$.s3latency', 'MyService', 'S3Latency');

Will extract all occurrences of { "s3latency": 89.5 } from the logs
and emit them to the CloudWatch metric MyService/S3Latency.
  • Loading branch information
rix0rrr authored Sep 7, 2018
1 parent 1b134a5 commit f70394c
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 1 deletion.
11 changes: 11 additions & 0 deletions packages/@aws-cdk/aws-logs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ Example:
Remember that if you want to use a value from the log event as the metric value,
you must mention it in your pattern somewhere.

A very simple MetricFilter can be created by using the `logGroup.extractMetric()`
helper function:

```ts
logGroup.extractMetric('$.jsonField', 'Namespace', 'MetricName');
```

Will extract the value of `jsonField` wherever it occurs in JSON-structed
log records in the LogGroup, and emit them to CloudWatch Metrics under
the name `Namespace/MetricName`.

### Patterns

Patterns describe which log events match a subscription or metric filter. There
Expand Down
25 changes: 24 additions & 1 deletion packages/@aws-cdk/aws-logs/lib/log-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import cdk = require('@aws-cdk/cdk');
import { LogStream } from './log-stream';
import { cloudformation, LogGroupArn, LogGroupName } from './logs.generated';
import { MetricFilter } from './metric-filter';
import { IFilterPattern } from './pattern';
import { FilterPattern, IFilterPattern } from './pattern';
import { ILogSubscriptionDestination, SubscriptionFilter } from './subscription-filter';

/**
Expand Down Expand Up @@ -83,6 +83,29 @@ export abstract class LogGroupRef extends cdk.Construct {
logGroupArn: new LogGroupArn(new cdk.Output(this, 'LogGroupArn', { value: this.logGroupArn }).makeImportValue())
};
}

/**
* Extract a metric from structured log events in the LogGroup
*
* Creates a MetricFilter on this LogGroup that will extract the value
* of the indicated JSON field in all records where it occurs.
*
* The metric will be available in CloudWatch Metrics under the
* indicated namespace and name.
*
* @param jsonField JSON field to extract (example: '$.myfield')
* @param metricNamespace Namespace to emit the metric under
* @param metricName Name to emit the metric under
*/
public extractMetric(jsonField: string, metricNamespace: string, metricName: string) {
new MetricFilter(this, `${metricNamespace}_${metricName}`, {
logGroup: this,
metricNamespace,
metricName,
filterPattern: FilterPattern.exists(jsonField),
metricValue: jsonField
});
}
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-logs/lib/metric-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ export class MetricFilter extends cdk.Construct {
constructor(parent: cdk.Construct, id: string, props: MetricFilterProps) {
super(parent, id);

// It looks odd to map this object to a singleton list, but that's how
// we're supposed to do it according to the docs.
//
// > Currently, you can specify only one metric transformation for
// > each metric filter. If you want to specify multiple metric
// > transformations, you must specify multiple metric filters.
//
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-metricfilter.html
new cloudformation.MetricFilterResource(this, 'Resource', {
logGroupName: props.logGroup.logGroupName,
filterPattern: props.filterPattern.logPatternString,
Expand Down
25 changes: 25 additions & 0 deletions packages/@aws-cdk/aws-logs/test/test.loggroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,31 @@ export = {
// THEN
expect(stack2).to(haveResource('AWS::Logs::LogStream', {}));

test.done();
},

'extractMetric'(test: Test) {
// GIVEN
const stack = new Stack();
const lg = new LogGroup(stack, 'LogGroup');

// WHEN
lg.extractMetric('$.myField', 'MyService', 'Field');

// THEN
expect(stack).to(haveResource('AWS::Logs::MetricFilter', {
FilterPattern: "{ $.myField = \"*\" }",
LogGroupName: { Ref: "LogGroupF5B46931" },
MetricTransformations: [
{
MetricName: "Field",
MetricNamespace: "MyService",
MetricValue: "$.myField"
}
]
}));

test.done();
}

};

0 comments on commit f70394c

Please sign in to comment.