diff --git a/packages/@aws-cdk/aws-logs/README.md b/packages/@aws-cdk/aws-logs/README.md index 1f223bc6b26de..27d010fcd48b9 100644 --- a/packages/@aws-cdk/aws-logs/README.md +++ b/packages/@aws-cdk/aws-logs/README.md @@ -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 diff --git a/packages/@aws-cdk/aws-logs/lib/log-group.ts b/packages/@aws-cdk/aws-logs/lib/log-group.ts index cd909091ea092..143989d731dab 100644 --- a/packages/@aws-cdk/aws-logs/lib/log-group.ts +++ b/packages/@aws-cdk/aws-logs/lib/log-group.ts @@ -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'; /** @@ -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 + }); + } } /** diff --git a/packages/@aws-cdk/aws-logs/lib/metric-filter.ts b/packages/@aws-cdk/aws-logs/lib/metric-filter.ts index c9828bcb3dd03..f213c82588d55 100644 --- a/packages/@aws-cdk/aws-logs/lib/metric-filter.ts +++ b/packages/@aws-cdk/aws-logs/lib/metric-filter.ts @@ -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, diff --git a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts index c3990056e9357..53551b7608506 100644 --- a/packages/@aws-cdk/aws-logs/test/test.loggroup.ts +++ b/packages/@aws-cdk/aws-logs/test/test.loggroup.ts @@ -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(); } + };