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

ken*_*ken 5 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
Run Code Online (Sandbox Code Playgroud)

但是,我不想将所有错误都交给这个状态,而是我希望以不同的方式处理一些错误。所以起初我认为如果我用给定的“名称”抛出一个 Javascript/Node 错误,那么这个名称将是我可以在ErrorEquals配置中分支的东西。例子:

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

但很快意识到这个名字只是被添加到Causestep-function的部分而不是分支的东西。然后我尝试像这样扩展基本 Error 类:

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

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

"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)\"]}"
}
Run Code Online (Sandbox Code Playgroud)

因此,我仍然不清楚如何区分源自 Node 且在 step 函数中分支的错误。

注意:对于 Java,它似乎确实正确地拾取了错误类(尽管我在 Java 方面所做的测试要少得多)

kin*_*no1 5

以下是我如何让 Step Functions 将自定义错误和消息报告为它的ErrorCause。注意我使用的是 Node.js 8.10 Lambda 运行时asynctry/catch

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;
  }
};
Run Code Online (Sandbox Code Playgroud)

为简单起见,我忽略了catch(e)这里的错误对象。stack如果需要,您也可以将其输入到 GenericError 中。

这个 lambda 函数返回:

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

Step Functions 将其转换为:

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

在它的LambdaFunctionFailed事件历史中,并最终将它再次转换为这个状态输出(取决于我们的ResultPath- 这里没有任何):

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


Mar*_*ski 3

您应该使用 Lambda 返回抛出的异常callback。创建 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:*
Run Code Online (Sandbox Code Playgroud)

重要部分是 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);
};
Run Code Online (Sandbox Code Playgroud)

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

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

像这样返回的错误将被传递到 Step Functions,而不会丢失错误名称。我建议try-catch在 Lambda 函数中创建一些 top 语句,您可以在其中简单地调用callback错误。