Skip to content

Commit a703baa

Browse files
feat: initial commit
0 parents  commit a703baa

20 files changed

+7248
-0
lines changed

.commitlintrc.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
extends: ['@commitlint/config-conventional'],
3+
};

.editorconfig

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
[*]
7+
indent_style = space
8+
indent_size = 2
9+
end_of_line = lf
10+
charset = utf-8
11+
trim_trailing_whitespace = true
12+
insert_final_newline = true

.eslintignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
coverage

.eslintrc.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module.exports = {
2+
env: {
3+
es2021: true,
4+
node: true,
5+
},
6+
extends: [
7+
'airbnb-base',
8+
'eslint:recommended',
9+
'plugin:import/errors',
10+
'plugin:import/warnings',
11+
'plugin:import/typescript',
12+
'plugin:@typescript-eslint/recommended',
13+
'prettier/@typescript-eslint',
14+
'plugin:prettier/recommended',
15+
],
16+
parser: '@typescript-eslint/parser',
17+
parserOptions: {
18+
ecmaVersion: 12,
19+
sourceType: 'module',
20+
},
21+
plugins: ['@typescript-eslint'],
22+
rules: {
23+
'import/prefer-default-export': 'off',
24+
'import/extensions': 'off',
25+
'prettier/prettier': 'error',
26+
'no-shadow': 'off',
27+
'no-use-before-define': 'off',
28+
'lines-between-class-members': 'off',
29+
},
30+
settings: {
31+
'import/resolver': {
32+
typescript: {
33+
alwaysTryTypes: true, // always try to resolve types under `<roo/>@types` directory even it doesn't contain any source code, like `@types/unist`
34+
},
35+
},
36+
},
37+
};

.gitignore

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# package directories
2+
node_modules
3+
jspm_packages
4+
5+
# Serverless directories
6+
.serverless
7+
8+
# Webpack directories
9+
.webpack
10+
11+
# Yarn
12+
yarn-error.log
13+
14+
# Coverage
15+
coverage
16+
17+
# Dotenv
18+
!.env.example
19+
.env*
20+
.esbuild
21+
.env
22+
.DS_Store

.huskyrc.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const tasks = (arr) => arr.join(' && ');
2+
3+
module.exports = {
4+
hooks: {
5+
'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
6+
'pre-commit': tasks(['lint-staged', 'yarn test']),
7+
},
8+
};

.lintstagedrc.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
'*.{js,ts}': ['eslint --fix', 'prettier --write'],
3+
};

.prettierignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
coverage

.prettierrc.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
singleQuote: true,
3+
trailingComma: 'all',
4+
};

package.json

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"name": "powertools-logger-ts",
3+
"version": "1.0.0",
4+
"description": "AWS Powertools logger TypeScript integration example",
5+
"scripts": {
6+
"start": "serverless offline",
7+
"deploy:dev": "serverless deploy --aws-profile personal --stage dev",
8+
"deploy:production": "serverless deploy --aws-profile personal --stage production",
9+
"test": "exit 0",
10+
"lint": "eslint '*/**/*.{js,ts}'",
11+
"prettier": "prettier --write '*/**/*.{js,ts}'"
12+
},
13+
"engines": {
14+
"node": "16.x",
15+
"yarn": ">= 1.19.1"
16+
},
17+
"dependencies": {
18+
"@aws-lambda-powertools/logger": "1.4.1",
19+
"@middy/core": "3.6.2",
20+
"aws-lambda": "1.0.7",
21+
"http-errors": "2.0.0",
22+
"serverless": "3.23.0",
23+
"source-map-support": "0.5.19"
24+
},
25+
"devDependencies": {
26+
"@commitlint/cli": "11.0.0",
27+
"@commitlint/config-conventional": "11.0.0",
28+
"@serverless/typescript": "3.21.0",
29+
"@types/aws-lambda": "8.10.108",
30+
"@types/lodash": "4.14.186",
31+
"@types/node": "14.14.6",
32+
"@typescript-eslint/eslint-plugin": "4.13.0",
33+
"@typescript-eslint/parser": "4.13.0",
34+
"commitlint": "11.0.0",
35+
"esbuild": "0.15.12",
36+
"eslint": "7.17.0",
37+
"eslint-config-airbnb-base": "14.2.1",
38+
"eslint-config-prettier": "7.1.0",
39+
"eslint-import-resolver-typescript": "2.3.0",
40+
"eslint-plugin-import": "2.22.1",
41+
"eslint-plugin-prettier": "3.3.1",
42+
"husky": "4.3.7",
43+
"lint-staged": "10.5.3",
44+
"prettier": "2.2.1",
45+
"serverless-esbuild": "1.33.0",
46+
"serverless-offline": "11.2.1",
47+
"serverless-prune-plugin": "1.5.0",
48+
"serverless-webpack": "5.2.0",
49+
"ts-loader": "8.0.10",
50+
"ts-node": "9.1.1",
51+
"tsconfig-paths-webpack-plugin": "3.3.0",
52+
"typescript": "4.0.5"
53+
},
54+
"author": "Christian Rich",
55+
"license": "MIT"
56+
}

