@@ -4,12 +4,13 @@ const { LambdaClient, InvokeCommand } = require("@aws-sdk/client-lambda");
4
4
const cwl = new CloudWatchLogsClient ( ) ;
5
5
const lambda = new LambdaClient ( { apiVersion : '2015-03-31' } ) ; // Update to the appropriate Lambda API version you require
6
6
const maxRetryCounter = 3 ;
7
+ const timeoutThreshold = 12000 ;
7
8
8
- async function createSubscriptionFilter ( lambdaLogGroupName , destinationArn , roleArn ) {
9
- var params = { } ;
9
+ async function createSubscriptionFilter ( lambdaLogGroupName , destinationArn , roleArn , additionalArgs ) {
10
+ var params = { } ;
10
11
if ( destinationArn . startsWith ( "arn:aws:lambda" ) ) {
11
12
params = {
12
- destinationArn : destinationArn ,
13
+ destinationArn : destinationArn ,
13
14
filterName : 'SumoLGLBDFilter' ,
14
15
filterPattern : '' ,
15
16
logGroupName : lambdaLogGroupName
@@ -28,6 +29,7 @@ async function createSubscriptionFilter(lambdaLogGroupName, destinationArn, role
28
29
try {
29
30
const cmd = new PutSubscriptionFilterCommand ( params ) ;
30
31
await cwl . send ( cmd ) ;
32
+ additionalArgs . subscribeCount += 1
31
33
console . log ( "Successfully subscribed logGroup: " , lambdaLogGroupName ) ;
32
34
} catch ( err ) {
33
35
console . log ( "Error in subscribing" , lambdaLogGroupName , err ) ;
@@ -58,7 +60,7 @@ function filterLogGroups(event, logGroupRegex) {
58
60
return false ;
59
61
}
60
62
61
- async function subscribeExistingLogGroups ( logGroups , retryCounter ) {
63
+ async function subscribeExistingLogGroups ( logGroups , retryCounter , additionalArgs ) {
62
64
var logGroupRegex = new RegExp ( process . env . LOG_GROUP_PATTERN , "i" ) ;
63
65
var destinationArn = process . env . DESTINATION_ARN ;
64
66
var roleArn = process . env . ROLE_ARN ;
@@ -70,8 +72,8 @@ async function subscribeExistingLogGroups(logGroups, retryCounter) {
70
72
console . log ( "Unmatched logGroup: " , logGroupName ) ;
71
73
return Promise . resolve ( ) ;
72
74
} else {
73
- return createSubscriptionFilter ( logGroupName , destinationArn , roleArn ) . catch ( function ( err ) {
74
- if ( err && err . code == "ThrottlingException ") {
75
+ return createSubscriptionFilter ( logGroupName , destinationArn , roleArn , additionalArgs ) . catch ( function ( err ) {
76
+ if ( err && err . message === "Rate exceeded ") {
75
77
failedLogGroupNames . push ( { logGroupName : logGroupName } ) ;
76
78
}
77
79
} ) ;
@@ -80,80 +82,110 @@ async function subscribeExistingLogGroups(logGroups, retryCounter) {
80
82
81
83
if ( retryCounter <= maxRetryCounter && failedLogGroupNames . length > 0 ) {
82
84
console . log ( "Retrying Subscription for Failed Log Groups due to throttling with counter number as " + retryCounter ) ;
83
- await subscribeExistingLogGroups ( failedLogGroupNames , retryCounter + 1 ) ;
85
+ await subscribeExistingLogGroups ( failedLogGroupNames , retryCounter + 1 , additionalArgs ) ;
84
86
}
85
87
}
86
88
87
- async function processExistingLogGroups ( token , context , errorHandler ) {
89
+ async function processExistingLogGroups ( context , token , additionalArgs , errorHandler ) {
88
90
var params = { limit : 50 } ;
89
91
if ( token ) {
90
92
params = {
91
93
limit : 50 ,
92
94
nextToken : token
93
95
} ;
94
96
}
95
-
97
+
96
98
try {
99
+ console . log ( "Previous record count " + additionalArgs . recordCount ) ;
97
100
const data = await cwl . send ( new DescribeLogGroupsCommand ( params ) ) ;
98
- console . log (
99
- "fetched logGroups: " + data . logGroups . length + " nextToken: " + data . nextToken
100
- ) ;
101
- await subscribeExistingLogGroups ( data . logGroups , 1 ) ;
102
-
101
+ additionalArgs . recordCount += data . logGroups . length ;
102
+ console . log ( "Updated record count " + additionalArgs . recordCount ) ;
103
+ await subscribeExistingLogGroups ( data . logGroups , 1 , additionalArgs ) ;
104
+ console . log ( "Updated subscribeCount " + additionalArgs . subscribeCount ) ;
103
105
if ( data . nextToken ) {
104
- console . log (
105
- "Log Groups remaining...Calling the lambda again with token " + data . nextToken
106
- ) ;
107
- await invoke_lambda ( context , data . nextToken , errorHandler ) ;
108
- console . log ( "Lambda invoke complete with token " + data . nextToken ) ;
106
+ const remainingTime = context . getRemainingTimeInMillis ( ) ; // 60000
107
+ const diffTime = remainingTime - timeoutThreshold // 14552-12000=2792
108
+ if ( diffTime < timeoutThreshold ) {
109
+ additionalArgs . invokeCount += 1
110
+ console . log ( "Lambda invoke complete with token " + data . nextToken ) ;
111
+ console . log ( "InvokeCount " + additionalArgs . invokeCount ) ;
112
+ await invoke_lambda ( context , data . nextToken , additionalArgs , errorHandler ) ;
113
+ return
114
+ }
115
+ console . log ( "Remaining time " + remainingTime ) ;
116
+ console . log ( "Log Groups remaining...Calling the lambda again with token " + data . nextToken ) ;
117
+ await processExistingLogGroups ( context , data . nextToken , additionalArgs , errorHandler )
109
118
} else {
110
- console . log ( "All Log Groups are subscribed to Destination Type " + process . env . DESTINATION_ARN ) ;
119
+ console . log ( "Total " + additionalArgs . subscribeCount + " out of " + additionalArgs . recordCount
120
+ + " Log Groups are subscribed to Destination Type "
121
+ + process . env . DESTINATION_ARN ) ;
122
+ console . log ( "Last invokeCount " + additionalArgs . invokeCount ) ;
111
123
errorHandler ( null , "Success" ) ;
112
124
}
113
125
} catch ( err ) {
114
126
errorHandler ( err , "Error in fetching logGroups" ) ;
115
127
}
116
128
}
117
-
118
- async function invoke_lambda ( context , token , errorHandler ) {
119
- var payload = { "existingLogs" : "true" , "token" : token } ;
120
- try {
121
- await lambda . send ( new InvokeCommand ( {
122
- InvocationType : 'Event' ,
123
- FunctionName : context . functionName ,
124
- Payload : JSON . stringify ( payload )
125
- } ) ) ;
126
- } catch ( err ) {
129
+
130
+ async function invoke_lambda ( context , token , additionalArgs , errorHandler ) {
131
+ var payload = { "existingLogs" : "true" , "token" : token , "additionalArgs" : additionalArgs } ;
132
+ try {
133
+ await lambda . send ( new InvokeCommand ( {
134
+ InvocationType : 'Event' ,
135
+ FunctionName : context . functionName ,
136
+ Payload : JSON . stringify ( payload )
137
+ } ) ) ;
138
+ } catch ( err ) {
127
139
errorHandler ( err , "Error invoking Lambda" ) ;
128
- }
129
140
}
130
-
131
- async function processEvents ( env , event , errorHandler ) {
132
- var logGroupName = event . detail . requestParameters . logGroupName ;
133
- if ( filterLogGroups ( event , env . LOG_GROUP_PATTERN ) ) {
134
- console . log ( "Subscribing: " , logGroupName , env . DESTINATION_ARN ) ;
135
- await createSubscriptionFilter ( logGroupName , env . DESTINATION_ARN , env . ROLE_ARN )
136
- . catch ( function ( err ) {
137
- errorHandler ( err , "Error in Subscribing." ) ;
138
- } ) ;
139
- } else {
141
+ }
142
+
143
+ async function delay ( ms ) {
144
+ return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
145
+ }
146
+
147
+ async function processEvents ( env , event , additionalArgs , errorHandler , retryCounter = 0 ) {
148
+ var logGroupName = event . detail . requestParameters . logGroupName ;
149
+ if ( filterLogGroups ( event , env . LOG_GROUP_PATTERN ) ) {
150
+ console . log ( "Subscribing: " , logGroupName , env . DESTINATION_ARN ) ;
151
+ try {
152
+ await createSubscriptionFilter ( logGroupName , env . DESTINATION_ARN , env . ROLE_ARN , additionalArgs ) ;
153
+ } catch ( err ) {
154
+ errorHandler ( err , "Error in Subscribing." ) ;
155
+ if ( err && err . message === "Rate exceeded" && retryCounter <= maxRetryCounter ) {
156
+ retryCounter += 1
157
+ const delayTime = Math . pow ( 2 , retryCounter ) * 1000 ; // Exponential backoff
158
+ console . log ( `ThrottlingException encountered. Retrying in ${ delayTime } ms...Attempt ${ retryCounter } /${ maxRetryCounter } ` ) ;
159
+ await delay ( delayTime ) ;
160
+ await processEvents ( env , event , additionalArgs , errorHandler , retryCounter ) ;
161
+ }
162
+ } ;
163
+ } else {
140
164
console . log ( "Unmatched: " , logGroupName , env . DESTINATION_ARN ) ;
141
- }
142
165
}
143
-
144
- exports . handler = async function ( event , context , callback ) {
145
- console . log ( "Invoking Log Group connector function" ) ;
146
- function errorHandler ( err , msg ) {
147
- if ( err ) {
148
- console . log ( err , msg ) ;
166
+ }
167
+
168
+ exports . handler = async function ( event , context , callback ) {
169
+ let additionalArgs = {
170
+ recordCount : 0 ,
171
+ subscribeCount : 0 ,
172
+ invokeCount : 0
173
+ } ;
174
+ if ( event . additionalArgs ) {
175
+ additionalArgs = event . additionalArgs
176
+ }
177
+ console . log ( "Invoking Log Group connector function" ) ;
178
+ function errorHandler ( err , msg ) {
179
+ if ( err ) {
180
+ console . log ( err , msg ) ;
149
181
callback ( err ) ;
150
182
} else {
151
183
callback ( null , "Success" ) ;
152
184
}
153
185
}
154
186
if ( event . existingLogs == "true" ) {
155
- await processExistingLogGroups ( event . token , context , errorHandler ) ;
187
+ await processExistingLogGroups ( context , event . token , additionalArgs , errorHandler ) ;
156
188
} else {
157
- await processEvents ( process . env , event , errorHandler ) ;
189
+ await processEvents ( process . env , event , additionalArgs , errorHandler ) ;
158
190
}
159
- } ;
191
+ } ;
0 commit comments