1 | const defaults = {
|
2 | logger: console.log,
|
3 | awsContext: false,
|
4 | omitPaths: [],
|
5 | mask: undefined,
|
6 | replacer: undefined
|
7 | };
|
8 | const inputOutputLoggerMiddleware = (opts = {})=>{
|
9 | const { logger, awsContext, omitPaths, mask, replacer } = {
|
10 | ...defaults,
|
11 | ...opts
|
12 | };
|
13 | if (typeof logger !== 'function') {
|
14 | throw new Error('logger must be a function', {
|
15 | cause: {
|
16 | package: '@middy/input-output-logger'
|
17 | }
|
18 | });
|
19 | }
|
20 | const omitPathTree = buildPathTree(omitPaths);
|
21 | const omitAndLog = (param, request)=>{
|
22 | const message = {
|
23 | [param]: request[param]
|
24 | };
|
25 | if (awsContext) {
|
26 | message.context = pick(request.context, awsContextKeys);
|
27 | }
|
28 | let cloneMessage = message;
|
29 | if (omitPaths.length) {
|
30 | cloneMessage = structuredClone(message, replacer);
|
31 | omit(cloneMessage, {
|
32 | [param]: omitPathTree[param]
|
33 | });
|
34 | }
|
35 | logger(cloneMessage);
|
36 | };
|
37 | const omit = (obj, pathTree = {})=>{
|
38 | if (Array.isArray(obj) && pathTree['[]']) {
|
39 | for(let i = 0, l = obj.length; i < l; i++){
|
40 | omit(obj[i], pathTree['[]']);
|
41 | }
|
42 | } else if (isObject(obj)) {
|
43 | for(const key in pathTree){
|
44 | if (pathTree[key] === true) {
|
45 | if (mask) {
|
46 | obj[key] = mask;
|
47 | } else {
|
48 | delete obj[key];
|
49 | }
|
50 | } else {
|
51 | omit(obj[key], pathTree[key]);
|
52 | }
|
53 | }
|
54 | }
|
55 | };
|
56 | const inputOutputLoggerMiddlewareBefore = async (request)=>omitAndLog('event', request);
|
57 | const inputOutputLoggerMiddlewareAfter = async (request)=>omitAndLog('response', request);
|
58 | const inputOutputLoggerMiddlewareOnError = async (request)=>{
|
59 | if (request.response === undefined) return;
|
60 | omitAndLog('response', request);
|
61 | };
|
62 | return {
|
63 | before: inputOutputLoggerMiddlewareBefore,
|
64 | after: inputOutputLoggerMiddlewareAfter,
|
65 | onError: inputOutputLoggerMiddlewareOnError
|
66 | };
|
67 | };
|
68 | const awsContextKeys = [
|
69 | 'functionName',
|
70 | 'functionVersion',
|
71 | 'invokedFunctionArn',
|
72 | 'memoryLimitInMB',
|
73 | 'awsRequestId',
|
74 | 'logGroupName',
|
75 | 'logStreamName',
|
76 | 'identity',
|
77 | 'clientContext',
|
78 | 'callbackWaitsForEmptyEventLoop'
|
79 | ];
|
80 | const pick = (originalObject = {}, keysToPick = [])=>{
|
81 | const newObject = {};
|
82 | for (const path of keysToPick){
|
83 | if (originalObject[path] !== undefined) {
|
84 | newObject[path] = originalObject[path];
|
85 | }
|
86 | }
|
87 | return newObject;
|
88 | };
|
89 | const buildPathTree = (paths)=>{
|
90 | const tree = {};
|
91 | for (let path of paths.sort().reverse()){
|
92 | if (!Array.isArray(path)) path = path.split('.');
|
93 | if (path.includes('__proto__')) continue;
|
94 | path.slice(0).reduce((a, b, idx)=>{
|
95 | if (idx < path.length - 1) {
|
96 | a[b] ??= {};
|
97 | return a[b];
|
98 | }
|
99 | a[b] = true;
|
100 | return true;
|
101 | }, tree);
|
102 | }
|
103 | return tree;
|
104 | };
|
105 | const isObject = (value)=>value && typeof value === 'object' && value.constructor === Object;
|
106 | export default inputOutputLoggerMiddleware;
|
107 |
|