Skip to content

Commit

Permalink
feat(logs): add support for fieldIndexPolicies in log group L2 Constr…
Browse files Browse the repository at this point in the history
…uct (aws#33416)

### Issue # (if applicable)

aws#33366

Closes aws#33366 

### Reason for this change

Field Indexing for CloudWatch Logs (CWL) was launched in Nov 2024. A lot of CWL customers are asking for indexing support in L2 construct. This feature will enable that property under FieldIndexPolicies as a JSON object in the LogGroup construct.

### Description of changes

The change here is just populating the `fieldIndexPolicies` property of the LogGroup CFN with the list of fields provided by the user. The format of this property will be like this:

```
const fieldIndexPolicy = new FieldIndexPolicy({
  fields: ['Operation', 'RequestId'],
});

new LogGroup(this, 'LogGroupLambda', {
  dataProtectionPolicy: dataProtectionPolicy,
  fieldIndexPolicies: [fieldIndexPolicy],
});
```

### Describe any new or updated permissions being added

No new permissions have been added.


### Description of how you validated changes

Added unit tests. Will add integ tests after getting a confirmation from the CDK team on the implementation.

### Checklist
- [X] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
yashkh-amzn authored Mar 5, 2025
1 parent ba2dfd1 commit 6c882e0
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 5 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@
]
}
},
"FieldIndexPolicies": [
{
"Fields": [
"Operation",
"RequestId"
]
}
],
"RetentionInDays": 731
},
"UpdateReplacePolicy": "Retain",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { App, Stack, StackProps } from 'aws-cdk-lib';
import { IntegTest } from '@aws-cdk/integ-tests-alpha';
import { LogGroup, DataProtectionPolicy, DataIdentifier, CustomDataIdentifier } from 'aws-cdk-lib/aws-logs';
import { LogGroup, DataProtectionPolicy, DataIdentifier, CustomDataIdentifier, FieldIndexPolicy } from 'aws-cdk-lib/aws-logs';

class LogGroupIntegStack extends Stack {
constructor(scope: App, id: string, props?: StackProps) {
Expand All @@ -19,8 +19,13 @@ class LogGroupIntegStack extends Stack {
s3BucketAuditDestination: bucket,
});

const fieldIndexPolicy = new FieldIndexPolicy({
fields: ['Operation', 'RequestId'],
});

new LogGroup(this, 'LogGroupLambda', {
dataProtectionPolicy: dataProtectionPolicy,
fieldIndexPolicies: [fieldIndexPolicy],
});
}
}
Expand Down
23 changes: 23 additions & 0 deletions packages/aws-cdk-lib/aws-logs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,29 @@ new logs.LogGroup(this, 'LogGroupLambda', {
});
```

## Field Index Policies

Creates or updates a field index policy for the specified log group. You can use field index policies to create field indexes on fields found in log events in the log group. Creating field indexes lowers the costs for CloudWatch Logs Insights queries that reference those field indexes, because these queries attempt to skip the processing of log events that are known to not match the indexed field. Good fields to index are fields that you often need to query for and fields that have high cardinality of values.

For more information, see [Create field indexes to improve query performance and reduce costs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatchLogs-Field-Indexing.html).

Only log groups in the Standard log class support field index policies.
Currently, this array supports only one field index policy object.

Example:

```ts

const fieldIndexPolicy = new logs.FieldIndexPolicy({
fields: ['Operation', 'RequestId'],
});

