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>