diff --git a/packages/@aws-cdk/aws-stepfunctions/README.md b/packages/@aws-cdk/aws-stepfunctions/README.md index b7c54e428d877..b2deba80f5356 100644 --- a/packages/@aws-cdk/aws-stepfunctions/README.md +++ b/packages/@aws-cdk/aws-stepfunctions/README.md @@ -673,6 +673,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 diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index efb9f5659c31d..e60c2a29a3eb3 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -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'; @@ -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 */ @@ -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; } /** @@ -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); diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json index 8d53040da7428..c2941b6f2546e 100644 --- a/packages/@aws-cdk/aws-stepfunctions/package.json +++ b/packages/@aws-cdk/aws-stepfunctions/package.json @@ -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" }, @@ -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" }, diff --git a/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts index 2aab5ea1070d8..ab1aaf1b46263 100644 --- a/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/test/test.state-machine.ts @@ -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'; @@ -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(); + }, };