new logs.LogGroup(this, 'LogGroup', {
logGroupName: 'cdkIntegLogGroup',
fieldIndexPolicies: [fieldIndexPolicy],
});
```

## Notes

Be aware that Log Group ARNs will always have the string `:*` appended to
Expand Down
34 changes: 34 additions & 0 deletions packages/aws-cdk-lib/aws-logs/lib/field-index-policy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Construct } from 'constructs';

/**
* Creates a field index policy for CloudWatch Logs log groups.
*/
export class FieldIndexPolicy {
private readonly fieldIndexPolicyProps: FieldIndexPolicyProps;

constructor(props: FieldIndexPolicyProps) {
if (props.fields.length > 20) {
throw new Error('A maximum of 20 fields can be indexed per log group');
}
this.fieldIndexPolicyProps = props;
}

/**
* @internal
*/
public _bind(_scope: Construct) {
return { Fields: this.fieldIndexPolicyProps.fields };
}
}

/**
* Properties for creating field index policies
*/
export interface FieldIndexPolicyProps {
/**
* List of fields to index in log events.
*
* @default no fields
*/
readonly fields: string[];
}
1 change: 1 addition & 0 deletions packages/aws-cdk-lib/aws-logs/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './log-retention';
export * from './policy';
export * from './query-definition';
export * from './data-protection-policy';
export * from './field-index-policy';

// AWS::Logs CloudFormation Resources:
export * from './logs.generated';
15 changes: 15 additions & 0 deletions packages/aws-cdk-lib/aws-logs/lib/log-group.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Construct } from 'constructs';
import { DataProtectionPolicy } from './data-protection-policy';
import { FieldIndexPolicy } from './field-index-policy';
import { LogStream } from './log-stream';
import { CfnLogGroup } from './logs.generated';
import { MetricFilter } from './metric-filter';
Expand Down Expand Up @@ -506,6 +507,13 @@ export interface LogGroupProps {
*/
readonly dataProtectionPolicy?: DataProtectionPolicy;

/**
* Field Index Policies for this log group.
*
* @default - no field index policies for this log group.
*/
readonly fieldIndexPolicies?: FieldIndexPolicy[];

/**
* How long, in days, the log contents will be retained.
*
Expand Down Expand Up @@ -630,6 +638,12 @@ export class LogGroup extends LogGroupBase {
}

const dataProtectionPolicy = props.dataProtectionPolicy?._bind(this);
const fieldIndexPolicies: any[] = [];
if (props.fieldIndexPolicies) {
props.fieldIndexPolicies.forEach((fieldIndexPolicy) => {
fieldIndexPolicies.push(fieldIndexPolicy._bind(this));
});
}

const resource = new CfnLogGroup(this, 'Resource', {
kmsKeyId: props.encryptionKey?.keyArn,
Expand All @@ -643,6 +657,7 @@ export class LogGroup extends LogGroupBase {
Statement: dataProtectionPolicy?.statement,
Configuration: dataProtectionPolicy?.configuration,
} : undefined,
...(props.fieldIndexPolicies && { fieldIndexPolicies: fieldIndexPolicies }),
});

resource.applyRemovalPolicy(props.removalPolicy);
Expand Down
70 changes: 69 additions & 1 deletion packages/aws-cdk-lib/aws-logs/test/loggroup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as iam from '../../aws-iam';
import * as kms from '../../aws-kms';
import { Bucket } from '../../aws-s3';
import { App, CfnParameter, Fn, RemovalPolicy, Stack } from '../../core';
import { LogGroup, RetentionDays, LogGroupClass, DataProtectionPolicy, DataIdentifier, CustomDataIdentifier, ILogGroup, ILogSubscriptionDestination, FilterPattern } from '../lib';
import { LogGroup, RetentionDays, LogGroupClass, DataProtectionPolicy, DataIdentifier, CustomDataIdentifier, ILogGroup, ILogSubscriptionDestination, FilterPattern, FieldIndexPolicy } from '../lib';

describe('log group', () => {
test('set kms key when provided', () => {
Expand Down Expand Up @@ -921,6 +921,66 @@ describe('log group', () => {
});
});

test('set field index policy with four fields indexed', () => {
// GIVEN
const stack = new Stack();

const fieldIndexPolicy = new FieldIndexPolicy({
fields: ['Operation', 'RequestId', 'timestamp', 'message'],
});

// WHEN
const logGroupName = 'test-field-index-log-group';
new LogGroup(stack, 'LogGroup', {
logGroupName: logGroupName,
fieldIndexPolicies: [fieldIndexPolicy],
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', {
LogGroupName: logGroupName,
FieldIndexPolicies: [{
Fields: [
'Operation',
'RequestId',
'timestamp',
'message',
],
}],
});
});

test('set more than 20 field indexes in a field index policy', () => {
let message;
try {
// GIVEN
const stack = new Stack();
const fieldIndexPolicy = new FieldIndexPolicy({
fields: createMoreThan20FieldIndexes(),
});

// WHEN
const logGroupName = 'test-field-multiple-field-index-policies';
new LogGroup(stack, 'LogGroup', {
logGroupName: logGroupName,
fieldIndexPolicies: [fieldIndexPolicy],
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Logs::LogGroup', {
LogGroupName: logGroupName,
FieldIndexPolicies: [{
Fields: ['abc'],
}],
});
} catch (e) {
message = (e as Error).message;
}

expect(message).toBeDefined();
expect(message).toEqual('A maximum of 20 fields can be indexed per log group');
});

describe('subscription filter', () => {
test('add subscription filter with custom name', () => {
// GIVEN
Expand Down Expand Up @@ -953,6 +1013,14 @@ function dataDrivenTests(cases: string[], body: (suffix: string) => void): void
}
}

function createMoreThan20FieldIndexes(): string[] {
let arr: string[] = [];
for (let i = 0; i < 23; i++) {
arr.push('abc' + i.toString());
}
return arr;
}

class FakeDestination implements ILogSubscriptionDestination {
public bind(_scope: Construct, _sourceLogGroup: ILogGroup) {
return {
Expand Down

0 comments on commit 6c882e0

Please sign in to comment.