bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/docs/definitions.md (about) 1 --- 2 layout: default 3 title: Definitions (RuleConf) 4 order: 3 5 --- 6 7 <div class="row"> 8 <div class="col-sm-3" > 9 <div class="sidebar" data-spy="affix" data-offset-top="0" data-offset-bottom="0" markdown="1"> 10 11 * Some TOC 12 {:toc} 13 14 </div> 15 </div> 16 17 <div class="doc-body col-sm-9" markdown="1"> 18 19 <p class="title h1">{{page.title}}</p> 20 21 {% raw %} 22 23 ## Changes Since 0.5.0 24 Since 0.5.0, the config has been split into two different files. 25 26 ### System 27 System config is documented [here](/system_configuration). 28 29 ### Definitions 30 This file is documented in the rest of this page. It includes settings that 31 do not require a Bosun restart to take effect e.g. alerts, templates, 32 notifications, and its location is defined by [the system configuration's 33 RuleFilePath](/system_configuration#rulefilepath). 34 35 It can be edited from the [Rule Editor](/usage#definition-rule-saving) in the 36 web UI when the [`EnableSave` setting](/system_configuration#enablesave) is 37 enabled. 38 39 The file is divided into sections, each of which having a type and a 40 name, followed by `{` and ending with `}`. Each section is a definition 41 of e.g. an alert or a notification. Key/value pairs follow, written as 42 `key = value`. Multi-line strings are supported using backticks (\`) to 43 delimit start and end of string. Comments go from `#` to end of line. 44 45 ## Alert Definitions 46 An alert is defined with the following syntax: 47 48 ``` 49 alert uniqueAlertName { 50 $variable = value 51 ... 52 keyword = value 53 ... 54 } 55 ``` 56 57 The minimum requirement for an alert is that it have a `warn` or `crit` expression. However, the most common case is to define at least: `warn`, `warnNotification`, `crit`, `critNotification`, and `template`. 58 59 ### Alert Keywords 60 61 #### crit 62 {: .keyword} 63 The expression to evaluate to set a critical severity state for an incident that is instantiated from the alert definition. The expression's [return type](/expressions#data-types) must return a Scalar or NumberSet. 64 65 No crit notifications will be sent if `critNotification` is not declared in the alert definition. However, it will still appear on the dashboard. 66 67 #### critNotification 68 {: .keyword} 69 A comma-separated list of notifications to trigger on critical a state (when the crit expression is non-zero). This line may appear multiple times and duplicate notifications, which will be merged so only one of each notification is triggered. [Lookup tables](/definitions#lookup-tables) may be used when `lookup("table", "key")` is the only `critNotification` value. This means you can't mix notifications names with lookups in the same `critNotification`. However, since an alert can have multiple `critNotification` entries you make one entry that has a lookup, and another that has notification names. 70 71 #### depends 72 {: .keyword} 73 74 `depends` is an expression that makes the alert dependent on another alert. If the depends expression evaluates to non-zero than the alert will be [unevaluated](/usage#additional=states). This is most frequently used in conjunction with the [`alert()` expression function](/expressions#alertname-string-key-string-numberset). 75 76 Note that the depends feature does not work when using Bosun's testing in the Rule Editor UI. 77 78 Given the example that follows there would be two incidents in a warn state: `dependOnMe{host=a}` and `iDependOnOthers{host=b}`. There is *no* incident for `iDependOnOthers{host=a}` because the dependency was true for host a. There *is* an incident for `iDependOnOthers{host=b}` since the dependency was false in the case of host b. 79 80 Example: 81 82 ``` 83 alert dependOnMe { 84 template = depend 85 $series = merge(series("host=a", 0, 1), series("host=b", 0, 0)) 86 # host a is 1, host b is 0 87 warn = avg($series) 88 } 89 90 alert iDependOnOthers { 91 template = depend 92 depends = alert("dependOnMe", "warn") 93 $series = merge(series("host=a", 0, 1), series("host=b", 0, 1)) 94 # host a and b are 1 95 warn = avg($series) 96 } 97 98 template depend { 99 subject = `{{.Alert.Name}}: {{.Group}}` 100 } 101 ``` 102 103 #### ignoreUnknown 104 {: .keyword} 105 Setting `ignoreUnknown = true` will prevent an alert from becoming unknown. This is often used where you expect the tagsets or data for an alert to be sparse and/or you want to ignore things that stop sending information. 106 107 #### log 108 {: .keyword} 109 Setting `log = true` will make the alert behave as a "log alert". It will never show up on the dashboard, but will execute notifications every check interval where the status is abnormal. `maxLogFrequency` can be used to throttle the notifications. 110 111 #### maxLogFrequency 112 {: .keyword} 113 Setting `maxLogFrequency = true` will throttle [log](/definitions#log) notifications to the specified duration. `maxLogFrequency = 5m` will ensure that notifications only fire once every 5 minutes for any given alert key. Only valid on alerts that have `log = true`. 114 115 #### runEvery 116 {: .keyword} 117 Multiple of global system configuration value [CheckFrequency](/system_configuration#checkfrequency) at which to run this alert. If unspecified, the system configuration value [DefaultRunEvery](/system_configuration#defaultrunevery) will be used for the alert frequency. 118 119 #### squelch 120 {: .keyword} 121 `squelch` is comma-separated list of `tagk=tagv` pairs. `tagv` is a regex. If the current tag group matches all values, the alert is squelched, and will not trigger as crit or warn. For example, `squelch = host=ny-web.*,tier=prod` will match any group that has at least that host and tier. Note that the group may have other tags assigned to it, but since all elements of the squelch list were met, it is considered a match. Multiple squelch lines may appear; a tag group matches if any of the squelch lines match. 122 123 This can also be defined at the global of level of the configuration. 124 125 When using squelch, alerts will be removed even if they are not within the scope of the final tagset. The common case of this would be using the `t` ([transpose function]()) to reduce the number of final results. So when doing this, results will still be removed because they are removed at the expression level for the `warn` and `crit` expressions. 126 127 #### template 128 {: .keyword} 129 The name of the [template](/definitions#templates) that will be used to send alerts to the specified notifications for the alert. 130 131 #### unjoinedOk 132 {: .keyword} 133 If present, expressions within the alert will ignore unjoined expression errors. Unjoins happen when expressions with in an alert use a comparison operator (i.e. `>` or `&&`), and there are tagsets in one set but are not in the other set. 134 135 #### unknown 136 {: .keyword} 137 `unknown` is the duration (i.e. `unknown = 5m` ) at which to mark an incident as [unknown](/usage#severity-states) if it can not be evaluated. It defaults the system configuration global variable [CheckFrequency](/system_configuration#checkfrequency). Bosun remembers the tagsets it has seen for an alert and determines an alert to be unknown when a tagset is no longer present for the alert. 138 139 #### unknownIsNormal 140 {: .keyword} 141 Setting `unknownIsNormal = true` will convert unknown events for an incident into a normal event. 142 143 This is often useful if you are alerting on log messages where the absence of log messages means that the state should go back to normal. Using `ignoreUnknown` with this setting would be unnecessary. 144 145 #### warn 146 {: .keyword} 147 The expression to evaluate to set a warn state for an incident that is instantiated from the alert definition. 148 149 The expression must evaluate to a NumberSet or a Scalar (See [Data Types](/expressions#data-types)). 0 is false (do not trigger) and any non-zero value is true (will trigger). 150 151 If the crit expression is true, the warn expression will not be evaluated as crit supersedes warn. 152 153 No warn notifications will be sent if `warnNotification` is not declared in the alert definition. It will still however appear on the dashboard. 154 155 #### warnNotification 156 {: .keyword} 157 Identical to `critNotification` above, but the condition evaluates to warning state. 158 159 160 ## Variables 161 Variables are in the form of `$foo = someText` where someText continues until the end of the line. These are not variables in the sense that they hold a value, rather they are simply text replacement done by the the parsers. 162 163 <div class="admonition"> 164 <p class="admonition-title">Tip</p> 165 <p>Because this is text replacement, it is important to note that variables do <em>not</em> impact the order of operations. So it may be necessary at times to enclose the variable in parenthesis (either when setting the value, or referencing it).</p> 166 </div> 167 168 They can be referenced by `$foo` or by `${foo}`, the later being useful if you want to use the variable in a context where whitespace does not immediately follow the value. 169 170 ### Global Variables 171 172 Global Variables exist outside of any section and should be defined before they are used. 173 174 Global variables can be overridden in sections defining a variable within the scope of the section that has the same name. 175 176 ## Templates 177 Templates are used to construct what alerts will look like when they are sent. They are pointed to in the definition of an alert by the [template keyword](/definitions#template). They are like "views" in web frameworks. 178 179 Templates in Bosun are built on top of go's templating. The subject is rendered using the golang [text/template](http://golang.org/pkg/text/template/) package (plaintext) and the body is rendered using the golang [html/template](https://golang.org/pkg/html/template/) package (html). 180 181 For learning the fundamentals of templates (how to do conditionals, loops. etc) read the [text/template](http://golang.org/pkg/text/template/) documentation since Bosun templates add on top of that. 182 183 Variable expansion is not performed on templates because `$` is used in the template language, but a [`V()` function](/definitions#vstring-string) is provided for global variables and alert variables are available in [.Alert.Vars](/definitions#alertvars). 184 185 Macros can not be used in templates, however, templates can [include other templates](/definitions#template-inclusions). 186 187 Note that templates are rendered when the expression is evaluated and it is non-normal. This is to eliminate changes in what is presented in the template because the data has changed in the tsdb since the alert was triggered. 188 189 ### The Unknown Template 190 Since there is limited information for an alert that is unknown, and since unknowns can be grouped the unknown template is different. 191 192 The unknown template (set by the global option `unknownTemplate`) acts differently than alert templates. It receives groups of alerts since unknowns tend to happen in groups (i.e., a host stops reporting and all alerts for that host trigger unknown at the same time). 193 194 Variables and function available to the unknown template: 195 196 * Group: list of names of alerts 197 * Name: group name 198 * Time: [time](http://golang.org/pkg/time/#Time) this group triggered unknown 199 * States: unknown incident states (only use when grouping unknown disabled using [unknownMinGroupSize](http://bosun.org/definitions#unknownmingroupsize)) 200 * IncidentUnknownLink: creates a link to Bosun’s incident view same as `.Incident` (require incident id) 201 202 Example: 203 204 ``` 205 template ut { 206 subject = {{.Name}}: {{.Group | len}} unknown alerts 207 body = ` 208 <p>Time: {{.Time}} 209 <p>Name: {{.Name}} 210 <p>Alerts: 211 {{range .Group}} 212 <br>{{.}} 213 {{end}}` 214 } 215 216 unknownTemplate = ut 217 ``` 218 219 ``` 220 template testunknownBody { 221 subject = {{.Name}}{{ if .States.Tags }}.{{ replace .States.Tags "," "." -1 }}{{else}}{{end}} 222 body = ` 223 <p>Time: {{.Time}} 224 <p>Name: {{.Name}} 225 <p>AlertKey: {{.States.AlertKey}} 226 <p>Incident Id: {{.States.Id}} 227 <p>Link: {{.IncidentUnknownLink .States.Id}} 228 ` 229 } 230 231 unknownBody = testunknownBody 232 ``` 233 234 In general it is better to stick with the system default by not defining an unknown template. The system default is: 235 236 Body: 237 238 ``` 239 <p>Time: {{.Time}} 240 <p>Name: {{.Name}} 241 <p>Alerts: 242 {{range .Group}} 243 <br>{{.}} 244 {{end}} 245 ``` 246 247 Subject: 248 249 ``` 250 {{.Name}}: {{.Group | len}} unknown alerts 251 ``` 252 253 The template for grouped unknowns can not be changed and is hard coded into Bosun and has the following body: 254 255 ``` 256 <p>Threshold of {{ .Threshold }} reached for unknown notifications. The following unknown 257 group emails were not sent. 258 <ul> 259 {{ range $group, $alertKeys := .Groups }} 260 <li> 261 {{ $group }} 262 <ul> 263 {{ range $ak := $alertKeys }} 264 <li>{{ $ak }}</li> 265 {{ end }} 266 <ul> 267 </li> 268 {{ end }} 269 </ul> 270 ``` 271 272 ### Template Inclusions 273 Templates can include other templates that have been defined as in the example below. The templates are combined when rendered. This is useful to build reusable pieces in templates such as headers and footers. All templates with the same name are combined together across bosun templates, so you can reference them by the bosun template name you would like to include. 274 275 ``` 276 template include { 277 body = `<p>This gets included!</p>` 278 subject = `include example` 279 } 280 281 template includes { 282 body = `{{ template "include" . }}` 283 subject = `includes example` 284 } 285 286 alert include { 287 template = includes 288 warn = 1 289 } 290 ``` 291 292 ### Template CSS 293 HTML templates will "inline" css. Since email doesn't support `<style>` blocks, an inliner ([douceur](https://github.com/aymerick/douceur)) takes a style block, and process the HTML to include those style rules as style attributes. 294 295 Example: 296 297 ``` 298 template header { 299 body = ` 300 <style> 301 td, th { 302 padding-right: 10px; 303 } 304 </style> 305 ` 306 } 307 308 template table { 309 body = ` 310 {{ template "header" . }} 311 <table> 312 <tr> 313 <th>One</th> 314 <!-- Will be rendered as: 315 <th style="padding-right: 10px;">One</th> --> 316 <th>Two</th> 317 <tr> 318 <td>This will have</td> 319 <td>Styling applied when rendered</td> 320 </tr> 321 </table> 322 ` 323 subject = `table example` 324 } 325 326 alert table { 327 template = table 328 warn = 1 329 } 330 ``` 331 332 ### Template Keywords 333 334 #### body 335 {: .keyword} 336 The message body. This is always formated as HTML. 337 338 #### subject 339 {: .keyword} 340 The subject of the template. This is also the text that will be used in the dashboard for triggered incidents. The format of the subject is plaintext. 341 342 #### custom fields 343 {: .keyword} 344 Any other key/value pairs will add "custom" templates to the template. A notification may select these to send as its' content instead of just using subject/body. You can add any number of these as you like, with whatever name you choose. 345 346 ### Template Variables 347 Template variables hold information specific to the instance of an alert. They are bound to the template's root context. That means that when you reference them in a block they need to be referenced differently just like context bound functions ([see template function types](/definitions#template-function-types)) 348 349 #### Example of template variables 350 This example shows examples of the template variables documented below that are simple enough to be in a table: 351 352 ``` 353 alert vars { 354 template = vars 355 warn = avg(q("avg:rate:os.cpu{host=*bosun*}", "5m", "")) 356 } 357 358 template vars { 359 body = ` 360 <!-- Examples of Variables --> 361 <table> 362 <tr> 363 <th>Variable</th> 364 <th>Example Value</th> 365 </tr> 366 <tr> 367 <!-- Incident Id --> 368 <td>Id</td> 369 <td>{{ .Id }}</td> 370 </tr> 371 <tr> 372 <!-- Start Time of Incident --> 373 <td>Start</td> 374 <td>{{ .Start }}</td> 375 </tr> 376 <tr> 377 <!-- Alert Key --> 378 <td>AlertKey</td> 379 <td>{{.AlertKey}}</td> 380 </tr> 381 <tr> 382 <!-- The Tags for the Alert instance --> 383 <td>Tags</td> 384 <td>{{.Tags}}</td> 385 </tr> 386 <tr> 387 <!-- The rendered subject field of the template. --> 388 <td>Subject</td> 389 <td>{{.Subject}}</td> 390 </tr> 391 <tr> 392 <!-- Boolean that is true if the alert has not been acknowledged --> 393 <td>NeedAck</td> 394 <td>{{.NeedAck}}</td> 395 </tr> 396 <tr> 397 <!-- Boolean that is true if the alert is unevaluated (due to a dependency) --> 398 <td>Unevaluated</td> 399 <td>{{.Unevaluated}}</td> 400 </tr> 401 <tr> 402 <!-- Status object representing current severity --> 403 <td>CurrentStatus</td> 404 <td>{{.CurrentStatus}}</td> 405 </tr> 406 <tr> 407 <!-- Status object representing the highest severity --> 408 <td>WorstStatus</td> 409 <td>{{.WorstStatus}}</td> 410 </tr> 411 <tr> 412 <!-- Status object representing the the most recent non-normal severity --> 413 <td>LastAbnormalStatus</td> 414 <td>{{.LastAbnormalStatus}}</td> 415 </tr> 416 <tr> 417 <!-- Unix epoch (as int64) representing the time of LastAbnormalStatus --> 418 <td>LastAbnormalTime</td> 419 <td>{{.LastAbnormalTime}}</td> 420 </tr> 421 <tr> 422 <!-- The name of the alert --> 423 <td>Alert.Name</td> 424 <td>{{.Alert.Name}}</td> 425 </tr> 426 <tr> 427 <!-- Get Override Uknown Duration --> 428 <td>Alert.Unknown</td> 429 <td>{{.Alert.Unknown}}</td> 430 </tr> 431 <tr> 432 <!-- Get Ignore Unknown setting for the alert (bool) --> 433 <td>Alert.IgnoreUnknown</td> 434 <td>{{.Alert.IgnoreUnknown}}</td> 435 </tr> 436 <tr> 437 <!-- Get UnknownsNormal setting for the alert (bool) --> 438 <td>Alert.UnknownsNormal</td> 439 <td>{{.Alert.UnknownsNormal}}</td> 440 </tr> 441 <tr> 442 <!-- Get UnjoinedOk setting for the alert (bool) --> 443 <td>Alert.UnjoinedOK</td> 444 <td>{{.Alert.UnjoinedOK}}</td> 445 </tr> 446 <tr> 447 <!-- Get the Log setting for the alert (bool) --> 448 <td>Alert.Log</td> 449 <td>{{.Alert.Log}}</td> 450 </tr> 451 <tr> 452 <!-- Get the MaxLogFrequency setting for the alert --> 453 <td>Alert.MaxLogFrequency</td> 454 <td>{{.Alert.MaxLogFrequency}}</td> 455 </tr> 456 <tr> 457 <!-- Get the root template name --> 458 <td>Alert.TemplateName</td> 459 <td>{{.Alert.TemplateName}}</td> 460 </tr> 461 462 </table> 463 ` 464 subject = `vars example` 465 } 466 ``` 467 468 #### .Actions 469 {: .var} 470 `.Actions` is a slice of of [action objects](/definitions#action) of actions taken on the incident. They are ordered by time from past to recent. This list will be empty when using Bosun's testing UI. 471 472 Example: 473 474 ``` 475 <table> 476 <tr> 477 <th>User</th> 478 <th>Action Type</th> 479 <th>Time</th> 480 <th>Message</th> 481 <tr> 482 {{ range $action := .Actions }} 483 <tr> 484 <td>{{.User}}</th> 485 <td>{{.Type}}</th> 486 <td>{{.Time}}</th> 487 <td>{{.Message}}</th> 488 </tr> 489 {{ end }} 490 </table> 491 ``` 492 493 #### .Alert.Crit 494 {: .var} 495 `.Alert.Crit` is a [bosun expression object](/definitions#expr) that maps to the crit expression in the alert. It is only meant to be used to display the expression, or run the expression by passing it to functions like `.Eval`. 496 497 Example: 498 499 ``` 500 template expr { 501 body = ` 502 Expr: {{.Alert.Warn}}</br> 503 <!-- note that an error from eval is not checked in this example, 504 see other examples for error checking --> 505 Result: {{.Eval .Alert.Warn}} 506 ` 507 subject = `expr example` 508 } 509 510 alert expr { 511 template = expr 512 warn = 1 + 1 513 crit = 3 514 } 515 ``` 516 517 #### .Alert.Depends 518 {: .var} 519 Like `.Alert.Crit` but the [depends](/definitions#depends) expression. 520 521 522 #### .Alert.IgnoreUnknown 523 {: .var} 524 `.Alert.IgnoreUnknown` is a bool that will be true if [ignoreUnknown](/definitions#ignoreunknown) is set on the alert. 525 526 #### .Alert.Log 527 {: .var} 528 `.Alert.Log` is a bool that is true if this is a [log alert](/definitions#log). 529 530 #### .Alert.MaxLogFrequency 531 {: .var} 532 `.Alert.MaxLogFrequency` is a golang [time.Duration](https://golang.org/pkg/time/#Duration) that shows the [maxLogFrequency](/definitions#maxlogfrequency) settings for the alert. 533 534 #### .Alert.Name 535 {: .var} 536 `.Alert.Name` holds the the name of the alert. For example for an alert defined `alert myAlert { ... }` the value would be myAlert. 537 538 #### .Alert.RunEvery 539 {: .var} 540 `.Alert.RunEvery` is an integer that shows an alerts [runEvery](/definitions#runevery) setting. 541 542 #### .Alert.TemplateName 543 {: .var} 544 `.Alert.TemplateName` is the name of the template that the alert is configured to use. 545 546 #### .Alert.Text 547 {: .var} 548 `.Alert.Text` is the raw text of the alert definition as a string. It includes comments: 549 550 ``` 551 template text { 552 body = `<pre>{{.Alert.Text}}</pre>` 553 subject = `text example` 554 } 555 556 alert text { 557 # A comment 558 template = text 559 warn = 1 560 } 561 ``` 562 563 #### .Alert.UnjoinedOk 564 {: .var} 565 `.Alert.UnjoinedOk` is a bool that is true of the [unjoinedOk](/definitions#unjoinedok) alert setting is set to true. This makes it so when doing operations with two sets, if there are items in one set that have no match they will be ignored instead of triggering an error. 566 567 #### .Alert.Unknown 568 {: .var} 569 `.Alert.Unknown` is a golang [time.Duration](https://golang.org/pkg/time/#Duration) that is the duration for unknowns if the alert uses the [unknown](/definitions#unknown) alert keyword to override the global duration. It will be zero if the alert is using the global setting. 570 571 #### .Alert.UnknownsNormal 572 {: .var} 573 `.Alert.UnknownsNormal` is a bool that is true [unknownIsNormal](/definitions#unknownisnormal) is set for the alert. 574 575 #### .Alert.Vars 576 {: .var} 577 `.Alert.Vars` is a map of string to string. Any variables declared in the alert definition get an entry in the map. The key is name of the variable without the dollar sign prefix, and the value is the text that the variable maps to (Variables in Bosun don't store results, and are just simple text replacement.) If the variable does not exist than an empty string will be returned. Global variables are only accessible via a mapping in the alert definition as show in the example below (or using the [V() template function](/definitions#vstring-string)). 578 579 Example: 580 581 ``` 582 $aGlobalVar = "Hiya" 583 584 template alert.vars { 585 body = ` 586 {{.Alert.Vars.foo}} 587 588 <!-- baz returns an empty string since it is not defined --> 589 {{.Alert.Vars.baz}} 590 591 <!-- Global vars don't work --> 592 {{.Alert.Vars.aGlobalVar }} 593 594 <!-- Workaround for Global vars --> 595 {{.Alert.Vars.fakeGlobal }} 596 ` 597 subject = `alert vars example` 598 } 599 600 alert alert.vars { 601 template = alert.vars 602 $foo = 1 + 1 603 $fakeGlobal = $aGlobalVar 604 warn = $foo 605 } 606 ``` 607 608 #### .Alert.Warn 609 {: .var} 610 Like the [`.Alert.Crit` template variable](/definitions#alertcrit) but the warning expression. 611 612 #### .AlertKey 613 {: .var} 614 `.AlertKey` is a string representation of the alert key. The alert key is in the format `alertname{tagset}`. For example `diskused{host=ny-bosun01,disk=/}`. 615 616 #### .Attachments 617 {: .var} 618 When the graph functions that generate images are used they are added to `.Attachments`. Although it is available, you should *not* need to access this variable from templates. It is a slice of pointers to Attachment objects. An attachment has three fields, Data (a byte slice), Filename (string), and ContentType string. 619 620 #### .CurrentStatus 621 {: .var} 622 `.CurrentStatus` is a [status object](/definitions#status) representing the current severity state of the incident. This will be "none" when using Bosun's testing UI. 623 624 #### .Errors 625 {: .var} 626 A slice of strings that gets appended to when a [context-bound function](/definitions#context-bound) returns an error. 627 628 #### .Events 629 {: .var} 630 The value of `.Events` is a slice of [Event](/definitions#event) objects. 631 632 Example: 633 634 ``` 635 template test { 636 body = ` 637 <table> 638 639 <tr> 640 <th>Time</th> 641 <th>Status</th> 642 <th>Warn Value</th> 643 <th>Crit Value</th> 644 </tr> 645 646 {{ range $event := .Events}} 647 <tr> 648 <td>{{ $event.Time }}</td> 649 <td>{{ $event.Status }}</td> 650 <!-- Ensure Warn or Crit are not nil since they are pointers --> 651 <td>{{ if $event.Warn }} {{$event.Warn.Value }} {{ else }} {{ "none" }} {{ end }}</td> 652 <td>{{ if $event.Crit }} {{$event.Crit.Value }} {{ else }} {{ "none" }} {{ end }}</td> 653 </tr> 654 {{ end }} 655 </table> 656 ` 657 subject = `events example` 658 } 659 ``` 660 661 #### .Expr 662 {: .var} 663 The value of `.Expr` is the warn or crit expression that was used to evaluate the alert in the format of a string. 664 665 #### .Id 666 {: .var} 667 `.Id` is a unique number that identifies an incident in Bosun. It is an int64, see the documentation on the [lifetime of an incident](/usage#the-lifetime-of-an-incident) to understand when new incidents are created. 668 669 #### .IsEmail 670 {: .var} 671 The value of is `IsEmail` is true if the template is being rendered for an email. This allows you to use the same template for different types of notifications conditionally within the template. 672 673 674 #### .LastAbnormalStatus 675 {: .var} 676 `.LastAbnormalStatus` is a [status object](/definitions#status) representing the the most recent non-normal severity for the incident. This will be "none" when using Bosun's testing UI. 677 678 #### .LastAbnormalTime 679 {: .var} 680 `.LastAbnormalTime` is a time.Time object that will json marshall itself as Unix time. It represents the time of `.LastAbnormalStatus`. 681 682 #### .NeedAck 683 {: .var} 684 `.NeedAck` is a boolean value that is true if the alert has not been acknowledged yet. 685 686 #### .Open 687 {: .var} 688 `.Open` is a boolean value that is true if the alert has not been closed yet. 689 690 #### .PreviousIds 691 692 `.PerviousIds` is a slice of Incident IDs (int64) that exist for the alertkey (Alert Name + TagSet) of the incident ordered with the most recent previous incident first. This can be useful to generate links to previous incidents or to get information about previous incidents using the context-bound [`.GetIncidentState` template function](/definitions#getincidentstateid-int64-incidentstate). 693 694 In Bosun's testing UI this will be populated by the previous Incidents of a real incident if you set the incident number in the interface to match a real incident id. 695 696 #### .Result 697 {: .var} 698 `.Result` is a pointer to a "result object". This is *not* the same as the [result object](/definitions#result-1) i.e. the object return by `.Eval`. Rather is has the following properties: 699 700 * Value: The number returned by the expression (technically a float64) 701 * Expr: The expression evaluated as a string 702 703 If the severity status is Warn that it will be the result of the Warn expression, or if it crit than it will be a pointer to the result of the crit expression. 704 705 Example: 706 707 ``` 708 template result { 709 body = ` 710 {{ if notNil .Result }} 711 {{ .Result.Expr }} 712 {{ .Result.Value }} 713 {{ end }} 714 ` 715 subject = `result example` 716 } 717 718 alert result { 719 template = result 720 warn = 1 721 crit = 2 722 } 723 ``` 724 725 #### .Start 726 {: .var} 727 `.Start` is the the time the incident started and a golang [time.Time](https://golang.org/pkg/time/#Time) object. This means you can work with the time object if you need to, but a simple `{{ .Start }}` will print the time in 728 729 #### .Subject 730 {: .var} 731 `.Subject` is the rendered subject field of the template as a string. It is only available in the body, and does not show up via Bosun's testing UI. 732 733 #### .Tags 734 {: .var} 735 `.Tags` is a string representation of the tags for the alert. It is in the format of `tagkey=tagvalue,tag=tagvalue`. For example `host=ny-bosun01,disk=/` 736 737 #### .Unevaulated 738 {: .var} 739 `.Unevaluated` is a boolean value that is true if the alert did not trigger because of a [dependency](/definitions#depends). This field would only show true when viewed on the dashboard. 740 741 #### .WorstStatus 742 {: .var} 743 `.WorstStatus` is a [status object](/definitions#status) representing the highest severity reached in the lifetime of the incident. This will be "none" when using Bosun's testing UI. 744 745 ### Template Function Types 746 Template functions come in two types. Functions that are global, and context-bound functions. Unfortunately, it is important to understand the difference because they are called differently in functions and have different behavior in regards to [error handling](/definitions#template-error-handling). 747 748 #### Global 749 Calling global functions is simple. The syntax is just the function name and arguments. I can be used in regular format or a chained pipe format. 750 751 ``` 752 template global_type_example { 753 body = ` 754 <!-- Regular Format --> 755 {{ bytes 12312313 }} 756 <!-- Pipe Format --> 757 {{ 12312313 | bytes }} 758 ` 759 subject = `global example` 760 } 761 ``` 762 763 #### Context Bound 764 Context bound functions, like Global functions, can be called in regular or pipe format. What makes them different is the syntax used to call them. Context bound functions have a period before them, such as `{{ .Eval }}`. Essentially they are methods on that act on the instance of an alert/template combination when it is rendered and perform queries. 765 766 They are bound to the parent context. The "parent context" is essentially the top level namespace within a template. This is because they access data associated with the instance of the template when it sent. 767 768 What this practically means, is that when you are inside a block within a template (for example, inside a range look) context-bound functions need to be called with `{{ $.Func }}` to reference the parent context. 769 770 ``` 771 template context_bound_type_example { 772 body = ` 773 <!-- Context Bound at top level --> 774 {{ .Eval .Alert.Vars.avg_cpu }} 775 776 <!-- Context Bound in Block --> 777 {{ range $x := .Events }} 778 {{ $.Eval $.Alert.Vars.avg_cpu }} 779 {{ end }} 780 ` 781 subject = `context bound example` 782 } 783 ``` 784 785 ### Template Error handling 786 Templates can throw errors at runtime (i.e. when a notification is sent). Although the configuration check makes sure that templates are valid, you can still do things like try to reference objects that are nil pointers. 787 788 When a template fails to render: 789 790 * Email: A generic notification will be emailed to the people that would have received the alert. 791 * Post notification (where the subject is used): The following text will be posted `error: template rendering error for alert <alertkey>` where the alert key is something like `os.cpu{host=a}` 792 793 In order to prevent the template from completely failing and resulting in the generic notification, errors can be handled inside the application. 794 795 Errors are handled differently depending on the [type of the function](/definitions#template-function-types) (Context Bound vs Global). When context bound functions have errors the error string is appended to the [`.Errors` template variable](/definitions#errors). This is not the case for global functions. 796 797 Global functions always returns strings except for parseDuration. When global functions error than `.Errors` is *not* appended to, but the string that would have been returned with an error is show in the template. parseDuration returns nil when it errors, and in this one exception you can't see what the error is. 798 799 If the function returns a string or an image (technically an interface) the error message will be displayed in the template. If an object is returned (i.e. a result sets, a slice, etc) nil is returned and the user can check for that in the template. In both cases `.Errors` will be appended to if it is a context bound functions. 800 801 See the examples in the functions that follow to see examples of Error handling. 802 803 ### Template Functions 804 805 #### Context-Bound Functions 806 807 ##### .AzureResourceLink(prefix, rType, rsg, name string) (string) 808 {: .func} 809 810 `.AzureResourceLink` returns a https link to Azure's Portal for the resource. `prefix` identifies the subscription, an empty string as a value is the same as "default". `rType` is the resource type, `rsg` is the name of the resource group, and `name` is the name of the resource. 811 812 If there is an error an empty string is returned and .Errors is appended to. 813 814 Example: 815 816 ``` 817 template AzureResourceLink.Example { 818 subject = `` 819 body = ` 820 {{- $portalLink := .AzureResourceLink "default" .Alert.Vars.resType .Group.rsg .Group.name -}} 821 {{- if ne $portalLink "" -}} 822 <a href="{{ $portalLink }}">Azure Portal for Resource</a> 823 {{- else -}} 824 <p>Error Creating Azure Portal Link: {{ .LastError }}</p> 825 {{- end -}} 826 ` 827 } 828 829 alert AzureResourceLink.Example { 830 template = AzureResourceLink.Example 831 $resType = Microsoft.Compute/virtualMachines 832 $resources = azrt("$resType") 833 $q = azmulti("Percentage CPU", "", $resources, "avg", "1m", "5m", "") 834 $avgQ = avg($q) 835 warn = $avgQ > 50 836 } 837 ``` 838 839 ##### .AzureResourceTags(prefix, rType, rsg, name string) (map[string]string) 840 {: .func} 841 842 `.AzureResourceTags` returns the Azure tags associated with the resource as a map. `prefix` identifies the subscription, an empty string as a value is the same as "default". `rType` is the resource type, `rsg` is the name of the resource group, and `name` is the name of the resource. 843 844 If there is an error then nil will be returned and .Errors is appended to. 845 846 ``` 847 template AzureResourceTags.Example { 848 subject = `` 849 body = ` 850 {{- $azTags := .AzureResourceTags "default" .Alert.Vars.resType .Group.rsg .Group.name -}} 851 {{- if notNil $azTags -}} 852 <table> 853 <tr> 854 <th>Key</th> 855 <th>Value</th> 856 </tr> 857 {{ range $k, $v := $azTags }} 858 <tr> 859 <td>{{ $k }}</td> 860 <td>{{ $v }}</td> 861 </tr> 862 {{ end }} 863 </table> 864 {{- else -}} 865 <p>{{ .LastError }}</p> 866 {{- end }} 867 ` 868 } 869 870 alert AzureResourceTags.Example { 871 template = AzureResourceTags.Example 872 $resType = Microsoft.Compute/virtualMachines 873 $resources = azrt("$resType") 874 $q = azmulti("Percentage CPU", "", $resources, "avg", "1m", "5m", "") 875 $avgQ = avg($q) 876 warn = $avgQ > 50 877 } 878 ``` 879 880 881 882 ##### .Ack() (string) 883 {: .func} 884 885 `.Ack` creates a link to Bosun's view for alert acknowledgement. This is generated using the [system configuration's Hostname](/system_configuration#hostname) value as the root of the link. 886 887 888 ##### .UseElastic(host string) 889 {: .func} 890 891 `.UseElastic` set the ElasticHost context object which is used in ESQuery and ESQueryAll functions mentioned below. 892 893 Querying [foo](system_configuration#example-2) cluster: 894 895 ``` 896 template test { 897 subject = {{.Last.Status}}: {{.Alert.Name}} on {{.Group.host}} 898 body = ` 899 {{ $filter := (.Eval .Alert.Vars.filter)}} 900 {{ $index := (.Eval .Alert.Vars.index)}} 901 {{ .UseElastic "foo" }} 902 {{range $i, $x := .ESQuery $index $filter "5m" "" 10}} 903 <p>{{$x.machinename}}</p> 904 {{end}} 905 ` 906 } 907 ``` 908 909 ##### .ESQuery(indexRoot expr.ESIndexer, filter expr.ESQuery, sduration, eduration string, size int) ([]interface{}) 910 {: .func} 911 912 `.ESQuery` returns a slice of elastic documents. The function behaves like the escount and esstat [elastic expression functions](/expressions#elastic-query-functions) but returns documents instead of statistics about those documents. The number of documents is limited to the provided size argument. Each item in the slice is the a document that has been marshaled to a golang interface{}. This means the contents are dynamic like elastic documents. If there is an error, then nil is returned and `.Errors` is appended to. 913 914 The group (aka tags) of the alert is used to further filter the results. This is implemented by taking each key/value pair in the alert, and adding them as an [elastic term query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html). 915 916 Example: 917 918 ``` 919 template esquery { 920 body = ` 921 {{ $filter := (.Eval .Alert.Vars.filter)}} 922 {{ $index := (.Eval .Alert.Vars.index)}} 923 {{ $esResult := .ESQuery $index $filter "5m" "" 10 }} 924 {{ if notNil $esResult }} 925 {{ range $row := $esResult }} 926 <p>{{ range $key, $value := $row }} 927 <!-- Show the Key, Value, and the Type of the value. Values could be objects 928 but dereferencing their properties if they don't exist could cause the template 929 to fail to render --> 930 {{ $key }}: {{ $value }} ({{ $value | printf "%T" }}), 931 {{ end }}<p> 932 {{ end }} 933 {{ else }} 934 <p>{{ .LastError }} 935 {{end}} 936 ` 937 subject = `esquery example` 938 } 939 940 alert esquery { 941 template = esquery 942 $index = esls("logstash") 943 $filter = esregexp("logsource", "ny-.*") 944 crit = avg(escount($index, "logsource", $filter, "2m", "10m", "")) 945 } 946 ``` 947 948 <div class="admonition warning"> 949 <p class="admonition-title">Warning</p> 950 <p>Currently ESQuery and ESQueryAll do not have a short timeout than the timeouts for elastic expressions. Therefore be aware that using these functions could slow down template processing since templates are procedural.</p> 951 </div> 952 953 ##### .ESQueryAll(indexRoot expr.ESIndexer, filter expr.ESQuery, sduration, eduration string, size int) (interface{}) 954 {: .func} 955 956 `.ESQueryAll` behaves just like `.ESQuery`, but the tag filtering to filter results to match the alert instance is *not* applied. 957 958 ##### .Eval(string|Expression) (resultValue) 959 {: .func} 960 961 Executes the given expression and returns the first result that includes the tag key/value pairs of the alert instance. In other words, it evaluates the expression within the context of the alert. So if you have an alert that could trigger multiple incidents (i.e. `host=*`) then the expression will return data specific to the host for this alert. 962 963 If the expression results in an error `nil` will be returned and `.Errors` will be appended to. If the result set is empty, than `NaN` is returned. Otherwise the value of the first matching result in the set is returned. That result can be any type of value that Bosun can return since the type returned by Eval is dependent on what the return type of the expression it evaluates. Mostly commonly it will be float64 when used to evaluate an expression that is enclosed in a reduction like in the following example: 964 965 ``` 966 alert eval { 967 template = eval 968 $series = merge(series("host=a", 0, .2), series("host=b", 0, .5)) 969 $r = avg($series) 970 crit = $r 971 } 972 973 template eval { 974 body = ` 975 {{$v := .Eval .Alert.Vars.r }} 976 <!-- If $v is not nil (which is what .Eval returns on errors) --> 977 {{ if notNil $v }} 978 {{ $v }} 979 {{ else }} 980 {{ .LastError }} 981 {{ end }} 982 ` 983 subject = `eval example` 984 } 985 ``` 986 987 The above would display "0.2" for host "a". More simply, you template could just be `{{.Eval .Alert.Vars.r}}` and it would display 0.2 assuming there are no errors. 988 989 <div class="admonition"> 990 <p class="admonition-title">Info</p> 991 <p>The "filtering" implementation currently behaves differently with OpenTSDB for Eval and Graph. The query text will actually be replaced so that it only queries that tags for the alert. This behavior may be removed in the future.</p> 992 </div> 993 994 ##### .EvalAll(string|Expression|ResultSlice) (Result) 995 {: .func} 996 997 `.EvalAll` executes the given expression and returns a slice of [ResultSlice](/definitions#resultslice). The type of each results depends on the return type of the expression. Mostly commonly one uses a an expression that returns a numberSet ([see data types](/expressions#data-types)). If there is an error nil is returned and `.Errors` is appended to. 998 999 Example: 1000 1001 ``` 1002 alert evalall { 1003 template = evalall 1004 # the type returned by the avg() func is a seriesSet 1005 $cpu = q("sum:rate{counter,,1}:os.cpu{host=ny-bosun*}", "5m", "") 1006 # the type returned by the avg() func is a numberSet 1007 $cpu_avg = avg($cpu) 1008 warn = 1 1009 } 1010 1011 template evalall { 1012 body = ` 1013 {{ $numberSetResult := .EvalAll .Alert.Vars.cpu_avg }} 1014 {{ if notNil $numberSetResult }} 1015 <table> 1016 <tr> 1017 <th>Host</th> 1018 <th>Avg CPU Value</th> 1019 <tr> 1020 {{ range $result := $numberSetResult }} 1021 <tr> 1022 <!-- Show the value of the host tag key --> 1023 <td>{{$result.Group.host}}</td> 1024 <!-- Since we know the value of cpu_avg is a numberSet which contains floats. 1025 we pipe the float to printf to make it so it only print to two decimal places --> 1026 <td>{{$result.Value | printf "%.2f"}}</td> 1027 </tr> 1028 {{end}} 1029 <table> 1030 {{ else }} 1031 {{ .LastError }} 1032 {{ end }} 1033 1034 <!-- You could end up with other types, but their usage is not recommended, but to illustrate the point working with a seriesSet is show --> 1035 {{ $seriesSetResult := .EvalAll .Alert.Vars.cpu }} 1036 {{ if notNil $seriesSetResult }} 1037 {{ range $result := $seriesSetResult }} 1038 <h2>{{ $result.Group }}</h2> 1039 <table> 1040 <tr> 1041 <th>Time</th> 1042 <th>Value</th> 1043 <tr> 1044 <!-- these are *not* sorted --> 1045 {{ range $time, $value := $result.Value }} 1046 <tr> 1047 <td>{{ $time }}</td> 1048 <td>{{ $value }}</td> 1049 </tr> 1050 {{end}} 1051 <table> 1052 {{ end }} 1053 {{ else }} 1054 {{ .LastError }} 1055 {{ end }} 1056 1057 ` 1058 subject = `evalall example` 1059 } 1060 ``` 1061 1062 1063 ##### .GetMeta(metric, key string, tags string|TagSet) (object|string) 1064 {: .func} 1065 1066 `.GetMeta` fetches information from Bosun's metadata store. This function returns two types of metadata: metric metadata and metadata attached to tags. If the metric argument is a non-empty string, them metric metadata is fetched, otherwise tag metadata is fetched. 1067 1068 For both Metric and Tag metadata, in case where the key is a non-empty string then a string will be returned which will either be the value or an error. In cases where key is an empty string, a slice of objects is returned unless there is an error in which case nil is returned. The example shows these cases. 1069 1070 When a slice of objects are returned, the objects have the following properties: 1071 1072 * `Metric`: A string representing the metric name 1073 * `Tags`: A map of tag keys to tag values (string[string]) for the metadata 1074 * `Name`: The key of the metadata (same as key argument to this function, if provided) 1075 * `Value`: A string 1076 * `Time`: Last time this Metadata was updated 1077 1078 For Metric metadata the Tags field will be empty, and for tag metadata the metric field is empty. 1079 1080 For Tag metadata, metadata is returned that includes the key/value pairs provided as an argument. So for example, `host=a` would also return metadata that is tagged `host=a,interface=eth0`. 1081 1082 1083 Example: 1084 1085 ``` 1086 alert meta { 1087 template = meta 1088 $metric = os.mem.free 1089 warn = avg(q("avg:rate:$metric{host=*bosun*}", "5m", "")) 1090 } 1091 1092 template meta { 1093 body = ` 1094 <h1>Metric Metadata</h1> 1095 <!-- Metric Metadata as slice --> 1096 {{ $metricMetadata := .GetMeta .Alert.Vars.metric "" "" }} 1097 {{ if notNil $metricMetadata }} 1098 <h2>Metric Metadata as Slice</h2> 1099 <table> 1100 <tr> 1101 <th>Property</th> 1102 <th>Value</th> 1103 </tr> 1104 {{ range $prop := $metricMetadata }} 1105 <tr> 1106 <td>{{ $prop.Name }}</td> 1107 <td>{{ $prop.Value }}</td> 1108 </tr> 1109 {{ end }} 1110 </table> 1111 {{ else }} 1112 {{ .LastError }} 1113 {{ end }} 1114 1115 <h2>Metric Metadata as string values</h2> 1116 <!-- Metric Metadata as strings (specific keys) --> 1117 Desc: {{ .GetMeta .Alert.Vars.metric "desc" "" }}</br> 1118 Unit: {{ .GetMeta .Alert.Vars.metric "unit" "" }}</br> 1119 RateType: {{ .GetMeta .Alert.Vars.metric "rate" "" }}</br> 1120 1121 <h1>Tag Metadata<h1> 1122 <h2>Tag Metadata as slice</h2> 1123 {{ $tagMeta := .GetMeta "" "" "host=ny-web01" }} 1124 {{ if notNil $tagMeta }} 1125 <table> 1126 <tr> 1127 <th>Property</th> 1128 <th>Tags</th> 1129 <th>Value</th> 1130 <th>Last Touched Time</th> 1131 </tr> 1132 {{ range $metaSend := $tagMeta }} 1133 <tr> 1134 <td>{{ $metaSend.Name }}</td> 1135 <td>{{ $metaSend.Tags }}</td> 1136 <td>{{ $metaSend.Value }}</td> 1137 <td>{{ $metaSend.Time }}</td> 1138 </tr> 1139 {{ end }} 1140 </table> 1141 {{ else }} 1142 {{ .LastError }} 1143 {{ end }} 1144 1145 <h2>Keyed Tag Metadata</h2> 1146 <!-- Will return first match --> 1147 {{ $singleTagMeta := .GetMeta "" "memory" "host==ny-web01" }} 1148 {{ if notNil $singleTagMeta }} 1149 {{ $singleTagMeta }} 1150 {{ else }} 1151 {{ .LastError }} 1152 {{ end }} 1153 ` 1154 subject = `meta example` 1155 } 1156 ``` 1157 1158 ##### .Graph(string|Expression, yAxisLabel string) (image) 1159 {: .func} 1160 1161 Creates a graph of the expression. It will error (that can not be handled) if the return type of the expression is not a `seriesSet` ([see data types](/expressions#data-types)). If the expression is a an OpenTSDB query, it will be auto downsampled so that there are approx no more than 1000 points per series in the graph. Like `.Eval`, it filters the results to only those that include the tag key/value pairs of the alert instance. In other words, in the example, for an alert on `host=a` only the series for host a would be graphed. 1162 1163 If the optional yAxisLabel argument is provided it will be shown as a label on the y axis. 1164 1165 When the rendered graph is viewed in Bosun's UI (either the config test UI, or the dashboard) than the Graph will be an SVG. For email notifications the graph is rendered into a PNG. This is because most email providers don't allow SVGs embedded in emails. 1166 1167 If there is an error executing `.Graph` than a string showing the error will be returned instead of an image and `.Errors` will be appended to. 1168 1169 Example: 1170 1171 ``` 1172 alert graph { 1173 template = graph 1174 $series = merge(series("host=a", 0, 1, 15, 2, 30, 3), series("host=b", 0, 2, 15, 3, 30, 1)) 1175 $r = avg($series) 1176 crit = $r 1177 } 1178 1179 template graph { 1180 body = ` 1181 {{$v := .Graph .Alert.Vars.series "Random Numbers" }} 1182 <!-- If $v is not nil (which is what .Graph returns on errors) --> 1183 {{ if notNil $v }} 1184 {{ $v }} 1185 {{ else }} 1186 {{ .LastError }} 1187 {{ end }} 1188 ` 1189 subject = `graph example` 1190 } 1191 ``` 1192 1193 ##### .GraphAll(string|Expression, yAxisLabel string) (image) 1194 {: .func} 1195 1196 `.GraphAll` behaves exactly like the [`.Graph` function](/definitions#graphstringexpression-unit-string-image) but does not filter results to match the tagset of the alert. So if you changed the call in the example for `.Graph` to be `.GraphAll`, in an alert about `host=a` the series for both host a and host b would displayed (unlike Graph where only the series for host a would be displayed). 1197 1198 ##### .GraphLink(string) (string) 1199 {: .func} 1200 1201 `.GraphLink` creates a link to Bosun's Expression Editor page. It populates the expression, date, and time fields and executes the expression. It also sets the view to the 'Graph' tab. The argument to the function is the expression that will fill in the text editor in the expression page. The date and time fields will also be set to the time the template was rendered. This is generated using the [system configuration's Hostname](/system_configuration#hostname) value as the root of the link. 1202 1203 ##### .Group() (TagSet) 1204 {: .func} 1205 1206 A map of tags keys to their corresponding values for the alert. 1207 1208 ##### .GetIncidentState(id int64) (IncidentState) 1209 {: .func} 1210 1211 `.GetIncidentState` returns an [IncidentState Object](/definitions#incidentstate) for the given incident id. If there is no incident for the Id or there is an error then nil is returned and `.Errors` is appended to. 1212 1213 Example: 1214 1215 ``` 1216 template previous.incidents { 1217 <style> 1218 td, th { 1219 padding-right: 10px; 1220 padding-left: 2px; 1221 border: 1px solid black; 1222 } 1223 </style> 1224 <h3>Previous Incidents with Ack/Close/Note Actions</h3> 1225 1226 <table> 1227 <thead> 1228 <tr> 1229 <th colspan="3">Incident</th> 1230 <th colspan="4">Actions</th> 1231 </tr> 1232 <tr> 1233 <th>Id</th> 1234 <th>Duration</th> 1235 <th>Event Count</th> 1236 <th>Who</th> 1237 <th>Action</th> 1238 <th>When</th> 1239 <th>Message</th> 1240 </tr> 1241 </thead> 1242 {{- range $id := .PreviousIds -}} 1243 {{- $pi := $.GetIncidentState $id -}} 1244 {{- if notNil $pi -}} 1245 {{- $filteredActions := makeSlice -}} 1246 {{- $incidentDuration := $pi.Start.Sub $pi.Start -}} 1247 {{- if $pi.End -}} 1248 {{- $incidentDuration = $pi.End.Sub $pi.Start -}} 1249 {{- end -}} 1250 {{- range $action := $pi.Actions -}} 1251 {{- $actionString := $action.Type.String -}} 1252 {{- $closed := eq $actionString "Closed" -}} 1253 {{- $ackd := eq $actionString "Acknowledged" -}} 1254 {{- $note := eq $actionString "Note" -}} 1255 {{- if or $closed $ackd $note }} 1256 {{- $filteredActions = append $filteredActions $action -}} 1257 {{- end -}} 1258 {{- end -}} 1259 1260 <tr> 1261 {{ $actionLen := len $filteredActions }} 1262 <td {{ if gt $actionLen 1 -}} rowspan="{{- len $filteredActions }}" {{- end -}}> 1263 <a target="_blank" href="https://bosun.ds.stackexchange.com/incident?id={{$pi.Id}}">#{{$pi.Id }}</a> 1264 </td> 1265 <td {{ if gt $actionLen 1 -}} rowspan="{{- len $filteredActions }}" {{- end -}}> 1266 {{ $incidentDuration.Truncate 1e9 }} 1267 </td> 1268 <td {{ if gt $actionLen 1 -}} rowspan="{{- len $filteredActions }}" {{- end -}}> 1269 {{ len $pi.Events }} 1270 </td> 1271 {{- range $ia, $action := $filteredActions -}} 1272 {{- if gt $ia 0 -}}</tr><tr></td>{{ end }} 1273 <td>{{ $action.User }}</td> 1274 <td>{{ $action.Type | printf "%s" }}</td> 1275 <td>{{ $action.Time.Format "2006-01-02 15:04" }}</td> 1276 <td>{{ $action.Message }}</td> 1277 {{ if gt $ia 0 }}</tr>{{ end }} 1278 {{- end -}} 1279 </tr> 1280 1281 {{- else -}} 1282 <tr><td rowspan=7>{{ .LastError }}</td></tr> 1283 {{- end -}} 1284 {{- end -}} 1285 </table> 1286 ` 1287 } 1288 1289 ``` 1290 1291 ##### .HTTPGet(url string) string 1292 {: .func} 1293 1294 `.HTTPGet` fetches a url and returns the raw text as a string, unless there is an error or an http response code `>= 300` in which case the response code or error is displayed and `.Errors` is appended to. The client will identify itself as Bosun via the user agent header. Since templates are procedural and this meant to for fetching extra information, the timeout is set to five seconds. Otherwise this function could severely slow down the delivery of notifications. 1295 1296 Example: 1297 1298 ``` 1299 template httpget { 1300 body = ` 1301 {{ .HTTPGet "http://localhost:9090"}} 1302 ` 1303 subject = `httpget example` 1304 } 1305 1306 alert httpget { 1307 template = httpget 1308 warn = 1 1309 } 1310 ``` 1311 1312 ##### .HTTPGetJSON(url string) (*jsonq.JsonQuery) 1313 {: .func} 1314 1315 (TODO: Document, link to jsonq library and how to work with objects. Note limitation about top level object being an array) 1316 1317 ##### .HTTPPost(url, bodyType, data string) (string) 1318 {: .func} 1319 1320 `.HTTPPost` sends a HTTP POST request to the specified url. The data is provided a string, and bodyType will set the Content-Type HTTP header. It will return the response or error as a string in the same way that the [`.HTTPGet` template function](/definitions#httpgeturl-string-string) behaves. It will also shares [`.HTTPGet`'s](/definitions#httpgeturl-string-string) timeout. 1321 1322 Example: 1323 1324 ``` 1325 template httppost { 1326 body = ` 1327 {{ .HTTPPost "http://localhost:9090" "application/json" "{ \"Foo\": \"bar\" }" }} 1328 ` 1329 subject = `httppost example` 1330 } 1331 1332 alert httppost { 1333 template = httppost 1334 warn = 1 1335 } 1336 ``` 1337 1338 ##### .Incident() (string) 1339 {: .func} 1340 1341 `.Incident` creates a link to Bosun's incident view. This is generated using the [system configuration's Hostname](/system_configuration#hostname) value as the root of the link. 1342 1343 ##### .Last() (string) 1344 {: .func} 1345 1346 The most recent [Event](/definitions#event) in the `.History` array. This does not return any events when using Bosun's testing UI. 1347 1348 ##### .LastError() (string) 1349 {: .func} 1350 1351 Returns the string representation of the last Error from the [`.Errors` alert variable](/definitions#errors), or an empty string if there are no errors. This only contains errors from context-bound functions. 1352 1353 ##### .LeftJoin(expression|string) ([][]Result) 1354 {: .func} 1355 1356 `LeftJoin` allows you to construct tables from the results of multiple expressions. `LeftJoin` takes two or more expressions that return numberSets as arguments. The function evaluates each expression. It then joins the results of other expressions to the first expression. The join is based on the tag sets of the results. If the tagset is a subset or equal the results of the first expression, it will be joined. 1357 1358 The output can be thought of as a table that is structured as an array of rows, where each row is an array. More technically it is a slice of slices that point to [Result](/definitions#result) objects where each result will be a numberSet type. 1359 1360 If the expression results in an error nil will be returned and `.Errors` will be appended to. 1361 1362 Example: 1363 1364 ``` 1365 alert leftjoin { 1366 template = leftjoin 1367 # Host Based 1368 $osDisksMinPercentFree = last(q("min:os.disk.fs.percent_free{host=*}", "5m", "")) 1369 1370 # Host and Disk Based 1371 $osDiskPercentFree = last(q("sum:os.disk.fs.percent_free{disk=*,host=*}", "5m", "")) 1372 $osDiskUsed = last(q("sum:os.disk.fs.space_used{disk=*,host=*}", "5m", "")) 1373 $osDiskTotal = last(q("sum:os.disk.fs.space_total{disk=*,host=*}", "5m", "")) 1374 $osDiskFree = $osDiskTotal - $osDiskUsed 1375 1376 #Host Based Alert 1377 warn = $osDiskPercentFree > 5 1378 } 1379 1380 template leftjoin { 1381 body = ` 1382 <h3>Disk Space Utilization</h3> 1383 {{ $joinResult := .LeftJoin .Alert.Vars.osDiskPercentFree .Alert.Vars.osDiskUsed .Alert.Vars.osDiskTotal .Alert.Vars.osDiskFree }} 1384 <!-- $joinResult will be nill if there is an error from .LeftJoin --> 1385 {{ if notNil $joinResult }} 1386 <table> 1387 <tr> 1388 <th>Mountpoint</th> 1389 <th>Percent Free</th> 1390 <th>Space Used</th> 1391 <th>Space Free</th> 1392 <th>Space Total</th> 1393 </tr> 1394 <!-- loop over each row of the result. In this case, each host/disk --> 1395 {{ range $x := $joinResult }} 1396 <!-- Each column in the row is the results in the same order as they 1397 were passed to .LeftJoin. The index function is built-in to Go's template 1398 language and gets the nth element of a slice (in this case, each column of 1399 the row --> 1400 {{ $pf := index $x 0}} 1401 {{ $du := index $x 1}} 1402 {{ $dt := index $x 2}} 1403 {{ $df := index $x 3}} 1404 <!-- .LeftJoin is like EvalAll and GraphAll in that it does not filter 1405 results to the tags of the alert instance, but rather returns all results. 1406 So we compare the result's host to that of the host for the alert to only 1407 show disks related to the host that the alert is about. --> 1408 {{ if eq $pf.Group.host $.Group.host }} 1409 <tr> 1410 <td>{{$pf.Group.disk}}</td> 1411 <td>{{$pf.Value | pct}}</td> 1412 <td>{{$du.Value | bytes }}</td> 1413 <td>{{$df.Value | bytes }}</td> 1414 <td>{{$dt.Value | bytes}}</td> 1415 </tr> 1416 {{end}} 1417 {{end}} 1418 {{ else }} 1419 Error Creating Table: {{ .LastError }} 1420 {{end}} 1421 ` 1422 subject = `leftjoin example` 1423 } 1424 ``` 1425 1426 ##### .Lookup(table string, key string) (string) 1427 {: .func} 1428 1429 `.Lookup` returns a string to the corresponding value in a [lookup table](/definitions#lookup-tables) given the table and key. It uses the tags of the alert instance as the tags used against the lookup tables. 1430 1431 See the [main lookup example](/definitions#main-lookup-example) for example usage in a template. 1432 1433 ##### .LookupAll(table string, key string, tags string|tagset) (string) 1434 {: .func} 1435 1436 `.LookupAll` behaves like `Lookup` except that you specify the tags. The tags can me a string such as `"host=a,dc=us"` or can be a tagset (i.e. the return of the [.Group](/definitions#group-tagset) template function). 1437 1438 See the [main lookup example](/definitions#main-lookup-example) for example usage in a template. 1439 1440 ##### .Rule() (string) 1441 {: .func} 1442 1443 `.Rule` creates a link to Bosun's rule editor page. This is useful to provide a quick link to the view someone would use to edit the alert. This is generated using the [system configuration's Hostname](/system_configuration#hostname) value as the root of the link. The link will set the the alert, which template should be rendered, and time on the rule editor page. The time that represents "now" will be the time of the alert. The rule editor's alert will be set to point to the alert definition that corresponds to this alert. However, it will always be loading the current definitions, so it is possible that the alert or template definitions will have changed since the template was rendered. 1444 1445 ##### .Shorten(url string) (string) 1446 {: .func} 1447 1448 `.Shorten` uses Bosun's url shortner service to return a shortlink for the `url` argument. For example: `<a href="{{.Ack | .Shorten}}">Ack Short</a>`. 1449 1450 If there is an error generating the shortlink an empty string is returned and `.Errors` is appended to. 1451 1452 ##### .SlackAttachment() (slack.Attachment) 1453 {: .func} 1454 1455 `.SlackAttachment` returns an object that represents a [Slack Message Attachment](https://api.slack.com/docs/message-attachments) which can be used to build Slack notifications. 1456 1457 When this function is called the Attachment will have the following properties set: 1458 1459 * `color`: Sets the slack color based on the Status of the alert: Normal: `good`, Warning: `warning`, Critical: `danger`, and Unknown: `#439FE0`. 1460 * `ts`: Sets the timestamp to the .LastAbnormalTime for the alert. 1461 1462 The Attachment has the following methods in order to set the values of its fields: 1463 1464 * `.AddActions(...interface{})`: Adds one or more objects to the Actions field of the Attachment. Generally one would use the [global template function `slackLinkButton`](/definitions#slacklinkbuttontext-url-style-string-slackaction) to create the objects that are beeing added. 1465 * `.AddFields(...interface{})`: Adds one or more objects to the Fields field of the Attachment. Generally one would use the [global template function `slackField`](/definitions#slackfieldtitle-string-value-interface-short-bool-slackfield) to create the objects that are beeing added. 1466 * `.SetColor(color string)`: Sets the Color property. 1467 * `.SetFallback(fallback string)`: Sets the Fallback property. 1468 * `.SetAuthorID(authorID string)`: Sets the AuthorID property. 1469 * `.SetAuthorName(authorName string)`: Sets the AuthorName property. 1470 * `.SetAuthorSubname(authorSubname string)`: Sets the AuthorSubname property. 1471 * `.SetAuthorLink(authorLink string)`: Sets the SetAuthorLink property. 1472 * `.SetAuthorIcon(authorIcon string)`: Sets AuthorIco, the property. 1473 * `.SetTitle(title string)`: Sets the Title property. 1474 * `.SetTitleLink(titleLink string)`: Sets the TitleLink property. 1475 * `.SetPretext(pretext string)`: Sets the Pretext property. 1476 * `.SetText(text string)`: Sets the Text property. 1477 * `.SetImageURL(url string)`: Sets the ImageURL property. 1478 * `.SetThumbURL(url string)`: Sets the ThumbURL property. 1479 * `.SetFooter(footer string)`: Sets the Footer property. 1480 * `.SetFooterIcon(footerIcon string)`: Sets the FooterIcon property. 1481 * `.SetTs(ts int64)`: Sets the Ts property (unix timestamp). 1482 1483 Example: 1484 1485 ``` 1486 alert slack.example.alert { 1487 $avgQ = avg(series("host=foo", 0, 1)) 1488 warn = $avgQ 1489 template = slack.example.template 1490 warnNotification = slack.example.notification 1491 } 1492 1493 template slack.example.template { 1494 body = `This text will be body of <a href="http://bosun.example.com/">Bosun's dashboard</a>` 1495 subject = `` 1496 slack = ` 1497 {{- $a := .SlackAttachment -}} 1498 1499 {{- $a.SetTitle "Better Check your alert!" -}} 1500 {{- $a.SetTitleLink .Incident -}} 1501 1502 {{- $exampleButton := slackLinkButton "Go To Example.com" "http://example.com" "" -}} 1503 {{- $ruleButton := slackLinkButton "Alert Configuration" .Rule "" -}} 1504 {{- $ackButton := slackLinkButton "Ack Alert" .Ack "" -}} 1505 1506 {{- $a.AddActions $exampleButton $ruleButton $ackButton -}} 1507 1508 {{- $avgQ := .Eval .Alert.Vars.avgQ -}} 1509 1510 {{- $exampleField := slackField "A Number" $avgQ true -}} 1511 {{- $utcField := slackField "UTC Time" (.LastAbnormalTime.Format "2006-01-02T15:04:05") true -}} 1512 {{- $a.AddFields $exampleField $utcField -}} 1513 1514 {{- $a.SetText (printf "%v: avg for host %v is %v" .Last.Status .Group.host $avgQ) -}} 1515 1516 {{- makeMap "attachments" (makeSlice $a) | json -}}` 1517 } 1518 1519 notification slack.example.notification { 1520 post = http://localhost 1521 bodyTemplate = slack 1522 } 1523 ``` 1524 1525 #### Global Functions 1526 1527 ##### append: append(a []interface{}, b interface{}) interface{} 1528 {: .func} 1529 1530 `append` exposes go append function and lets you append an object to an existing slice (returning a new slice). This is useful since [Go version 1.11 allows you to modify template variables](https://golang.org/doc/go1.11#text/template). 1531 1532 This can be combined with `makeSlice` to build slices to then seralize to JSON. For example: 1533 1534 ``` 1535 notification test { 1536 actionBody = myActionBody 1537 post = http://localhost:2345 1538 contentType = application/json 1539 } 1540 1541 alert test { 1542 template = test 1543 $seriesA = series("host=server01", epoch(), 1) 1544 $seriesB = series("host=server02", epoch(), 2) 1545 warn = avg(merge($seriesA, $seriesB)) 1546 warnNotification = test 1547 } 1548 1549 template test { 1550 subject = `` 1551 body = `` 1552 myActionBody = ` 1553 {{- $s := makeSlice -}} 1554 {{- range $x := .States -}} 1555 {{- $s = append $s (index $x.Events 0).Time -}} 1556 {{- end -}} 1557 {{- $s | json }} 1558 ` 1559 } 1560 ``` 1561 1562 ``` 1563 # Mixed Type matrix example (can be used with above example) 1564 template test { 1565 subject = `` 1566 body = `` 1567 myActionBody = ` 1568 {{- $s := makeSlice -}} 1569 {{- range $x := .States -}} 1570 {{- range $a := $x.Actions }} 1571 {{- $s = append $s (makeSlice $a.Time $a.User $a.Message $a.Type) -}} 1572 {{- end -}} 1573 {{- end -}} 1574 {{- $s | json }} 1575 ` 1576 } 1577 ``` 1578 1579 ##### bytes(string|int|float) (string) 1580 {: .func} 1581 1582 `bytes` converts a number of bytes into a human readable number with a postfix (such as KB or MB). Conversions are base ten and not base two. 1583 1584 Example: 1585 1586 ``` 1587 template bytes { 1588 body = ` 1589 <!-- bytes uses base ten, *not* base two --> 1590 {{ .Alert.Vars.ten | bytes }},{{ .Alert.Vars.two | bytes }} 1591 <!-- results are 97.66KB,1000.00KB --> 1592 ` 1593 subject = `bytes example` 1594 } 1595 1596 alert bytes { 1597 template = bytes 1598 $ten = 100000 1599 $two = 1024000 1600 warn = $ten 1601 } 1602 ``` 1603 1604 ##### html(string) (htemplate.HTML) 1605 {: .func} 1606 1607 `html` takes a string and makes it part of the template. This allows you to inject HTML from variables set in the alert definition. A use case for this is when you have many alerts that share a template and fields you have standardized. For example, you might have a `$notes` variable that you attach to all alerts. This way when filling out the notes for an alert, you can include things like HTML links. 1608 1609 Example: 1610 1611 ``` 1612 template htmlFunc { 1613 body = `All our templates always show the notes: <!-- the following will be rendered as subscript --> 1614 {{ .Alert.Vars.notes | html }}` 1615 subject = `html example` 1616 } 1617 1618 alert htmlFunc { 1619 template = htmlFunc 1620 $notes = <sub>I'm ashmaed about the reason for this alert so this is in subscript...</sub>. In truth, the solution to this alert is the solution to all technical problems, go ask on <a href="https://stackoverflow.com" target="blank">StackOverflow</a> 1621 warn = 1 1622 } 1623 ``` 1624 1625 ##### json(value) (string) 1626 {: .func} 1627 1628 `json` takes a value of any type (usualy a Golang's map) and returns its JSON encoding. It uses the Golang's [`json.Marshal` function](https://golang.org/pkg/encoding/json/#Marshal). Use this function when you need to make a JSON string to communicate to an HTTP API. 1629 1630 Example: 1631 1632 ``` 1633 $apiToken = s3cret 1634 1635 alert json { 1636 template = json 1637 crit = 1 1638 } 1639 1640 template json { 1641 subject = `json example` 1642 body = ` 1643 {{- $html_link := printf "<a href=\"%v\">#%v</a>" .Incident .Id -}} 1644 {{- $text := printf "Alert %v is in %v state, see %v for details" .AlertKey .CurrentStatus $html_link -}} 1645 {{- makeMap "key" (V "$apiToken") "incident" .Id "description" (makeSlice .Subject $text) | json -}} 1646 ` 1647 } 1648 ``` 1649 1650 ##### makeMap(values ...) (map[string]interface{}) 1651 {: .func} 1652 1653 `makeMap` takes a flat list of key-value pairs and turns them into a Golang's map. Keys (odd arguments) must be strings, values (even arguments) can be of any type. This function is useful only in a combination with other functions that help generating JSON, because a template cannot render Golang's map directly. See the [`json` function](definitions#jsonmapstringinterface-string) for a more advanced example. 1654 1655 Example: 1656 1657 ``` 1658 alert makeMap { 1659 template = makeMap 1660 crit = 1 1661 } 1662 1663 template makeMap { 1664 subject = `makeMap example` 1665 body = `{{ makeMap "incident" .Id "description" .Subject | json }}` 1666 } 1667 ``` 1668 1669 ##### makeSlice(values ...) ([]interface{}) 1670 {: .func} 1671 1672 `makeSlice` creates a Golang's slice out a list of given arguments. This function is useful only in a combination with other functions that help generating JSON, because a template cannot render Golang's slice directly. See the [`json` function](definitions#jsonmapstringinterface-string) for a more advanced example. 1673 1674 Example: 1675 1676 ``` 1677 alert makeSlice { 1678 template = makeSlice 1679 crit = 1 1680 } 1681 1682 template makeSlice { 1683 subject = `makeSlice example` 1684 body = `{{ makeSlice .Id .Subject | json }}` 1685 } 1686 ``` 1687 1688 ##### notNil(value) (bool) 1689 {: .func} 1690 1691 `notNil` returns true if the value is nil. This is only meant to be used with error checking on context-bound functions. 1692 1693 1694 ##### parseDuration(string) (*time.Duration) 1695 {: .func} 1696 1697 `parseDuration` maps to Golang's [time.ParseDuration](http://golang.org/pkg/time/#ParseDuration). It returns a pointer to a time.Duration. If there is an error nil will be returned. Unfortunately the error message for this particular can not be seen. 1698 1699 Example: 1700 1701 ``` 1702 template parseDuration { 1703 body = ` 1704 <!-- More commonly you would use .Last.Time.Add , but .Last does not function in the testing interface --> 1705 Doomsday: {{ .Start.Add (parseDuration (.Eval .Alert.Vars.secondsUntilDoom | printf "%fs"))}} 1706 <!-- result is: Doomsday: 2021-02-11 15:11:06.727332631 +0000 UTC --> 1707 ` 1708 subject = `parseDuration example` 1709 } 1710 1711 alert parseDuration { 1712 template = parseDuration 1713 $secondsUntilDoom = 123453245 1714 warn = $secondsUntilDoom 1715 } 1716 ``` 1717 1718 ##### pct(number) (string) 1719 {: .func} 1720 1721 `pct` formats a number as percent. It preserves two decimal places and adds a "%" suffix. It does not do any calculations (in other words, it does *not* multiply the number by 100). 1722 1723 Example: 1724 1725 ``` 1726 template pct { 1727 body = ` 1728 <!-- Need to eval to get number type instead of string --> 1729 {{ .Eval .Alert.Vars.value | pct }} 1730 <!-- result is: 55.56% --> 1731 ` 1732 subject = `pct example` 1733 } 1734 1735 alert pct { 1736 template = pct 1737 $value = 55.55555 1738 warn = $value 1739 } 1740 ``` 1741 1742 ##### replace(s, old, new string, n int) (string) 1743 {: .func} 1744 1745 `replace` maps to golang's [strings.Replace](http://golang.org/pkg/strings/#Replace) function. Which states: 1746 1747 > Replace returns a copy of the string s with the first n non-overlapping instances of old replaced by new. If old is empty, it matches at the beginning of the string and after each UTF-8 sequence, yielding up to k+1 replacements for a k-rune string. If n < 0, there is no limit on the number of replacements 1748 1749 Example: 1750 1751 ``` 1752 template replace { 1753 body = ` 1754 {{ replace "Foo.Bar.Baz" "." " " -1 }} 1755 <!-- result is: Foo Bar Baz --> 1756 ` 1757 subject = `replace example` 1758 } 1759 1760 alert replace { 1761 template = replace 1762 warn = 1 1763 } 1764 ``` 1765 1766 ##### short(string) (string) 1767 {: .func} 1768 1769 `short` Trims the string to everything before the first period. Useful for turning a FQDN into a shortname. For example: `{{short "foo.baz.com"}}` in a template will return `foo` 1770 1771 ##### slackLinkButton(text, url, style string) slack.Action 1772 {: .func} 1773 1774 The `slackLinkButton` function creates a Slack Action with the type set to "button" and the text, url, and style fields as provided as arguments. This is can be used with [the SlackAttachment function](/definitions#slackattachment-slackattachment) and `.AddActions` method for the slack.Attachment. 1775 1776 Example: 1777 1778 ``` 1779 {{- $a := .SlackAttachment -}} 1780 1781 {{- $exampleButton := slackLinkButton "Go To Example.com" "http://example.com" "" -}} 1782 {{- $ruleButton := slackLinkButton "Alert Configuration" .Rule "" -}} 1783 {{- $ackButton := slackLinkButton "Ack Alert" .Ack "" -}} 1784 1785 {{- $a.AddActions $exampleButton $ruleButton $ackButton -}} 1786 1787 1788 {{- makeMap "attachments" (makeSlice $a) | json -}}` 1789 ``` 1790 1791 ##### slackField(title string, value interface{}, short bool) slack.Field 1792 {: .func} 1793 1794 The `slackField` function creates a Slack Field objects based on the arguments. This is can be used with [the SlackAttachment function](/definitions#slackattachment-slackattachment) and `.AddFields` method for the slack.Attachment. 1795 1796 Example: 1797 1798 ``` 1799 {{- $a := .SlackAttachment -}} 1800 1801 {{- $avgQ := .Eval .Alert.Vars.avgQ -}} 1802 1803 {{- $exampleField := slackField "A Number" $avgQ true -}} 1804 {{- $utcField := slackField "UTC Time" (.LastAbnormalTime.Format "2006-01-02T15:04:05") true -}} 1805 {{- $a.AddFields $exampleField $utcField -}} 1806 1807 1808 {{- makeMap "attachments" (makeSlice $a) | json -}}` 1809 ``` 1810 1811 ##### V(string) (string) 1812 {: .func} 1813 1814 The `V` func allows you to access [global variables](/definitions#global-variables) from within templates. This does not recognize variables defined in alerts. 1815 1816 Example: 1817 1818 ``` 1819 $myGlobalVar = Kon'nichiwa 1820 $overRide = I shall be seen 1821 1822 template globalvar { 1823 body = ` 1824 <p>{{ V "$myGlobalVar" }}</p> 1825 <!-- renders to Kon'nichiwa --> 1826 <p>{{ .Alert.Vars.myGlobalVar }}</p> 1827 <!-- renders an empty string --> 1828 <p>{{ V "$overRide" }}</p> 1829 <!-- render to "I shall be seen" since expression variable overrides do *not* work in templates --> 1830 ` 1831 subject = `V example` 1832 } 1833 1834 alert globalvar { 1835 template = globalvar 1836 $overRide = I am not seen 1837 warn = 1 1838 } 1839 ``` 1840 1841 ### Types available in Templates 1842 Since templating is based on Go's template language, certain types will be returned. Understanding these types can help you construct richer alert notifications. 1843 1844 #### Action 1845 {: .type} 1846 1847 An Action is an object that represents actions that people do on an incident. It has the following fields: 1848 1849 * `User`: a string of the username for the person that took the action 1850 * `Message`: a string of an optional message that a user added to the action when taking it 1851 * `Time`: a time.Time object representing the time the action it was taken 1852 * `ActionType`: an int representing the type of action. The values and their strings are: 1853 * 0: "none" 1854 * 1: "Acknowledged" 1855 * 2: "Closed" 1856 * 3: "Forgotten" 1857 * 4: "ForceClosed" 1858 * 5: "Purged" 1859 * 6: "Note" 1860 1861 Example usage can be seen under the [`.Actions` template variable](/definitions#actions). 1862 1863 #### Event 1864 {: .type} 1865 1866 An Event represent a change in the [severity state](/usage#severity-states) within the [duration of an incident](/usage#the-lifetime-of-an-incident). When an incident triggers, it will have at least one event. An Event contains the following fields 1867 1868 * `Warn`: A pointer to an [Event Result](definitions#event-result) that the warn expression generated if the event has a warning status. 1869 * `Crit`: A pointer to an [Event Result](definitions#event-result) if the event has a critical status. 1870 * `Status`: An integer representing the current severity (normal, warning, critical, unknown). As long as it is printed as a string, one will get the textual representation. The status field has identification methods: `IsNormal()`, `IsWarning()`, `IsCritical()`, `IsUnknown()`, `IsError()` which return a boolean. 1871 * `Time`: A [Go time.Time object](https://golang.org/pkg/time/#Time) representing the time of the event. All the methods you find in Go's documentation attached to time.Time are available in the template 1872 * `Unevaluated`: A boolean value if the alert was unevaluated. Alerts on unevaluated when the current was using the [`depends` alert keyword](/definitions#depends) to depend on another alert, and that other alert was non-normal. 1873 1874 It is important to note that the `Warn` and `Crit` fields are pointers. So if there was no `Warn` result and you attempt to access a property of `Warn` then you would get a template error at runtime. Therefore when referecing any fields of `Crit` or `Warn` such as `.Crit.Value`, it is vital that you ensure the `Warn` or `Crit` property of the Event is not a nil pointer first. 1875 1876 See the example under the [`.Events` template variable](/definitions#events) to see how to use events inside a template. 1877 1878 #### Event Result 1879 {: .type} 1880 1881 An Event Result (note: in the code this is actually a `models.Result`) has two properties: 1882 1883 * `Expr`: A string representation of the full expression used to generate the value of the Result's Value. 1884 * `Value`: A float representing the calculated result of the expression. 1885 1886 There is a third property **Computations**. But it is not recommended that you access it even though it is available and it will not be documented. 1887 1888 #### Expr 1889 {: .type} 1890 1891 A `.Expr` is a bosun expression. Although various properties and methods are attached to it, it should only be used for printing (to see the underlying text) and for passing it to function that evaluate expressions such as [`.Eval`](/definitions#evalstringexpression-resultvalue) within templates. 1892 1893 #### IncidentState 1894 {: type} 1895 1896 An `IncidentState` is the main object that contains information about an incident. The IncidentState is always embedded into a template's context making these fields available context-bound template variables (Read: All IncidentState fields are available as template variables, but not all template variables are available as IncidentState fields (IncidentState is a strict/proper subset of Template Variables: `IncidentState ⊊ TemplateVars`)). 1897 1898 This type is also returned by [`.GetIncidentState` template function](/definitions#getincidentstateid-int64-incidentstate). The following fields are available: 1899 1900 * `Id`: The Id of the incident as int64 1901 * `Start`: a [time.Time](https://golang.org/pkg/time/#Time) object, see [Template Variables `.Start`](/definitons#start) 1902 * `End`: a pointer to a time.Time object, will be a nil pointer if the incident has not ended yet 1903 * `AlertKey`: see [Template Variables .AlertKey](/definitions#alertkey) 1904 * `Alert`: string representation of AlertKey 1905 * `Result`: A pointer to an embedded [`Result` type](/definitions#result-1) 1906 * `Events`: a slice of [Event objects](/definitions#event), see [Template Variables `.Events`](/definitons#events) 1907 * `Actions`: a slice of [Action objects](/definitions#action), see [Template Variables `.Events`](/definitons#actions) 1908 * `Subject`: string representation of the subject of the alert, see [Template Variables `.Events`](/definitions#subject-1) 1909 * `NeedAck`, `Open`, `Unevaluated`: are all bool fields. See [Template Variable `.NeedAck`](/definitions#needack), [Template Variable Open](/definitions#open), and [Template Variable `.Unevaluated`](/definitions#unevaulated) 1910 * `CurrentStatus`, `WorstStatus`, `LastAbnormalStatus` are all [`Status` objects](/definitions#status). See See [Template Variable `.CurrentStatus`](/definitions#currentstatus), [Template Variable `.WorstStatus`](/definitions#worststatus), and [Template Variable `.LastAbnormalStatus`](/definitions#lastabnormalstatus) 1911 * `LastAbnormalTime` is time.Time object that will marshall itself as Unix time. See [Template Variable `.LastAbnormalTime`](/definitions#lastabnormaltime) 1912 * `PreviousIds` is a slice of Incident IDs (int64) of previous Incidents. See [Template Variable `.LastAbnormalTime`](/definitions#previousids) 1913 * `NextId` is the ID of a future incident for the same AlertKey. If there is no future incident, then the value is 0. 1914 * `Notifications` is a string slice of all notifications names that were sent for the incident at the present time. 1915 1916 #### Result 1917 {: .type} 1918 1919 A `Result` as two fields: 1920 1921 1. `Group`: The Group is the TagSet of the result. 1922 2. `Value`: The Value of the Result 1923 1924 A tagset is a map of of string to string (`map[string]string` in Go). The keys represent the tag key and their corresponding values are the tag values. 1925 1926 The Value can be of different types. Technically, it is a go `interface{}` with two methods, `Type()` which returns the type of the `Value()` which returns an `interface{}` as well, but will be of the type returned by the `Type()` method. 1927 1928 The most common case of dealing with Results in a ResultSlice is to use the `.EvalAll` func on an expression that would return a NumberSet. See the example under [EvalAll](/definitions#evalall). 1929 1930 #### ResultSlice 1931 {: .type} 1932 1933 A `ResultSlice` is returned by using the [`.EvalAll` template function](/definitions#evalallstringexpressionresultslice-result). It is a slice of pointers to [`Result` objects](/definitions#result-1). Each result represents the an item in the set when the type is something like a NumberSet or a SeriesSet. 1934 1935 #### Status 1936 {: .type} 1937 1938 The `Status` type is an integer that represents the current severity status of the incident with associated string repsentation. The possible values and their string representation is: 1939 1940 * 0 for "none" 1941 * 1 for "normal" 1942 * 2 for "warning" 1943 * 3 for "critical" 1944 * 4 for "unknown" 1945 1946 #### TagSet 1947 A `TagSet` (technically an `opentsdb.TagSet`, but is not actually particular to OpenTSDB) a map of key values to key tags. Both the value and key are strings (`map[string]string`). 1948 1949 ## Notifications 1950 Notifications are referenced by alerts via [warnNotification](/definitions#warnnotification) and [critNotification](/definitions#critnotification) keywords. They specify actions (such as email) to perform when incidents change severity or user performed actions on incidents (See the [lifetime of an incident](/usage#the-lifetime-of-an-incident)). When actions execute they use the templates that are pointed to by the alert that references the notification. 1951 1952 Notifications are independent of each other and executed concurrently (if there are many notifications for an alert, one will not block the other). 1953 1954 More specific documentation on how to fully customize notifications can be found on [this page](/notifications). 1955 1956 ### Chained Notifications 1957 Notifications can also be chained to other notifications (or even itself) using the optional `next` and `timeout` notification keywords. Chained notifications will execute until an alert is acknowledged or closed. 1958 1959 ### Notification keywords 1960 1961 #### bodyTemplate 1962 {: .keyword} 1963 Specify a template name to use for the notification body. Default is `body`, or for email notifications `emailBody` if it is present. 1964 1965 #### contentType 1966 {: .keyword} 1967 1968 If your body for a POST notification requires a different Content-Type header than the default of `application/x-www-form-urlencoded`, you may set the `contentType` variable. 1969 1970 #### email 1971 {: .keyword} 1972 1973 `email` is a list of email addresses. The format is comma separated email addresses in the format of either `Person Name <addr@domain.com>` or `addr@domain.com`. When this is specified emails are enabled. They will use the subject and body fields of the template that the alert references. 1974 1975 #### emailSubjectTemplate 1976 {: .keyword} 1977 Specify a template name to use for the email subject. Defualts to `emailSubject`, or just `subject` if the template doesn't have one. 1978 1979 #### get 1980 {: .keyword} 1981 1982 `get` will make an HTTP get call to the url provided as a value. 1983 1984 #### getTemplate 1985 {: .keyword} 1986 1987 `getTemplate` will use the specified template as a URL, and will make an HTTP request call to it. 1988 1989 #### groupActions 1990 {: .keyword} 1991 chooses whether or not multiple actions performed at once (like a user acking multiple alerts), should be sent as one notification, or as many. Default is `true`. Set to `false` to get one notification per alert key. 1992 1993 #### next 1994 {: .keyword} 1995 1996 `next` is name of next notification to execute after `timeout` and is how you construct notification chains. It can be itself. 1997 1998 #### post 1999 {: .keyword} 2000 2001 `post` will send an HTTP post to specified url. The subject field of the template referenced from the alert is sent as the request body. Content type is set as `application/x-www-form-urlencoded` by default, but may be overriden by setting the `contentType` variable for the notification. 2002 2003 #### postTemplate 2004 {: .keyword} 2005 2006 `postTemplate` will use the specified template as a URL, and will make an HTTP post request to it. 2007 2008 #### print 2009 {: .keyword} 2010 2011 `print` Prints template subject to stdout. The value of `print` is ignored, so just use: `print = true`. 2012 2013 #### runOnActions 2014 {: .keyword} 2015 Specifies which actions types this notification will run on. If set to `all` or `true`, will send all actions. If set to `none` or `false`, it will send on none. 2016 Otherwise, this should be a comma-seperated list of action types to include, from `Ack`, `Close`, `Forget`, `ForceClose`, `Purge`, `Note`, `DelayedClose`, or `CancelClose`. 2017 2018 #### timeout 2019 {: .keyword} 2020 2021 `timeout` is the duration to wait until the notification specified in `next` is executed. If `next` is specified without a `timeout` then it will happen immediately. 2022 2023 #### unknownMinGroupSize 2024 {: .keyword} 2025 Minimum grouping of unknown alert keys that should be sent together. Bosun will try to group related unkowns by common tags if it has at least this many. Set to `0` to disable grouping, and send a notification per alert key. 2026 2027 #### unknownThreshold 2028 {: .keyword} 2029 2030 Maximum number of unknown notifications to send in a single 'batch'. After this many are sent, bosun will send the remainder of the unkown notifications in a single notification using the "multiple unknown groups" template. Set to `0` to specify no limit. 2031 2032 #### action templates 2033 {: .keyword} 2034 You can specify templates to use for actions by setting keys of the form ``action{TemplateType}{ActionType?}` 2035 2036 Where "templateType" is one of `Body`, `Get`, `Post`, or `EmailSubject`, and "ActionType" if present, is one of `Ack`, `Close`, `Forget`, `ForceClose`, `Purge`, `Note`, `DelayedClose`, or `CancelClose`. If Action Type is not specified, it will apply to all actions types, unless specifically overridden. 2037 2038 If nothing is specified for an action type, a built-in template will be used. 2039 2040 See [this page](/notifications) for more details on customizing action notifications. 2041 2042 #### unknown templates 2043 {: .keyword} 2044 2045 Set `unknownBody`, `unknownPost`, `unknownGet`, and `unknownEmailSubject` 2046 2047 or 2048 2049 `unknownMultiBody`, `unknownMultiPost`, `unknownMultiGet`, and `unknownMultiEmailSubject`. 2050 2051 to control which template is used for unknown notifications. If not specified, default built-in templates will be used. 2052 2053 See [this page](/notifications) for more details on customizing unknown notifications. 2054 2055 ### Notification Examples 2056 2057 ``` 2058 # HTTP Post to a chatroom, email in 10m if not ack'd 2059 notification chat { 2060 next = email 2061 timeout = 10m 2062 post = http://chat.example.com/room/1?key=KEY&message=whatever 2063 } 2064 2065 # email foo and bar each day until ack'd 2066 notification email { 2067 email = foo@example.com, bar@example.com 2068 next = email 2069 timeout = 1d 2070 } 2071 2072 2073 2074 # post to a slack.com chatroom via Incoming Webhooks integration 2075 notification slack{ 2076 post = https://hooks.slack.com/services/abcdef 2077 bodyTemplate = slackBody 2078 } 2079 2080 template slack { 2081 slackBody = {"text": "{{.Subject}}"} 2082 jsonBody = {"text": "{{.Subject}}", "apiKey"="2847abc23"} 2083 } 2084 2085 #post json 2086 notification json{ 2087 post = https://someurl.com/submit 2088 body = jsonBody 2089 contentType = application/json 2090 } 2091 ``` 2092 2093 ## Lookup tables 2094 Lookup tables are tables you create that store information about tags. They can be used in 3 main ways: 2095 2096 1. To set different values (i.e. thresholds) in expressions for different tags in a response set 2097 2. To change the notification of an alert based on the tags in the response (notification lookups) 2098 3. To associate information with tags that can be referenced from templates 2099 2100 Lookup tables are named, then have entries based on Tags, and within each entry there are arbirary key / value pairs. To access the data in expressions either the [lookup](/expressions#lookuptable-string-key-string-numberset) or [lookupSeries](/expressions#lookupseriesseries-seriesset-table-string-key-string-numberset) expression functions are used. 2101 2102 When using notification lookups, the "lookup" function is actually different from the expression lookup function behind the scenes. Users using `lookupSeries` in expressions should still use `lookup` when defining notification lookups. 2103 2104 The syntax is: 2105 2106 ``` 2107 lookup <tableName> { 2108 entry <tagKey=(tagValue|glob),(tagKey=...) { 2109 <key> = <value> 2110 <key2> = <value> 2111 ... 2112 } 2113 entry ... 2114 } 2115 ``` 2116 2117 Multiple tags can be used: 2118 2119 ``` 2120 lookup cpu { 2121 entry host=web-*,dc=eu { 2122 high = 0.5 2123 } 2124 entry host=sql-*,dc=us { 2125 high = 0.8 2126 } 2127 entry host=*,dc=us { 2128 high = 0.3 2129 } 2130 entry host=*,dc=* { 2131 high = 0.4 2132 } 2133 } 2134 ``` 2135 2136 ### Main Lookup Example 2137 This example shows all three uses of lookups: expression value switching, notification lookups, and lookup usage in templates. 2138 2139 ``` 2140 notification uhura { 2141 print = true 2142 } 2143 2144 notification spock { 2145 print = true 2146 } 2147 2148 lookup exampleTable { 2149 entry host=a { 2150 threshold = 9 2151 fish = Ohhh... a Red Snapper - Hmmm... Very Tasty 2152 contact_crew = spock 2153 } 2154 # You took the Box! Lets see whats in the box! 2155 entry host=* { 2156 threshold = 3 2157 fish = Nothing! Absolutely Nothing! Stupid! You so Stupid! 2158 contact_crew = uhura 2159 } 2160 } 2161 2162 alert exampleTable { 2163 template = lookup 2164 $series = merge(series("host=a", 0, 10), series("host=b", 0, 2)) 2165 $r = avg($series) 2166 2167 # lookup depends on Bosun's index of datapoints to get possible tag values 2168 $lk = $r > lookup("exampleTable", "threshold") 2169 2170 # lookupSeries uses the series to get the possible tag values 2171 $lks = $r > lookupSeries($series, "exampleTable", "threshold") 2172 2173 warn = $lks 2174 2175 # spock will be contacted for host a, uhura for all others 2176 warnNotification = lookup("exampleTable", "contact_crew") 2177 } 2178 2179 template lookup { 2180 body = ` 2181 <h1>.Lookup</h1> 2182 2183 <p>You Got a: {{ .Lookup "exampleTable" "fish" }}</p> 2184 <!-- For host a this will render to "Ohhh... a Red Snapper - Hmmm... Very Tasty" --> 2185 <!-- It is just a shorthand for {{.LookupAll "exampleTable" "fish" .Group }} --> 2186 2187 <h2>.LookupAll</h2> 2188 2189 <p>The fish for host "b" will always be {{ .LookupAll "exampleTable" "fish" "host=b" }}</p> 2190 <!-- For host a this will render to "Nothing! Absolutely Nothing! Stupid! You so Stupid!" 2191 since we requested host=b specifically --> 2192 ` 2193 subject = `lookup example` 2194 } 2195 ``` 2196 2197 ## Macros 2198 2199 Macros are sections that can define anything (including variables). It is not an error to reference an unknown variable in a macro. Other sections can reference the macro with `macro = name`. The macro's data will be expanded with the current variable definitions and inserted at that point in the section. Multiple macros may be thus referenced at any time. Macros may reference other macros. For example: 2200 2201 ``` 2202 $default_time = "2m" 2203 2204 macro m1 { 2205 $w = 80 2206 warnNotification = default 2207 } 2208 2209 macro m2 { 2210 macro = m1 2211 $c = 90 2212 } 2213 2214 alert os.high_cpu { 2215 $q = avg(q("avg:rate:os.cpu{host=ny-nexpose01}", $default_time, "")) 2216 macro = m2 2217 warn = $q > $w 2218 crit = $q >= $c 2219 } 2220 ``` 2221 2222 Will yield a warn expression for the os.high_cpu alert: 2223 2224 ``` 2225 avg(q("avg:rate:os.cpu{host=ny-nexpose01}", "2m", "")) > 80 2226 ``` 2227 2228 and set `warnNotification = default` for that alert. 2229 2230 {% endraw %} 2231 2232 </div> 2233 </div>