Help requested: Obtaining alert generator ID

Hello OpenSearch Community,

Context:
I am working on a project involving IoT data, where data from various devices is sent to AWS IoT Core and then indexed into an OpenSearch Provisioned Instance (version 2.7, latest available on AWS).
I have successfully set up the infrastructure, connected my devices and I’m indexing data without issues. Data can be visualized on dashboards, etc.
I also created a Per query monitor that checks the battery voltage of these devices and triggers an alert when the voltage goes above 4 volts (It’s always above 4 volts, I’m using that value just for testing).

Issue:
I am looking for a way to associate each alert with the specific device that generated it, so that I can take appropriate action. I’m using a CURL to the API to get the active alerts, however, the response does not include this information (device_data.id).

Configuration:

Curl to get last indexed data (for reference)
curl -XGET \
      -u 'my-user:My-password' \
      -G \
      -d 'size=1' \
      -d 'sort=timestamp:desc' \
      'https://os.fakedomain.com/my-data-index/_search'
Response of that curl
{
  "_shards": {
    "failed": 0,
    "skipped": 0,
    "successful": 5,
    "total": 5
  },
  "hits": {
    "hits": [
      {
        "_id": "65xxxxxxxxxxxxxxxxxxxxxxxx",
        "_index": "my-data-index",
        "_score": null,
        "_source": {
          "client_data": {
            "id": "15yyyyyyyyyyyyyyyyyyyyyyy",
            "name": "Some Name"
          },
          "device_data": {
            "battery": {
              "external_voltage": 11.891475,
              "voltage": 4.194689869880676
            },
            "id": "0x324zzzzzzzzzzzzzzz",
            "name": "Water Pump 2"
          },
          "location": "-66.66666,-66.66666",
          "sensor_data": {
            "Current (A)": 0.1,
            "Voltage (V)": 225.1
          },
          "timestamp": 1693946818753
        },
        "sort": [
          1693946818753
        ]
      }
    ],
    "max_score": null,
    "total": {
      "relation": "gte",
      "value": 10000
    }
  },
  "timed_out": false,
  "took": 1317
}
Alert Configuration
{
  "name": "Test Monitor - Overvoltage (Battery)",
  "type": "monitor",
  "monitor_type": "query_level_monitor",
  "enabled": false,
  "schedule": {
    "period": {
      "unit": "MINUTES",
      "interval": 1
    }
  },
  "inputs": [
    {
      "search": {
        "indices": [
          "my-data-index"
        ],
        "query": {
          "size": 0,
          "aggregations": {
            "metric": {
              "max": {
                "field": "device_data.battery.voltage"
              }
            },
            "terms_agg": {
              "terms": {
                "field": "device_data.id"
              }
            }
          },
          "query": {
            "bool": {
              "filter": [
                {
                  "range": {
                    "datetime": {
                      "gte": "{{period_end}}||-1m",
                      "lte": "{{period_end}}",
                      "format": "epoch_millis"
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  ],
  "triggers": [
    {
      "query_level_trigger": {
        "id": "dat6Yxxxxxxxxxxxxxx",
        "name": "Test Trigger - Voltage over 4",
        "severity": "1",
        "condition": {
          "script": {
            "source": "return ctx.results[0].aggregations.metric.value == null ? false : ctx.results[0].aggregations.metric.value > 4",
            "lang": "painless"
          }
        },
        "actions": [
          {
            "id": "dqtxxxxxxxxxxxxxxxxx",
            "name": "Notify Slack Test",
            "destination_id": "hWcyyyyyyyyyyyyyyyyy",
            "message_template": {
              "source": "{\"channel\": \"GGGGGGG\", \"text\": \"Test\\n {{ctx}}\"}",
              "lang": "mustache"
            },
            "throttle_enabled": false,
            "subject_template": {
              "source": "",
              "lang": "mustache"
            }
          }
        ]
      }
    }
  ],
  "ui_metadata": {
    "schedule": {
      "timezone": null,
      "frequency": "interval",
      "period": {
        "unit": "MINUTES",
        "interval": 1
      },
      "daily": 0,
      "weekly": {
        "tue": false,
        "wed": false,
        "thur": false,
        "sat": false,
        "fri": false,
        "mon": false,
        "sun": false
      },
      "monthly": {
        "type": "day",
        "day": 1
      },
      "cronExpression": "0 */1 * * *"
    },
    "monitor_type": "query_level_monitor",
    "search": {
      "searchType": "graph",
      "timeField": "datetime",
      "aggregations": [
        {
          "aggregationType": "max",
          "fieldName": "device_data.battery.voltage"
        }
      ],
      "groupBy": [
        "device_data.id"
      ],
      "bucketValue": 1,
      "bucketUnitOfTime": "m",
      "filters": []
    }
  }
}
Curl to get Alerts
curl -XGET \
      -H 'Content-Type: application/json' \
      -u 'my-user:My-password' \
      'https://os.fakedomain.com/_plugins/_alerting/monitors/alerts'
Response of Alerts CURL
{
  "alerts": [
    {
      "acknowledged_time": null,
      "action_execution_results": [
        {
          "action_id": "dqt6xxxxxxxxxxxxx",
          "last_execution_time": 1693940207312,
          "throttled_count": 0
        }
      ],
      "alert_history": [],
      "end_time": null,
      "error_message": null,
      "finding_ids": [],
      "id": "cNClZxxxxxxxxxxxxx",
      "last_notification_time": 1693940207436,
      "monitor_id": "d6t6Yxxxxxxxxxxxxxx",
      "monitor_name": "Test Monitor - Overvoltage (Battery)",
      "monitor_version": 14,
      "related_doc_ids": [],
      "schema_version": 4,
      "severity": "1",
      "start_time": 1693939256074,
      "state": "ACTIVE",
      "trigger_id": "dat6xxxxxxxxxxxxxxx",
      "trigger_name": "Test Trigger - Voltage over 4",
      "version": 17
    }
  ],
  "totalAlerts": 1
}

Request:
I’ve been reading documentation and doing multiple tests, with no luck. I believe that I should get more information on the related_doc_ids or finding_ids fields of the alert, but as you can see, they are empty. Despite my efforts, I haven’t been able to associate the alerts with the specific devices that generated them.

Could someone please guide me on how to extract which device_data.id generated the alert? I’m open to using sequential CURL requests to achieve this if necessary. Any help or suggestions would be greatly appreciated.

Thank you in advance for your assistance!

Hi again,
I have a small update: I’m still using a Per query monitor, but I changed it from Visual editor to Extraction query editor. The alarm triggers (just as before), but now it finally shows the device_data.id field as {{ctx.results.0.hits.hits.0._source.device_data.id}} when I send the notification via Slack. :smile:

New Alert Configuration
{
  "name": "Test Monitor - Overvoltage (Battery) (Extraction)",
  "type": "monitor",
  "monitor_type": "query_level_monitor",
  "enabled": false,
  "schedule": {
    "period": {
      "unit": "MINUTES",
      "interval": 1
    }
  },
  "inputs": [
    {
      "search": {
        "indices": [
          "my-data-index"
        ],
        "query": {
          "size": 1,
          "query": {
            "bool": {
              "filter": [
                {
                  "match_all": {
                    "boost": 1
                  }
                },
                {
                  "exists": {
                    "field": "client_data.id",
                    "boost": 1
                  }
                },
                {
                  "exists": {
                    "field": "device_data.id",
                    "boost": 1
                  }
                },
                {
                  "exists": {
                    "field": "sensor_data.Current (A)",
                    "boost": 1
                  }
                },
                {
                  "exists": {
                    "field": "timestamp",
                    "boost": 1
                  }
                }
              ],
              "adjust_pure_negative": true,
              "boost": 1
            }
          },
          "version": true,
          "_source": {
            "includes": [
              "client_data",
              "device_data",
              "location",
              "sensor_data"
            ],
            "excludes": [
              "device_data.fake-field-for-testing-purposes"
            ]
          },
          "stored_fields": "device_data.id",
          "docvalue_fields": [
            {
              "field": "@timestamp",
              "format": "date_time"
            },
            {
              "field": "client_data.id"
            },
            {
              "field": "timestamp"
            }
          ],
          "sort": [
            {
              "timestamp": {
                "order": "desc"
              }
            }
          ],
          "aggregations": {  },
          "highlight": {
            "pre_tags": [
              "@opensearch-dashboards-highlighted-field@"
            ],
            "post_tags": [
              "@/opensearch-dashboards-highlighted-field@"
            ],
            "fragment_size": 2147483647,
            "fields": {
              "*": {}
            }
          }
        }
      }
    }
  ],
  "triggers": [
    {
      "query_level_trigger": {
        "id": "5tBxxxxxxxxxxxxxxxxxxxxx",
        "name": "Test Trigger - Voltage over 4 (Extraction)",
        "severity": "1",
        "condition": {
          "script": {
            "source": "ctx.results[0].hits.hits[0]._source.device_data.battery.voltage > 4",
            "lang": "painless"
          }
        },
        "actions": [
          {
            "id": "59Bmyyyyyyyyyyyyyyyyyyy",
            "name": "Notify Slack Test",
            "destination_id": "hWcPzzzzzzzzzzzzzzz",
            "message_template": {
              "source": "{\n    \"channel\": \"C0XXXXXXXXX\",\n   \"text\": \"*CTX*: \\n{{ctx}} \\n\\n\\n *Device_ID*: {{ctx.results.0.hits.hits.0._source.device_data.id}}\"\n}\n",
              "lang": "mustache"
            },
            "throttle_enabled": false,
            "subject_template": {
              "source": "",
              "lang": "mustache"
            }
          }
        ]
      }
    }
  ],
  "ui_metadata": {
    "schedule": {
      "timezone": null,
      "frequency": "interval",
      "period": {
        "unit": "MINUTES",
        "interval": 1
      },
      "daily": 0,
      "weekly": {
        "tue": false,
        "wed": false,
        "thur": false,
        "sat": false,
        "fri": false,
        "mon": false,
        "sun": false
      },
      "monthly": {
        "type": "day",
        "day": 1
      },
      "cronExpression": "0 */1 * * *"
    },
    "monitor_type": "query_level_monitor",
    "search": {
      "searchType": "query",
      "timeField": "",
      "aggregations": [],
      "groupBy": [],
      "bucketValue": 1,
      "bucketUnitOfTime": "h",
      "filters": []
    }
  }
}

The frustrating part: I still can’t retrieve that value when making a GET request to the alerts via the API. :sob:

Any ideas? Could someone provide guidance on how to approach this? Any suggestions are more than welcome.

Curl to get Alerts
curl -XGET \
      -H 'Content-Type: application/json' \
      -u 'my-user:My-password' \
      -G \
      -d 'alertState=ACTIVE' \
      'https://os.fakedomain.com/_plugins/_alerting/monitors/alerts'
Response of Alerts CURL
{
  "alerts": [
    {
      "acknowledged_time": null,
      "action_execution_results": [
        {
          "action_id": "59BmXXXXXXXXXXXXXXXX",
          "last_execution_time": 1694127745261,
          "throttled_count": 0
        }
      ],
      "alert_history": [],
      "end_time": null,
      "error_message": null,
      "finding_ids": [],
      "id": "itBwcYYYYYYYYYYYYYYY",
      "last_notification_time": 1694127745280,
      "monitor_id": "4dBzzzzzzzzzzzzzzzzz",
      "monitor_name": "Test Monitor - Overvoltage (Battery) (Extraction)",
      "monitor_version": 5,
      "related_doc_ids": [],
      "schema_version": 4,
      "severity": "1",
      "start_time": 1694120327463,
      "state": "ACTIVE",
      "trigger_id": "5tBmxxxxxxxxxxx",
      "trigger_name": "Test Trigger - Voltage over 4",
      "version": 3
    }
  ],
  "totalAlerts": 1
}

Thanks in advance.

Hi again,

Here’s another update: It seems impossible to obtain the information I need using the Alerts API (_plugins/_alerting/monitors/alerts).

I’m considering creating a new Notification Channel of type: Custom Webhook that targets the same OpenSearch instance I’m using but on a different index (alerts-index). Each time an alert is triggered, a new document will be generated in that index.

To retrieve the information I need, I can simply query the alerts-index, filtering for records with a timestamp greater than 10 minutes ago.

However, this approach has two significant downsides:

  1. It feels quite unnatural, as the alerts-index will essentially function as a historical index that will grow without limits, necessitating daily, weekly, or hourly cleanup depending on the volume of records.
  2. I’m unable to create the new Notification Channel because there’s no way to include the user/password in the API call. I’m currently using the following curl command (with the -u 'my-user:My-password' section), but I was not able to replicate it on the Notification Channel.
CURL
curl -X POST \
    -H 'Content-Type: application/json' \
    -u 'my-user:My-password' \
    -d '{
          "timestamp": 1694131206839,
          "client_id": "15f3....",
          "device_id": "0x3242....",
          "trigger_id": "5tBmc...."
        }' \
    'https://os.fakedomain.com/alerts-index/_doc'

Could someone please confirm if I’m heading in the right direction? If so, how can I provide the user/password credentials for the API call in the Notification Channel?

Thanks!

I completely forgot to update this post, but here’s the information just in case someone else encounters the same issue.

I was finally able to make it work with a workaround:

  1. I created a Monitor with the following settings:
    • Type: Per query monitor
    • Defining method: Extraction query editor
    • In the Extraction query, I added the fields I needed to the _source section to make them available.
  2. I set up a Notification Channel that can be used to write to the same OpenSearch instance:
    • Channel Name: LocalOpensearch (I couldn’t think of a better name)
    • Webhook Url: The public URL to the index (https://os.fakedomain.com/alert-index/_doc)
      • Unfortunately, OpenSearch has a security setting that prevents connections from localhost, so I had to use the public address. This might increase outbound traffic costs.
    • Headers:
      • Content-Type: application/json
      • Authorization: Basic eW9...jIh (Check the note below for generating this token)
  3. I created a Trigger
    • It has a Custom webhook Action that writes to a different index on the same OpenSearch instance (Channel Name: LocalOpensearch)
    • The body of the request contains the JSON I want to index.

Note on generating the Authorization:
Since it’s not possible to send the ‘-u’ parameter directly, the only alternative is to encode it using the following method (the token can be generated in the Google Chrome console): btoa(unescape(encodeURIComponent('your-user' + ':' + 'My-Password-1122!')))
This will result in 'eW91ci11c2VyOk15LVBhc3N3b3JkLTExMjIh' . You just need to send this as Basic eW91ci11c2VyOk15LVBhc3N3b3JkLTExMjIh.