serverless.yaml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
service: ${file(./package.json):name}
2+
frameworkVersion: "3"
3+
plugins:
4+
- serverless-esbuild
5+
- serverless-offline
6+
- serverless-prune-plugin
7+
package:
8+
individually: true
9+
provider:
10+
name: aws
11+
region: ap-southeast-2
12+
runtime: nodejs16.x
13+
environment:
14+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1"
15+
NODE_OPTIONS: "--enable-source-maps --stack-trace-limit=1000"
16+
STAGE: ${sls:stage}
17+
custom:
18+
serverless-offline:
19+
allowCache: true
20+
useChildProcesses: true
21+
httpPort: 3000
22+
prune:
23+
automatic: true
24+
number: 3
25+
esbuild:
26+
bundle: true
27+
minify: false
28+
sourcemap: true
29+
exclude:
30+
- aws-sdk
31+
target: node16
32+
platform: node
33+
concurrency: 10
34+
functions:
35+
myFunction:
36+
handler: src/handlers/my-handler.handler
37+
events:
38+
- http:
39+
path: /
40+
method: get
41+
cors: true

src/handlers/my-handler.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { APIGatewayProxyResult } from 'aws-lambda';
2+
import logger from '@/services/logger';
3+
import middy from '@middy/core';
4+
import { setLoggerContext } from '@/middleware/set-logger-context';
5+
6+
export const baseHandler = async (): Promise<APIGatewayProxyResult> => {
7+
try {
8+
logger.info('Invoking Lambda handler function', { data: { type: 'user' } });
9+
return {
10+
statusCode: 200,
11+
body: JSON.stringify({ OK: 200 }),
12+
};
13+
} catch (error) {
14+
const { message } = error;
15+
logger.error('Error Invoking Lambda handler function', {
16+
data: { message },
17+
});
18+
return {
19+
statusCode: 500,
20+
body: JSON.stringify({ message }),
21+
};
22+
}
23+
};
24+
25+
export const handler = middy(baseHandler).use(setLoggerContext(logger));

src/middleware/set-logger-context.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Logger } from '@aws-lambda-powertools/logger';
2+
import middy from '@middy/core';
3+
import {
4+
APIGatewayProxyEvent,
5+
APIGatewayProxyResult,
6+
Context,
7+
} from 'aws-lambda';
8+
9+
export const setLoggerContext = (
10+
logger: Logger,
11+
): middy.MiddlewareObj<APIGatewayProxyEvent, APIGatewayProxyResult> => {
12+
const before = (request: middy.Request): void => {
13+
const { context }: { context: Context } = request;
14+
logger.addContext(context);
15+
};
16+
17+
return {
18+
before,
19+
};
20+
};

src/services/env/index.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export const isLocalhost = (env: NodeJS.ProcessEnv = process.env): boolean =>
2+
!isAWS(env);
3+
4+
export const isOffline = (env: NodeJS.ProcessEnv = process.env): boolean =>
5+
env.IS_OFFLINE === 'true';
6+
7+
export const isAWS = (env: NodeJS.ProcessEnv = process.env): boolean =>
8+
'AWS_LAMBDA_FUNCTION_NAME' in env &&
9+
'LAMBDA_TASK_ROOT' in env &&
10+
!isOffline();
11+
12+
export const getNodeEnv = (
13+
env: NodeJS.ProcessEnv = process.env,
14+
): string | undefined => env.NODE_ENV;

