Developer for Serverless
이 실습은 CDK를 이용하여 서버리스를 개발하는 방법을 다룹니다.
## CDK 프로젝트 셋업
Lambda를 개발하는 방법은 여러가지가 있습니다. AWS콘솔을 사용하는 방법, AWS SAM을 사용하는 방법, 그리고 CDK가 있습니다. Lambda를 콘솔에서 개발하는 것도 좋지만, 권한 및 빠른 배포, 작업이력을 남기기 위해서는 IaC를 활용하는 것이 좋습니다. 서버리스 프로젝트만 작업을 하신다면 AWS SAM을 추천드립니다. 만약 더 많은 리소스를 포함시키고 작업하신다면 CDK도 좋은 선택이 될 수 있습니다.
### CDK를 사용한 프로젝드 생성
CDK프로젝트를 초기화합니다.원하는 directory에서 아래 명령어를 입력합니다.
```
cdk init --language typescript
```
아래와 같이 파일이 생성된 것을 보실 수 있습니다.

cdk는 스택단위로 개발하는 것이 좋습니다. 배포가 되는단위를 잘 나누어야 추후 개발하고 확장할때 편합니다. 현재는 **developlambda Stack**하나가 선언되어있습니다.
## Lambda 코드 생성하기
루트디렉토리에 Lambdas 폴더를 생성합니다. 그리고 폴더내에 lambda-rest.ts 파일을 생성하고 아래 코드를 입력합니다.
```typescript=
import * as AWS from 'aws-sdk';
import { v4 as uuidv4 } from 'uuid';
const TABLE_NAME = process.env.TABLE_NAME || '';
const PRIMARY_KEY = process.env.PRIMARY_KEY || '';
const ddb = new AWS.DynamoDB.DocumentClient();
const RESERVED_RESPONSE = `Error: You're using AWS reserved keywords as attributes`,
DYNAMODB_EXECUTION_ERROR = `Error: Execution update, caused a Dynamodb error, please take a look at your CloudWatch Logs.`;
export const handler = async (event: any = {}): Promise<any> => {
if (!event.body) {
return { statusCode: 400, body: 'Invalid request, the parameter body is required' };
}
const item = typeof event.body == 'object' ? event.body : JSON.parse(event.body);
item[PRIMARY_KEY] = uuidv4();
const params = {
TableName: TABLE_NAME,
Item: item
};
try {
await ddb.put(params).promise();
// console.log('Error happened here!');
// console.log('Debug this, and clear these logs!');
return {
statusCode: 201,
// statusCode: 500,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,DELETE,PUT,GET"
},
body: 'success'
};
} catch (dbError) {
const errorResponse = (dbError as AWS.AWSError)?.code === 'ValidationException' && (dbError as AWS.AWSError)?.message.includes('reserved keyword') ?
DYNAMODB_EXECUTION_ERROR : RESERVED_RESPONSE;
return { statusCode: 500, body: errorResponse };
}
};
```
위 코드는 DynamoDB를 사용하기위한 uuid가 포함되어있습니다. 모듈은 설치할 수 있게 package.json 파일도 생성하겠습니다. Lambdas폴더 아래 새 파일을 생성하고 package.json이름을 입력합니다. 그리고 아래 코드를 입력합니다.
```jsonld=
{
"name": "rest-service",
"version": "1.0.0",
"description": "Lambdas to handle rest apis",
"private": true,
"license": "MIT",
"devDependencies": {
"@types/node": "*",
"@types/uuid": "*"
},
"dependencies": {
"aws-sdk": "*",
"uuid": "*"
}
}
```
root의 package.json에서 postinstall을 추가합니다. cdk가 빌드될때 lambda도 같이 빌드시키겠습니다. scripts에 아래와 같이 postinstall을 추가합니다.
```jsonld=
"scripts": {
"postinstall": "npm install ./lambdas",
"build": "tsc",
"watch": "tsc -w",
"test": "jest",
"cdk": "cdk"
},
```
프로젝트 루트에서 아래 명령어를 입력합니다.
```bash=
npm install
```
## CDK에서 Lambda 생성하기
이제 Lambda코드가 동작할 lambda를 생성해보겠습니다.
lib폴더의 developlambda-stack.ts에 아래와 같이 입력합니다.
```typescript=
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { RemovalPolicy } from 'aws-cdk-lib';
import { NodejsFunction, NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs';
import { AttributeType, Table } from 'aws-cdk-lib/aws-dynamodb';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { join } from 'path';
export class DeveloplambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const productTablePrimaryKey = 'ProductId';
const productTable = new Table(this, 'ProductTable', {
partitionKey: { name: productTablePrimaryKey, type: AttributeType.STRING },
removalPolicy: RemovalPolicy.DESTROY,
});
const nodeJsFunctionProps: NodejsFunctionProps = {
bundling: {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
],
},
environment: {
PRIMARY_KEY: productTablePrimaryKey,
TABLE_NAME: productTable.tableName,
LOG_LEVELS: "normal"
},
runtime: Runtime.NODEJS_16_X,
}
const createProductLambda = new NodejsFunction(this, 'CreateProductFunction', {
entry: join(__dirname, '../lambdas', 'lambda-rest.ts'),
...nodeJsFunctionProps,
});
productTable.grantReadWriteData(createProductLambda);
createProductLambda.addAlias('production');
}
}
```
위 코드는 CDK에서 다이나모디비와 람다를 생성하여 코드와 연결하고 있습니다.
이제 CDK 프로젝트를 빌드하고 배포해봅시다.
```bash=
npm install
cdk synth
cdk deploy
```
이제 Lambda 콘솔로 이동합니다. 검색창에 developlambdastack을 입력합니다. 그리고 해당 람다를 클릭합니다.

스크롤을 내려 test탭을 선택한다음, test내용에 아래 내용을 입력합니다.
```jsonld=
{
"body": {
"name": "tv",
"price": "100",
"description": "super tv"
}
}
```
테스트를 실행하면 201이 리턴되며 잘 동작한 것을 확인할 수 있습니다.

다이나모디비에서도 데이타가 저장된 것을 확인 할 수 있습니다.
## CDK Pipeline을 이용한 Lambda 개발
CDK 파이프라인은 AWS CDK 애플리케이션을 원활하게 지속적으로 제공하기 위한 구성 라이브러리 모듈입니다. AWS CDK 앱의 소스 코드를 AWS CodeCommit, GitHub 또는 AWS CodeStar에 체크인할 때마다 CDK Pipelines는 자동으로 새 버전을 빌드, 테스트 및 배포할 수 있습니다.
CDK 파이프라인은 자동으로 업데이트됩니다. 애플리케이션 단계 또는 스택을 추가하면 파이프라인은 해당 새 단계 또는 스택을 배포하도록 자동으로 재구성됩니다.
그럼 CDK프로젝트에 파이프라인을 추가해보겠습니다.
루트에 파이프라인 스택을 만들겠습니다. developpipeline-stack.ts 파일을 생성하고 아래 코드를 입력합니다.
```typescript=
import { Construct } from "constructs";
import * as cdk from "aws-cdk-lib";
import { DeveloplambdaStack } from './developlambda-stack';
import * as codecommit from "aws-cdk-lib/aws-codecommit";
import * as pipelines from "aws-cdk-lib/pipelines";
import * as iam from "aws-cdk-lib/aws-iam";
export class DevelopPipelineStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
let repository = new codecommit.Repository(this, "Repository", {
repositoryName: `Repository-${this.stackName}`,
description: "Code Repository for DeveloperWorkshop00",
});
let appStage = new AppStage(this, "AppStage", { stackName: this.stackName });
let pipeline = new pipelines.CodePipeline(this, "Pipeline", {
pipelineName: `Pipeline-${this.stackName}`,
publishAssetsInParallel: false,
synth: new pipelines.ShellStep("Synth", {
input: pipelines.CodePipelineSource.codeCommit(repository, "main"),
commands: [
"npm install",
"npm run build",
"npx cdk synth"
],
}),
// Turn this on because the pipeline uses Docker image assets
dockerEnabledForSelfMutation: true,
codeBuildDefaults: {
buildEnvironment:{
privileged:true
},
rolePolicy: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["s3:*"],
resources: ["*"],
}),
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["cloudfront:*"],
resources: ["*"],
}),
],
},
});
pipeline.addStage(appStage);
new cdk.CfnOutput(this, "RepositoryCloneUrlHttp", {
value: repository.repositoryCloneUrlHttp,
description: "Code Repository Clone Url Http",
});
}
}
interface AppStageProps extends cdk.StageProps {
stackName: string;
}
class AppStage extends cdk.Stage {
constructor(scope: Construct, id: string, props: AppStageProps) {
super(scope, id, props);
const EventBridgeStack = new DeveloplambdaStack(this, 'DevelopLambdaStack', {});
}
}
```
bin/ 폴더의 developlambda.ts의 new DevelopLambda 스택 선언을 삭제하고, 파이프라인 스택을 추가합니다.
```typescript=
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { DevelopPipelineStack } from '../lib/developpipeline-stack';
const app = new cdk.App();
new DevelopPipelineStack(app, 'DeveloppipelineStack');
```
이제 기존스택을 삭제하고 파이프라인으로 배포하겠습니다. CDK를 다시 빌드하고, CDK가 배포 가능한스택 리스트를 확인합니다.
```bash=
cdk synth
cdk list
```
- DeveloppipelineStack
- DeveloppipelineStack/AppStage/DevelopLambdaStack
두개의 배포가능한 스택이 있습니다. 아래 명령어로 DeveloppipelineStack을 배포합니다.
```bash=
CDK Deploy DeveloppipelineStack
```
CDK프로젝트를 배포할 파이프라인과 CodeCommit레포지토리가 생성되어있는 것을 볼 수 있습니다. 콘솔의 CodeCommit으로 이동합니다. DeveloppipelineStack에서 생성한 Git 레포지토리를 볼 수 있습니다.

Git 레포지토리를 클릭하고 url을 복사합니다.
루프 폴더에서 git을 초기화합니다.
```bash=
git init
git remote add <codecommit https url>
```
*codecommit https url* 은 CodeCommit https(GRC)의 주소로 변경합니다. origin설정이 완료되었으면 이제 CodeCommit으로 push합니다.
Push하기전에 쓸데없는 파일들이 git에 올라가지 않도록 .gitignore파일을 만들어줍니다. 루트에 .gitignore를 생성하고 아래 코드를 붙여넣습니다.
```typescript=
# package-lock
package-lock.json*
# cdk
cdk.out/
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
```
이제 커밋을 생성하고 push하겠습니다.
```
git add .
git commit -m "initial commit"
git push --set-upstream origin main
```
CodePipeline으로 가보면 CDK가 생성한 파이프라인이 동작하는 것을 볼 수 있습니다.

파이프라인이 완료된 후 CloudFormation을 확인하면 파이프라인이 생성한 스택들을 볼 수 있습니다. 이제 CDK를 변경한 후 코드를 push하면 생성한 리소스와 lambda코드가 배포되는 것을 볼 수 있습니다.
## 멀티 파이프라인을 통한 개발환경 구축하기
## Lambda Alias를 이용한 환경 관리
버전을 사용하여 기능 배포를 관리할 수 있습니다. 예를 들어, 안정적인 프로덕션 버전 사용자에게 영향을 주지 않고 베타 테스트를 위한 새 버전의 함수를 게시할 수 있습니다. Lambda는 함수를 게시할 때마다 새 버전의 함수를 생성합니다. 새 버전은 게시되지 않은 함수 버전의 복사본입니다. 게시되지 않은 버전의 이름은 $LATEST입니다.
## Lambda Local 테스트
로컬에서 Lambda를 테스트하는 방법을 알아보겠습니다. Lambda를 로컬에서 실행시키기위해서는 두가지가 필요합니다. AWS SAM과 Docker입니다. SAM을 이용하여 Local에서 Lambda를 실행하겠습니다. 먼저 우리가 테스트할 람다의 리소스 이름을 확인하겠습니다. 아래 명령어로 cdk가 생성한 람다의 이름을 확인해봅니다.
```bash=
cdk synth
```
lib/developerlambdastack.ts에서 lambda의 리소스 이름을 확인해보면 **CreateProductFunction**인것을 확인할 수 있습니다.
```typescript=
const createProductLambda = new NodejsFunction(this, 'CreateProductFunction', {
entry: join(__dirname, '../lambdas', 'lambda-rest.ts'),
...nodeJsFunctionProps,
});
```
이제 cdk.out폴더에서 **DeveloperLambdaStack.template.json**파일을 열어 ctrl + f로 CreateProductFunction 검색합니다.
Lambda명뒤에 해쉬값이 붙은 것을 볼 수 있습니다. 역할과 여러가지 리소스가 있을텐데 그중 Type이 AWS::Lambda::Function인 것을 찾습니다.
```jsonld=
"Resources": {
"CreateProductFunctionD4A3469E": {
"Type": "AWS::Lambda::Function",
"Properties": {
...
}
}
```
아래 명령어로 Lambda를 로컬에서 실행합니다. SAM을 사용합니다.
:::warning
Docker가 반드시 설치되어있어야 합니다. Docker를 설치했는데도 찾을 수 없다는 메세지가 나오면 도커의 어드벤스드 셋팅에서 **Allow the default Docker socket to be used (requires password)
**를 체크하고 다시시작해주세요.
:::
```bash=
sam local invoke -t ./cdk.out/DeveloplambdaStack.template.json CreateProductFunction
```
Lambda가 빌드되고 실행되는 걸 볼 수 있습니다. 이벤트와 환경변수에 필요한 값이 없기 때문에 오류가 발생했을 것입니다.

이벤트와 환경변수를 생성해서 같이 던저보겠습니다.
lambds 폴더에 event.json을 생성하고 아래 코드를 입력합니다 .
```jsonld=
{
"body": {
"name":"super table",
"price":"100",
"description":"this is super table"
}
}
```
환경변수를 만들기위해 env.json도 생성하고 아래 코드를 입력합니다.
```jsonld=
{
"Parameters": {
"TABLE_NAME": "<ProductTableNAME>",
"PRIMARY_KEY": "ProductId"
}
}
```
**ProductTableNAME**은 실제 다이나모디비테이블의 이름을 입력해야합니다. CloudFormation의 DevelopLambdaStack에서 DynamoDB Table리소스의 이름을 확인해서 붙여넣습니다.
다시 로컬에서 lambda를 호출합니다.
```bash=
sam local invoke --env-vars lambdas/env.json -e lambdas/event.json -t ./cdk.out/DeveloplambdaStack.template.json CreateProductFunction
```
VS Code를 사용한 디버깅도 가능합니다. CDK의 루트 프로젝트에서 launch.json을 생성합니다. VS Code지시에 따라서 생성하면 편합니다. add configuration을 선택한 후 **AWS SAM: Direct Lambda handler invoke**를 선택합니다.

옵션값을 채웁니다. 위 프로젝트는 아래 launch.json을 사용하면됩니다.
:::warning
environmentVariable에 DynamodbTable명을 꼭 수정해주세요.
:::
```jsonld=
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "aws-sam",
"request": "direct-invoke",
"name": "Invoke Lambda",
"invokeTarget": {
"target": "code",
"lambdaHandler": "lambdas/lambda-rest.handler",
"projectRoot": "${workspaceFolder}"
},
"lambda": {
"runtime": "nodejs16.x",
"payload": {
"json": {
"body": {
"name":"super table2",
"price":"100",
"description":"this is super table"
}
}
},
"environmentVariables": {
"TABLE_NAME": "바꿔주세요",
"PRIMARY_KEY": "ProductId"
}
}
}
]
}
```
브래이크포인트를 걸고 벌래버튼을 누르면 디버깅을 시작할 수 있습니다.
## Lambda 모니터링
**Invocations** – 람다가 호출된 횟수.
**Duration** – 람다 코드가 이벤트를 처리하는 데 소요되는 평균, 최소 및 최대 시간
**Error count and success rate (%)** – 오류 없이 완료된 호출 비율
**Throttles** – 동시성 제한으로 인해 호출이 실패한 횟수
**IteratorAge** – 스트림 이벤트 소스의 경우, Lambda가 항목을 수신하고 함수를 호출했을 때 배치의 마지막 항목의 수명
**Async delivery failures** – Lambda가 대상 또는 배달 못한 편지 대기열에 쓰려고 할 때 발생한 오류 수
**Concurrent executions** – 이벤트를 처리 중인 함수 인스턴스의 수
Lambda 콘솔의 스크롤을 내려 모니터링을 클릭합니다.

상단의 탭에서 메트릭, 로그, 트래이스를 확인할 수 있습니다.
## StepFunction을 사용한 워크플로우
람다의 결과를 SNS로 보내고, 챗봇을 사용하여 슬랙으로 전송하는 작업을 해보겠습니다. Stepfunction studio에서 간단한 내용을 작성하겠습니다.
### Chatbot 및 SNS 설정
:::info
chatbot을 CDK로 설정하기위해선 slack client key를 이용해야합니다. 과정이 복잡하니 chatbot은 콘솔에서 작성하고 CDK와 연동하겠습니다.
:::
Chatbot 콘솔에 접속하여 Configuration slack을 선택하고 원하는 슬랙계정과 연결한 워크스페이스를 생성합니다. 
CDK를 사용하여 SNS를 생성해보겠습니다.
CDK프로젝트의 lib/DevelopLambdaStack.ts에 아래 내용을 추가합니다.
```typescript=
import * as sns from 'aws-cdk-lib/aws-sns';
...
const topic = new sns.Topic(this, 'chattopic', {
displayName: 'chatbot subscription topic',
});
```
전체 코드는 아래와 같습니다.
```typescript=
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { RemovalPolicy } from 'aws-cdk-lib';
import { NodejsFunction, NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs';
import { AttributeType, Table } from 'aws-cdk-lib/aws-dynamodb';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { join } from 'path';
import * as sns from 'aws-cdk-lib/aws-sns';
export class DeveloplambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const productTablePrimaryKey = 'ProductId';
const productTable = new Table(this, 'ProductTable', {
partitionKey: { name: productTablePrimaryKey, type: AttributeType.STRING },
removalPolicy: RemovalPolicy.DESTROY,
});
const nodeJsFunctionProps: NodejsFunctionProps = {
bundling: {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
],
},
environment: {
PRIMARY_KEY: productTablePrimaryKey,
TABLE_NAME: productTable.tableName,
LOG_LEVELS: "normal",
ENVIRONMENT: process.env.ENVIRONMENT || ''
},
runtime: Runtime.NODEJS_16_X,
}
const createProductLambda = new NodejsFunction(this, 'CreateProductFunction', {
entry: join(__dirname, '../lambdas', 'lambda-rest.ts'),
...nodeJsFunctionProps,
});
productTable.grantReadWriteData(createProductLambda);
createProductLambda.addAlias('production');
const topic = new sns.Topic(this, 'chattopic', {
displayName: 'chatbot subscription topic',
});
}
}
```
이제 chatbot에서 슬랙과 SNS를 연결합니다. 챗봇에서 이전에 생성한 슬랙워크스페이스에서 채널을 생성하고 CDK에서 만든 토픽을 연결합니다. Configure new channel을 선택하고 필요한 내용을 입력합니다.

### StepFunction 설정
스텝펑션의 워크플로우도 CDK안에서 작업가능합니다. 하지만 워크플로우 특성상 비주얼편집기를 사용하는게 더 편하기 때문에 StepFunction도 콘솔의 stepfunction studio를 이용하겠습니다.
이곳에서 적절한 Stepfunction 을 작성합니다.
### EventBridge Rule
EventBridge에서 생성한 stepfunction의 상태를 전송해줄 룰을 지정합니다.

이제 해당 스텝펑션의 상태가 변경될때, 채팅을 전달합니다.
## StepFunction 모니터링
Stepfunction의 모니터링은 스탭펑션의 로그레벨을 off에서 변경합니다.

그리고 스텝펑션 안에서 동작하는 서비스의 로그와 메트릭의 집계도 필요합니다.
이를 위해 필요한 메트릭과 로그를 대쉬보드화 하는게 좋습니다.
- ActivitiesStarted
- ActivitiesTimedOut
- ExecutionsStarted
- ExecutionsTimedOut
- LambdaFunctionsStarted
- LambdaFunctionsTimedOut
https://docs.aws.amazon.com/ko_kr/step-functions/latest/dg/procedure-cw-metrics.html
## CloudWatch Log to Third party
CloudWatch는 다음 타사 파트너를 위한 빠른 설정 환경을 제공합니다. 이 워크플로를 사용하려면 대상에 대한 대상 URL과 API 키만 제공하면 됩니다. CloudWatch는 Kinesis Data Firehose 전송 스트림 및 필요한 IAM 역할 생성을 포함한 나머지 설정을 처리합니다.
### 메트릭 스트림 생성

### 로그 스트림 생성
구독을 사용하면 CloudWatch Logs에서 로그 이벤트의 실시간 피드에 액세스하고 이를 Amazon Kinesis 스트림, Amazon Kinesis Data Firehose 스트림 또는 AWS Lambda와 같은 다른 서비스에 전달하여 사용자 지정 처리, 분석 또는 작업을 수행할 수 있습니다. 다른 시스템에 로딩 중입니다. 로그 이벤트가 수신 서비스로 전송되면 base64로 인코딩되고 gzip 형식으로 압축됩니다.
로그 이벤트 구독을 시작하려면 이벤트가 전달될 Kinesis Data Streams 스트림과 같은 수신 리소스를 생성합니다. 구독 필터는 AWS 리소스에 전달되는 로그 이벤트를 필터링하는 데 사용할 필터 패턴과 일치하는 로그 이벤트를 보낼 위치에 대한 정보를 정의합니다.
각 로그 그룹에는 최대 2개의 구독 필터가 연결될 수 있습니다.

### DataDog Agent를 통한 메트릭,로그전송
Lambda Layer에 DataDog Extention을 추가하여 지표와 로그를 수집할 수 있습니다. 아래 문서를 참고하세요.
https://docs.datadoghq.com/serverless/libraries_integrations/extension/