|
| 1 | +The **Multi-window selector** feature in OpenObserve enables users to **define multiple time windows in a scheduled alert (SQL mode)**, **compare log, metrics, or traces across those time windows**, and **determine whether to send an alert notification based on the comparison results**. |
| 2 | + |
| 3 | +## Access The Multi-window Selector |
| 4 | + |
| 5 | +You can apply the multi-window selector feature to both new and existing scheduled alerts in SQL mode. |
| 6 | + |
| 7 | +#### For new alerts: |
| 8 | + |
| 9 | +1. Select **Alerts**. |
| 10 | +2. Select the folder or create a new folder as per requirement. |
| 11 | +3. Click **Add Alert**. |
| 12 | +4. Select **Scheduled Alerts**. |
| 13 | +5. Select **SQL**. |
| 14 | +6. Navigate to the **Multi-window Selector** section. |
| 15 | + |
| 16 | +#### For existing alerts: |
| 17 | + |
| 18 | +1. Select the existing alert from the **Alerts** page (the selected alert must be scheduled and created in the SQL mode). |
| 19 | +2. Click the edit icon. |
| 20 | +3. In the **Update Alert** page, navigate to the **Multi-window Selector** section. |
| 21 | + |
| 22 | +### How to Use Multi-window Selector |
| 23 | + |
| 24 | +> **Use Case**: You want to monitor **purchase events where users had to retry the action (retry count > 0)**. Specifically, you want to check if the number of such purchase events has **increased by more than 5% in the last 30 minutes**, compared to the same 30-minute period yesterday. If yes, you want to receive an **alert by email** that clearly states the percentage increase, so you can take necessary actions. |
| 25 | +
|
| 26 | +Follow these steps to configure the **Multi-window Selector** feature in your scheduled alert: |
| 27 | + |
| 28 | +#### Step 1: Write the SQL Query |
| 29 | + |
| 30 | +**Tip**: Use the Logs page to write and test your SQL query. |
| 31 | + |
| 32 | +```sql |
| 33 | + |
| 34 | +SELECT |
| 35 | + |
| 36 | + histogram(_timestamp, '15 minutes') AS time_s, |
| 37 | + COUNT(_timestamp) AS cnt |
| 38 | + |
| 39 | +FROM |
| 40 | + |
| 41 | + "openobserve_app_analytics_log_stream" |
| 42 | + |
| 43 | +WHERE |
| 44 | + |
| 45 | + pdata_dr_level1_level2_level3_level4_level5_level6_level7_retry > 0 |
| 46 | + AND eventtype = 'purchase' |
| 47 | + |
| 48 | +GROUP BY |
| 49 | + |
| 50 | + time_s |
| 51 | +``` |
| 52 | +Explanation of the above SQL query: |
| 53 | + |
| 54 | +- `histogram(_timestamp, '15 minutes')`: Buckets events into 15-minute intervals. |
| 55 | +- **Filters**: Only include events of type `purchase` that have retry count greater than zero. |
| 56 | +- **Group by**: Each time bucket. |
| 57 | + |
| 58 | +#### Step 2: Define the Period |
| 59 | + |
| 60 | +Specify the time range for which you want to evaluate the data (for example, the last 30 minutes). |
| 61 | + |
| 62 | +#### Step 3: Select Multi-window |
| 63 | + |
| 64 | +In the **Multi-window Selector**, select the historical time window you want to compare against the current time window. |
| 65 | + |
| 66 | +For example, if you want to compare the current 30-minute window with the same window 1 day ago, select **1 day ago**. |
| 67 | + |
| 68 | +At runtime, the alert manager will run **two SQL queries**: |
| 69 | + |
| 70 | +1. One for the **current time window** (for example, 9:30 AM – 10:00 AM). |
| 71 | +2. One for the **selected past time window** (for example, 9:30 AM – 10:00 AM, previous day). |
| 72 | + |
| 73 | +**Understand SQL Query Output Before Writing VRL** |
| 74 | + |
| 75 | +When **Multi-window Selector** is used, OpenObserve passes the results of your SQL queries to your VRL function as an **array of arrays**. |
| 76 | + |
| 77 | +Example input to your VRL function: |
| 78 | + |
| 79 | +``` |
| 80 | +[ |
| 81 | +
|
| 82 | + [ // Current time window result (result[0]) |
| 83 | +
|
| 84 | + { "time_s": "10:00", "cnt": 20 }, |
| 85 | +
|
| 86 | + { "time_s": "10:15", "cnt": 23 } |
| 87 | +
|
| 88 | + ], |
| 89 | +
|
| 90 | + [ // Past time window result (result[1]) |
| 91 | +
|
| 92 | + { "time_s": "10:00", "cnt": 40 }, |
| 93 | +
|
| 94 | + { "time_s": "10:15", "cnt": 91 } |
| 95 | +
|
| 96 | + ] |
| 97 | +
|
| 98 | +] |
| 99 | +``` |
| 100 | +Inside the VRL function, you can access: |
| 101 | + |
| 102 | +- `result[0]`: Current time window data |
| 103 | +- `result[1]`: Past time window data |
| 104 | + |
| 105 | +#### Step 4: Write the VRL Function |
| 106 | + |
| 107 | +Create a VRL function to process the SQL query results and perform the desired comparison. |
| 108 | + |
| 109 | +**Key points:** |
| 110 | + |
| 111 | +1. Always start your VRL function with `#ResultArray#` when using the **Multi-window Selector**. |
| 112 | +This ensures that your VRL function receives a **multi-dimensional array** input, where: |
| 113 | + |
| 114 | + - `result[0]` = current time window data |
| 115 | + - `result[1]` = past time window data |
| 116 | +Without `#ResultArray#`, the input is a flat array, and it becomes unreliable to distinguish current vs. past data. |
| 117 | + |
| 118 | +2. The **special variable `.` (dot)** holds this input. It contains the entire query result. To make this input easier to work with, we **assign it to a variable called `result`** and ensure it is treated as an array: <br> |
| 119 | +`result = array!(.)` <br> |
| 120 | +Note that `array!(.)` tells VRL to ensure the input is treated as an array. If the input is not an array, VRL will throw an error to alert you. Always use `array!(.)` for clarity and safety. |
| 121 | + |
| 122 | +**Tip**: Write and test your VRL function using the VRL playground in Logs page or [Vector.dev Playground](https://playground.vrl.dev/). |
| 123 | + |
| 124 | +**VRL function example**: |
| 125 | +``` |
| 126 | +#ResultArray# |
| 127 | +
|
| 128 | +# Initialize variables |
| 129 | +
|
| 130 | +prev_data = [] |
| 131 | +
|
| 132 | +curr_data = [] |
| 133 | +
|
| 134 | +res = [] |
| 135 | +
|
| 136 | +result = array!(.) |
| 137 | +
|
| 138 | +# Process if we have at least 2 windows of data |
| 139 | +
|
| 140 | +if length(result) >= 2 { |
| 141 | +
|
| 142 | + today_data = result[0] |
| 143 | +
|
| 144 | + yesterday_data = result[1] |
| 145 | +
|
| 146 | + # Sum counts for both windows |
| 147 | +
|
| 148 | + cnt_yesterday = 0.0 |
| 149 | +
|
| 150 | + cnt_today = 0.0 |
| 151 | +
|
| 152 | + for_each(array!(yesterday_data)) -> |index, p_value| { |
| 153 | +
|
| 154 | + cnt_yesterday, err = cnt_yesterday + p_value.cnt |
| 155 | +
|
| 156 | + } |
| 157 | +
|
| 158 | + for_each(array!(today_data)) -> |index, p_value| { |
| 159 | +
|
| 160 | + cnt_today, err = cnt_today + p_value.cnt |
| 161 | +
|
| 162 | + } |
| 163 | +
|
| 164 | + # Calculate difference and percentage |
| 165 | +
|
| 166 | + if cnt_yesterday > 0.0 { |
| 167 | +
|
| 168 | + diff = cnt_today - cnt_yesterday |
| 169 | +
|
| 170 | + diff_percentage, err = (diff) * 100.0 / cnt_yesterday |
| 171 | +
|
| 172 | + # Alert condition: if increase is more than 5% |
| 173 | +
|
| 174 | + if diff_percentage > 5.0 { |
| 175 | +
|
| 176 | + diff_data = { |
| 177 | +
|
| 178 | + "diff": diff, |
| 179 | +
|
| 180 | + "diff_percentage": diff_percentage |
| 181 | +
|
| 182 | + } |
| 183 | +
|
| 184 | + temp = [] |
| 185 | +
|
| 186 | + temp = push(temp, diff_data) |
| 187 | +
|
| 188 | + res = push(res, temp) |
| 189 | +
|
| 190 | + } |
| 191 | +
|
| 192 | + } |
| 193 | +
|
| 194 | +} |
| 195 | +
|
| 196 | +# Set the final result |
| 197 | +
|
| 198 | +. = res |
| 199 | +
|
| 200 | +. |
| 201 | +``` |
| 202 | + |
| 203 | +**Understand the VRL Function Output** |
| 204 | + |
| 205 | +The VRL function outputs: |
| 206 | + |
| 207 | +- **Empty array** ([ ]): If the increase is **not** more than 5%. |
| 208 | +- **Non-empty array**: If the increase **exceeds** 5%. Example: |
| 209 | + |
| 210 | +``` |
| 211 | +[ |
| 212 | +
|
| 213 | + [ |
| 214 | +
|
| 215 | + { |
| 216 | +
|
| 217 | + "diff": 10, |
| 218 | +
|
| 219 | + "diff_percentage": 10.0 |
| 220 | +
|
| 221 | + } |
| 222 | +
|
| 223 | + ] |
| 224 | +
|
| 225 | +] |
| 226 | +``` |
| 227 | + |
| 228 | +#### Step 5: Define the Threshold |
| 229 | + |
| 230 | +Use the VRL function output to define the threshold. |
| 231 | +Set the threshold as: |
| 232 | + |
| 233 | +**Trigger when count of VRL output >= 1. This means:** |
| 234 | + |
| 235 | +- If the VRL output array is **empty** -> count is 0 -> **no alert is sent**. |
| 236 | +- If the VRL output has **results** -> count is 1 or more -> **alert is triggered**. |
| 237 | + |
| 238 | +Set the **Threshold** as **>= 1** |
| 239 | + |
| 240 | +#### Step 6: Select Destination |
| 241 | + |
| 242 | +Specify where you want to receive the alert notification- email or webhook. |
| 243 | + |
| 244 | +#### Step 7: Create Row Template |
| 245 | + |
| 246 | +Design the row template to customize the alert content. |
| 247 | + |
| 248 | +For example: |
| 249 | + |
| 250 | +Include fields from your VRL output such as: |
| 251 | + |
| 252 | +- `diff` (difference in counts) |
| 253 | +- `diff_percentage` (percentage increase) |
| 254 | + |
| 255 | +This ensures your email notification is informative and actionable. |
| 256 | + |
| 257 | +Example row template: |
| 258 | + |
| 259 | +``` |
| 260 | +Alert: Purchase events with retries increased by {{ diff_percentage }}%* |
| 261 | +Details: Count difference: {{ diff }} |
| 262 | +``` |
| 263 | + |
| 264 | +#### Step 8: Define the Frequency |
| 265 | + |
| 266 | +Determines how often the alert manager runs the query throughout the day (e.g., every 30 minutes). |
| 267 | + |
| 268 | +**Important:** |
| 269 | + |
| 270 | +- Frequency determines **how often** the alert manager runs your queries. |
| 271 | +- Period defines **what time range** is checked at each run. |
| 272 | + |
| 273 | +For instance: |
| 274 | + |
| 275 | +- **Period**: Last 30 minutes |
| 276 | +- **Frequency**: Every 30 minutes |
| 277 | +- **Multi-window selector**: 1 day ago |
| 278 | + |
| 279 | +At 10:00 AM, OpenObserve alert manager executes SQL for: |
| 280 | + |
| 281 | +- Current window: 9:30 AM – 10:00 AM, today |
| 282 | +- Past window: 9:30 AM – 10:00 AM, one day ago |
| 283 | + |
| 284 | +At 10:30 AM, OpenObserve alert manager executes SQL for: |
| 285 | + |
| 286 | +- Current window: 10:00 AM – 10:30 AM, today |
| 287 | +- Past window: 10:00 AM – 10:30 AM, one day ago |
| 288 | + |
| 289 | +#### Step 9: Save the Alert |
| 290 | + |
| 291 | +### FAQ |
| 292 | + |
| 293 | +**What happens if I forget to include #ResultArray# in my VRL function?** |
| 294 | + |
| 295 | +If you do not include `#ResultArray#`, your VRL function will receive a flat array:** |
| 296 | + |
| 297 | +``` |
| 298 | +[ |
| 299 | +
|
| 300 | + { "timestamp": "10:00", "cnt": 20 }, |
| 301 | +
|
| 302 | + { "timestamp": "10:15", "cnt": 23 }, |
| 303 | +
|
| 304 | + { "timestamp": "10:00", "cnt": 40 }, |
| 305 | +
|
| 306 | + { "timestamp": "10:15", "cnt": 91 } |
| 307 | +
|
| 308 | +] |
| 309 | +``` |
| 310 | +You cannot distinguish current window from past window data from the above array. |
| 311 | + |
0 commit comments