src/services/logger/formatters/aws.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { LogFormatter } from '@aws-lambda-powertools/logger';
2+
import {
3+
LogAttributes,
4+
UnformattedAttributes,
5+
} from '@aws-lambda-powertools/logger/lib/types';
6+
7+
export class DetailedLogFormatter extends LogFormatter {
8+
public formatAttributes(attr: UnformattedAttributes): LogAttributes {
9+
return {
10+
message: attr.message,
11+
service: attr.serviceName,
12+
environment: attr.environment,
13+
awsRegion: attr.awsRegion,
14+
correlationIds: {
15+
awsRequestId: attr.lambdaContext?.awsRequestId,
16+
xRayTraceId: attr.xRayTraceId,
17+
},
18+
lambdaFunction: {
19+
name: attr.lambdaContext?.functionName,
20+
arn: attr.lambdaContext?.invokedFunctionArn,
21+
memoryLimitInMB: attr.lambdaContext?.memoryLimitInMB,
22+
version: attr.lambdaContext?.functionVersion,
23+
coldStart: attr.lambdaContext?.coldStart,
24+
},
25+
logLevel: attr.logLevel,
26+
timestamp: this.formatTimestamp(attr.timestamp),
27+
logger: {
28+
sampleRateValue: attr.sampleRateValue,
29+
},
30+
};
31+
}
32+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { LogFormatter } from '@aws-lambda-powertools/logger';
2+
import {
3+
LogAttributes,
4+
UnformattedAttributes,
5+
} from '@aws-lambda-powertools/logger/lib/types';
6+
7+
export class LocalhostLogFormatter extends LogFormatter {
8+
// eslint-disable-next-line class-methods-use-this
9+
public formatAttributes(attr: UnformattedAttributes): LogAttributes {
10+
return {
11+
logLevel: attr.logLevel,
12+
message: attr.message,
13+
};
14+
}
15+
}

src/services/logger/index.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { LogFormatter, Logger } from '@aws-lambda-powertools/logger';
2+
import { isAWS } from '../env';
3+
import { DetailedLogFormatter } from './formatters/aws';
4+
import { LocalhostLogFormatter } from './formatters/local';
5+
6+
// Log format is driven by the current runtime environment
7+
const logFormatter: LogFormatter = isAWS()
8+
? new DetailedLogFormatter() // Highly detailed logs for AWS
9+
: new LocalhostLogFormatter(); // Just a few key fields for localhost
10+
11+
const logger: Logger = new Logger({
12+
logFormatter,
13+
logLevel: process.env.LOG_LEVEL || 'DEBUG',
14+
serviceName: process.env.AWS_LAMBDA_FUNCTION_NAME,
15+
});
16+
17+
// Singleton getter
18+
const getLogger = (): Logger => logger;
19+
export default getLogger();

tsconfig.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"extends": "./tsconfig.paths.json",
3+
"compilerOptions": {
4+
"lib": ["ESNext"],
5+
"moduleResolution": "node",
6+
"noUnusedLocals": true,
7+
"noUnusedParameters": true,
8+
"removeComments": true,
9+
"sourceMap": true,
10+
"target": "es2017",
11+
"outDir": "lib",
12+
"esModuleInterop": true,
13+
"resolveJsonModule": true
14+
},
15+
"include": ["src/**/*.ts"],
16+
"exclude": [
17+
"node_modules/**/*",
18+
".serverless/**/*",
19+
".webpack/**/*",
20+
"_warmup/**/*",
21+
".vscode/**/*"
22+
]
23+
}

tsconfig.paths.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"baseUrl": ".",
4+
"paths": {
5+
"@/*": [
6+
"src/*"
7+
]
8+
}
9+
}
10+
}

0 commit comments

Comments
 (0)