@@ -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,53 +193,88 @@ object DocumentLevelMonitorRunner : MonitorRunner() {
192
193
}
193
194
}
194
195
monitorResult = monitorResult.copy(inputResults = InputRunResults (listOf (inputRunResults)))
196
+
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
+ }
206
+ }
207
+
208
+ val idQueryMap: Map <String , DocLevelQuery > = queries.associateBy { it.id }
209
+
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
+ )
231
+ }
232
+ }
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
244
+ )
245
+ }
246
+
247
+ // TODO: Update the Document as part of the Trigger and return back the trigger action result
248
+ return monitorResult.copy(triggerResults = triggerResults)
195
249
} catch (e: Exception ) {
196
- logger.error(" Failed to start Document-level-monitor ${monitor.name} " , e)
250
+ val errorMessage = ExceptionsHelper .detailedMessage(e)
251
+ monitorCtx.alertService!! .upsertMonitorErrorAlert(monitor, errorMessage)
252
+ logger.error(" Failed running Document-level-monitor ${monitor.name} " , e)
197
253
val alertingException = AlertingException (
198
- ExceptionsHelper .unwrapCause(e).cause?.message.toString() ,
254
+ errorMessage ,
199
255
RestStatus .INTERNAL_SERVER_ERROR ,
200
256
e
201
257
)
202
- monitorResult = monitorResult.copy(error = alertingException, inputResults = InputRunResults (emptyList(), alertingException))
258
+ return monitorResult.copy(error = alertingException, inputResults = InputRunResults (emptyList(), alertingException))
203
259
}
260
+ }
204
261
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]!!
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 "
213
275
}
214
276
}
215
-
216
- val idQueryMap: Map <String , DocLevelQuery > = queries.associateBy { it.id }
217
-
218
- val triggerResults = mutableMapOf<String , DocumentLevelTriggerRunResult >()
219
- monitor.triggers.forEach {
220
- triggerResults[it.id] = runForEachDocTrigger(
221
- monitorCtx,
222
- monitorResult,
223
- it as DocumentLevelTrigger ,
224
- monitor,
225
- idQueryMap,
226
- docsToQueries,
227
- queryToDocIds,
228
- dryrun
229
- )
230
- }
231
-
232
- // Don't update monitor if this is a test monitor
233
- if (! isTempMonitor) {
234
- MonitorMetadataService .upsertMetadata(
235
- monitorMetadata.copy(lastRunContext = updatedLastRunContext),
236
- true
237
- )
238
- }
239
-
240
- // TODO: Update the Document as part of the Trigger and return back the trigger action result
241
- return monitorResult.copy(triggerResults = triggerResults)
277
+ return errorMessage
242
278
}
243
279
244
280
private suspend fun runForEachDocTrigger (
@@ -285,16 +321,6 @@ object DocumentLevelMonitorRunner : MonitorRunner() {
285
321
alerts.add(alert)
286
322
}
287
323
288
- if (findingDocPairs.isEmpty() && monitorResult.error != null ) {
289
- val alert = monitorCtx.alertService!! .composeDocLevelAlert(
290
- listOf (),
291
- listOf (),
292
- triggerCtx,
293
- monitorResult.alertError() ? : triggerResult.alertError()
294
- )
295
- alerts.add(alert)
296
- }
297
-
298
324
val shouldDefaultToPerExecution = defaultToPerExecutionAction(
299
325
monitorCtx.maxActionableAlertCount,
300
326
monitorId = monitor.id,
@@ -567,8 +593,15 @@ object DocumentLevelMonitorRunner : MonitorRunner() {
567
593
searchSourceBuilder.query(boolQueryBuilder)
568
594
searchRequest.source(searchSourceBuilder)
569
595
570
- val response: SearchResponse = monitorCtx.client!! .suspendUntil {
571
- monitorCtx.client!! .execute(SearchAction .INSTANCE , searchRequest, it)
596
+ var response: SearchResponse
597
+ try {
598
+ response = monitorCtx.client!! .suspendUntil {
599
+ monitorCtx.client!! .execute(SearchAction .INSTANCE , searchRequest, it)
600
+ }
601
+ } catch (e: Exception ) {
602
+ throw IllegalStateException (
603
+ " Failed to run percolate search for sourceIndex [$index ] and queryIndex [$queryIndex ] for ${docs.size} document(s)" , e
604
+ )
572
605
}
573
606
574
607
if (response.status() != = RestStatus .OK ) {
0 commit comments