Skip to content

Instantly share code, notes, and snippets.

@vicly
Last active July 15, 2018 05:01
Show Gist options
  • Save vicly/9c01c98c33a20d986f7b8e6a51c18bc0 to your computer and use it in GitHub Desktop.
Save vicly/9c01c98c33a20d986f7b8e6a51c18bc0 to your computer and use it in GitHub Desktop.
[AWS Lambda handles CloudWatch log event] #AWS
  1. Lambda stack exports lambda ARN
  2. App stack defines log group
  3. App stack defines subscription to the log group for lambda

So, by design, many app stacks share the same lambda stack to handle log events.

Stack

App Stack LogGroup

  ServiceLogGroup:
    Type: "AWS::Logs::LogGroup"
    DeletionPolicy: Retain
    Properties:
      LogGroupName:
        Fn::Sub: ${AWS::StackName}/ecs/Service/MyService
      RetentionInDays: 15

App Stack Subscription

  # CloudwatchLogsFilterArn is the output of Lambda stack
  ServiceLogSubscription:
    Type: "AWS::Logs::SubscriptionFilter"
    DependsOn:
      - ServiceLogGroup
    Properties:
      DestinationArn:
        Fn::ImportValue: CloudwatchLogsFilterArn
      FilterPattern: "{ ( $.category = \"my.pkg.ACTIVITY\" || $.category = \"my.pkg.METRICS\") && $.publish IS TRUE }"
      LogGroupName:
        Fn::Sub: ${AWS::StackName}/ecs/Service/MyService

Message Transition

CloudWatch log event

{
  "awslogs": {
    "data": <Base64 encoded and GZipped String>
  }
}

Decoded message

// see http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html?shortFooter=true#LambdaFunctionExample

{
    "owner": "111111111111",
    "logGroup": "CloudTrail",
    "logStream": "111111111111_CloudTrail_us-east-1",
    "subscriptionFilters": [
        "Destination"
    ],
    "messageType": "DATA_MESSAGE",
    "logEvents": [
        {
            "id": "31953106606966983378809025079804211143289615424298221568",
            "timestamp": 1432826855000,
            "message": "<App specific JSON>"
        },
        ...
    ]
}

Lambda decodes message

public Void handleRequest(Object input, Context context) {
    byte[] bytes = Base64.getDecoder().decode(data);

    try (GZIPInputStream zi = new GZIPInputStream(new ByteArrayInputStream(bytes))) {
      String result = IOUtils.toString(zi);
      message = MAPPER.readValue(result, AwsCloudWatchLogMessage.class); // POJO
      
      for (AwsCloudWatchLogEvent e : message.getLogEvents()) {
        String appLogEventJson = e.getMessage();
        
        ObjectNode json = MAPPER.readValue(logEvent.getMessage(), ObjectNode.class);
        Optional<JsonNode> xyz = Optional.ofNullable(json.get("xyz"));
        ...
      }

    } catch (IOException e) {
      //...
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment