Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stepfunctions): execution history logging options #6933

Merged
merged 13 commits into from
Apr 3, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/@aws-cdk/aws-stepfunctions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,22 @@ new cloudwatch.Alarm(this, 'ThrottledAlarm', {
});
```

## Logging

Enable logging to CloudWatch by passing a logging configuration with a
destination LogGroup:

```ts
const logGroup = new logs.LogGroup(stack, 'MyLogGroup');

new stepfunctions.StateMachine(stack, 'MyStateMachine', {
definition: stepfunctions.Chain.start(new stepfunctions.Pass(stack, 'Pass')),
logs: {
destinations: logGroup,
level: stepfunctions.LogLevel.ALL,
}
});
```

## Future work

Expand Down
84 changes: 84 additions & 0 deletions packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import * as iam from '@aws-cdk/aws-iam';
import * as logs from '@aws-cdk/aws-logs';
import { Construct, Duration, IResource, Resource, Stack } from '@aws-cdk/core';
import { StateGraph } from './state-graph';
import { CfnStateMachine } from './stepfunctions.generated';
Expand All @@ -24,6 +25,56 @@ export enum StateMachineType {
STANDARD = 'STANDARD'
}

/**
* Defines which category of execution history events are logged.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/cloudwatch-log-level.html
*
* @default ERROR
*/
export enum LogLevel {
/**
* No Logging
*/
OFF = 'OFF',
/**
* Log everything
*/
ALL = 'ALL',
/**
* Log all errors
*/
ERROR= 'ERROR',
/**
* Log fatal errors
*/
FATAL = 'FATAL'
}

/**
* Defines what execution history events are logged and where they are logged.
*/
export interface LogOptions {
/**
* The log group where the execution history events will be logged.
*/
readonly destination: logs.ILogGroup;

/**
* Determines whether execution data is included in your log.
*
* @default true
*/
readonly includeExecutionData?: boolean;

/**
* Defines which category of execution history events are logged.
*
* @default ERROR
*/
readonly level?: LogLevel;
}

/**
* Properties for defining a State Machine
*/
Expand Down Expand Up @@ -60,6 +111,13 @@ export interface StateMachineProps {
* @default StateMachineType.STANDARD
*/
readonly stateMachineType?: StateMachineType;

/**
* Defines what execution history events are logged and where they are logged.
*
* @default No logging
*/
readonly logs?: LogOptions;
}

/**
Expand Down Expand Up @@ -132,11 +190,37 @@ export class StateMachine extends StateMachineBase {

this.stateMachineType = props.stateMachineType ? props.stateMachineType : StateMachineType.STANDARD;

let loggingConfiguration: CfnStateMachine.LoggingConfigurationProperty | undefined;
if (props.logs) {
const conf = props.logs;
loggingConfiguration = {
destinations: [{ cloudWatchLogsLogGroup: { logGroupArn: conf.destination.logGroupArn } }],
includeExecutionData: conf.includeExecutionData,
level: conf.level || 'ERROR'
};
// https://docs.aws.amazon.com/step-functions/latest/dg/cw-logs.html#cloudwatch-iam-policy
this.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'logs:CreateLogDelivery',
'logs:GetLogDelivery',
'logs:UpdateLogDelivery',
'logs:DeleteLogDelivery',
'logs:ListLogDeliveries',
'logs:PutResourcePolicy',
'logs:DescribeResourcePolicies',
'logs:DescribeLogGroups'
],
resources: ['*']
}));
}

const resource = new CfnStateMachine(this, 'Resource', {
stateMachineName: this.physicalName,
stateMachineType: props.stateMachineType ? props.stateMachineType : undefined,
roleArn: this.role.roleArn,
definitionString: Stack.of(this).toJsonString(graph.toGraphJson()),
loggingConfiguration
});

resource.node.addDependency(this.role);
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-stepfunctions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"@aws-cdk/aws-cloudwatch": "0.0.0",
"@aws-cdk/aws-events": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-logs": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^2.0.0"
},
Expand All @@ -82,6 +83,7 @@
"@aws-cdk/aws-cloudwatch": "0.0.0",
"@aws-cdk/aws-events": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-logs": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^2.0.0"
},
Expand Down
64 changes: 63 additions & 1 deletion packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect, haveResource } from '@aws-cdk/assert';
import * as logs from '@aws-cdk/aws-logs';
import * as cdk from '@aws-cdk/core';
import { Test } from 'nodeunit';
import * as stepfunctions from '../lib';
Expand Down Expand Up @@ -63,5 +64,66 @@ export = {
}));

test.done();
}
},

'log configuration'(test: Test) {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const logGroup = new logs.LogGroup(stack, 'MyLogGroup');

new stepfunctions.StateMachine(stack, 'MyStateMachine', {
definition: stepfunctions.Chain.start(new stepfunctions.Pass(stack, 'Pass')),
logs: {
destination: logGroup,
level: stepfunctions.LogLevel.FATAL,
includeExecutionData: false
}
});

// THEN
expect(stack).to(haveResource('AWS::StepFunctions::StateMachine', {
DefinitionString: '{"StartAt":"Pass","States":{"Pass":{"Type":"Pass","End":true}}}',
LoggingConfiguration: {
Destinations: [{
CloudWatchLogsLogGroup: {
LogGroupArn: {
'Fn::GetAtt': ['MyLogGroup5C0DAD85', 'Arn']
}
}
}],
IncludeExecutionData: false,
Level: 'FATAL'
}
}));

expect(stack).to(haveResource('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [{
Action: [
'logs:CreateLogDelivery',
'logs:GetLogDelivery',
'logs:UpdateLogDelivery',
'logs:DeleteLogDelivery',
'logs:ListLogDeliveries',
'logs:PutResourcePolicy',
'logs:DescribeResourcePolicies',
'logs:DescribeLogGroups'
],
Effect: 'Allow',
Resource: '*'
}],
Version: '2012-10-17'
},
PolicyName: 'MyStateMachineRoleDefaultPolicyE468EB18',
Roles: [
{
Ref: 'MyStateMachineRoleD59FFEBC'
}
]
}));

test.done();
},
};