管理AWS步骤函数中的错误流

时间:2018-03-19 21:59:39

标签: amazon-web-services aws-step-functions

我有一个主要用Javascript编写的Lambda函数的AWS步骤函数/状态机(尽管一个是用Java编写的),我想更好地管理错误处理。

我有一个错误条件被捕获然后被转发到流中的另一个状态没有问题。因此,例如,我的状态机中的以下状态定义将执行传递到NotifyOfError状态,在那里我能够通过电子邮件和短信正确地了解错误状态。

Closure:
  Type: Task
  Resource: >-
    arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:xxx-services-${opt:stage}-transportClosure
  Next: WaitForCloudWatch
  Catch:
    - ErrorEquals:
        - "States.ALL"
      ResultPath: "$.error-info"
      Next: NotifyOfError

然而,不是将所有错误交给这个状态,而是有一些错误,我喜欢处理不同的错误。所以起初我以为如果我把一个Javascript / Node错误与给定的"名称"那个名字就是我可以在 ErrorEquals 配置中分支的东西。例如:

 catch(e) {
      if (e.message.indexOf('something') !== -1) {
           e.name = "SomethingError";
               throw e;
      }

但很快就意识到这个名字只是在步骤函数的Cause部分之前,而不是分支的东西。然后我尝试扩展基本的Error类,如下所示:

export default class UndefinedAssignment extends Error {
  constructor(e: Error) {
    super(e.message);
    this.stack = e.stack;
  }
}

但抛出此错误实际上什么也没做,这意味着当它出现在Step Function中时,Error类型仍然只是"错误":

"error-info": {
    "Error": "Error",
    "Cause": "{\"errorMessage\":\"Error: the message",\"errorType\":\"Error\",\"stackTrace\":[\"db.set.catch.e (/var/task/lib/prepWorker/Handler.js:247:23)\",\"process._tickDomainCallback (internal/process/next_tick.js:135:7)\"]}"
}

所以我还不清楚如何区分在步骤函数中 branchable 的Node中出现的错误。

  

注意:使用Java,它似乎 正确拾取错误类(虽然我在Java方面做的测试少得多)

2 个答案:

答案 0 :(得分:1)

以下是我如何使用“步骤功能”将自定义错误和消息报告为ErrorCause。注意我使用带有asynctry/catch的Node.js 8.10 Lambda运行时。

exports.handler = async (event) => {
  function GenericError(name, message) {
    this.name = name;
    this.message = message;
  }
  GenericError.prototype = new Error();
  try {
    // my implementation which might throw an error
    // ...
  }
  catch (e) {
    console.log(e);
    let error = new GenericError('CustomError', 'my message');
    throw error;
  }
};

为简单起见,我忽略了catch(e)中的错误对象。如果需要,您还可以将其stack提供给GenericError。

这个lambda函数返回:

{
  "errorMessage": "my message",
  "errorType": "CustomError",
  "stackTrace": [
    "exports.handler (/var/task/index.js:33:28)"
  ]
}

步骤功能将其转换为:

{
  "error": "CustomError",
  "cause": {
    "errorMessage": "my message",
    "errorType": "CustomError",
    "stackTrace": [
      "exports.handler (/var/task/index.js:33:28)"
    ]
  }
}

在其LambdaFunctionFailed事件历史记录中,最终将其再次转换为此状态输出(取决于我们的ResultPath - 此处不包含任何内容):

{
  "Error": "CustomError",
  "Cause": "{\"errorMessage\":\"my message\",\"errorType\":\"CustomError\",\"stackTrace\":[\"exports.handler (/var/task/index.js:33:28)\"]}"
}

答案 1 :(得分:0)

您应该使用callback从Lambda返回抛出的异常。创建lambda和状态机的示例Cloud Formation模板:

AWSTemplateFormatVersion: 2010-09-09
Description: Stack creating AWS Step Functions state machine and lambda function throwing custom error. 

Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          exports.handler = function(event, context, callback) {
              function SomethingError(message) {
                  this.name = "SomethingError";
                  this.message = message;
              }
              SomethingError.prototype = new Error();

              const error = new SomethingError("something-error");
              callback(error);
          };
      Runtime: "nodejs6.10"
      Timeout: 25

  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt StatesExecutionRole.Arn
      DefinitionString: !Sub
        - >
          {
            "Comment": "State machine for nodejs error handling experiment",
            "StartAt": "FirstState",
            "States": {
              "FirstState": {
                "Type": "Task",
                "Resource": "${ThrowErrorResource}",
                "Next": "Success",
                "Catch": [
                  {
                    "ErrorEquals": ["SomethingError"],
                    "ResultPath": "$.error",
                    "Next": "CatchSomethingError"
                  }
                ]
              },
              "Success": {
                "Type": "Pass",
                "End": true
              },
              "CatchSomethingError": {
                "Type": "Pass",
                "Result": {
                  "errorHandlerOutput": "Huh, I catched an error"
                },
                "ResultPath": "$.errorHandler",
                "End": true
              }
            }
          }
        - ThrowErrorResource: !GetAtt LambdaFunction.Arn

  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service:
              - lambda.amazonaws.com
          Action:
            - sts:AssumeRole

  StatesExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - !Sub states.${AWS::Region}.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: ExecuteLambda
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                Resource: arn:aws:lambda:*:*:function:*

基本部分是Lambda函数定义:

exports.handler = function(event, context, callback) {
    function SomethingError(message) {
        this.name = "SomethingError";
        this.message = message;
    }
    SomethingError.prototype = new Error();

    const error = new SomethingError("something-error");
    callback(error);
};

此处定义了自定义名称的自定义错误。当然你也可以简单地覆盖名称(但我不建议这样做):

exports.handler = function(event, context, callback) {
    var e = new Error();
    e.name = "SomethingError";
    callback(e);
};

返回错误,因为它将传递给步骤函数而不会丢失错误名称。我建议您在Lambda函数中创建一些顶级try-catch语句,只需向callback调用错误。