@@ -20,6 +20,7 @@ import org.opensearch.alerting.model.DocumentLevelTriggerRunResult
20
20
import org.opensearch.alerting.model.InputRunResults
21
21
import org.opensearch.alerting.model.MonitorMetadata
22
22
import org.opensearch.alerting.model.MonitorRunResult
23
+ import org.opensearch.alerting.model.userErrorMessage
23
24
import org.opensearch.alerting.opensearchapi.suspendUntil
24
25
import org.opensearch.alerting.script.DocumentLevelTriggerExecutionContext
25
26
import org.opensearch.alerting.util.AlertingException
@@ -192,63 +193,88 @@ object DocumentLevelMonitorRunner : MonitorRunner() {
192
193
}
193
194
}
194
195
monitorResult = monitorResult.copy(inputResults = InputRunResults (listOf (inputRunResults)))
195
- } catch (e: Exception ) {
196
- logger.error(" Failed to start Document-level-monitor ${monitor.name} " , e)
197
- val alertingException = AlertingException (
198
- ExceptionsHelper .unwrapCause(e).cause?.message.toString(),
199
- RestStatus .INTERNAL_SERVER_ERROR ,
200
- e
201
- )
202
- monitorResult = monitorResult.copy(error = alertingException, inputResults = InputRunResults (emptyList(), alertingException))
203
- }
204
196
205
- /*
206
- populate the map queryToDocIds with pairs of <DocLevelQuery object from queries in monitor metadata &
207
- list of matched docId from inputRunResults>
208
- this fixes the issue of passing id, name, tags fields of DocLevelQuery object correctly to TriggerExpressionParser
209
- */
210
- queries.forEach {
211
- if (inputRunResults.containsKey(it.id)) {
212
- queryToDocIds[it] = inputRunResults[it.id]!!
197
+ /*
198
+ populate the map queryToDocIds with pairs of <DocLevelQuery object from queries in monitor metadata &
199
+ list of matched docId from inputRunResults>
200
+ this fixes the issue of passing id, name, tags fields of DocLevelQuery object correctly to TriggerExpressionParser
201
+ */
202
+ queries.forEach {
203
+ if (inputRunResults.containsKey(it.id)) {
204
+ queryToDocIds[it] = inputRunResults[it.id]!!
205
+ }
213
206
}
214
- }
215
207
216
- val idQueryMap: Map <String , DocLevelQuery > = queries.associateBy { it.id }
208
+ val idQueryMap: Map <String , DocLevelQuery > = queries.associateBy { it.id }
217
209
218
- val triggerResults = mutableMapOf<String , DocumentLevelTriggerRunResult >()
219
- // If there are no triggers defined, we still want to generate findings
220
- if (monitor.triggers.isEmpty()) {
221
- if (dryrun == false && monitor.id != Monitor .NO_ID ) {
222
- docsToQueries.forEach {
223
- val triggeredQueries = it.value.map { queryId -> idQueryMap[queryId]!! }
224
- createFindings(monitor, monitorCtx, triggeredQueries, it.key, true )
210
+ val triggerResults = mutableMapOf<String , DocumentLevelTriggerRunResult >()
211
+ // If there are no triggers defined, we still want to generate findings
212
+ if (monitor.triggers.isEmpty()) {
213
+ if (dryrun == false && monitor.id != Monitor .NO_ID ) {
214
+ docsToQueries.forEach {
215
+ val triggeredQueries = it.value.map { queryId -> idQueryMap[queryId]!! }
216
+ createFindings(monitor, monitorCtx, triggeredQueries, it.key, true )
217
+ }
218
+ }
219
+ } else {
220
+ monitor.triggers.forEach {
221
+ triggerResults[it.id] = runForEachDocTrigger(
222
+ monitorCtx,
223
+ monitorResult,
224
+ it as DocumentLevelTrigger ,
225
+ monitor,
226
+ idQueryMap,
227
+ docsToQueries,
228
+ queryToDocIds,
229
+ dryrun
230
+ )
225
231
}
226
232
}
227
- } else {
228
- monitor.triggers.forEach {
229
- triggerResults[it.id] = runForEachDocTrigger(
230
- monitorCtx,
231
- monitorResult,
232
- it as DocumentLevelTrigger ,
233
- monitor,
234
- idQueryMap,
235
- docsToQueries,
236
- queryToDocIds ,
237
- dryrun
233
+ // Don't update monitor if this is a test monitor
234
+ if ( ! isTempMonitor) {
235
+ // If any error happened during trigger execution, upsert monitor error alert
236
+ val errorMessage = constructErrorMessageFromTriggerResults(triggerResults = triggerResults)
237
+ if (errorMessage.isNotEmpty()) {
238
+ monitorCtx.alertService !! .upsertMonitorErrorAlert(monitor = monitor, errorMessage = errorMessage)
239
+ }
240
+
241
+ MonitorMetadataService .upsertMetadata(
242
+ monitorMetadata.copy(lastRunContext = updatedLastRunContext) ,
243
+ true
238
244
)
239
245
}
240
- }
241
246
242
- // Don't update monitor if this is a test monitor
243
- if (! isTempMonitor) {
244
- MonitorMetadataService .upsertMetadata(
245
- monitorMetadata.copy(lastRunContext = updatedLastRunContext),
246
- true
247
+ // TODO: Update the Document as part of the Trigger and return back the trigger action result
248
+ return monitorResult.copy(triggerResults = triggerResults)
249
+ } catch (e: Exception ) {
250
+ val errorMessage = ExceptionsHelper .detailedMessage(e)
251
+ monitorCtx.alertService!! .upsertMonitorErrorAlert(monitor, errorMessage)
252
+ logger.error(" Failed running Document-level-monitor ${monitor.name} " , e)
253
+ val alertingException = AlertingException (
254
+ errorMessage,
255
+ RestStatus .INTERNAL_SERVER_ERROR ,
256
+ e
247
257
)
258
+ return monitorResult.copy(error = alertingException, inputResults = InputRunResults (emptyList(), alertingException))
248
259
}
260
+ }
249
261
250
- // TODO: Update the Document as part of the Trigger and return back the trigger action result
251
- return monitorResult.copy(triggerResults = triggerResults)
262
+ private fun constructErrorMessageFromTriggerResults (
263
+ triggerResults : MutableMap <String , DocumentLevelTriggerRunResult >? = null
264
+ ): String {
265
+ var errorMessage = " "
266
+ if (triggerResults != null ) {
267
+ val triggersErrorBuilder = StringBuilder ()
268
+ triggerResults.forEach {
269
+ if (it.value.error != null ) {
270
+ triggersErrorBuilder.append(" [${it.key} ]: [${it.value.error!! .userErrorMessage()} ]" ).append(" | " )
271
+ }
272
+ }
273
+ if (triggersErrorBuilder.isNotEmpty()) {
274
+ errorMessage = " Trigger errors: $triggersErrorBuilder "
275
+ }
276
+ }
277
+ return errorMessage
252
278
}
253
279
254
280
private suspend fun runForEachDocTrigger (
@@ -295,16 +321,6 @@ object DocumentLevelMonitorRunner : MonitorRunner() {
295
321
alerts.add(alert)
296
322
}
297
323
298
- if (findingDocPairs.isEmpty() && monitorResult.error != null ) {
299
- val alert = monitorCtx.alertService!! .composeDocLevelAlert(
300
- listOf (),
301
- listOf (),
302
- triggerCtx,
303
- monitorResult.alertError() ? : triggerResult.alertError()
304
- )
305
- alerts.add(alert)
306
- }
307
-
308
324
val shouldDefaultToPerExecution = defaultToPerExecutionAction(
309
325
monitorCtx.maxActionableAlertCount,
310
326
monitorId = monitor.id,
@@ -576,8 +592,15 @@ object DocumentLevelMonitorRunner : MonitorRunner() {
576
592
searchSourceBuilder.query(boolQueryBuilder)
577
593
searchRequest.source(searchSourceBuilder)
578
594
579
- val response: SearchResponse = monitorCtx.client!! .suspendUntil {
580
- monitorCtx.client!! .execute(SearchAction .INSTANCE , searchRequest, it)
595
+ var response: SearchResponse
596
+ try {
597
+ response = monitorCtx.client!! .suspendUntil {
598
+ monitorCtx.client!! .execute(SearchAction .INSTANCE , searchRequest, it)
599
+ }
600
+ } catch (e: Exception ) {
601
+ throw IllegalStateException (
602
+ " Failed to run percolate search for sourceIndex [$index ] and queryIndex [$queryIndex ] for ${docs.size} document(s)" , e
603
+ )
581
604
}
582
605
583
606
if (response.status() != = RestStatus .OK ) {
0 commit comments