Commit 530aa19b authored by Brian Brazil's avatar Brian Brazil

Merge pull request #444 from brian-brazil/group-ignoring

Add docs for ignoring and new group_* semantics.
parents cbde8e57 54043b11
...@@ -5,6 +5,7 @@ output/ ...@@ -5,6 +5,7 @@ output/
# Temporary file directory # Temporary file directory
tmp/ tmp/
downloads/
# Crash Log # Crash Log
crash.log crash.log
......
sudo: false sudo: false
language: ruby language: ruby
branches: branches:
only: only:
- master - master
script: make deploy script: make deploy
before_install: before_install:
- eval "$(ssh-agent -s)" - eval "$(ssh-agent -s)"
- openssl aes-256-cbc -K $encrypted_2ba894bc7c2f_key -iv $encrypted_2ba894bc7c2f_iv -in prometheus_rsa.enc -out prometheus_rsa -d - openssl aes-256-cbc -K $encrypted_2ba894bc7c2f_key -iv $encrypted_2ba894bc7c2f_iv -in prometheus_rsa.enc -out prometheus_rsa -d
- chmod 600 prometheus_rsa - chmod 600 prometheus_rsa
- ssh-add prometheus_rsa - ssh-add prometheus_rsa
env:
global:
secure: aNZoB4UDtdelUDxFTTJa5Dxm8gXorKGmQWYaFIXzuBwLvFMp1toIY+amsMrGBbnvxrJosO4764GpS8sWRT/Jbr252sxjOe8N4jfbtJUX29U2aNFQSWVc/o8VnKFW1NI5cjBAZ9OOvgpS6KUGkXZ2/PTmvgxvyqGS4pPovIM1OOo=
...@@ -12,3 +12,4 @@ gem 'builder' ...@@ -12,3 +12,4 @@ gem 'builder'
gem 'rb-inotify', :require => false gem 'rb-inotify', :require => false
gem 'rb-fsevent', :require => false gem 'rb-fsevent', :require => false
gem 'rb-fchange', :require => false gem 'rb-fchange', :require => false
gem 'nanoc-cachebuster'
...@@ -42,6 +42,8 @@ GEM ...@@ -42,6 +42,8 @@ GEM
multi_json (1.11.2) multi_json (1.11.2)
nanoc (3.7.3) nanoc (3.7.3)
cri (~> 2.3) cri (~> 2.3)
nanoc-cachebuster (0.3.1)
nanoc (>= 3.3.0)
nokogiri (1.6.3.1) nokogiri (1.6.3.1)
mini_portile (= 0.6.0) mini_portile (= 0.6.0)
posix-spawn (0.3.9) posix-spawn (0.3.9)
...@@ -75,6 +77,7 @@ DEPENDENCIES ...@@ -75,6 +77,7 @@ DEPENDENCIES
guard-nanoc guard-nanoc
kramdown kramdown
nanoc nanoc
nanoc-cachebuster
nokogiri nokogiri
pygments.rb pygments.rb
rb-fchange rb-fchange
......
compile: DOWNLOADS := prometheus alertmanager blackbox_exporter haproxy_exporter mysqld_exporter node_exporter pushgateway statsd_exporter
rm -rf output
clean:
rm -rf output downloads
compile: clean downloads
bundle exec nanoc bundle exec nanoc
deploy: github_pages_export github_pages_push deploy: github_pages_export github_pages_push
downloads: $(DOWNLOADS:%=downloads/%/repo.json) $(DOWNLOADS:%=downloads/%/releases.json)
downloads/%/repo.json:
@mkdir -p $(dir $@)
@echo "curl -sf -H 'Accept: application/vnd.github.v3+json' <GITHUB_AUTHENTICATION> https://api.github.com/repos/prometheus/$* > $@"
@curl -sf -H 'Accept: application/vnd.github.v3+json' $(GITHUB_AUTHENTICATION) https://api.github.com/repos/prometheus/$* > $@
downloads/%/releases.json:
@mkdir -p $(dir $@)
@echo "curl -sf -H 'Accept: application/vnd.github.v3+json' <GITHUB_AUTHENTICATION> https://api.github.com/repos/prometheus/$*/releases > $@"
@curl -sf -H 'Accept: application/vnd.github.v3+json' $(GITHUB_AUTHENTICATION) https://api.github.com/repos/prometheus/$*/releases > $@
github_pages_export: compile github_pages_export: compile
cd output && \ cd output && \
echo prometheus.io > CNAME && \ echo prometheus.io > CNAME && \
......
# Prometheus Documentation # Prometheus Documentation [![Build Status](https://travis-ci.org/prometheus/docs.svg?branch=master)](https://travis-ci.org/prometheus/docs)
This repository contains both the content and the static-site generator code for the This repository contains both the content and the static-site generator code for the
Prometheus documentation site. Prometheus documentation site.
......
...@@ -17,8 +17,13 @@ compile '/assets/*' do ...@@ -17,8 +17,13 @@ compile '/assets/*' do
end end
route '/assets/*' do route '/assets/*' do
# /assets/foo.html/ → /foo.html # Cachebuster currently doesn't fingerprint all needed files (SVG and font
item.identifier[0..-2] # extensions are missing), so we need to differentiate here.
if Nanoc::Cachebuster::FILETYPES_TO_FINGERPRINT.include?(item[:extension])
item.identifier[0..-(3 + item[:extension].length)] + fingerprint(item[:filename]) + '.' + item[:extension]
else
item.identifier[0..-2]
end
end end
route '/README/' do route '/README/' do
...@@ -40,7 +45,6 @@ compile '*' do ...@@ -40,7 +45,6 @@ compile '*' do
if item[:title] == 'README.md' if item[:title] == 'README.md'
# Don't filter; this should propagate verbatim to the output GitHub repository. # Don't filter; this should propagate verbatim to the output GitHub repository.
elsif item[:extension] == 'md' elsif item[:extension] == 'md'
#filter :kramdown
filter :redcarpet, options: {filter_html: true, autolink: true, no_intraemphasis: true, fenced_code_blocks: true, gh_blockcode: true, tables: true}, renderer_options: {with_toc_data: true} filter :redcarpet, options: {filter_html: true, autolink: true, no_intraemphasis: true, fenced_code_blocks: true, gh_blockcode: true, tables: true}, renderer_options: {with_toc_data: true}
filter :add_anchors filter :add_anchors
filter :bootstrappify filter :bootstrappify
...@@ -61,6 +65,8 @@ compile '*' do ...@@ -61,6 +65,8 @@ compile '*' do
else else
layout 'default' layout 'default'
end end
filter :cache_buster
end end
route '/blog/' do route '/blog/' do
...@@ -78,7 +84,7 @@ end ...@@ -78,7 +84,7 @@ end
route '*' do route '*' do
if item[:extension] == 'css' if item[:extension] == 'css'
# Write item with identifier /foo/ to /foo.css # Write item with identifier /foo/ to /foo.css
item.identifier.chop + '.css' item.identifier.chop + fingerprint(item[:filename]) + '.css'
elsif item.binary? elsif item.binary?
# Write item with identifier /foo/ to /foo.ext # Write item with identifier /foo/ to /foo.ext
item.identifier.chop + '.' + item[:extension] item.identifier.chop + '.' + item[:extension]
......
...@@ -16,6 +16,8 @@ catch up on anything you missed. ...@@ -16,6 +16,8 @@ catch up on anything you missed.
In the future, we will use this blog to publish more articles and announcements In the future, we will use this blog to publish more articles and announcements
to help you get the most out of Prometheus. to help you get the most out of Prometheus.
<!-- more -->
## Using Prometheus ## Using Prometheus
Posts on how to use Prometheus comprise the majority of online content. Here Posts on how to use Prometheus comprise the majority of online content. Here
......
...@@ -22,6 +22,7 @@ In this blog post, we will take a closer look at the built-in service discovery ...@@ -22,6 +22,7 @@ In this blog post, we will take a closer look at the built-in service discovery
some practical examples. As an additional resource, see some practical examples. As an additional resource, see
[Prometheus's configuration documentation](/docs/operating/configuration). [Prometheus's configuration documentation](/docs/operating/configuration).
<!-- more -->
## Prometheus and targets ## Prometheus and targets
...@@ -35,7 +36,7 @@ labels set by an earlier stage: ...@@ -35,7 +36,7 @@ labels set by an earlier stage:
1. Global labels, which are assigned to every target scraped by the Prometheus instance. 1. Global labels, which are assigned to every target scraped by the Prometheus instance.
2. The `job` label, which is configured as a default value for each scrape configuration. 2. The `job` label, which is configured as a default value for each scrape configuration.
3. Labels that are set per target group within a scrape configuration. 3. Labels that are set per target group within a scrape configuration.
4. Advanced label manipulation via [_relabeling_](/docs/operating/configuration/#target-relabeling-relabel_config). 4. Advanced label manipulation via [_relabeling_](/docs/operating/configuration/#relabel_config).
Each stage overwrites any colliding labels from the earlier stages. Eventually, we have a flat Each stage overwrites any colliding labels from the earlier stages. Eventually, we have a flat
set of labels that describe a single target. Those labels are then attached to every time series that set of labels that describe a single target. Those labels are then attached to every time series that
...@@ -76,7 +77,7 @@ scrape_configs: ...@@ -76,7 +77,7 @@ scrape_configs:
job: 'job2' job: 'job2'
``` ```
Through a mechanism named [_relabeling_](http://prometheus.io/docs/operating/configuration/#target-relabeling-relabel_config), Through a mechanism named [_relabeling_](http://prometheus.io/docs/operating/configuration/#relabel_config),
any label can be removed, created, or modified on a per-target level. This any label can be removed, created, or modified on a per-target level. This
enables fine-grained labeling that can also take into account metadata coming enables fine-grained labeling that can also take into account metadata coming
from the service discovery. Relabeling is the last stage of label assignment from the service discovery. Relabeling is the last stage of label assignment
...@@ -124,7 +125,7 @@ This rule transforms a target with the label set: ...@@ -124,7 +125,7 @@ This rule transforms a target with the label set:
You could then also remove the source labels in an additional relabeling step. You could then also remove the source labels in an additional relabeling step.
You can read more about relabeling and how you can use it to filter targets in the You can read more about relabeling and how you can use it to filter targets in the
[configuration documentation](/docs/operating/configuration#target-relabeling-relabel_config). [configuration documentation](/docs/operating/configuration#relabel_config).
Over the next sections, we will see how you can leverage relabeling when using service discovery. Over the next sections, we will see how you can leverage relabeling when using service discovery.
...@@ -219,7 +220,7 @@ has the `production` or `canary` Consul tag, a respective `group` label is assig ...@@ -219,7 +220,7 @@ has the `production` or `canary` Consul tag, a respective `group` label is assig
Each target's `instance` label is set to the node name provided by Consul. Each target's `instance` label is set to the node name provided by Consul.
A full documentation of all configuration parameters for service discovery via Consul A full documentation of all configuration parameters for service discovery via Consul
can be found on the [Prometheus website](/docs/operating/configuration#target-relabeling-relabel_config). can be found on the [Prometheus website](/docs/operating/configuration#relabel_config).
## Custom service discovery ## Custom service discovery
......
...@@ -19,6 +19,8 @@ detect and handle with custom-built rules. The Prometheus [query ...@@ -19,6 +19,8 @@ detect and handle with custom-built rules. The Prometheus [query
language](../../../../../docs/querying/basics/) gives you the tools to discover language](../../../../../docs/querying/basics/) gives you the tools to discover
these anomalies while avoiding false positives. these anomalies while avoiding false positives.
<!-- more -->
## Building a query ## Building a query
A common problem within a service is when a small number of servers are not A common problem within a service is when a small number of servers are not
...@@ -83,8 +85,6 @@ ALERT InstanceLatencyOutlier ...@@ -83,8 +85,6 @@ ALERT InstanceLatencyOutlier
> >
1 1
FOR 30m FOR 30m
SUMMARY "{{$labels.instance}} in {{$labels.job}} is a latency outlier"
DESCRIPTION "{{$labels.instance}} has latency of {{humanizeDuration $value}}"
``` ```
## Automatic actions ## Automatic actions
...@@ -107,23 +107,13 @@ configuration that uses it could look like this: ...@@ -107,23 +107,13 @@ configuration that uses it could look like this:
``` ```
# A simple notification configuration which only sends alert notifications to # A simple notification configuration which only sends alert notifications to
# an external webhook. # an external webhook.
notification_config { receivers:
name: "restart_webhook" - name: restart_webhook
webhook_config { webhook_configs:
url: "http://example.org/my/hook" url: "http://example.org/my/hook"
}
} route:
receiver: restart_webhook
# An aggregation rule which matches all alerts with the label
# alertname="InstanceLatencyOutlier" and sends them using the "restart_webhook"
# notification configuration.
aggregation_rule {
filter {
name_re: "alertname"
value_re: "InstanceLatencyOutlier"
}
notification_config_name: "restart_webhook"
}
``` ```
...@@ -135,4 +125,4 @@ Alertmanager's generic webhook support can trigger automatic remediations. ...@@ -135,4 +125,4 @@ Alertmanager's generic webhook support can trigger automatic remediations.
This all combines to enable oncall engineers to focus on problems where they can This all combines to enable oncall engineers to focus on problems where they can
have the most impact. have the most impact.
When defining alerts for your services, see also our [alerting best practices](http://prometheus.io/docs/practices/alerting/). When defining alerts for your services, see also our [alerting best practices](/docs/practices/alerting/).
...@@ -30,6 +30,8 @@ or *dhtech*. This post is going to focus on the work of dhtech and how we used ...@@ -30,6 +30,8 @@ or *dhtech*. This post is going to focus on the work of dhtech and how we used
Prometheus during DreamHack Summer 2015 to try to kick our monitoring up another Prometheus during DreamHack Summer 2015 to try to kick our monitoring up another
notch. notch.
<!-- more -->
## The equipment ## The equipment
Turns out that to build a highly performant network for 10,000+ Turns out that to build a highly performant network for 10,000+
computers, you need at least the same number of network ports. In our case these computers, you need at least the same number of network ports. In our case these
...@@ -56,7 +58,7 @@ below. ...@@ -56,7 +58,7 @@ below.
[![The DreamHack network core](https://c2.staticflickr.com/4/3951/18679671439_10ce7a8eb4_c.jpg)](https://www.flickr.com/photos/dreamhack/18679671439) [![The DreamHack network core](https://c2.staticflickr.com/4/3951/18679671439_10ce7a8eb4_c.jpg)](https://www.flickr.com/photos/dreamhack/18679671439)
<center>*The DreamHack network core*</center> <center>*The DreamHack network core*</center>
[![Network planning map](http://i.imgur.com/ZCQa2Abl.png)](http://i.imgur.com/ZCQa2Ab.png) [![Network planning map](/assets/dh_network_planning_map.png)](/assets/dh_network_planning_map.png)
<center>*The planning map for the distribution and core layers. The core is <center>*The planning map for the distribution and core layers. The core is
clearly visible in "Hall D"*</center> clearly visible in "Hall D"*</center>
...@@ -96,7 +98,7 @@ snmpcollector (SNMP) and ipplan-pinger (ICMP), closely followed by dhcpinfo ...@@ -96,7 +98,7 @@ snmpcollector (SNMP) and ipplan-pinger (ICMP), closely followed by dhcpinfo
systems into [node_exporter](https://github.com/prometheus/node_exporter)'s systems into [node_exporter](https://github.com/prometheus/node_exporter)'s
textfile collector. textfile collector.
[![dhmon Architecture](http://i.imgur.com/6gN3MRp.png)](http://i.imgur.com/6gN3MRp.png) [![dhmon Architecture](/assets/dh_dhmon_architecture.png)](/assets/dh_dhmon_architecture.png)
<center>*The current architecture plan of dhmon as of Summer 2015*</center> <center>*The current architecture plan of dhmon as of Summer 2015*</center>
We use Prometheus as a central timeseries storage and querying engine, but we We use Prometheus as a central timeseries storage and querying engine, but we
...@@ -119,7 +121,7 @@ very short sampling intervals. In the end, we found no reason for why we can't ...@@ -119,7 +121,7 @@ very short sampling intervals. In the end, we found no reason for why we can't
use Prometheus for this data as well - we will definitely try to replace our use Prometheus for this data as well - we will definitely try to replace our
memcached with Prometheus at the next DreamHack. memcached with Prometheus at the next DreamHack.
[![dhmon Visualization](http://i.imgur.com/D5I0Ztbl.png)](http://i.imgur.com/D5I0Ztb.png) [![dhmon Visualization](/assets/dh_dhmon_visualization.png)](/assets/dh_dhmon_visualization.png)
<center>*The overview of our access layer visualized by dhmon*</center> <center>*The overview of our access layer visualized by dhmon*</center>
## Prometheus setup ## Prometheus setup
...@@ -196,7 +198,7 @@ Let's also look at how an alert for an almost full DHCP scope looks like: ...@@ -196,7 +198,7 @@ Let's also look at how an alert for an almost full DHCP scope looks like:
We found the syntax to define alerts easy to read and understand even if you had We found the syntax to define alerts easy to read and understand even if you had
no previous experience with Prometheus or time series databases. no previous experience with Prometheus or time series databases.
[![Prometheus alerts for DreamHack](http://i.imgur.com/RV5gM7Ol.png)](http://i.imgur.com/RV5gM7O.png) [![Prometheus alerts for DreamHack](/assets/dh_prometheus_alerts.png)](/assets/dh_prometheus_alerts.png)
<center>*Oops! Turns out we have some bad uplinks, better run out and fix <center>*Oops! Turns out we have some bad uplinks, better run out and fix
it!*</center> it!*</center>
...@@ -208,7 +210,7 @@ Every time someone asked us something about the network, we crafted a query to ...@@ -208,7 +210,7 @@ Every time someone asked us something about the network, we crafted a query to
get the answer and saved it as a dashboard widget. The most interesting ones get the answer and saved it as a dashboard widget. The most interesting ones
were then added to an overview dashboard that we proudly displayed. were then added to an overview dashboard that we proudly displayed.
[![dhmon Dashboard](http://i.imgur.com/yYtC8vLl.png)](http://i.imgur.com/yYtC8vL.png) [![dhmon Dashboard](/assets/dh_dhmon_dashboard.png)](/assets/dh_dhmon_dashboard.png)
<center>*The DreamHack Overview dashboard powered by PromDash*</center> <center>*The DreamHack Overview dashboard powered by PromDash*</center>
## The future ## The future
......
...@@ -24,6 +24,8 @@ In this post we will implement a small utility program that connects a custom ...@@ -24,6 +24,8 @@ In this post we will implement a small utility program that connects a custom
service discovery approach based on [etcd](https://coreos.com/etcd/), the service discovery approach based on [etcd](https://coreos.com/etcd/), the
highly consistent distributed key-value store, to Prometheus. highly consistent distributed key-value store, to Prometheus.
<!-- more -->
## Targets in etcd and Prometheus ## Targets in etcd and Prometheus
Our fictional service discovery system stores services and their Our fictional service discovery system stores services and their
......
...@@ -22,20 +22,22 @@ Docker and Boexever in 2014. Over the years, Prometheus was growing more and ...@@ -22,20 +22,22 @@ Docker and Boexever in 2014. Over the years, Prometheus was growing more and
more mature and although it was already solving people's monitoring problems, more mature and although it was already solving people's monitoring problems,
it was still unknown to the wider public. it was still unknown to the wider public.
<!-- more -->
## Going public ## Going public
Everything changed for us a year ago, in January of 2015. After more than two Everything changed for us a year ago, in January of 2015. After more than two
years of development and internal usage, we felt that Prometheus was ready for years of development and internal usage, we felt that Prometheus was ready for
a wider audience and decided to go fully public with our official [announcement a wider audience and decided to go fully public with our official [announcement
blog post](https://developers.soundcloud.com/blog/prometheus-monitoring-at-soundcloud), blog post](https://developers.soundcloud.com/blog/prometheus-monitoring-at-soundcloud),
a [website](http://prometheus.io/), and a series of a [website](https://prometheus.io/), and a series of
[related](http://www.boxever.com/tags/monitoring) [related](http://www.boxever.com/tags/monitoring)
[posts](http://5pi.de/2015/01/26/monitor-docker-containers-with-prometheus/). [posts](http://5pi.de/2015/01/26/monitor-docker-containers-with-prometheus/).
We already received a good deal of attention during the first week after the We already received a good deal of attention during the first week after the
announcement, but nothing could prepare us for what happened a week later: announcement, but nothing could prepare us for what happened a week later:
someone unknown to us (hello there, someone unknown to us (hello there,
[jjwiseman](https://news.ycombinator.com/user?id=jjwiseman)!) had submitted [jjwiseman](https://news.ycombinator.com/user?id=jjwiseman)!) had submitted
[the Prometheus website](http://prometheus.io/) to Hacker News and somehow their [the Prometheus website](https://prometheus.io/) to Hacker News and somehow their
post had made it [all the way to the top](https://news.ycombinator.com/item?id=8995696). post had made it [all the way to the top](https://news.ycombinator.com/item?id=8995696).
This is when things started going slightly crazy in a good way. We saw a sharp This is when things started going slightly crazy in a good way. We saw a sharp
...@@ -105,10 +107,10 @@ or another has become too long to mention all of them: ...@@ -105,10 +107,10 @@ or another has become too long to mention all of them:
[CoreOS](https://coreos.com/), [Docker](https://docker.com), [CoreOS](https://coreos.com/), [Docker](https://docker.com),
[Boxever](http://www.boxever.com/), [Boxever](http://www.boxever.com/),
[DigitalOcean](https://www.digitalocean.com/), [Financial Times](http://www.ft.com/), [DigitalOcean](https://www.digitalocean.com/), [Financial Times](http://www.ft.com/),
[Improbable](http://improbable.io/), [KPMG](https://kpmg.com), and many more. [Improbable](http://improbable.io/), [KPMG](https://www.kpmg.com), and many more.
Even the world's largest digital festival, Even the world's largest digital festival,
[DreamHack](https://www.dreamhack.se), has [used [DreamHack](https://www.dreamhack.se), has [used
Prometheus](http://prometheus.io/blog/2015/06/24/monitoring-dreamhack/) to keep Prometheus](/blog/2015/06/24/monitoring-dreamhack/) to keep
tabs on their network infrastructure in 2015, and tabs on their network infrastructure in 2015, and
[FOSDEM](https://fosdem.org/2016/) will do so in 2016. [FOSDEM](https://fosdem.org/2016/) will do so in 2016.
......
---
title: Custom Alertmanager Templates
created_at: 2016-03-03
kind: article
author_name: Fabian Reinartz
---
The Alertmanager handles alerts sent by Prometheus servers and sends
notifications about them to different receivers based on their labels.
A receiver can be one of many different integrations such as PagerDuty, Slack,
email, or a custom integration via the generic webhook interface (for example [JIRA](https://github.com/fabxc/jiralerts)).
## Templates
The messages sent to receivers are constructed via templates.
Alertmanager comes with default templates but also allows defining custom
ones.
In this blog post, we will walk through a simple customization of Slack
notifications.
We use this simple Alertmanager configuartion that sends all alerts to Slack:
```yaml
global:
slack_api_url: '<slack_webhook_url>'
route:
receiver: 'slack-notifications'
# All alerts in a notification have the same value for these labels.
group_by: [alertname, datacenter, app]
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#alerts'
```
By default, a Slack message sent by Alertmanager looks like this:
![](/assets/blog/2016-03-03/slack_alert_before.png)
It shows us that there is one firing alert, followed by the label values of
the alert grouping (alertname, datacenter, app) and further label values the
alerts have in common (critical).
<!-- more -->
## Customize
If you have alerts, you should also have documentation on how to handle them –
a runbook. A good approach to that is having a wiki that has a section for
each app you are running with a page for each alert.
Suppose we have such a wiki running at `https://internal.myorg.net/wiki/alerts`.
Now we want links to these runbooks shown in our Slack messages.
In our template, we need access to the "alertname" and the "app" label. Since
these are labels we group alerts by, they are available in the `GroupLabels`
map of our templating data.
We can directly add custom templating to our Alertmanager's [Slack configuration](/docs/alerting/configuration/#slack-receiver-slack_config)
that is used for the `text` section of our Slack message.
The [templating language](https://godoc.org/text/template) is the one provided
by the Go programming language.
```yaml
global:
slack_api_url: '<slack_webhook_url>'
route:
- receiver: 'slack-notifications'
group_by: [alertname, datacenter, app]
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#alerts'
# Template for the text field in Slack messages.
text: 'https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}'
```
We reload our Alertmanager by sending a `SIGHUP` or restart it to load the
changed configuration. Done.
Our Slack notifications now look like this:
![](/assets/blog/2016-03-03/slack_alert_after.png)
### Template files
Alternatively, we can also provide a file containing named templates, which
are then loaded by Alertmanager. This is especially helpful for more complex
templates that span many lines.
We create a file `/etc/alertmanager/templates/myorg.tmpl` and create a
template in it named "slack.myorg.text":
```
{{ define "slack.myorg.text" }}https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}{{ end}}
```
Our configuration now loads the template with the given name for the "text"
field and we provide a path to our custom template file:
```yaml
global:
slack_api_url: '<slack_webhook_url>'
route:
- receiver: 'slack-notifications'
group_by: [alertname, datacenter, app]
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#alerts'
text: '{{ template "slack.myorg.text" . }}'
templates:
- '/etc/alertmanager/templates/myorg.tmpl'
```
We reload our Alertmanager by sending a `SIGHUP` or restart it to load the
changed configuration and the new template file. Done.
---
title: Interview with Life360
created_at: 2016-03-23
kind: article
author_name: Brian Brazil
---
*This is the first in a series of interviews with users of Prometheus, allowing
them to share their experiences of evaluating and using Prometheus. Our first
interview is with Daniel from Life360.*
## Can you tell us about yourself and what Life360 does?
I’m Daniel Ben Yosef, a.k.a, dby, and I’m an Infrastructure Engineer for
[Life360](https://www.life360.com/), and before that, I’ve held systems
engineering roles for the past 9 years.
Life360 creates technology that helps families stay connected, we’re the Family
Network app for families. We’re quite busy handling these families - at peak
we serve 700k requests per minute for 70 million registered families.
[<img src="/assets/blog/2016-03-23/life360_horizontal_logo_gradient_rgb.png" style="width: 444px; height:177px"/>](https://www.life360.com/)
We manage around 20 services in production, mostly handling location requests
from mobile clients (Android, iOS, and Windows Phone), spanning over 150+
instances at peak. Redundancy and high-availability are our goals and we strive
to maintain 100% uptime whenever possible because families trust us to be
available.
We hold user data in both our MySQL multi-master cluster and in our 12-node
Cassandra ring which holds around 4TB of data at any given time. We have
services written in Go, Python, PHP, as well as plans to introduce Java to our
stack. We use Consul for service discovery, and of course our Prometheus setup
is integrated with it.
<!-- more -->
## What was your pre-Prometheus monitoring experience?
Our monitoring setup, before we switched to Prometheus, included many
components such as:
* Copperegg (now Idera)
* Graphite + Statsd + Grafana
* Sensu
* AWS Cloudwatch
We primarily use MySQL, NSQ and HAProxy and we found that all of the monitoring
solutions mentioned above were very partial, and required a lot of
customization to actually get all working together.
## Why did you decide to look at Prometheus?
We had a few reasons for switching to Prometheus, one of which is that we
simply needed better monitoring.
Prometheus has been known to us for a while, and we have been tracking it and
reading about the active development, and at a point (a few months back) we
decided to start evaluating it for production use.
The PoC results were incredible. The monitoring coverage of MySQL was amazing,
and we also loved the JMX monitoring for Cassandra, which had been sorely
lacking in the past.
[![Cassandra Client Dashboard](/assets/blog/2016-03-23/cx_client.png)](/assets/blog/2016-03-23/cx_client.png)
## How did you transition?
We started with a relatively small box (4GB of memory) as an initial point. It
was effective for a small number of services, but not for our full monitoring
needs.
We also initially deployed with Docker, but slowly transitioned to its own box
on an r3.2xl instance (60GB ram), and that holds all of our service monitoring
needs with 30 days of in-memory data.
We slowly started introducing all of our hosts with the Node Exporter and built
Grafana graphs, up to the point where we had total service coverage.
We were also currently looking at InfluxDB for long term storage, but due to
[recent developments](https://influxdata.com/blog/update-on-influxdb-clustering-high-availability-and-monetization/),
this may no longer be a viable option.
We then added exporters for MySQL, Node, Cloudwatch, HAProxy, JMX, NSQ (with a
bit of our own code), Redis and Blackbox (with our own contribution to add
authentication headers).
[![NSQ Overview Dashboard](/assets/blog/2016-03-23/nsq_overview.png)](/assets/blog/2016-03-23/nsq_overview.png)
## What improvements have you seen since switching?
The visibility and instrumentation gain was the first thing we saw. Right
before switching, we started experiencing Graphite’s scalability issues, and
having an in-place replacement for Graphite so stakeholders can continue to use
Grafana as a monitoring tool was extremely valuable to us. Nowadays, we are
focusing on taking all that data and use it to detect anomalies, which will
eventually become alerts in the Alert Manager.
## What do you think the future holds for Life360 and Prometheus?
We currently have one of our projects instrumented directly with a Prometheus
client, a Python-based service. As we build out new services, Prometheus is
becoming our go-to for instrumentation, and will help us gain extremely
meaningful alerts and stats about our infrastructure.
We look forward to growing with the project and keep contributing.
*Thank you Daniel! The source for Life360's dashboards is shared on [Github](https://github.com/life360/prometheus-grafana-dashboards).*
---
title: Interview with ShowMax
created_at: 2016-05-01
kind: article
author_name: Brian Brazil
---
*This is the second in a series of interviews with users of Prometheus, allowing
them to share their experiences of evaluating and using Prometheus.*
## Can you tell us about yourself and what ShowMax does?
I’m Antonin Kral, and I’m leading research and architecture for
[ShowMax](http://www.showmax.com). Before that, I’ve held architectural and CTO
roles for the past 12 years.
ShowMax is a subscription video on demand service that launched in South Africa
in 2015. We’ve got an extensive content catalogue with more than 20,000
episodes of TV shows and movies. Our service is currently available in 65
countries worldwide. While better known rivals are skirmishing in America and
Europe, ShowMax is battling a more difficult problem: how do you binge-watch
in a barely connected village in sub-Saharan Africa? Already 35% of video
around the world is streamed, but there are still so many places the revolution
has left untouched.
![ShowMax logo](/assets/blog/2016-05-01/showmax-logo.png)
We are managing about 50 services running mostly on private clusters built
around CoreOS. They are primarily handling API requests from our clients
(Android, iOS, AppleTV, JavaScript, Samsung TV, LG TV etc), while some of them
are used internally. One of the biggest internal pipelines is video encoding
which can occupy 400+ physical servers when handling large ingestion batches.
The majority of our back-end services are written in Ruby, Go or Python. We use
EventMachine when writing apps in Ruby (Goliath on MRI, Puma on JRuby). Go is
typically used in apps that require large throughput and don’t have so much
business logic. We’re very happy with Falcon for services written in Python.
Data is stored in PostgreSQL and ElasticSearch clusters. We use etcd and custom
tooling for configuring Varnishes for routing requests.
<!-- more -->
## What was your pre-Prometheus monitoring experience?
The primary use-cases for monitoring systems are:
* Active monitoring and probing (via Icinga)
* Metrics acquisition and creation of alerts based on these metrics (now Prometheus)
* Log acquisition from backend services
* Event and log acquisition from apps
The last two use-cases are handled via our logging infrastructure. It consists
of a collector running in the service container, which is listening on local
Unix socket. The socket is used by apps to send messages to the outside world.
Messages are transferred via RabbitMQ servers to consumers. Consumers are
custom written or hekad based. One of the main message flows is going towards
the service ElasticSearch cluster, which makes logs accessible for Kibana and
ad-hoc searches. We also save all processed events to GlusterFS for archival
purposes and/or further processing.
We used to run two metric acquisition pipelines in parallel. The first is based
on Collectd + StatsD + Graphite + Grafana and the other using Collectd +
OpenTSDB. We have struggled considerably with both pipelines. We had to deal
with either the I/O hungriness of Graphite, or the complexity and inadequate
tooling around OpenTSDB.
## Why did you decide to look at Prometheus?
After learning from our problems with the previous monitoring system, we looked
for a replacement. Only a few solutions made it to our shortlist. Prometheus
was one of the first, as Jiri Brunclik, our head of Operations at the time, had
received a personal recommendation about the system from former colleagues at
Google.
The proof of concept went great. We got a working system very quickly. We also
evaluated InfluxDB as a main system as well as a long-term storage for
Prometheus. But due to recent developments, this may no longer be a viable
option for us.
## How did you transition?
We initially started with LXC containers on one of our service servers, but
quickly moved towards a dedicated server from Hetzner, where we host the
majority of our services. We’re using PX70-SSD, which is Intel® Xeon® E3-1270
v3 Quad-Core Haswell with 32GB RAM, so we have plenty of power to run
Prometheus. SSDs allow us to have retention set to 120 days. Our logging
infrastructure is built around getting logs locally (receiving them on Unix
socket) and then pushing them towards the various workers.
![Diagram of ShowMax logging infrastructure. Shows flow of log messages from the source via processors to various consumers.](/assets/blog/2016-05-01/Loggin_infrastructure.png)
Having this infrastructure available made pushing metrics a logical choice
(especially in pre-Prometheus times). On the other side, Prometheus is
primarily designed around the paradigm of scraping metrics. We wanted to stay
consistent and push all metrics towards Prometheus initially. We have created a
Go daemon called prometheus-pusher. It’s responsible for scraping metrics from
local exporters and pushing them towards the Pushgateway. Pushing metrics has
some positive aspects (e.g. simplified service discovery) but also quite a few
drawbacks (e.g. making it hard to distinguish between a network partition vs. a
crashed service). We made Prometheus-pusher available on
[GitHub](https://github.com/ShowMax/prometheus-pusher), so you can try it
yourself.
![Grafana dashboard showing April 5th 2016 log processors traffic.](/assets/blog/2016-05-01/log_processors.png)
The next step was for us to figure out what to use for managing dashboards and
graphs. We liked the Grafana integration, but didn’t really like how Grafana
manages dashboard configurations. We are running Grafana in a Docker
container, so any changes should be kept out of the container. Another problem
was the lack of change tracking in Grafana.
We have thus decided to write a generator which takes YAML maintained within
git and generates JSON configs for Grafana dashboards. It is furthemore able to
deploy dashboards to Grafana started in a fresh container without the need for
persisting changes made into the container. This provides you with automation,
repeatability, and auditing.
We are pleased to announce that this tool is also now available under an Apache
2.0 license on [GitHub](https://github.com/ShowMax/grafana-dashboards-generator).
## What improvements have you seen since switching?
An improvement which we saw immediately was the stability of Prometheus. We
were fighting with stability and scalability of Graphite prior to this, so
getting that sorted was a great win for us. Furthemore the speed and stability
of Prometheus made access to metrics very easy for developers. Prometheus is
really helping us to embrace the DevOps culture.
Tomas Cerevka, one of our backend developers, was testing a new version of the
service using JRuby. He needed a quick peek into the heap consumption of that
particular service. He was able to get that information in a snap. For us,
this speed is essential.
![Heap size consumed by JRuby worker during troubleshooting memory issues on JVM.](/assets/blog/2016-05-01/ui_fragments-heap-zoom.png)
## What do you think the future holds for ShowMax and Prometheus?
Prometheus has become an integral part of monitoring in ShowMax and it is going
to be with us for the foreseeable future. We have replaced our whole metric
storage with Prometheus, but the ingestion chain remains push based. We are
thus thinking about following Prometheus best practices and switching to a pull
model.
We’ve also already played with alerts. We want to spend more time on this topic
and come up with increasingly sophisticated alert rules.
---
title: When (not) to use varbit chunks
created_at: 2016-05-08
kind: article
author_name: Björn “Beorn” Rabenstein
---
The embedded time serie database (TSDB) of the Prometheus server organizes the
raw sample data of each time series in chunks of constant 1024 bytes size. In
addition to the raw sample data, a chunk contains some meta-data, which allows
the selection of a different encoding for each chunk. The most fundamental
distinction is the encoding version. You select the version for newly created
chunks via the command line flag `-storage.local.chunk-encoding-version`. Up to
now, there were only two supported versions: 0 for the original delta encoding,
and 1 for the improved double-delta encoding. With release
[0.18.0](https://github.com/prometheus/prometheus/releases/tag/0.18.0), we
added version 2, which is another variety of double-delta encoding. We call it
_varbit encoding_ because it involves a variable bit-width per sample within
the chunk. While version 1 is superior to version 0 in almost every aspect,
there is a real trade-off between version 1 and 2. This blog post will help you
to make that decision. Version 1 remains the default encoding, so if you want
to try out version 2 after reading this article, you have to select it
explicitly via the command line flag. There is no harm in switching back and
forth, but note that existing chunks will not change their encoding version
once they have been created. However, these chunks will gradually be phased out
according to the configured retention time and will thus be replaced by chunks
with the encoding specified in the command-line flag.
<!-- more -->
## What is varbit encoding?
From the beginning, we designed the chunked sample storage for easy addition of
new encodings. When Facebook published a
[paper on their in-memory TSDB Gorilla](http://www.vldb.org/pvldb/vol8/p1816-teller.pdf),
we were intrigued by a number of similarities between the independently
developed approaches of Gorilla and Prometheus. However, there were also many
fundamental differences, which we studied in detail, wondering if we could get
some inspiration from Gorilla to improve Prometheus.
On the rare occasion of a free weekend ahead of me, I decided to give it a
try. In a coding spree, I implemented what would later (after a considerable
amount of testing and debugging) become the varbit encoding.
In a future blog post, I will describe the technical details of the
encoding. For now, you only need to know a few characteristics for your
decision between the new varbit encoding and the traditional double-delta
encoding. (I will call the latter just “double-delta encoding” from now on but
note that the varbit encoding also uses double deltas, just in a different
way.)
## What are the advantages of varbit encoding?
In short: It offers a way better compression ratio. While the double-delta
encoding needs about 3.3 bytes per sample for real-life data sets, the varbit
encoding went as far down as 1.28 bytes per sample on a typical large
production server at SoundCloud. That's almost three times more space efficient
(and even slightly better than the 1.37 bytes per sample reported for Gorilla –
but take that with a grain of salt as the typical data set at SoundCloud might
look different from the typical data set at Facebook).
Now think of the implications: Three times more samples in RAM, three times
more samples on disk, only a third of disk ops, and since disk ops are
currently the bottleneck for ingestion speed, it will also allow ingestion to
be three times faster. In fact, the recently reported new ingestion record of
800,000 samples per second was only possible with varbit chunks – and with an
SSD, obviously. With spinning disks, the bottleneck is reached far earlier, and
thus the 3x gain matters even more.
All of this sounds too good to be true…
## So where is the catch?
For one, the varbit encoding is more complex. The computational cost to encode
and decode values is therefore somewhat increased, which fundamentally affects
everything that writes or reads sample data. Luckily, it is only a proportional
increase of something that usually contributes only a small part to the total
cost of an operation.
Another property of the varbit encoding is potentially way more relevant:
samples in varbit chunks can only be accessed sequentially, while samples in
double-delta encoded chunks are randomly accessible by index. Since writes in
Prometheus are append-only, the different access patterns only affect reading
of sample data. The practical impact depends heavily on the nature of the
originating PromQL query.
A pretty harmless case is the retrieval of all samples within a time
interval. This happens when evaluating a range selector or rendering a
dashboard with a resolution similar to the scrape frequency. The Prometheus
storage engine needs to find the starting point of the interval. With
double-delta chunks, it can perform a binary search, while it has to scan
sequentially through a varbit chunk. However, once the starting point is found,
all remaining samples in the interval need to be decoded sequentially anyway,
which is only slightly more expensive with the varbit encoding.
The trade-off is different for retrieving a small number of non-adjacent
samples from a chunk, or for plainly retrieving a single sample in a so-called
instant query. Potentially, the storage engine has to iterate through a lot of
samples to find the few samples to be returned. Fortunately, the most common
source of instant queries are rule evaluations referring to the latest sample
in each involved time series. Not completely by coincidence, I recently
improved the retrieval of the latest sample of a time series. Essentially, the
last sample added to a time series is cached now. A query that needs only the
most recent sample of a time series doesn't even hit the chunk layer anymore,
and the chunk encoding is irrelevant in that case.
Even if an instant query refers to a sample in the past and therefore has to
hit the chunk layer, most likely other parts of the query, like the index
lookup, will dominate the total query time. But there are real-life queries
where the sequential access pattern required by varbit chunks will start to
matter a lot.
## What is the worst-case query for varbit chunks?
The worst case for varbit chunks is if you need just one sample from somewhere
in the middle of _each_ chunk of a very long time series. Unfortunately, there
is a real use-case for that. Let's assume a time series compresses nicely
enough to make each chunk last for about eight hours. That's about three chunks
a day, or about 100 chunks a month. If you have a dashboard that displays the
time series in question for the last month with a resolution of 100 data
points, the dashboard will execute a query that retrieves a single sample from
100 different chunks. Even then, the differences between chunk encodings will
be dominated by other parts of the query execution time. Depending on
circumstances, my guess would be that the query might take 50ms with
double-delta encoding and 100ms with varbit encoding.
However, if your dashboard query doesn't only touch a single time series but
aggregates over thousands of time series, the number of chunks to access
multiplies accordingly, and the overhead of the sequential scan will become
dominant. (Such queries are frowned upon, and we usually recommend to use a
[recording rule](https://prometheus.io/docs/querying/rules/#recording-rules)
for queries of that kind that are used frequently, e.g. in a dashboard.) But
with the double-delta encoding, the query time might still have been
acceptable, let's say around one second. After the switch to varbit encoding,
the same query might last tens of seconds, which is clearly not what you want
for a dashboard.
## What are the rules of thumb?
To put it as simply as possible: If you are neither limited on disk capacity
nor on disk ops, don't worry and stick with the default of the classical
double-delta encoding.
However, if you would like a longer retention time or if you are currently
bottle-necked on disk ops, I invite you to play with the new varbit
encoding. Start your Prometheus server with
`-storage.local.chunk-encoding-version=2` and wait for a while until you have
enough new chunks with varbit encoding to vet the effects. If you see queries
that are becoming unacceptably slow, check if you can use
[recording rules](https://prometheus.io/docs/querying/rules/#recording-rules)
to speed them up. Most likely, those queries will gain a lot from that even
with the old double-delta encoding.
If you are interested in how the varbit encoding works behind the scenes, stay
tuned for another blog post in the not too distant future.
---
title: Prometheus to Join the Cloud Native Computing Foundation
created_at: 2016-05-09
kind: article
author_name: Julius Volz on behalf of the Prometheus core developers
---
Since the inception of Prometheus, we have been looking for a sustainable
governance model for the project that is independent of any single company.
Recently, we have been in discussions with the newly formed [Cloud Native
Computing Foundation](https://cncf.io/) (CNCF), which is backed by Google,
CoreOS, Docker, Weaveworks, Mesosphere, and [other leading infrastructure
companies](https://cncf.io/about/members).
Today, we are excited to announce that the CNCF's Technical Oversight Committee
[voted unanimously](http://lists.cncf.io/pipermail/cncf-toc/2016-May/000198.html) to
accept Prometheus as a second hosted project after Kubernetes! You can find
more information about these plans in the
[official press release by the CNCF](https://cncf.io/news/news/2016/05/cloud-native-computing-foundation-accepts-prometheus-second-hosted-project).
By joining the CNCF, we hope to establish a clear and sustainable project
governance model, as well as benefit from the resources, infrastructure, and
advice that the independent foundation provides to its members.
We think that the CNCF and Prometheus are an ideal thematic match, as both
focus on bringing about a modern vision of the cloud.
In the following months, we will be working with the CNCF on finalizing the
project governance structure. We will report back when there are more details
to announce.
<%= atom_feed :title => 'Prometheus Blog', :author_name => '© Prometheus Authors 2015', <%= atom_feed :title => 'Prometheus Blog', :author_name => '© Prometheus Authors 2015',
:author_uri => 'http://prometheus.io/blog/', :limit => 10, :author_uri => 'https://prometheus.io/blog/', :limit => 10,
:logo => 'http://prometheus.io/assets/prometheus_logo.png', :logo => 'https://prometheus.io/assets/prometheus_logo.png',
:icon => 'http://prometheus.io/assets/favicons/favicon.ico' %> :icon => 'https://prometheus.io/assets/favicons/favicon.ico' %>
--- ---
title: Blog title: Blog
--- ---
<div class="col-md-9"> <div class="row">
<% sorted_articles.each do |post| %> <div class="col-md-9">
<div class="blog doc-content"> <% sorted_articles.each do |post| %>
<h1><%= link_to post[:title], post.path %></h1> <div class="blog doc-content">
<aside>Posted at: <%= get_pretty_date(post) %> by <%= post[:author_name]%></aside> <h1><%= link_to post[:title], post.path %></h1>
<article class="doc-content"> <aside>Posted at: <%= get_pretty_date(post) %> by <%= post[:author_name]%></aside>
<%= get_post_start(post) %> <article class="doc-content">
</article> <%= get_post_start(post) %>
</div> </article>
<% end %> </div>
<% end %>
</div>
<%= render 'blog_sidebar' %>
</div> </div>
<%= render 'blog_sidebar' %>
--- ---
title: Community title: Community
--- ---
<div class="col-md-8 col-md-offset-2 doc-content"> <div class="row">
<h1>Community</h1> <div class="col-md-12 doc-content">
<p> <h1>Community</h1>
Prometheus is developed in the open. Here are some of the channels we use <p>
to communicate and contribute: Prometheus is developed in the open. Here are some of the channels we use
</p> to communicate and contribute:
<p> </p>
<strong>Mailing list:</strong> <p>
<a href="https://groups.google.com/forum/#!forum/prometheus-developers">prometheus-developers</a> Google Group <strong>IRC:</strong>
</p> <a href="https://webchat.freenode.net/?channels=#prometheus"><code>#prometheus</code></a> on <a href="http://freenode.net/">irc.freenode.net</a>
<p> </p>
<strong>Twitter:</strong> <p>
<a href="https://twitter.com/PrometheusIO">PrometheusIO</a> <strong>Mailing list:</strong>
</p> <a href="https://groups.google.com/forum/#!forum/prometheus-developers">prometheus-developers</a> Google Group
<p> </p>
<strong>IRC:</strong> <code>#prometheus</code> on <a href="http://freenode.net/">irc.freenode.net</a> <p>
</p> <strong>Twitter:</strong>
<p> <a href="https://twitter.com/PrometheusIO">@PrometheusIO</a>
</p>
<p>
<strong>Issue tracker:</strong> We use the GitHub issue tracker for the various <a href="http://github.com/prometheus">Prometheus repositories</a> <strong>Issue tracker:</strong> We use the GitHub issue tracker for the various <a href="http://github.com/prometheus">Prometheus repositories</a>
</p> </p>
<h1>Contributing</h1> <h1>Contributing</h1>
<p> <p>
We welcome community contributions! Please see the We welcome community contributions! Please see the
<code>CONTRIBUTING.md</code> file in the respective Prometheus repository <code>CONTRIBUTING.md</code> file in the respective Prometheus repository
for instructions on how to submit changes. If you are planning on making for instructions on how to submit changes. If you are planning on making
more elaborate or controversial changes, please discuss them on the mailing more elaborate or controversial changes, please discuss them on the mailing
list before sending a pull request. list before sending a pull request.
</p> </p>
<h1>Commercial support</h1> <h1>Commercial support</h1>
<p> <p>
This is a list of third-party companies which provide support or This is a list of third-party companies which provide support or
consulting services for Prometheus. Prometheus is an independent open source consulting services for Prometheus. Prometheus is an independent open source
project which does not endorse any company. project which does not endorse any company.
</p> </p>
<ul> <ul>
<li><a href="http://www.robustperception.io">Robust Perception</a></li> <li><a href="http://www.robustperception.io">Robust Perception</a></li>
</ul> </ul>
<h1>Acknowledgements</h1> <h1>Acknowledgements</h1>
<p> <p>
Prometheus was initially started by Prometheus was initially started by
<a href="http://www.matttproud.com">Matt T. Proud</a> and <a href="http://www.matttproud.com">Matt T. Proud</a> and
<a href="http://juliusv.com">Julius Volz</a>. The majority of its <a href="http://juliusv.com">Julius Volz</a>. The majority of its
development has been sponsored by <a href="https://soundcloud.com">SoundCloud</a>. development has been sponsored by <a href="https://soundcloud.com">SoundCloud</a>.
</p> </p>
<p> <p>
We would also like to acknowledge early contributions by engineers from We would also like to acknowledge early contributions by engineers from
<a href="https://www.docker.com/">Docker</a> and <a href="http://www.boxever.com/">Boxever</a>. <a href="https://www.docker.com/">Docker</a> and <a href="http://www.boxever.com/">Boxever</a>.
</p> </p>
<p> <p>
Special thanks to <a href="https://digitalocean.com/">DigitalOcean</a> for providing hosting resources. Special thanks to <a href="https://digitalocean.com/">DigitalOcean</a> for providing hosting resources.
</p> </p>
<p> <p>
The Prometheus logo was contributed by <a href="http://www.makingstuffmove.com/">Robin Greenwood</a>. The Prometheus logo was contributed by <a href="http://www.makingstuffmove.com/">Robin Greenwood</a>.
</p> </p>
</div>
</div> </div>
/* Move down content because we have a fixed navbar that is 50px tall */
body { body {
font-family: 'Open Sans', serif; font-family: 'Open Sans', 'Helvetica Neue', Helvetica, sans-serif;
} }
.navbar { .navbar {
margin-bottom: 0; margin-bottom: 0;
min-height: 60px;
} }
.navbar-brand { .navbar-brand {
font-size: 20px; margin-top: 4px;
font-family: Lato, sans-serif;
font-size: 26px;
font-weight: 300;
color: #aaa; color: #aaa;
} }
.navbar-brand img { .navbar-brand img {
height: 27px; height: 30px;
display: inline; display: inline;
margin-top: -4px; margin-top: -5px;
margin-right: 3px; margin-right: 3px;
} }
.navbar-toggle {
margin-top: 22px;
}
.navbar-jumbotron .navbar {
min-height: 83px;
}
.navbar-jumbotron .navbar-brand {
margin-top: 14px;
}
.navbar-jumbotron .main-nav {
margin-top: 17px;
}
.main-nav {
margin-top: 4px;
letter-spacing: 1px;
font-family: 'Lato', sans-serif;
font-size: 16px;
text-transform: uppercase;
}
.jumbotron { .jumbotron {
background-color: #e6522c; background-color: #e6522c;
background-image: url("/assets/jumbotron-background.png"); background-image: url("/assets/jumbotron-background.png");
text-align: center; text-align: center;
font-family: Lato, sans-serif;
text-shadow: rgba(0, 0, 0, 0.2) 0px 2px 0px;
margin-bottom: 50px;
padding: 40px 0; padding: 40px 0;
} }
.jumbotron h1 { .jumbotron h1 {
margin-top: 30px;
font-size: 52px;
font-weight: 300;
color: #fff; color: #fff;
} }
.jumbotron p { .jumbotron .subtitle {
color: rgba(255,255,255,0.7); font-weight: 300;
font-size: 32px;
color: rgba(255,255,255,0.8);
margin-bottom: 20px;
} }
.jumbotron a.btn { .jumbotron a.btn {
border: none; border: none;
background-color: rgba(0,0,0,0.15); background-color: rgba(0,0,0,0.15);
color: #fff; color: #fff;
padding: 20px 25px 20px 25px;
margin: 15px 10px 0 10px;
text-transform: uppercase;
} }
.jumbotron a.btn:hover { .jumbotron a.btn:hover {
...@@ -45,32 +84,126 @@ body { ...@@ -45,32 +84,126 @@ body {
color: #fff; color: #fff;
} }
.jumbotron img { .feature-item {
height: 100px; font-family: 'Lato', sans-serif;
padding-bottom: 15px; font-weight: 300;
cursor: pointer;
} }
.jumbotron .subtitle { .feature-item:hover {
margin-bottom: 20px; background-color: #fad9d1;
border-radius: 3px;
} }
.main .fa { .feature-item a {
color: #e6522c; text-decoration: none;
color: none;
} }
.main h2 { .feature-item h2 {
color: #333;
font-weight: 300;
font-size: 25px; font-size: 25px;
white-space: nowrap; white-space: nowrap;
} }
.feature-item .fa {
margin-right: 5px;
color: #e6522c;
}
.feature-item p {
font-size: 16px;
line-height: 1.8em;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
color: #111;
}
.top-hr {
margin-top: 30px;
}
.quote {
margin: 25px 0 25px 0;
font-family: 'Lato', sans-serif;
font-weight: 300;
text-align: center;
}
.quote-text {
width: 60%;
margin: auto;
font-size: 22px;
font-style: italic;
color: #be3511;
}
.quote-source {
font-size: 16px;
margin-top: 15px;
margin-left: 50px;
}
.open-source {
margin: 25px 0 20px 0;
font-family: 'Lato', sans-serif;
font-weight: 300;
text-align: center;
}
.open-source h1 {
margin-top: 0;
font-weight: 300;
}
.open-source p {
width: 50%;
margin: auto;
font-size: 20px;
}
.open-source .github-stars {
margin-top: 20px;
width: 160px;
height: 30px;
}
.trusted-by {
font-family: 'Lato', sans-serif;
font-size: 20px;
font-weight: 300;
padding-bottom: 10px;
}
.logos {
display: flex;
align-items: center;
justify-content: center;
height: 64px;
}
footer { footer {
font-size: 12px; font-size: 12px;
color: #333; color: #333;
} }
a.sc-logo img { /* Downloads related styles. */
margin-left: 3px; .download h2 {
margin-bottom: 3px; margin-top: 2em;
}
.download-selection {
clear: both;
}
.download-selection .btn-group {
padding-right: 1em;
}
.downloads .checksum {
color: #aaa;
font-style: italic;
} }
/* Docs-related styles. */ /* Docs-related styles. */
...@@ -80,10 +213,11 @@ a.sc-logo img { ...@@ -80,10 +213,11 @@ a.sc-logo img {
.side-nav { .side-nav {
margin-top: 20px; margin-top: 20px;
padding: 5px 20px 20px 20px; padding: 5px 20px 20px 7px;
} }
.side-nav .fa { .side-nav .fa {
width: 20px;
color: #555; color: #555;
} }
...@@ -96,14 +230,18 @@ a.sc-logo img { ...@@ -96,14 +230,18 @@ a.sc-logo img {
} }
.side-nav .nav-header { .side-nav .nav-header {
color: #e6522c;
text-transform: uppercase;
font-size: 16px;
display: block; display: block;
margin: 20px auto 15px auto; margin: 20px auto 15px auto;
font-size: 16px;
} }
.side-nav .active { .side-nav .nav-header a, .side-nav .nav-header {
color: #e6522c;
text-transform: uppercase;
text-decoration: none;
}
.side-nav ul.active li.active {
border-left: 3px solid #e6522c; border-left: 3px solid #e6522c;
margin-left: -2px; margin-left: -2px;
font-weight: bold; font-weight: bold;
...@@ -114,19 +252,17 @@ a.sc-logo img { ...@@ -114,19 +252,17 @@ a.sc-logo img {
} }
.doc-content { .doc-content {
margin-top: 25px; font-size: 16px;
}
.doc-content {
font-size: 18px;
} }
.doc-content p, .doc-content.ul, .doc-content .alert { .doc-content p, .doc-content.ul, .doc-content .alert {
margin: 25px 0 25px 0; margin: 15px 0 15px 0;
line-height: 1.5;
} }
.doc-content > h1 { .doc-content > h1 {
color: #e6522c; color: #e6522c;
font-size: 30px;
text-transform: uppercase; text-transform: uppercase;
margin: 40px 0 10px 0; margin: 40px 0 10px 0;
} }
...@@ -141,7 +277,7 @@ a.sc-logo img { ...@@ -141,7 +277,7 @@ a.sc-logo img {
.doc-content > h2 { .doc-content > h2 {
color: #e6522c; color: #e6522c;
font-size: 25px; font-size: 22px;
} }
.doc-content > h2 code { .doc-content > h2 code {
...@@ -150,7 +286,8 @@ a.sc-logo img { ...@@ -150,7 +286,8 @@ a.sc-logo img {
} }
.doc-content > h3 { .doc-content > h3 {
font-size: 22px; font-size: 20px;
font-weight: bold;
} }
.doc-content > h4 { .doc-content > h4 {
...@@ -203,7 +340,7 @@ a.sc-logo img { ...@@ -203,7 +340,7 @@ a.sc-logo img {
.toc { .toc {
padding: 1em; padding: 1em;
background-color: #eee; background-color: #f5f5f5;
} }
.toc-right { .toc-right {
...@@ -223,33 +360,21 @@ a.sc-logo img { ...@@ -223,33 +360,21 @@ a.sc-logo img {
} }
pre { pre {
border: 1px solid #ddd;
border-left: 4px solid #e6522c;
border-radius: 0;
font-family: "Courier New", Monaco, Menlo, Consolas, monospace; font-family: "Courier New", Monaco, Menlo, Consolas, monospace;
background-color: #f9f2f4; background-color: #f5f5f5;
border: 1px solid #333;
color: #333; color: #333;
padding: 15px;
} }
code { pre code {
color: #333; white-space: pre;
} }
.main p { code {
font-size: 16px; color: #333;
line-height: 1.8em;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
.main .desc {
margin-bottom: 20px;
}
.main-nav {
letter-spacing: 1px;
font-family: Avenir, 'Open Sans', serif;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
} }
aside { aside {
...@@ -261,14 +386,3 @@ aside { ...@@ -261,14 +386,3 @@ aside {
article { article {
margin: 10px 0 60px 0; margin: 10px 0 60px 0;
} }
.read-more > a {
color: #888;
}
.logos {
display: flex;
align-items: center;
justify-content: center;
height: 64px;
}
.routing-table {
font: 12px sans-serif;
}
.node circle {
stroke: #e6522c;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
.form-control.label-input {
padding: 2px;
width: 450px;
}
textarea {
border-color: #ddd;
height: 450px;
padding: 2px 0;
width: 100%;
font-family: monospace;
}
.block {
display: block;
}
.inline-block {
display: inline-block;
}
...@@ -6,196 +6,64 @@ nav_icon: sliders ...@@ -6,196 +6,64 @@ nav_icon: sliders
# Alertmanager # Alertmanager
The Alertmanager receives alerts from one or more Prometheus servers. The [Alertmanager](https://github.com/prometheus/alertmanager) handles alerts
It manages those alerts, including silencing, inhibition, aggregation and sent by client applications such as the Prometheus server.
sending out notifications via methods such as email, PagerDuty and HipChat. It takes care of deduplicating, grouping, and routing
them to the correct receiver integration such as email, PagerDuty, or OpsGenie.
**WARNING: The Alertmanager is still considered to be very experimental.** It also takes care of silencing and inhibition of alerts.
## Configuration The following describes the core concepts the Alertmanager implements. Consult
the [configuration documentation](../configuration) to learn how to use them
The Alertmanager is configured via command-line flags and a configuration file. in more detail.
The configuration file is an ASCII protocol buffer. To specify which ## Grouping
configuration file to load, use the `-config.file` flag.
Grouping categorizes alerts of similar nature into a single notification. This
``` is especially useful during larger outages when many systems fail at once and
./alertmanager -config.file alertmanager.conf hundreds to thousands of alerts may be firing simultaneously.
```
**Example:** Dozens or hundreds of instances of a service are running in your
To send all alerts to email, set the `-notification.smtp.smarthost` flag to cluster when a network partition occurs. Half of your service instances
an SMTP smarthost (such as a [Postfix null client](http://www.postfix.org/STANDARD_CONFIGURATION_README.html#null_client)) can no longer reach the database.
and use the following configuration: Alerting rules in Prometheus were configured to send an alert for each service
instance if it cannot communicate with the database. As a result hundreds of
``` alerts are sent to Alertmanager.
notification_config {
name: "alertmanager_test" As a user one only wants to get a single page while still being able to see
email_config { exactly which service instances were affected. Thus one can configure
email: "test@example.org" Alertmanager to group alerts by their cluster and alertname so it sends a
} single compact notification.
}
Grouping of alerts, timing for the grouped notifications, and the receivers
aggregation_rule { of those notificiations are configured by a routing tree in the configuration
notification_config_name: "alertmanager_test" file.
}
``` ## Inhibition
### Filtering Inhibition is a concept of suppressing notifications for certain alerts if
certain other alerts are already firing.
An aggregation rule can be made to apply to only some alerts using a filter.
**Example:** An alert is firing that informs that an entire cluster is not
For example, to apply a rule only to alerts with a `severity` label with the value `page`: reachable. Alertmanager can be configured to mute all other alerts concerning
this cluster if that particular alert is firing.
``` This prevents notifications for hundreds or thousands of firing alerts that
aggregation_rule { are unrelated to the actual issue.
filter {
name_re: "severity" Inhibitions are configured through the Alertmanager's configuration file.
value_re: "page"
} ## Silences
notification_config_name: "alertmanager_test"
} Silences are a straightforward way to simply mute alerts for a given time.
``` A silence is configured based on matchers, just like the routing tree. Incoming
alerts are checked whether they match all the equality or regular expression
Multiple filters can be provided. matchers of an active silence.
If they do, no notifications will be send out for that alert.
### Repeat Rate
By default an aggregation rule will repeat notifications every 2 hours. This can be changed using `repeat_rate_seconds`. Silences are configured in the web interface of the Alertmanager.
```
aggregation_rule { ## Client behavior
repeat_rate_seconds: 3600
notification_config_name: "alertmanager_test" The Alertmanager has [special requirements](../clients) for behavior of its
} client. Those are only relevant for advanced use cases where Prometheus
``` is not used to send alerts.
### Notifications
The Alertmanager has support for a growing number of notification methods.
Multiple notifications methods of one or more types can be used in the same
notification config.
The `send_resolved` field can be used with all notification methods to enable or disable
sending notifications that an alert has stopped firing.
#### Email
The `-notification.smtp.smarthost` flag must be set to an SMTP smarthost.
The `-notification.smtp.sender` flag may be set to change the default From address.
```
notification_config {
name: "alertmanager_email"
email_config {
email: "test@example.org"
}
email_config {
email: "foo@example.org"
}
}
```
Plain and CRAM-MD5 SMTP authentication methods are supported.
The `SMTP_AUTH_USERNAME`, `SMTP_AUTH_SECRET`, `SMTP_AUTH_PASSWORD` and
`SMTP_AUTH_IDENTITY` environment variables are used to configure them.
#### PagerDuty
The Alertmanager integrates as a [Generic API
Service](https://support.pagerduty.com/hc/en-us/articles/202830340-Creating-a-Generic-API-Service)
with PagerDuty.
```
notification_config {
name: "alertmanager_pagerduty"
pagerduty_config {
service_key: "supersecretapikey"
}
}
```
#### Pushover
```
notification_config {
name: "alertmanager_pushover"
pushover_config {
token: "mypushovertoken"
user_key: "mypushoverkey"
}
}
```
#### HipChat
```
notification_config {
name: "alertmanager_hipchat"
hipchat_config {
auth_token: "hipchatauthtoken"
room_id: 123456
}
}
```
#### Slack
```
notification_config {
name: "alertmanager_slack"
slack_config {
webhook_url: "webhookurl"
channel: "channelname"
}
}
```
#### Flowdock
```
notification_config {
name: "alertmanager_flowdock"
flowdock_config {
api_token: "4c7234902348234902384234234cdb59"
from_address: "aliaswithgravatar@example.com"
tag: "monitoring"
}
}
```
#### Generic Webhook
The Alertmanager supports sending notifications as JSON to arbitrary
URLs. This could be used to perform automated actions when an
alert fires or integrate with a system that the Alertmanager does not support.
```
notification_config {
name: "alertmanager_webhook"
webhook_config {
url: "http://example.org/my/hook"
}
}
```
An example of JSON message it sends is below.
```json
{
"version": "1",
"status": "firing",
"alert": [
{
"summary": "summary",
"description": "description",
"labels": {
"alertname": "TestAlert"
},
"payload": {
"activeSince": "2015-06-01T12:55:47.356+01:00",
"alertingRule": "ALERT TestAlert IF absent(metric_name) FOR 0y WITH ",
"generatorURL": "http://localhost:9090/graph#%5B%7B%22expr%22%3A%22absent%28metric_name%29%22%2C%22tab%22%3A0%7D%5D",
"value": "1"
}
}
]
}
```
This format is subject to change.
---
title: Clients
sort_rank: 6
nav_icon: sliders
---
# Sending alerts
__**Disclaimer**: Prometheus automatically takes care of sending alerts
generated by its configured [alerting rules](../rules). It is highly
recommended to configure alerting rules in Prometheus based on time series
data rather than implementing a direct client.__
The Alertmanager listens for alerts on an API endpoint at `/api/v1/alerts`.
Clients are expected to continously re-send alerts as long as they are still
active (usually on the order of 30 seconds to 3 minutes).
Clients can push a list of alerts to that endpoint via a POST request of
the following format:
```
[
{
"labels": {
"<labelname>": "<labelvalue>",
...
},
"annotations": {
"<labelname>": "<labelvalue>",
},
"startsAt": "<rfc3339>",
"endsAt": "<rfc3339>"
"generatorURL": "<generator_url>"
},
...
]
```
The labels are used to identify identical instances of an alert and to perform
deduplication. The annotations are always set to those received most recently
and are not identifying an alert.
Both timestamps are optional. If `startsAt` is omitted, the current time
is assigned by the Alertmanager. `endsAt` is only set if the end time of an
alert is known. Otherwise it will be set to a configurable timeout period from
the time since the alert was last received.
The `generatorURL` field is a unique back-link which identifies the causing
entity of this alert in the client.
Alertmanager also supports a legacy endpoint on `/api/alerts` which is
compatible with Prometheus versions 0.16.2 and lower.
---
title: Configuration
sort_rank: 3
nav_icon: sliders
---
# Configuration
[Alertmanager](https://github.com/prometheus/alertmanager) is configured via
command-line flags and a configuration file.
While the command-line flags configure immutable system parameters, the
configuration file defines inhibition rules, notification routing and
notification receivers.
The [visual editor](/webtools/alerting/routing-tree-editor) can assist in
building routing trees.
To view all available command-line flags, run `alertmanager -h`.
Alertmanager can reload its configuration at runtime. If the new configuration
is not well-formed, the changes will not be applied and an error is logged.
A configuration reload is triggered by sending a `SIGHUP` to the process.
## Configuration file
To specify which configuration file to load, use the `-config.file` flag.
The file is written in the [YAML format](http://en.wikipedia.org/wiki/YAML),
defined by the scheme described below.
Brackets indicate that a parameter is optional. For non-list parameters the
value is set to the specified default.
Generic placeholders are defined as follows:
* `<duration>`: a duration matching the regular expression `[0-9]+[smhdwy]`
* `<labelname>`: a string matching the regular expression `[a-zA-Z_][a-zA-Z0-9_]*`
* `<labelvalue>`: a string of unicode characters
* `<filepath>`: a valid path in the current working directory
* `<boolean>`: a boolean that can take the values `true` or `false`
* `<string>`: a regular string
* `<tmpl_string>`: a string which is template-expanded before usage
The other placeholders are specified separately.
A valid example file can be found [here](https://github.com/prometheus/alertmanager/blob/master/doc/examples/simple.yml).
The global configuration specifies parameters that are valid in all other
configuration contexts. They also serve as defaults for other configuration
sections.
```
global:
# ResolveTimeout is the time after which an alert is declared resolved
# if it has not been updated.
[ resolve_timeout: <duration> | default = 5m ]
# The default SMTP From header field.
[ smtp_from: <tmpl_string> ]
# The default SMTP smarthost used for sending emails.
[ smtp_smarthost: <string> ]
# The API URL to use for Slack notifications.
[ slack_api_url: <string> ]
[ pagerduty_url: <string> | default = "https://events.pagerduty.com/generic/2010-04-15/create_event.json" ]
[ opsgenie_api_host: <string> | default = "https://api.opsgenie.com/" ]
# Files from which custom notification template definitions are read.
# The last component may use a wildcard matcher, e.g. 'templates/*.tmpl'.
templates:
[ - <filepath> ... ]
# The root node of the routing tree.
route: <route>
# A list of notification receivers.
receivers:
- <receiver> ...
# A list of inhibition rules.
inhibit_rules:
[ - <inhibit_rule> ... ]
```
## Route `<route>`
A route block defines a node in a routing tree and its children. Its optional
configuration parameters are inherited from its parent node if not set.
Every alert enters the routing tree at the configured top-level route, which
must match all alerts (i.e. not have any configured matchers).
It then traverses the child nodes. If `continue` is set to false, it stops
after the first matching child. If `continue` is true on a matching node, the
alert will continue matching against subsequent siblings.
If an alert does not match any children of a node (no matching child nodes, or
none exist), the alert is handled based on the configuration paramters of the
current node.
```
[ receiver: <string> ]
[ group_by: '[' <labelname>, ... ']' ]
# Whether an alert should continue matching subsequent sibling nodes.
[ continue: <boolean> | default = false ]
# A set of equality matchers an alert has to fulfill to match the node.
match:
[ <labelname>: <labelvalue>, ... ]
# A set of regex-matchers an alert has to fulfill to match the node.
match_re:
[ <labelname>: <regex>, ... ]
# How long to initially wait to send a notification for a group
# of alerts. Allows to wait for an inhibiting alert to arrive or collect
# more initial alerts for the same group. (Usually ~0s to few minutes.)
[ group_wait: <duration> ]
# How long to wait before sending notification about new alerts that are
# in are added to a group of alerts for which an initial notification
# has already been sent. (Usually ~5min or more.)
[ group_interval: <duration> ]
# How long to wait before sending a notification again if it has already
# been sent successfully for an alert. (Usually ~3h or more).
[ repeat_interval: <duration> ]
# Zero or more child routes.
routes:
[ - <route> ... ]
```
### Example
```
# The root route with all parameters, which are inherited by the child
# routes if they are not overwritten.
route:
receiver: 'default-receiver'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
group_by: [cluster, alertname]
# All alerts that do not match the following child routes
# will remain at the root node and be dispatched to 'default-receiver'.
routes:
# All alerts with service=mysql or service=cassandra
# are dispatched to the database pager.
- receiver: 'database-pager'
group_wait: 10s
match_re:
service: mysql|cassandra
# All alerts with the team=frontend label match this sub-route.
# They are grouped by product and environment rather than cluster
# and alertname.
- receiver: 'frontend-pager'
group_by: [product, environment]
match:
team: frontend
```
## Inhibit rule `<inhibit_rule>`
An inhibition rule is a rule that mutes an alert matching a set of matchers
under the condition that an alert exists that matches another set of matchers.
Both alerts must have a set of equal labels.
```
# Matchers that have to be fulfilled in the alerts to be muted.
target_match:
[ <labelname>: <labelvalue>, ... ]
target_match_re:
[ <labelname>: <regex>, ... ]
# Matchers for which one or more alerts have to exist for the
# inhibition to take effect.
source_match:
[ <labelname>: <labelvalue>, ... ]
source_match_re:
[ <labelname>: <regex>, ... ]
# Labels that must have an equal value in the source and target
# alert for the inhibition to take effect.
[ equal: '[' <labelname>, ... ']' ]
```
## Receiver `<receiver>`
Receiver is a named configuration of one or more notification integrations.
__Other receiver implementations available in version 0.0.4 of Alertmanager
are not implemented yet. We are gladly accepting any contributions to add them
to the new implementation.__
```
# The unique name of the receiver.
name: <string>
# Configurations for several notification integrations.
email_configs:
[ - <email_config>, ... ]
pagerduty_configs:
[ - <pagerduty_config>, ... ]
pushover_configs:
[ - <pushover_config>, ... ]
slack_configs:
[ - <slack_config>, ... ]
opsgenie_configs:
[ - <opsgenie_config>, ... ]
webhook_configs:
[ - <webhook_config>, ... ]
```
## Email receiver `<email_config>`
```
# Whether or not to notify about resolved alerts.
[ send_resolved: <boolean> | default = false ]
# The email address to send notifications to.
to: <tmpl_string>
# The sender address.
[ from: <tmpl_string> | default = global.smtp_from ]
# The SMTP host through which emails are sent.
[ smarthost: <string> | default = global.smtp_smarthost ]
# The HTML body of the email notification.
[ html: <tmpl_string> | default = '{{ template "email.default.html" . }}' ]
# Further headers email header key/value pairs. Overrides any headers
# previously set by the notification implementation.
[ headers: { <string>: <tmpl_string>, ... } ]
```
## PagerDuty receiver `<pagerduty_config>`
PagerDuty notifications are sent via the [PagerDuty API](https://developer.pagerduty.com/documentation/integration/events).
```
# Whether or not to notify about resolved alerts.
[ send_resolved: <boolean> | default = true ]
# The PagerDuty service key.
service_key: <tmpl_string>
# The URL to send API requests to
[ url: <string> | default = global.pagerduty_url ]
# The client identification of the Alertmanager.
[ client: <tmpl_string> | default = '{{ template "pagerduty.default.client" . }}' ]
# A backlink to the sender of the notification.
[ client_url: <tmpl_string> | default = '{{ template "pagerduty.default.clientURL" . }}' ]
# A description of the incident.
[ description: <tmpl_string> | default = '{{ template "pagerduty.default.description" .}}' ]
# A set of arbitrary key/value pairs that provide further detail
# about the incident.
[ details: { <string>: <tmpl_string>, ... } | default = {
firing: '{{ template "pagerduty.default.instances" .Alerts.Firing }}'
resolved: '{{ template "pagerduty.default.instances" .Alerts.Resolved }}'
num_firing: '{{ .Alerts.Firing | len }}'
num_resolved: '{{ .Alerts.Resolved | len }}'
} ]
```
## Pushover receiver `<pushover_config>`
Pushover notifications are sent via the [Pushover API](https://pushover.net/api).
```
# The recipient user’s user key.
user_key: <string>
# Your registered application’s API token, see https://pushover.net/apps
token: <string>
# Notification title.
[ title: <tmpl_string> | default = '{{ template "pushover.default.title" . }}' ]
# Notification message.
[ message: <tmpl_string> | default = '{{ template "pushover.default.message" . }}' ]
# A supplementary URL shown alongside the message.
[ url: <tmpl_string> | default = '{{ template "pushover.default.url" . }}' ]
# Priority, see https://pushover.net/api#priority
[ priority: <tmpl_string> | default = '{{ if eq .Status "firing" }}2{{ else }}0{{ end }}' ]
# How often the Pushover servers will send the same notification to the user.
# Must be at least 30 seconds.
[ retry: <duration> | default = 1m ]
# How long your notification will continue to be retried for, unless the user
# acknowledges the notification.
[ expire: <duration> | default = 1h ]
```
## Slack receiver `<slack_config>`
Slack notifications are sent via [Slack webhooks](https://api.slack.com/incoming-webhooks).
```
# Whether or not to notify about resolved alerts.
[ send_resolved: <boolean> | default = false ]
# The Slack webhook URL.
[ api_url: <string> | default = global.slack_api_url ]
# The channel or user to send notifications to.
channel: <tmpl_string>
# API request data as defined by the Slack webhook API.
[ color: <tmpl_string> | default = '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}' ]
[ username: <tmpl_string> | default = '{{ template "slack.default.username" . }}'
[ title: <tmpl_string> | default = '{{ template "slack.default.title" . }}' ]
[ title_link: <tmpl_string> | default = '{{ template "slack.default.titlelink" . }}' ]
[ pretext: <tmpl_string> | default = '{{ template "slack.default.pretext" . }}' ]
[ text: <tmpl_string> | default = '{{ template "slack.default.text" . }}' ]
[ fallback: <tmpl_string> | default = '{{ template "slack.default.fallback" . }}' ]
```
## OpsGenie receiver `<opsgenie_config>`
OpsGenie notifications are sent via the [OpsGenie API](https://www.opsgenie.com/docs/web-api/alert-api).
```
# Whether or not to notify about resolved alerts.
[ send_resolved: <boolean> | default = true ]
# The API key to use when talking to the OpsGenie API.
api_key: <string>
# The host to send OpsGenie API requests to.
[ api_host: <string> | default = global.opsgenie_api_host ]
# A description of the incident.
[ description: <tmpl_string> | default = '{{ template "opsgenie.default.description" . }}' ]
# A backlink to the sender of the notification.
[ source: <tmpl_string> | default = '{{ template "opsgenie.default.source" . }}' ]
# A set of arbitrary key/value pairs that provide further detail
# about the incident.
[ details: { <string>: <tmpl_string>, ... } ]
```
## Webhook receiver `<webhook config>`
The webhook receiver allows configuring a generic receiver.
```
# Whether or not to notify about resolved alerts.
[ send_resolved: <boolean> | default = true ]
# The endpoint to send HTTP POST requests to.
url: <string>
```
The Alertmanager
will send HTTP POST requests in the following JSON format to the configured
endpoint:
```
{
"version": "2",
"status": "<resolved|firing>",
"alerts": [
{
"labels": <object>,
"annotations": <object>,
"startsAt": "<rfc3339>",
"endsAt": "<rfc3339>"
},
...
]
}
```
...@@ -7,14 +7,12 @@ nav_icon: sliders ...@@ -7,14 +7,12 @@ nav_icon: sliders
# Alerting Overview # Alerting Overview
Alerting with Prometheus is separated into two parts. Alerting rules in Alerting with Prometheus is separated into two parts. Alerting rules in
Prometheus servers send alerts to an Alertmanager. The Alertmanager then Prometheus servers send alerts to an Alertmanager. The [Alertmanager](../alertmanager)
manages those alerts, including silencing, inhibition, aggregation and sending then manages those alerts, including silencing, inhibition, aggregation and
out notifications via methods such as email, PagerDuty and HipChat. sending out notifications via methods such as email, PagerDuty and HipChat.
**WARNING: The Alertmanager is still considered to be very experimental.**
The main steps to setting up alerting and notifications are: The main steps to setting up alerting and notifications are:
* Setup and configure the Alertmanager * Setup and [configure](../configuration) the Alertmanager
* Configure Prometheus to talk to the Alertmanager with the `-alertmanager.url` flag * Configure Prometheus to talk to the Alertmanager with the `-alertmanager.url` flag
* Create alerting rules in Prometheus * Create [alerting rules](../rules) in Prometheus
--- ---
title: Alerting rules title: Alerting rules
sort_rank: 3 sort_rank: 5
--- ---
# Alerting rules # Alerting rules
...@@ -15,32 +15,60 @@ Alerting rules are configured in Prometheus in the same way as [recording ...@@ -15,32 +15,60 @@ Alerting rules are configured in Prometheus in the same way as [recording
rules](../../querying/rules). rules](../../querying/rules).
### Defining alerting rules ### Defining alerting rules
Alerting rules are defined in the following syntax: Alerting rules are defined in the following syntax:
ALERT <alert name> ALERT <alert name>
IF <expression> IF <expression>
[FOR <duration>] [ FOR <duration> ]
[WITH <label set>] [ LABELS <label set> ]
SUMMARY "<summary template>" [ ANNOTATIONS <label set> ]
DESCRIPTION "<description template>"
The optional `FOR` clause causes Prometheus to wait for a certain duration The optional `FOR` clause causes Prometheus to wait for a certain duration
between first encountering a new expression output vector element (like an between first encountering a new expression output vector element (like an
instance with a high HTTP error rate) and counting an alert as firing for this instance with a high HTTP error rate) and counting an alert as firing for this
element. Elements that are active, but not firing yet, are in pending state. element. Elements that are active, but not firing yet, are in pending state.
The `WITH` clause allows specifying a set of additional labels to be attached The `LABELS` clause allows specifying a set of additional labels to be attached
to the alert. Any existing conflicting labels will be overwritten. to the alert. Any existing conflicting labels will be overwritten. The label
values can be templated.
The `ANNOTATIONS` clause specifies another set of labels that are not
identifying for an alert instance. They are used to store longer additional
information such as alert descriptions or runbook links. The annotation values
can be templated.
#### Prometheus v0.16.2 and earlier
In previous Prometheus versions the rule syntax is as follows:
ALERT <alert name>
IF <expression>
[ FOR <duration> ]
[ WITH <label set> ]
SUMMARY <string>
DESCRIPTION <string>
[ RUNBOOK <string> ]
Annotations are not free form but fixed to a summary, a description, and a
runbook field. Labels are attached using the `WITH` rather than the `LABELS`
clause.
Label values in the `WITH` clause cannot be templated.
NOTE: **Note:** Old alerting rules can be converted to the new syntax using
[this script](https://gist.github.com/xbglowx/d798da98ff9937e33862b285d0121bde#gistcomment-1752515).
#### Templating
The `SUMMARY` should be a short, human-readable summary of the alert (suitable Label and annotation values can be templated using [console templates](../../visualization/consoles).
for e.g. an email subject line), while the `DESCRIPTION` clause should provide The `$labels` variable holds the label key/value pairs of an alert instance
a longer description. Both string fields allow the inclusion of template and `$value` holds the evaluated value of an alert instance.
variables derived from the firing vector elements of the alert:
# To insert a firing element's label values: # To insert a firing element's label values:
{{$labels.<labelname>}} {{ $labels.<labelname> }}
# To insert the numeric expression value of the firing element: # To insert the numeric expression value of the firing element:
{{$value}} {{ $value }}
Examples: Examples:
...@@ -48,20 +76,23 @@ Examples: ...@@ -48,20 +76,23 @@ Examples:
ALERT InstanceDown ALERT InstanceDown
IF up == 0 IF up == 0
FOR 5m FOR 5m
WITH { LABELS { severity = "page" }
severity="page" ANNOTATIONS {
summary = "Instance {{ $labels.instance }} down",
description = "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes.",
} }
SUMMARY "Instance {{$labels.instance}} down"
DESCRIPTION "{{$labels.instance}} of job {{$labels.job}} has been down for more than 5 minutes."
# Alert for any instance that have a median request latency >1s. # Alert for any instance that have a median request latency >1s.
ALERT ApiHighRequestLatency ALERT APIHighRequestLatency
IF api_http_request_latencies_ms{quantile="0.5"} > 1000 IF api_http_request_latencies_second{quantile="0.5"} > 1
FOR 1m FOR 1m
SUMMARY "High request latency on {{$labels.instance}}" ANNOTATIONS {
DESCRIPTION "{{$labels.instance}} has a median request latency above 1s (current value: {{$value}})" summary = "High request latency on {{ $labels.instance }}",
description = "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)",
}
### Inspecting alerts during runtime ### Inspecting alerts during runtime
To manually inspect which alerts are active (pending or firing), navigate to To manually inspect which alerts are active (pending or firing), navigate to
the "Alerts" tab of your Prometheus instance. This will show you the exact the "Alerts" tab of your Prometheus instance. This will show you the exact
label sets for which each defined alert is currently active. label sets for which each defined alert is currently active.
...@@ -74,6 +105,7 @@ transitions from active to inactive state. Once inactive, the time series does ...@@ -74,6 +105,7 @@ transitions from active to inactive state. Once inactive, the time series does
not get further updates. not get further updates.
### Sending alert notifications ### Sending alert notifications
Prometheus's alerting rules are good at figuring what is broken *right now*, Prometheus's alerting rules are good at figuring what is broken *right now*,
but they are not a fully-fledged notification solution. Another layer is needed but they are not a fully-fledged notification solution. Another layer is needed
to add summarization, notification rate limiting, silencing and alert to add summarization, notification rate limiting, silencing and alert
......
...@@ -27,7 +27,7 @@ scraped time series which serve to identify the scraped target: ...@@ -27,7 +27,7 @@ scraped time series which serve to identify the scraped target:
If either of these labels are already present in the scraped data, the behavior If either of these labels are already present in the scraped data, the behavior
depends on the `honor_labels` configuration option. See the depends on the `honor_labels` configuration option. See the
[scrape configuration documentation](/docs/operating/configuration/#scrape-configurations-scrape_config) [scrape configuration documentation](/docs/operating/configuration/#%3Cscrape_config%3E)
for more information. for more information.
For each instance scrape, Prometheus stores a sample in the following For each instance scrape, Prometheus stores a sample in the following
......
...@@ -22,8 +22,7 @@ currently running goroutines. Use gauges for this use case. ...@@ -22,8 +22,7 @@ currently running goroutines. Use gauges for this use case.
Client library usage documentation for counters: Client library usage documentation for counters:
* [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Counter) * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Counter)
* [Java](https://github.com/prometheus/client_java/blob/master/client/src/main/java/io/prometheus/client/metrics/Counter.java) * [Java](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Counter.java)
* [Java (simple client)](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Counter.java)
* [Ruby](https://github.com/prometheus/client_ruby#counter) * [Ruby](https://github.com/prometheus/client_ruby#counter)
* [Python](https://github.com/prometheus/client_python#counter) * [Python](https://github.com/prometheus/client_python#counter)
...@@ -39,8 +38,7 @@ running goroutines. ...@@ -39,8 +38,7 @@ running goroutines.
Client library usage documentation for gauges: Client library usage documentation for gauges:
* [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Gauge) * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Gauge)
* [Java](https://github.com/prometheus/client_java/blob/master/client/src/main/java/io/prometheus/client/metrics/Gauge.java) * [Java](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Gauge.java)
* [Java (simple client)](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Gauge.java)
* [Ruby](https://github.com/prometheus/client_ruby#gauge) * [Ruby](https://github.com/prometheus/client_ruby#gauge)
* [Python](https://github.com/prometheus/client_python#gauge) * [Python](https://github.com/prometheus/client_python#gauge)
...@@ -68,7 +66,7 @@ and differences to [summaries](#summary). ...@@ -68,7 +66,7 @@ and differences to [summaries](#summary).
Client library usage documentation for histograms: Client library usage documentation for histograms:
* [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Histogram) * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Histogram)
* [Java](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Histogram.java) (histograms are only supported by the simple client but not by the legacy client) * [Java](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Histogram.java)
* [Python](https://github.com/prometheus/client_python#histogram) * [Python](https://github.com/prometheus/client_python#histogram)
## Summary ## Summary
...@@ -92,7 +90,6 @@ to [histograms](#histogram). ...@@ -92,7 +90,6 @@ to [histograms](#histogram).
Client library usage documentation for summaries: Client library usage documentation for summaries:
* [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Summary) * [Go](http://godoc.org/github.com/prometheus/client_golang/prometheus#Summary)
* [Java](https://github.com/prometheus/client_java/blob/master/client/src/main/java/io/prometheus/client/metrics/Summary.java) * [Java](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Summary.java)
* [Java (simple client)](https://github.com/prometheus/client_java/blob/master/simpleclient/src/main/java/io/prometheus/client/Summary.java)
* [Ruby](https://github.com/prometheus/client_ruby#summary) * [Ruby](https://github.com/prometheus/client_ruby#summary)
* [Python](https://github.com/prometheus/client_python#summary) * [Python](https://github.com/prometheus/client_python#summary)
...@@ -21,9 +21,12 @@ HTTP endpoint on your application’s instance: ...@@ -21,9 +21,12 @@ HTTP endpoint on your application’s instance:
Unofficial third-party client libraries: Unofficial third-party client libraries:
* [Bash](https://github.com/aecolley/client_bash) * [Bash](https://github.com/aecolley/client_bash)
* [Common Lisp](https://github.com/deadtrickster/prometheus.cl)
* [Erlang](https://github.com/deadtrickster/prometheus.erl)
* [Haskell](https://github.com/fimad/prometheus-haskell) * [Haskell](https://github.com/fimad/prometheus-haskell)
* [Node.js](https://github.com/siimon/prom-client) * [Lua](https://github.com/knyar/nginx-lua-prometheus) for Nginx
* [.NET / C#](https://github.com/andrasm/prometheus-net) * [.NET / C#](https://github.com/andrasm/prometheus-net)
* [Node.js](https://github.com/siimon/prom-client)
When Prometheus scrapes your instance's HTTP endpoint, the client library When Prometheus scrapes your instance's HTTP endpoint, the client library
sends the current state of all tracked metrics to the server. sends the current state of all tracked metrics to the server.
...@@ -33,7 +36,7 @@ dependencies, you may also implement one of the supported [exposition ...@@ -33,7 +36,7 @@ dependencies, you may also implement one of the supported [exposition
formats](/docs/instrumenting/exposition_formats/) yourself to expose metrics. formats](/docs/instrumenting/exposition_formats/) yourself to expose metrics.
When implementing a new Prometheus client library, please follow the When implementing a new Prometheus client library, please follow the
[Prometheus Client Library Guidelines](https://docs.google.com/document/d/1zHwWVigeAITbaAp6BR4uCByRJH7rtTv4ve6SsoEXJ_Q/edit?usp=sharing). [guidelines on writing client libraries](/docs/instrumenting/writing_clientlibs).
Note that this document is still a work in progress. Please also consider Note that this document is still a work in progress. Please also consider
consulting the [development mailing list](https://groups.google.com/forum/#!forum/prometheus-developers). consulting the [development mailing list](https://groups.google.com/forum/#!forum/prometheus-developers).
We are happy to give advice on how to make your library as useful and We are happy to give advice on how to make your library as useful and
......
--- ---
title: Exporters and third-party integrations title: Exporters and integrations
sort_rank: 3 sort_rank: 4
--- ---
# Exporters and third-party integrations # Exporters and integrations
There are a number of libraries and servers which help in exporting existing There are a number of libraries and servers which help in exporting existing
metrics from third-party systems as Prometheus metrics. This is useful for metrics from third-party systems as Prometheus metrics. This is useful for
...@@ -22,6 +22,7 @@ These exporters are maintained as part of the official ...@@ -22,6 +22,7 @@ These exporters are maintained as part of the official
* [Consul exporter](https://github.com/prometheus/consul_exporter) * [Consul exporter](https://github.com/prometheus/consul_exporter)
* [Graphite exporter](https://github.com/prometheus/graphite_exporter) * [Graphite exporter](https://github.com/prometheus/graphite_exporter)
* [HAProxy exporter](https://github.com/prometheus/haproxy_exporter) * [HAProxy exporter](https://github.com/prometheus/haproxy_exporter)
* [InfluxDB exporter](https://github.com/prometheus/influxdb_exporter)
* [JMX exporter](https://github.com/prometheus/jmx_exporter) * [JMX exporter](https://github.com/prometheus/jmx_exporter)
* [Mesos task exporter](https://github.com/prometheus/mesos_exporter) * [Mesos task exporter](https://github.com/prometheus/mesos_exporter)
* [MySQL server exporter](https://github.com/prometheus/mysqld_exporter) * [MySQL server exporter](https://github.com/prometheus/mysqld_exporter)
...@@ -39,35 +40,48 @@ and maintained. We encourage the creation of more exporters but cannot ...@@ -39,35 +40,48 @@ and maintained. We encourage the creation of more exporters but cannot
vet all of them for best practices. Commonly, those exporters are vet all of them for best practices. Commonly, those exporters are
hosted outside of the Prometheus GitHub organization. hosted outside of the Prometheus GitHub organization.
* [Aerospike exporter](https://github.com/alicebob/asprom)
* [Apache exporter](https://github.com/neezgee/apache_exporter) * [Apache exporter](https://github.com/neezgee/apache_exporter)
* [BIG-IP exporter](https://github.com/ExpressenAB/bigip_exporter)
* [BIND exporter](https://github.com/digitalocean/bind_exporter) * [BIND exporter](https://github.com/digitalocean/bind_exporter)
* [Ceph exporter](https://github.com/digitalocean/ceph_exporter) * [Ceph exporter](https://github.com/digitalocean/ceph_exporter)
* [CouchDB exporter](https://github.com/gesellix/couchdb-exporter) * [CouchDB exporter](https://github.com/gesellix/couchdb-exporter)
* [Django exporter](https://github.com/korfuri/django-prometheus) * [Django exporter](https://github.com/korfuri/django-prometheus)
* [Google's mtail log data extractor](https://github.com/google/mtail) * [Google's mtail log data extractor](https://github.com/google/mtail)
* [Heka exporter](https://github.com/docker-infra/heka_exporter) * [Heka dashboard exporter](https://github.com/docker-infra/heka_exporter)
* [HTTP(s)/TCP/ICMP blackbox prober](https://github.com/discordianfish/blackbox_prober) * [Heka exporter](https://github.com/imgix/heka_exporter)
* [IoT Edison exporter](https://github.com/roman-vynar/edison_exporter)
* [Jenkins exporter](https://github.com/RobustPerception/python_examples/tree/master/jenkins_exporter) * [Jenkins exporter](https://github.com/RobustPerception/python_examples/tree/master/jenkins_exporter)
* [knxd exporter](https://github.com/RichiH/knxd_exporter)
* [Memcached exporter](https://github.com/Snapbug/memcache_exporter) * [Memcached exporter](https://github.com/Snapbug/memcache_exporter)
* [Meteor JS web framework exporter](https://atmospherejs.com/sevki/prometheus-exporter) * [Meteor JS web framework exporter](https://atmospherejs.com/sevki/prometheus-exporter)
* [Minecraft exporter module](https://github.com/Baughn/PrometheusIntegration) * [Minecraft exporter module](https://github.com/Baughn/PrometheusIntegration)
* [Mirth Connect exporter](https://github.com/vynca/mirth_exporter)
* [MongoDB exporter](https://github.com/dcu/mongodb_exporter) * [MongoDB exporter](https://github.com/dcu/mongodb_exporter)
* [Munin exporter](https://github.com/pvdh/munin_exporter) * [Munin exporter](https://github.com/pvdh/munin_exporter)
* [New Relic exporter](https://github.com/jfindley/newrelic_exporter) * [New Relic exporter](https://github.com/jfindley/newrelic_exporter)
* [Nginx metric library](https://github.com/knyar/nginx-lua-prometheus)
* [NSQ exporter](https://github.com/lovoo/nsq_exporter) * [NSQ exporter](https://github.com/lovoo/nsq_exporter)
* [OpenWeatherMap exporter](https://github.com/RichiH/openweathermap_exporter)
* [Passenger exporter](https://github.com/stuartnelson3/passenger_exporter)
* [PgBouncer exporter](http://git.cbaines.net/prometheus-pgbouncer-exporter/about) * [PgBouncer exporter](http://git.cbaines.net/prometheus-pgbouncer-exporter/about)
* [PostgreSQL exporter](https://github.com/wrouesnel/postgres_exporter) * [PostgreSQL exporter](https://github.com/wrouesnel/postgres_exporter)
* [PowerDNS exporter](https://github.com/janeczku/powerdns_exporter) * [PowerDNS exporter](https://github.com/janeczku/powerdns_exporter)
* [RabbitMQ exporter](https://github.com/kbudde/rabbitmq_exporter) * [RabbitMQ exporter](https://github.com/kbudde/rabbitmq_exporter)
* [Rancher exporter](https://github.com/infinityworksltd/prometheus-rancher-exporter)
* [Redis exporter](https://github.com/oliver006/redis_exporter) * [Redis exporter](https://github.com/oliver006/redis_exporter)
* [RethinkDB exporter](https://github.com/oliver006/rethinkdb_exporter) * [RethinkDB exporter](https://github.com/oliver006/rethinkdb_exporter)
* [Rsyslog exporter](https://github.com/digitalocean/rsyslog_exporter) * [Rsyslog exporter](https://github.com/digitalocean/rsyslog_exporter)
* [rTorrent exporter](https://github.com/mdlayher/rtorrent_exporter)
* [scollector exporter](https://github.com/tgulacsi/prometheus_scollector) * [scollector exporter](https://github.com/tgulacsi/prometheus_scollector)
* [SMTP/Maildir MDA blackbox prober](https://github.com/cherti/mailexporter) * [SMTP/Maildir MDA blackbox prober](https://github.com/cherti/mailexporter)
* [Speedtest.net exporter](https://github.com/RichiH/speedtest_exporter)
* [SQL query result set metrics exporter](https://github.com/chop-dbhi/prometheus-sql) * [SQL query result set metrics exporter](https://github.com/chop-dbhi/prometheus-sql)
* [Ubiquiti UniFi exporter](https://github.com/mdlayher/unifi_exporter)
* [Varnish exporter](https://github.com/jonnenauha/prometheus_varnish_exporter)
When implementing a new Prometheus exporter, please follow the When implementing a new Prometheus exporter, please follow the
[Prometheus Exporter Guidelines](https://docs.google.com/document/d/1JapuiRbp-XoyECgl2lPdxITrhm5IyCUq9iA_h6jp3OY/edit). [guidelines on writing exporters](/docs/instrumenting/writing_exporters)
Please also consider consulting the [development mailing Please also consider consulting the [development mailing
list](https://groups.google.com/forum/#!forum/prometheus-developers). We are list](https://groups.google.com/forum/#!forum/prometheus-developers). We are
happy to give advice on how to make your exporter as useful and consistent as happy to give advice on how to make your exporter as useful and consistent as
...@@ -79,11 +93,13 @@ Some third-party software already exposes Prometheus metrics natively, so no ...@@ -79,11 +93,13 @@ Some third-party software already exposes Prometheus metrics natively, so no
separate exporters are needed: separate exporters are needed:
* [cAdvisor](https://github.com/google/cadvisor) * [cAdvisor](https://github.com/google/cadvisor)
* [Doorman](https://github.com/youtube/doorman)
* [Etcd](https://github.com/coreos/etcd) * [Etcd](https://github.com/coreos/etcd)
* [Kubernetes-Mesos](https://github.com/mesosphere/kubernetes-mesos) * [Kubernetes-Mesos](https://github.com/mesosphere/kubernetes-mesos)
* [Kubernetes](https://github.com/GoogleCloudPlatform/kubernetes) * [Kubernetes](https://github.com/GoogleCloudPlatform/kubernetes)
* [RobustIRC](http://robustirc.net/) * [RobustIRC](http://robustirc.net/)
* [SkyDNS](https://github.com/skynetservices/skydns) * [SkyDNS](https://github.com/skynetservices/skydns)
* [Weave Flux](http://weaveworks.github.io/flux/)
## Other third-party utilities ## Other third-party utilities
......
--- ---
title: Exposition formats title: Exposition formats
sort_rank: 4 sort_rank: 6
--- ---
# Exposition formats # Exposition formats
...@@ -40,7 +40,7 @@ Prometheus). ...@@ -40,7 +40,7 @@ Prometheus).
| **Inception** | April 2014 | April 2014 | | **Inception** | April 2014 | April 2014 |
| **Supported in** | Prometheus version `>=0.4.0` | Prometheus version `>=0.4.0` | | **Supported in** | Prometheus version `>=0.4.0` | Prometheus version `>=0.4.0` |
| **Transmission** | HTTP | HTTP | | **Transmission** | HTTP | HTTP |
| **Encoding** | [32-bit varint-encoded record length-delimited](https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/AbstractMessageLite#writeDelimitedTo(java. io.OutputStream)) Protocol Buffer messages of type [io.prometheus.client.MetricFamily](https://github.com/prometheus/client_model/blob/086fe7ca28bde6cec2acd5223423c1475a362858/metrics.proto#L76- L81) | UTF-8, `\n` line endings | | **Encoding** | [32-bit varint-encoded record length-delimited](https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/AbstractMessageLite#writeDelimitedTo(java.io.OutputStream)) Protocol Buffer messages of type [io.prometheus.client.MetricFamily](https://github.com/prometheus/client_model/blob/086fe7ca28bde6cec2acd5223423c1475a362858/metrics.proto#L76- L81) | UTF-8, `\n` line endings |
| **HTTP `Content-Type`** | `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited` | `text/plain; version=0.0.4` (A missing `version` value will lead to a fall-back to the most recent text format version.) | | **HTTP `Content-Type`** | `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited` | `text/plain; version=0.0.4` (A missing `version` value will lead to a fall-back to the most recent text format version.) |
| **Optional HTTP `Content-Encoding`** | `gzip` | `gzip` | | **Optional HTTP `Content-Encoding`** | `gzip` | `gzip` |
| **Advantages** | <ul><li>Cross-platform</li><li>Size</li><li>Encoding and decoding costs</li><li>Strict schema</li><li>Supports concatenation and theoretically streaming (only server-side behavior would need to change)</li></ul> | <ul><li>Human-readable</li><li>Easy to assemble, especially for minimalistic cases (no nesting required)</li><li>Readable line by line (with the exception of type hints and docstrings)</li></ul> | | **Advantages** | <ul><li>Cross-platform</li><li>Size</li><li>Encoding and decoding costs</li><li>Strict schema</li><li>Supports concatenation and theoretically streaming (only server-side behavior would need to change)</li></ul> | <ul><li>Human-readable</li><li>Easy to assemble, especially for minimalistic cases (no nesting required)</li><li>Readable line by line (with the exception of type hints and docstrings)</li></ul> |
...@@ -113,13 +113,13 @@ format. The following conventions apply: ...@@ -113,13 +113,13 @@ format. The following conventions apply:
See also the example below. See also the example below.
``` ```
# HELP api_http_request_count The total number of HTTP requests. # HELP http_requests_total The total number of HTTP requests.
# TYPE api_http_request_count counter # TYPE http_requests_total counter
http_request_count{method="post",code="200"} 1027 1395066363000 http_requests_total{method="post",code="200"} 1027 1395066363000
http_request_count{method="post",code="400"} 3 1395066363000 http_requests_total{method="post",code="400"} 3 1395066363000
# Escaping in label values: # Escaping in label values:
msdos_file_access_time_ms{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.234e3 msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9
# Minimalistic line: # Minimalistic line:
metric_without_timestamp_and_labels 12.47 metric_without_timestamp_and_labels 12.47
......
--- ---
title: Pushing metrics title: Pushing metrics
sort_rank: 2 sort_rank: 3
--- ---
# Pushing metrics # Pushing metrics
...@@ -17,13 +17,15 @@ makes it easy to instrument even shell scripts without a client library. ...@@ -17,13 +17,15 @@ makes it easy to instrument even shell scripts without a client library.
[README.md](https://github.com/prometheus/pushgateway/blob/master/README.md). [README.md](https://github.com/prometheus/pushgateway/blob/master/README.md).
* For use from Java see the * For use from Java see the
[PushGateway](http://prometheus.github.io/client_java/io/prometheus/client/exporter/PushGateway.html) [PushGateway](https://prometheus.io/client_java/io/prometheus/client/exporter/PushGateway.html)
class. class.
* For use from Go see the [Push](http://godoc.org/github.com/prometheus/client_golang/prometheus#Push) and [PushAdd](http://godoc.org/github.com/prometheus/client_golang/prometheus#PushAdd) functions. * For use from Go see the [Push](http://godoc.org/github.com/prometheus/client_golang/prometheus#Push) and [PushAdd](http://godoc.org/github.com/prometheus/client_golang/prometheus#PushAdd) functions.
* For use from Python see [Exporting to a Pushgateway](https://github.com/prometheus/client_python#exporting-to-a-pushgateway). * For use from Python see [Exporting to a Pushgateway](https://github.com/prometheus/client_python#exporting-to-a-pushgateway).
* For use from Ruby see the [Pushgateway documentation](https://github.com/prometheus/client_ruby#pushgateway).
## Java batch job example ## Java batch job example
This example illustrates how to instrument a batch job and alert on it not having succeeded recently. This example illustrates how to instrument a batch job and alert on it not having succeeded recently.
......
---
title: Writing client libraries
sort_rank: 2
---
# Writing client libraries
This document covers what functionality and API Prometheus client libraries
should offer, with the aim of consistency across libraries, making the easy use
cases easy and avoiding offering functionality that may lead users down the
wrong path.
There are [10 languages already supported](/docs/instrumenting/clientlibs) at
the time of writing, so we’ve gotten a good sense by now of how to write a
client. These guidelines aim to help authors of new client libraries produce
good libraries.
## Conventions
MUST/MUST NOT/SHOULD/SHOULD NOT/MAY have the meanings given in
[https://www.ietf.org/rfc/rfc2119.txt](https://www.ietf.org/rfc/rfc2119.txt)
In addition ENCOURAGED means that a feature is desirable for a library to have,
but it’s okay if it’s not present. In other words, a nice to have.
Things to keep in mind:
* Take advantage of each language’s features.
* The common use cases should be easy.
* The correct way to do something should be the easy way.
* More complex use cases should be possible.
The common use cases are (in order):
* Counters without labels spread liberally around libraries/applications.
* Timing functions/blocks of code in Summaries/Histograms.
* Gauges to track current states of things (and their limits).
* Monitoring of batch jobs.
## Overall structure
Clients MUST be written to be callback based internally. Clients SHOULD
generally follow the structure described here.
The key class is the Collector. This has a method (typically called ‘collect’)
that returns zero or more metrics and their samples. Collectors get registered
with a CollectorRegistry. Data is exposed by passing a CollectorRegistry to a
class/method/function "bridge", which returns the metrics in a format
Prometheus supports. Every time the CollectorRegistry is scraped it must
callback to each of the Collectors’ collect method.
The interface most users interact with are the Counter, Gauge, Summary, and
Histogram Collectors. These represent a single metric, and should cover the
vast majority of use cases where a user is instrumenting their own code.
More advanced uses cases (such as proxying from another
monitoring/instrumentation system) require writing a custom Collector. Someone
may also want to write a "bridge" that takes a CollectorRegistry and produces
data in a format a different monitoring/instrumentation system understands,
allowing users to only have to think about one instrumentation system.
CollectorRegistry SHOULD offer `register()`/`unregister()` functions, and a
Collector SHOULD be allowed to be registered to multiple CollectorRegistrys.
Client libraries MUST be thread safe.
For non-OO languages such as C, client libraries should follow the spirit of
this structure as much as is practical.
### Naming
Client libraries SHOULD follow function/method/class names mentioned in this
document, keeping in mind the naming conventions of the language they’re
working in. For example, `set_to_current_time()` is good for a method name
Python, but `SetToCurrentTime()` is better in Go and `setToCurrentTime()` is
the convention in Java. Where names differ for technical reasons (e.g. not
allowing function overloading), documentation/help strings SHOULD point users
towards the other names.
Libraries MUST NOT offer functions/methods/classes with the same or similar
names to ones given here, but with different semantics.
## Metrics
The Counter, Gauge, Summary and Histogram [metric
types](/docs/concepts/metric_types/) are the primary interface by users.
Counter and Gauge MUST be part of the client library. At least one of Summary
and Histogram MUST be offered.
These should be primarily used as file-static variables, that is, global
variables defined in the same file as the code they’re instrumenting. The
client library SHOULD enable this. The common use case is instrumenting a piece
of code overall, not a piece of code in the context of one instance of an
object. Users shouldn’t have to worry about plumbing their metrics throughout
their code, the client library should do that for them (and if it doesn’t,
users will write a wrapper around the library to make it "easier" - which
rarely tends to go well).
There MUST be a default CollectorRegistry, the standard metrics MUST by default
implicitly register into it with no special work required by the user. There
MUST be a way to have metrics not register to the default CollectorRegistry,
for use in batch jobs and unittests. Custom collectors SHOULD also follow this.
Exactly how the metrics should be created varies by language. For some (Java,
Go) a builder approach is best, whereas for others (Python) function arguments
are rich enough to do it in one call.
For example in the Java Simpleclient we have:
```java
class YourClass {
static final Counter requests = Counter.build()
.name("requests_total")
.help("Requests.").register();
}
```
This will register requests with the default CollectorRegistry. By calling
`build()` rather than `register()` the metric won’t be registered (handy for
unittests), you can also pass in a CollectorRegistry to `register()` (handy for
batch jobs).
### Counter
[Counter](/docs/concepts/metric_types/#counter) is a monotonically increasing
counter. It MUST NOT allow the value to decrease, however it MAY be reset to 0
(such as by server restart).
A counter MUST have the following methods:
- `inc()`: Increment the counter by 1
- `inc(double v)`: Increment the counter by the given amount. MUST check that v >= 0.
A counter is ENCOURAGED to have:
A way to count exceptions throw/raised in a given piece of code, and optionally
only certain types of exceptions. This is count_exceptions in Python.
Counters MUST start at 0.
### Gauge
[Gauge](/docs/concepts/metric_types/#gauge) represents a value that can go up
and down.
A gauge MUST have the following methods:
- `inc()`: Increment the gauge by 1
- `inc(double v)`: Increment the gauge by the given amount
- `dec()`: Decrement the gauge by 1
- `dec(double v)`: Decrement the gauge by the given amount
- `set(double v)`: Set the gauge to the given value
Gauges MUST start at 0, you MAY offer a way for a given gauge to start at a
different number.
A gauge SHOULD have the following methods:
- `set_to_current_time()`: Set the gauge to the current unixtime in seconds.
A gauge is ENCOURAGED to have:
A way to track in-progress requests in some piece of code/function. This is
`track_inprogress` in Python.
A way to time a piece of code and set the gauge to its duration in seconds.
This is useful for batch jobs. This is startTimer/setDuration in Java and the
`time()` decorator/context manager in Python. This SHOULD match the pattern in
Summary/Histogram (though `set()` rather than `observe()`).
### Summary
A [summary](/docs/concepts/metric_types/#summary) samples observations (usually
things like request durations) over sliding windows of time and provides
instantaneous insight into their distributions, frequencies, and sums.
A summary MUST NOT allow the user to set "quantile" as a label name, as this is
used internally to designate summary quantiles. A summary is ENCOURAGED to
offer quantiles as exports, though these can’t be aggregated and tend to be
slow. A summary MUST allow not having quantiles, as just `_count`/`_sum` is
quite useful and this MUST be the default.
A summary MUST have the following methods:
- `observe(double v)`: Observe the given amount
A summary SHOULD have the following methods:
Some way to time code for users in seconds. In Python this is the `time()`
decorator/context manager. In Java this is startTimer/observeDuration. Units
other than seconds MUST NOT be offered (if a user wants something else, they
can do it by hand). This should follow the same pattern as Gauge/Histogram.
Summary `_count`/`_sum` MUST start at 0.
### Histogram
[Histograms](/docs/concepts/metric_types/#histogram) allow aggregatable
distributions of events, such as request latencies. This is at its core a
counter per bucket.
A histogram MUST NOT allow `le` as a user-set label, as `le` is used internally
to designate buckets.
A histogram MUST offer a way to manually choose the buckets. Ways to set
buckets in a `linear(start, width, count)` and `exponential(start, factor,
count)` fashion SHOULD be offered. Count MUST exclude the `+Inf` bucket.
A histogram SHOULD have the same default buckets as other client libraries.
Buckets MUST NOT be changeable once the metric is created.
A histogram MUST have the following methods:
- `observe(double v)`: Observe the given amount
A histogram SHOULD have the following methods:
Some way to time code for users in seconds. In Python this is the `time()`
decorator/context manager. In Java this is `startTimer`/`observeDuration`.
Units other than seconds MUST NOT be offered (if a user wants something else,
they can do it by hand). This should follow the same pattern as Gauge/Summary.
Histogram `_count`/`_sum` and the buckets MUST start at 0.
**Further metrics considerations**
Providing additional functionality in metrics beyond what’s documented above as
makes sense for a given language is ENCOURAGED.
If there’s a common use case you can make simpler then go for it, as long as it
won’t encourage undesirable behaviours (such as suboptimal metric/label
layouts, or doing computation in the client).
### Labels
Labels are one of the [most powerful
aspects](/docs/practices/instrumentation/#use-labels) of Prometheus, but
[easily abused](/docs/practices/instrumentation/#do-not-overuse-labels).
Accordingly client libraries must be very careful in how labels are offered to
users.
Client libraries MUST NOT under any circumstances allow users to have different
label names for the same metric for Gauge/Counter/Summary/Histogram or any
other Collector offered by the library.
If your client library does validation of metrics at collect time, it MAY also
verify this for custom Collectors.
While labels are powerful, the majority of metrics will not have labels.
Accordingly the API should allow for labels but not dominate it.
A client library MUST allow for optionally specifying a list of label names at
Gauge/Counter/Summary/Histogram creation time. A client library SHOULD support
any number of label names. A client library MUST validate that label names meet
the [documented
requirements](/docs/concepts/data_model/#metric-names-and-labels).
The general way to provide access to labeled dimension of a metric is via a
`labels()` method that takes either a list of the label values or a map from
label name to label value and returns a "Child". The usual
`.inc()`/`.dec()`/`.observe()` etc. methods can then be called on the Child.
The Child returned by `labels()` SHOULD be cacheable by the user, to avoid
having to look it up again - this matters in latency-critical code.
Metrics with labels SHOULD support a `remove()` method with the same signature
as `labels()` that will remove a Child from the metric no longer exporting it,
and a `clear()` method that removes all Children from the metric. These
invalidate caching of Children.
There SHOULD be a way to initialize a given Child with the default value,
usually just calling `labels()`. Metrics without labels MUST always be
initialized to avoid [problems with missing
metrics](/docs/practices/instrumentation/#avoid-missing-metrics).
### Metric names
Metric names must follow the
[specification](/docs/concepts/data_model/#metric-names-and-labels). As with
label names, this MUST be met for uses of Gauge/Counter/Summary/Histogram and
in any other Collector offered with the library.
Many client libraries offer setting the name in three parts:
`namespace_subsystem_name` of which only the `name` is mandatory.
Dynamic/generated metric names or subparts of metric names MUST be discouraged,
except when a custom Collector is proxying from other
instrumentation/monitoring systems. Generated/dynamic metric names are a sign
that you should be using labels instead.
### Metric description and help
Gauge/Counter/Summary/Histogram MUST require metric descriptions/help to be
provided.
Any custom Collectors provided with the client libraries MUST have
descriptions/help on their metrics.
It is suggested to make it a mandatory argument, but not to check that it’s of
a certain length as if someone really doesn’t want to write docs we’re not
going to convince them otherwise. Collectors offered with the library (and
indeed everywhere we can within the ecosystem) SHOULD have good metric
descriptions, to lead by example.
## Exposition
Clients MUST implement one of the documented [exposition
formats](/docs/instrumenting/exposition_formats).
Clients MAY implement more than one format. There SHOULD be a human readable
format offered.
If in doubt, go for the text format. It doesn’t have a dependency (protobuf),
tends to be easy to produce, is human readable and the performance benefits of
protobuf are not that significant for most use cases.
Reproducible order of the exposed metrics is ENCOURAGED (especially for human
readable formats) if it can be implemented without a significant resource cost.
## Standard and runtime collectors
Client libraries SHOULD offer what they can of the Standard exports, documented
at
[https://docs.google.com/document/d/1Q0MXWdwp1mdXCzNRak6bW5LLVylVRXhdi7_21Sg15xQ/edit](https://docs.google.com/document/d/1Q0MXWdwp1mdXCzNRak6bW5LLVylVRXhdi7_21Sg15xQ/edit)
In addition, client libraries are ENCOURAGED to also offer whatever makes sense
in terms of metrics for their language’s runtime (e.g. Garbage collection
stats).
These SHOULD be implemented as custom Collectors, and registered by default on
the default CollectorRegistry. There SHOULD be a way to disable these, as there
are some very niche use cases where they get in the way.
## Unit tests
Client libraries SHOULD have unit tests covering the core instrumentation
library and exposition.
Client libraries are ENCOURAGED to offer ways that make it easy for users to
unit-test their use of the instrumentation code. For example, the
`CollectorRegistry.get_sample_value` in Python.
## Packaging and dependencies
Ideally, a client library can be included in any application to add some
instrumentation, without having to worry about it breaking the application.
Accordingly, caution is advised when adding dependencies to the client library.
For example, if a user adds a library that uses a Prometheus client that
requires version 1.4 of protobuf but the application uses 1.2 elsewhere, what
will happen?
It is suggested that where this may arise, that the core instrumentation is
separated from the bridges/exposition of metrics in a given format. For
example, the Java simpleclient `simpleclient` module has no dependencies, and
the `simpleclient_servlet` has the HTTP bits.
## Performance considerations
As client libraries must be thread-safe, some form of concurrency control is
required and consideration must be given to performance on multi-core machines
and applications.
In our experience the least performant is mutexes.
Processor atomic instructions tend to be in the middle, and generally
acceptable.
Approaches that avoid different CPUs mutating the same bit of RAM work best,
such as the DoubleAdder in Java’s simpleclient. There is a memory cost though.
As noted above, the result of `labels()` should be cacheable. The concurrent
maps that tend to back metric with labels tend to be relatively slow.
Special-casing metrics without labels to avoid `labels()`-like lookups can help
a lot.
Metrics SHOULD avoid blocking when they are being incremented/decremented/set
etc. as it’s undesirable for the whole application to be held up while a scrape
is ongoing.
Having benchmarks of the main instrumentation operations, including labels, is
ENCOURAGED.
Resource consumption, particularly RAM, should be kept in mind when performing
exposition. Consider reducing the memory footprint by streaming results, and
potentially having a limit on the number of concurrent scrapes.
---
title: Writing exporters
sort_rank: 5
---
# Writing exporters
When directly instrumenting your own code, the general rules of how to
instrument code with a Prometheus client library can be followed quite
directly. When taking metrics from another monitoring or instrumentation
system, things tend not to be so black and white.
This document contains things you should consider when writing an exporter or
custom collector. The theory covered will also be of interest to those doing
direct instrumentation.
If you are writing an exporter and are unclear on anything here, contact us on
IRC (#prometheus on Freenode) or the [mailing list](/community).
## Maintainability and purity
The main decision you need to make when writing an exporter is how much work
you’re willing to put in to get perfect metrics out of it.
If the system in question has only a handful of metrics that rarely change,
then getting everything perfect is an easy choice (e.g. the [haproxy
exporter](https://github.com/prometheus/haproxy_exporter)).
If on the other hand the system has hundreds of metrics that change
continuously with new versions, if you try to get things perfect then you’ve
signed yourself up for a lot of ongoing work. The [mysql
exporter](https://github.com/prometheus/mysqld_exporter) is on this end of the
spectrum.
The [node exporter](https://github.com/prometheus/node_exporter) is a mix,
varying by module. For mdadm we have to hand-parse a file and come up with our
own metrics, so we may as well get the metrics right while we’re at it. For
meminfo on the other hand, the results vary across kernel versions so we end up
doing just enough of a transform to create valid metrics.
## Configuration
When working with applications, you should aim for an exporter that requires no
custom configuration by the user beyond telling it where the application is.
You may also need to offer the ability to filter out certain metrics if they
may be too granular and expensive on large setups (e.g. the haproxy exporter
allows filtering of per-server stats). Similarly there may be expensive metrics
that are disabled by default.
When working with monitoring systems, frameworks and protocols things are not
so simple.
In the best case the system in question has a similar enough data model to
Prometheus that you can automatically determine how to transform metrics. This
is the case for Cloudwatch, SNMP and Collectd. At most we need the ability to
let the user select which metrics they want to pull out.
In the more common case metrics from the system are completely non-standard,
depending on how the user is using it and what the underlying application is.
In that case the user has to tell us how to transform the metrics. The JMX
exporter is the worst offender here, with the graphite and statsd exporters
also requiring configuration to extract labels.
Providing something that produces some output out of the box and a selection of
example configurations is advised. When writing configurations for such
exporters, this document should be kept in mind.
YAML is the standard Prometheus configuration format.
## Metrics
### Naming
Follow the [best practices on metric naming](/docs/practices/naming).
Generally metric names should allow someone who’s familiar with Prometheus but
not a particular system to make a good guess as to what a metric means. A
metric named `http_requests_total` is not extremely useful - are these being
measured as they come in, in some filter or when they get to the user’s code?
And `requests_total` is even worse, what type of requests?
To put it another way with direct instrumentation, a given metric should exist
within exactly one file. Accordingly within exporters and collectors, a metric
should apply to exactly one subsystem and be named accordingly.
Metric names should never be procedurally generated, except when writing a
custom collector or exporter.
Metric names for applications should generally be prefixed by the exporter
name, e.g. `haproxy_up`.
Metrics must use base units (e.g. seconds, bytes) and leave converting them to
something more readable to the graphing software. No matter what units you end
up using, the units in the metric name must match the units in use. Similarly
expose ratios, not percentages (though a counter for each of the two components
of the ratio is better).
Metric names should not include the labels that they’re exported with (e.g.
`by_type`) as that won’t make sense if the label is aggregated away.
The one exception is when you’re exporting the same data with different labels
via multiple metrics, in which case that’s usually the sanest way to
distinguish them. For direct instrumentation this should only come up when
exporting a single metric with all the labels would have too high a
cardinality.
Prometheus metrics and label names are written in `snake_case`. Converting
`camelCase` to `snake_case` is desirable, though it doing so automatically
doesn’t always produce nice results for things like `myTCPExample` or `isNaN`
so sometimes it’s best to leave them as-is.
Exposed metrics should not contain colons, these are for users to use when
aggregating.
Only `[a-zA-Z0-9:_]` are valid in metric names, any other characters should be
sanitized to an underscore.
The `_sum`, `_count`, `_bucket` and `_total` suffixes are used by Summaries,
Histograms and Counters. Unless you’re producing one of those, avoid these
suffixes.
`_total` is a convention for counters, you should use it if you’re using the
COUNTER type.
The `process_` and `scrape_` prefixes are reserved. It’s okay to add your own
prefix on to these if they follow the [matching
semantics](https://docs.google.com/document/d/1Q0MXWdwp1mdXCzNRak6bW5LLVylVRXhdi7_21Sg15xQ/edit).
E.g. Prometheus has `scrape_duration_seconds` for how long a scrape took, it’s
good practice to have e.g. `jmx_scrape_duration_seconds` saying how long the
JMX collector took to do it’s thing. For process stats where you have access to
the pid, both Go and Python offer collectors that’ll handle this for you (see
the [haproxy exporter](https://github.com/prometheus/haproxy_exporter) for an
example).
When you have a successful request count and a failed request count, the best
way to expose this is as one metric for total requests and another metric for
failed requests. This makes it easy to calculate the failure ratio. Do not use
one metric with a failed/success label. Similarly with hit/miss for caches,
it’s better to have one metric for total and another for hits.
Consider the likelihood that someone using monitoring will do a code or web
search for the metric name. If the names are very well established and unlikely
to be used outside of the realm of people used to those names (e.g. SNMP and
network engineers) then leaving them as-is may be a good idea. This logic
doesn’t apply for e.g. MySQL as non-DBAs can be expected to be poking around
the metrics. A `HELP` string with the original name can provide most of the
same benefits as using the original names.
### Labels
Read the [general
advice](/docs/practices/instrumentation/#things-to-watch-out-for) on labels.
Avoid `type` as a label name, it’s too generic and meaningless. You should also
try where possible to avoid names that are likely to clash with target labels,
such as `region`, `zone`, `cluster`, `availability_zone`, `az`, `datacenter`,
`dc`, `owner`, `customer`, `stage`, `environment` and `env` - though if that’s
what the application calls something it’s best not to cause confusion by
renaming it.
Avoid the temptation to put things into one metric just because they share a
prefix. Unless you’re sure something makes sense as one metric, multiple
metrics is safer.
The label `le` has special meaning for Histograms, and `quantile` for
Summaries. Avoid these labels generally.
Read/write and send/receive are best as separate metrics, rather than as a
label. This is usually because you care about only one of them at a time, and
it’s easier to use them that way.
The rule of thumb is that one metric should make sense when summed or averaged.
There is one other case that comes up with exporters, and that’s where the data
is fundamentally tabular and doing otherwise would require users to do regexes
on metric names to be useable. Consider the voltage sensors on your
motherboard, while doing math across them is meaningless, it makes sense to
have them in one metric rather than having one metric per sensor. All values
within a metrics should (almost) always have the same unit (consider if fan
speeds were mixed in with the voltages, and you had no way to automatically
separate them).
Don’t do this:
<pre>
my_metric{label=a} 1
my_metric{label=b} 6
<b>my_metric{label=total} 7</b>
</pre>
or this:
<pre>
my_metric{label=a} 1
my_metric{label=b} 6
<b>my_metric{} 7</b>
</pre>
The former breaks people who do a `sum()` over your metric, and the latter
breaks sum and also is quite difficult to work with. Some client libraries
(e.g. Go) will actively try to stop you doing the latter in a custom collector,
and all client libraries should stop you from doing the former with direct
instrumentation. Never do either of these, rely on Prometheus aggregation
instead.
If your monitoring exposes a total like this, drop the total. If you have to
keep it around for some reason (e.g. the total includes things not counted
individually), use different metric names.
### Target labels, not static scraped labels
If you ever find yourself wanting to apply the same label to all of your
metrics, stop.
There’s generally two cases where this comes up.
The first is some label it’d be useful to have on the metrics that are about,
such as the version number of the software. Use the approach described at
[http://www.robustperception.io/how-to-have-labels-for-machine-roles/](http://www.robustperception.io/how-to-have-labels-for-machine-roles/)
instead.
The other case are what are really target labels. These are things like region,
cluster names, and so on, that come from your infrastructure setup rather than
the application itself. It’s not for an application to say where it fits in
your label taxonomy, that’s for the person running the Prometheus server to
configure and different people monitoring the same application may give it
different names.
Accordingly these labels belong up in the scrape configs of Prometheus via
whatever service discovery you’re using. It’s okay to apply the concept of
machine roles here as well, as it’s likely useful information for at least some
of the people scraping it.
### Types
You should try to match up the types of your metrics to Prometheus types. This
usually means counters and gauges. The `_count` and `_sum` of summaries are
also relatively common, and on occasion you’ll see quantiles. Histograms are
rare, if you come across one remember that the exposition format exposes
cumulative values.
Often it won’t be obvious what the type of a metric is (especially if you’re
automatically processing a set of metrics), use `UNTYPED` in that case. In
general `UNTYPED` is a safe default.
Counters can’t go down, so if you’ve a counter type coming from another
instrumentation system that has a way to decrement it (e.g. Dropwizard metrics)
that’s not a counter - it’s a gauge. `UNTYPED` is probably the best type to use
there, as `GAUGE` would be misleading if it were being used as a counter.
### Help strings
When you’re transforming metrics it’s useful for users to be able to track back
to what the original was, and what rules were in play that caused that
transform. Putting in the name of the collector/exporter, the id of any rule
that was applied and the name/details of the original metric into the help
string will greatly aid users.
Prometheus doesn’t like one metric having different help strings. If you’re
making one metric from many others, choose one of them to put in the help
string.
For examples of this, the SNMP exporter uses the OID and the JMX exporter puts
in a sample mBean name. The [haproxy
exporter](https://github.com/prometheus/haproxy_exporter) has hand-written
strings. The [node exporter](https://github.com/prometheus/node_exporter) has a
wide variety of examples.
### Drop less useful statistics
Some instrumentation systems expose 1m/5m/15m rates, average rates since
application start (called `mean` in dropwizard metrics for example), minimums,
maximums and standard deviations.
These should all be dropped, as they’re not very useful and add clutter.
Prometheus can calculate rates itself, and usually more accurately (these are
usually exponentially decaying averages). You don’t know what time the min/max
were calculated over, and the stddev is statistically useless (expose sum of
squares, `_sum` and `_count` if you ever need to calculate it).
Quantiles have related issues, you may choose to drop them or put them in a
Summary.
### Dotted strings
Many monitoring systems don’t have labels, instead doing things like
`my.class.path.mymetric.labelvalue1.labelvalue2.labelvalue3`.
The graphite and statsd exporters share a way of doing this with a small
configuration language. Other exporters should implement the same. It’s
currently implemented only in Go, and would benefit from being factored out
into a separate library.
## Collectors
When implementing the collector for your exporter, you should never use the
usual direct instrumentation approach and then update the metrics on each
scrape.
Rather create new metrics each time. In Go this is done with
[MustNewConstMetric](https://godoc.org/github.com/prometheus/client_golang/prometheus#MustNewConstMetric)
in your `Update()` method. For Python see
[https://github.com/prometheus/client_python#custom-collectors](https://github.com/prometheus/client_python#custom-collectors)
and for Java generate a `List<MetricFamilySamples>` in your collect method -
see
[StandardExports.java](https://github.com/prometheus/client_java/blob/master/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/StandardExports.java)
for an example.
The reason for this is firstly that two scrapes could happen at the same time,
and direct instrumentation uses what are effectively (file-level) global
variables so you’ll get race conditions. The second reason is that if a label
value disappears, it’ll still be exported.
Instrumenting your exporter itself via direct instrumentation is fine, e.g.
total bytes transferred or calls performed by the exporter across all scrapes.
For exporters such as the blackbox exporter and snmp exporter which aren’t tied
to a single target, these should only be exposed on a vanilla `/metrics` call -
not on a scrape of a particular target.
### Metrics about the scrape itself
Sometimes you’d like to export metrics that are about the scrape, like how long
it took or how many records you processed.
These should be exposed as gauges (as they’re about an event, the scrape) and
the metric name prefixed by the exporter name e.g.
`jmx_scrape_duration_seconds`. Usually the `_exporter` is excluded (and if the
exporter also makes sense to use as just a collector, definitely exclude it).
### Machine and process metrics
Many systems (e.g. elasticsearch) expose machine metrics such a CPU, memory and
filesystem information. As the node exporter provides these in the Prometheus
ecosystem, such metrics should be dropped.
In the Java world, many instrumentation frameworks expose process-level and
JVM-level stats such as CPU and GC. The Java client and JMX exporter already
include these in the preferred form via
[DefaultExports.java](https://github.com/prometheus/client_java/blob/master/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java),
so these should be dropped.
Similarly with other languages.
## Deployment
Each exporter should monitor exactly one instance application, preferably
sitting right beside it on the same machine. That means for every haproxy you
run, you run a `haproxy_exporter` process. For every machine with a mesos
slave, you run the mesos exporter on it (and another one for the master if a
machine has both).
The theory behind this is that for direct instrumentation this is what you’d be
doing, and we’re trying to get as close to that as we can in other layouts.
This means that all service discovery is done in Prometheus, not in exporters.
This also has the benefit that Prometheus has the target information it needs
to allow users probe your service with the blackbox exporter.
There are two exceptions:
The first is where running beside the application your monitoring is completely
nonsensical. SNMP, blackbox and IPMI are the main examples of this. IPMI and
SNMP as the devices are effectively black boxes that it’s impossible to run
code on (though if you could run a node exporter on them instead that’d be
better), and blackbox as if you’re monitoring something like a DNS name there’s
nothing to run on. In this case Prometheus should still do service discovery,
and pass on the target to be scraped. See the blackbox and SNMP exporters for
examples.
Note that it is only currently possible to write this type of exporter with the
Python and Java client libraries (the blackbox exporter which is written in Go
is doing the text format by hand, don’t do this).
The other is where you’re pulling some stats out of a random instance of a
system and don’t care which one you’re talking to. Consider a set of MySQL
slaves you wanted to run some business queries against the data to then export.
Having an exporter that uses your usual load balancing approach to talk to one
slave is the sanest approach.
This doesn’t apply when you’re monitoring a system with master-election, in
that case you should monitor each instance individually and deal with the
masterness in Prometheus. This is as there isn’t always exactly one master,
and changing what a target is underneath Prometheus’s feet will cause oddities.
### Scheduling
Metrics should only be pulled from the application when Prometheus scrapes
them, exporters should not perform scrapes based on their own timers. That is,
all scrapes should be synchronous.
Accordingly you should not set timestamps on the metric you expose, let
Prometheus take care of that. If you think you need timestamps, then you
probably need the pushgateway (without timestamps) instead.
If a metric is particularly expensive to retrieve (i.e. takes more than a
minute), it is acceptable to cache it. This should be noted in the `HELP`
string.
The default scrape timeout for Prometheus is 10 seconds. If your exporter can
be expected to exceed this, you should explicitly call this out in your user
docs.
### Pushes
Some applications and monitoring systems only push metrics e.g. statsd,
graphite and collectd.
There’s two considerations here.
Firstly, when do you expire metrics? Collected and things talking to Graphite
both export regularly, and when they stop we want to stop exposing the metrics.
Collected includes an expiry time so we use that, Graphite doesn’t so it’s a
flag on the exporter.
Statsd is a bit different, as it’s dealing with events rather than metrics. The
best model is to run one exporter beside each application and restart them when
the application restarts so that state is cleared.
The second is that these sort of systems tend to allow your users to send
either deltas or raw counters. You should rely on the raw counters as far as
possible, as that’s the general Prometheus model.
For service-level metrics (e.g. service-level batch jobs) you should have your
exporter push into the push gateway and exit after the event rather than
handling the state yourself. For instance-level batch metrics, there is no
clear pattern yet - options are either to abuse the node exporter’s textfile
collector, rely on in-memory state (probably best if you don’t need to persist
over a reboot) or implement similar functionality to the textfile collector.
### Failed scrapes
There are currently two patterns for failed scrapes where the application
you’re talking to doesn’t respond or has other problems.
The first is to return a 5xx error.
The seconds is to have an `myexporter_up` (e.g. `haproxy_up`) variable that’s
0/1 depending on whether the scrape worked.
The latter is better where there’s still some useful metrics you can get even
with a failed scrape, such as the haproxy exporter providing process stats. The
former is a tad easier for users to deal with, as `up` works in the usual way
(though you can’t distinguish between the exporter being down and the
application being down).
### Landing page
It’s nicer for users if visiting `http://yourexporter/` has a simple html page
with the name of the exporter, and a link to the `/metrics`.
### Port numbers
A user may have many exporters and Prometheus components on the same machine,
so to make that easier each has a unique port number.
[https://github.com/prometheus/prometheus/wiki/Default-port-allocations](https://github.com/prometheus/prometheus/wiki/Default-port-allocations)
is where we track them, this is publically editable.
Feel free to grab the next free port number when developing your exporter,
preferably before publicly announcing it. If you’re not ready to release yet,
putting your username and WIP is fine.
This is a registry to make our users’ lives a little easier, not a commitment
to develop particular exporters.
## Announcing
Once you’re ready to announce your exporter to the world, send an email to the
mailing list and send a PR to add it to [the list of available
exporters](https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exporters.md).
...@@ -65,7 +65,7 @@ also works well for many short-lived, frequently changing sets of time series. ...@@ -65,7 +65,7 @@ also works well for many short-lived, frequently changing sets of time series.
## Prometheus vs. InfluxDB ## Prometheus vs. InfluxDB
[InfluxDB](http://influxdb.com/) is a very promising new open-source time [InfluxDB](https://influxdata.com/) is a very promising new open-source time
series database. It did not exist when Prometheus development began, so we were series database. It did not exist when Prometheus development began, so we were
unable to consider it as an alternative at the time. Still, there are unable to consider it as an alternative at the time. Still, there are
significant differences between Prometheus and InfluxDB, and both systems are significant differences between Prometheus and InfluxDB, and both systems are
......
...@@ -35,7 +35,8 @@ on the Robust Perception blog to get started. ...@@ -35,7 +35,8 @@ on the Robust Perception blog to get started.
### What language is Prometheus written in? ### What language is Prometheus written in?
Most Prometheus components are written in Go. Some are also written in Java and Ruby. Most Prometheus components are written in Go. Some are also written in Java,
Python, and Ruby.
### How stable are Prometheus features, storage formats, and APIs? ### How stable are Prometheus features, storage formats, and APIs?
...@@ -146,8 +147,14 @@ the [exposition formats](/docs/instrumenting/exposition_formats/). ...@@ -146,8 +147,14 @@ the [exposition formats](/docs/instrumenting/exposition_formats/).
### Can I monitor machines? ### Can I monitor machines?
Yes, the [Node Exporter](https://github.com/prometheus/node_exporter) exposes Yes, the [Node Exporter](https://github.com/prometheus/node_exporter) exposes
an extensive set of machine-level metrics on Linux such as CPU usage, memory, an extensive set of machine-level metrics on Linux and other Unix systems such
disk utilization, filesystem fullness and network bandwidth. as CPU usage, memory, disk utilization, filesystem fullness and network
bandwidth.
### Can I monitor network devices?
Yes, the [SNMP Exporter](https://github.com/prometheus/snmp_exporter) allows
monitoring of devices that support SNMP.
### Can I monitor batch jobs? ### Can I monitor batch jobs?
...@@ -159,12 +166,6 @@ jobs. ...@@ -159,12 +166,6 @@ jobs.
See [exporters for third-party systems](/docs/instrumenting/exporters/). See [exporters for third-party systems](/docs/instrumenting/exporters/).
### Which Java client should I use?
New users are advised to use the
[simpleclient](https://github.com/prometheus/client_java/tree/master/simpleclient).
For more information, see the [comparison](https://github.com/prometheus/client_java/wiki).
### Can I monitor JVM applications via JMX? ### Can I monitor JVM applications via JMX?
Yes, for applications that you cannot instrument directly with the Java client Yes, for applications that you cannot instrument directly with the Java client
...@@ -233,9 +234,9 @@ you an idea, here are some results from benchmarks: ...@@ -233,9 +234,9 @@ you an idea, here are some results from benchmarks:
sustained an ingestion rate of 34k samples per second, belonging to sustained an ingestion rate of 34k samples per second, belonging to
170k time series, scraped from 600 targets. 170k time series, scraped from 600 targets.
* On a modern server with 64GiB RAM and SSD, Prometheus sustained an * On a modern server with 64GiB RAM, 32 CPU cores, and SSD, Prometheus
ingestion rate of 340k samples per second, belonging to 2M time sustained an ingestion rate of 525k samples per second, belonging to 1.4M
series, scraped from 1800 targets. time series, scraped from 1650 targets.
In both cases, there were no obvious bottlenecks. Various stages of the In both cases, there were no obvious bottlenecks. Various stages of the
processing pipelines reached their limits more or less at the same processing pipelines reached their limits more or less at the same
...@@ -245,3 +246,21 @@ Running out of inodes is highly unlikely in a usual set-up. There is a ...@@ -245,3 +246,21 @@ Running out of inodes is highly unlikely in a usual set-up. There is a
possible downside: If you want to delete Prometheus's storage possible downside: If you want to delete Prometheus's storage
directory, you will notice that some file systems are very slow when directory, you will notice that some file systems are very slow when
deleting files. deleting files.
### Why don't the Prometheus server components support TLS or authentication? Can I add those?
While TLS and authentication are frequently requested features, we have
intentionally not implemented them in any of Prometheus's server-side
components. There are so many different options and parameters for both (10+
options for TLS alone) that we have decided to focus on building the best
monitoring system possible rather than supporting fully generic TLS and
authentication solutions in every server component.
If you need TLS or authentication, we recommend putting a reverse proxy in
front of Prometheus. See for example [Adding Basic Auth to Prometheus with
Nginx](http://www.robustperception.io/adding-basic-auth-to-prometheus-with-nginx/).
Note that this applies only to inbound connections. Prometheus does support
[scraping TLS- and auth-enabled
targets](/docs/operating/configuration/#%3Cscrape_config%3E), and other
Prometheus components that create outbound connections have similar support.
...@@ -13,16 +13,15 @@ series data. ...@@ -13,16 +13,15 @@ series data.
## Downloading and running Prometheus ## Downloading and running Prometheus
[Download the latest release](https://github.com/prometheus/prometheus/releases) [Download the latest release](/download) of Prometheus for your platform, then
of Prometheus for your platform, then extract and run it: extract and run it:
``` ```
tar xvfz prometheus-*.tar.gz tar xvfz prometheus-*.tar.gz
cd prometheus-* cd prometheus-*
./prometheus
``` ```
It should fail to start, complaining about the absence of a configuration file. Before starting Prometheus, let's configure it.
## Configuring Prometheus to monitor itself ## Configuring Prometheus to monitor itself
...@@ -37,8 +36,6 @@ Prometheus configuration as a file named `prometheus.yml`: ...@@ -37,8 +36,6 @@ Prometheus configuration as a file named `prometheus.yml`:
``` ```
global: global:
scrape_interval: 15s # By default, scrape targets every 15 seconds. scrape_interval: 15s # By default, scrape targets every 15 seconds.
evaluation_interval: 15s # By default, scrape targets every 15 seconds.
# scrape_timeout is set to the global default (10s).
# Attach these labels to any time series or alerts when communicating with # Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager). # external systems (federation, remote storage, Alertmanager).
...@@ -53,7 +50,6 @@ scrape_configs: ...@@ -53,7 +50,6 @@ scrape_configs:
# Override the global default and scrape targets from this job every 5 seconds. # Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s scrape_interval: 5s
scrape_timeout: 10s
target_groups: target_groups:
- targets: ['localhost:9090'] - targets: ['localhost:9090']
...@@ -89,6 +85,10 @@ Blindly setting `GOMAXPROCS` to a high value can be ...@@ -89,6 +85,10 @@ Blindly setting `GOMAXPROCS` to a high value can be
counterproductive. See the relevant [Go counterproductive. See the relevant [Go
FAQs](http://golang.org/doc/faq#Why_no_multi_CPU). FAQs](http://golang.org/doc/faq#Why_no_multi_CPU).
Note that Prometheus by default uses around 3GB in memory. If you have a
smaller machine, you can tune Prometheus to use less memory. For details,
see the [memory usage documentation](/docs/operating/storage/#memory-usage).
## Using the expression browser ## Using the expression browser
Let us try looking at some data that Prometheus has collected about itself. To Let us try looking at some data that Prometheus has collected about itself. To
...@@ -190,7 +190,6 @@ scrape_configs: ...@@ -190,7 +190,6 @@ scrape_configs:
# Override the global default and scrape targets from this job every 5 seconds. # Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s scrape_interval: 5s
scrape_timeout: 10s
target_groups: target_groups:
- targets: ['localhost:8080', 'localhost:8081'] - targets: ['localhost:8080', 'localhost:8081']
...@@ -238,8 +237,7 @@ look like this: ...@@ -238,8 +237,7 @@ look like this:
``` ```
global: global:
scrape_interval: 15s # By default, scrape targets every 15 seconds. scrape_interval: 15s # By default, scrape targets every 15 seconds.
evaluation_interval: 15s # By default, scrape targets every 15 seconds. evaluation_interval: 15s # Evaluate rules every 15 seconds.
# scrape_timeout is set to the global default (10s).
# Attach these extra labels to all timeseries collected by this Prometheus instance. # Attach these extra labels to all timeseries collected by this Prometheus instance.
external_labels: external_labels:
...@@ -253,7 +251,6 @@ scrape_configs: ...@@ -253,7 +251,6 @@ scrape_configs:
# Override the global default and scrape targets from this job every 5 seconds. # Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s scrape_interval: 5s
scrape_timeout: 10s
target_groups: target_groups:
- targets: ['localhost:9090'] - targets: ['localhost:9090']
...@@ -262,7 +259,6 @@ scrape_configs: ...@@ -262,7 +259,6 @@ scrape_configs:
# Override the global default and scrape targets from this job every 5 seconds. # Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s scrape_interval: 5s
scrape_timeout: 10s
target_groups: target_groups:
- targets: ['localhost:8080', 'localhost:8081'] - targets: ['localhost:8080', 'localhost:8081']
......
...@@ -7,39 +7,32 @@ sort_rank: 2 ...@@ -7,39 +7,32 @@ sort_rank: 2
## Using pre-compiled binaries ## Using pre-compiled binaries
We provide precompiled binaries for released versions for most Prometheus We provide precompiled binaries for most official Prometheus components.
components. These may be found under the "Releases" tab of the respective Check out the [download section](/download) for a list of all available
GitHub repositories. For example, for the main Prometheus server, binary versions.
releases are available at
[https://github.com/prometheus/prometheus/releases](https://github.com/prometheus/prometheus/releases).
Debian and RPM packages are being worked on.
## From source ## From source
For building Prometheus from source, see the relevant [`README.md` For building Prometheus components from source, see the `Makefile` targets in
section](https://github.com/prometheus/prometheus/blob/master/README.md#use-make). the respective repository.
Note that this documentation (as published on NOTE: **Note:** The documentation on this website refers to the latest stable
[prometheus.io](http://prometheus.io)) refers to the latest production release (excluding pre-releases). The branch
release. The head of the [next-release](https://github.com/prometheus/docs/compare/next-release) refers
[prometheus/docs](https://github.com/prometheus/docs) GitHub to unreleased changes that are in master branches of source repos.
repository refers to the (possibly not yet released) head of the
[prometheus/prometheus](https://github.com/prometheus/prometheus) (and
other) repositories.
## Using Docker ## Using Docker
All Prometheus services are available as Docker images under the All Prometheus services are available as Docker images under the
[prom](https://hub.docker.com/u/prom/) organization. [prom](https://hub.docker.com/u/prom/) organization.
Running Prometheus on Docker is as simple as Running Prometheus on Docker is as simple as `docker run -p 9090:9090
`docker run -p 9090:9090 prom/prometheus`. This starts Prometheus with prom/prometheus`. This starts Prometheus with a sample configuration and
a sample configuration and exposes it on port 9090. exposes it on port 9090.
The Prometheus image uses a volume to store the actual metrics. For The Prometheus image uses a volume to store the actual metrics. For
production deployments it is highly recommended to use the production deployments it is highly recommended to use the
[Data Volume Container](https://docs.docker.com/userguide/dockervolumes/#creating-and-mounting-a-data-volume-container) [Data Volume Container](https://docs.docker.com/engine/userguide/containers/dockervolumes/#creating-and-mounting-a-data-volume-container)
pattern to ease managing the data on Prometheus upgrades. pattern to ease managing the data on Prometheus upgrades.
To provide your own configuration, there are several options. Here are To provide your own configuration, there are several options. Here are
......
...@@ -13,13 +13,11 @@ with Prometheus. ...@@ -13,13 +13,11 @@ with Prometheus.
## Blogs ## Blogs
* This site has its own [blog](http://prometheus.io/blog/). * This site has its own [blog](/blog/).
* [SoundCloud's blog post announcing Prometheus](https://developers.soundcloud.com/blog/prometheus-monitoring-at-soundcloud) * [SoundCloud's blog post announcing Prometheus](https://developers.soundcloud.com/blog/prometheus-monitoring-at-soundcloud)
– a more elaborate overview than the one given on this site. – a more elaborate overview than the one given on this site.
* Prometheus-related posts on the * Prometheus-related posts on the
[Robust Perception blog](http://www.robustperception.io/tag/prometheus/). [Robust Perception blog](http://www.robustperception.io/tag/prometheus/).
* The [monitoring series](http://www.boxever.com/tag/monitoring) on Boxever's
tech blog.
## Tutorials ## Tutorials
...@@ -34,10 +32,11 @@ with Prometheus. ...@@ -34,10 +32,11 @@ with Prometheus.
## Recorded talks ## Recorded talks
* [Prometheus: A Next-Generation Monitoring System](https://www.usenix.org/conference/srecon15europe/program/presentation/rabenstein) – Julius Volz and Björn Rabenstein at SREcon15 Europe, Dublin. * [Prometheus: A Next-Generation Monitoring System](https://www.usenix.org/conference/srecon15europe/program/presentation/rabenstein) – Julius Volz and Björn Rabenstein at SREcon15 Europe, Dublin.
* [Prometheus: A Next-Generation Monitoring System](https://www.youtube.com/watch?v=cwRmXqXKGtk) - Brian Brazil at FOSDEM 2016 ([slides](http://www.slideshare.net/brianbrazil/prometheus-a-next-generation-monitoring-system-fosdem-2016)).
* [What is your application doing right now?](http://youtu.be/Z0LlilNpX1U) – Matthias Gruter, Transmode, at DevOps Stockholm Meetup. * [What is your application doing right now?](http://youtu.be/Z0LlilNpX1U) – Matthias Gruter, Transmode, at DevOps Stockholm Meetup.
* [Prometheus workshop](https://vimeo.com/131581353) – Jamie Wilkinson at Monitorama PDX 2015 ([slides](https://docs.google.com/presentation/d/1X1rKozAUuF2MVc1YXElFWq9wkcWv3Axdldl8LOH9Vik/edit)). * [Prometheus workshop](https://vimeo.com/131581353) – Jamie Wilkinson at Monitorama PDX 2015 ([slides](https://docs.google.com/presentation/d/1X1rKozAUuF2MVc1YXElFWq9wkcWv3Axdldl8LOH9Vik/edit)).
* [Monitoring Hadoop with Prometheus](https://www.youtube.com/watch?v=qs2sqOLNGtw) – Brian Brazil at the Hadoop User Group Ireland ([slides](http://www.slideshare.net/brianbrazil/monitoring-hadoop-with-prometheus-hadoop-user-group-ireland-december-2015)). * [Monitoring Hadoop with Prometheus](https://www.youtube.com/watch?v=qs2sqOLNGtw) – Brian Brazil at the Hadoop User Group Ireland ([slides](http://www.slideshare.net/brianbrazil/monitoring-hadoop-with-prometheus-hadoop-user-group-ireland-december-2015)).
* In German: [Monitoring mit Prometheus](https://entropia.de/GPN15:Monitoring_mit_Prometheus) – Michael Stapelberg at Gulaschprogrammiernacht 15. * In German: [Monitoring mit Prometheus](https://media.ccc.de/v/eh16-43-monitoring_mit_prometheus#video&t=2804) – Michael Stapelberg at [Easterhegg 2016](https://eh16.easterhegg.eu/).
## Presentation slides ## Presentation slides
......
...@@ -11,7 +11,7 @@ sort_rank: 1 ...@@ -11,7 +11,7 @@ sort_rank: 1
monitoring and alerting toolkit originally built at monitoring and alerting toolkit originally built at
[SoundCloud](http://soundcloud.com). Since its inception in 2012, many [SoundCloud](http://soundcloud.com). Since its inception in 2012, many
companies and organizations have adopted Prometheus, and the project has a very companies and organizations have adopted Prometheus, and the project has a very
active developer and user community. It is now a standalone open source project active developer and user [community](/community). It is now a standalone open source project
and maintained independently of any company. and maintained independently of any company.
For a more elaborate overview, see the resources linked from the For a more elaborate overview, see the resources linked from the
......
...@@ -54,7 +54,7 @@ global: ...@@ -54,7 +54,7 @@ global:
# How long until a scrape request times out. # How long until a scrape request times out.
[ scrape_timeout: <duration> | default = 10s ] [ scrape_timeout: <duration> | default = 10s ]
# How frequently to evaluate rules by default. # How frequently to evaluate rules.
[ evaluation_interval: <duration> | default = 1m ] [ evaluation_interval: <duration> | default = 1m ]
# The labels to add to any time series or alerts when communicating with # The labels to add to any time series or alerts when communicating with
...@@ -169,6 +169,10 @@ kubernetes_sd_configs: ...@@ -169,6 +169,10 @@ kubernetes_sd_configs:
serverset_sd_configs: serverset_sd_configs:
[ - <serverset_sd_config> ... ] [ - <serverset_sd_config> ... ]
# List of AirBnB's Nerve service discovery configurations.
nerve_sd_configs:
[ - <nerve_sd_config> ... ]
# List of EC2 service discovery configurations. # List of EC2 service discovery configurations.
ec2_sd_configs: ec2_sd_configs:
[ - <ec2_sd_config> ... ] [ - <ec2_sd_config> ... ]
...@@ -237,8 +241,8 @@ A DNS-SD configuration allows specifying a set of DNS record names which ...@@ -237,8 +241,8 @@ A DNS-SD configuration allows specifying a set of DNS record names which
are periodically queried to discover a list of targets (host-port pairs). The are periodically queried to discover a list of targets (host-port pairs). The
DNS servers to be contacted are read from `/etc/resolv.conf`. DNS servers to be contacted are read from `/etc/resolv.conf`.
During the [relabeling phase](#target-relabeling-relabel_config), the meta During the [relabeling phase](#relabel_config), the meta
label `__meta_dns_srv_name` is available on each target and is set to the SRV label `__meta_dns_name` is available on each target and is set to the SRV
record name that produced the discovered target. record name that produced the discovered target.
``` ```
...@@ -295,9 +299,9 @@ services: ...@@ -295,9 +299,9 @@ services:
``` ```
Note that the IP number and port used to scrape the targets is assembled as Note that the IP number and port used to scrape the targets is assembled as
`<__meta_consul_address>:<__meta_consul_service_port`. However, in some `<__meta_consul_address>:<__meta_consul_service_port>`. However, in some
Consul setups, the relevant address is in `__meta_consul_service_address`. Consul setups, the relevant address is in `__meta_consul_service_address`.
In those cases, you can use the [relabel](#target-relabeling-relabel_config) In those cases, you can use the [relabel](#relabel_config)
feature to replace the special `__address__` label. feature to replace the special `__address__` label.
### `<kubernetes_sd_config>` ### `<kubernetes_sd_config>`
...@@ -372,7 +376,7 @@ tls_config: ...@@ -372,7 +376,7 @@ tls_config:
[ retry_interval: <duration> | default = 1s ] [ retry_interval: <duration> | default = 1s ]
``` ```
### `<marathon_sd_configs>` ### `<marathon_sd_config>`
CAUTION: Marathon SD is in beta: breaking changes to configuration are still CAUTION: Marathon SD is in beta: breaking changes to configuration are still
likely in future releases. likely in future releases.
...@@ -438,6 +442,29 @@ paths: ...@@ -438,6 +442,29 @@ paths:
Serverset data must be in the JSON format, the Thrift format is not currently supported. Serverset data must be in the JSON format, the Thrift format is not currently supported.
### `<nerve_sd_config>`
Nerve SD configurations allow retrieving scrape targets from [AirBnB's Nerve]
(https://github.com/airbnb/nerve) which are stored in
[Zookeeper](https://zookeeper.apache.org/).
The following meta labels are available on targets during relabeling:
* `__meta_nerve_path`: the full path to the emdpoint node in Zookeeper
* `__meta_nerve_endpoint_host`: the host of the endpoint
* `__meta_nerve_endpoint_port`: the port of the endpoint
* `__meta_nerve_endpoint_name`: the name of the endpoint
```
# The Zookeeper servers.
servers:
- <host>
# Paths can point to a single service, or the root of a tree of services.
paths:
- <string>
[ timeout: <duration> | default = 10s ]
```
### `<ec2_sd_config>` ### `<ec2_sd_config>`
CAUTION: EC2 SD is in beta: breaking changes to configuration are still CAUTION: EC2 SD is in beta: breaking changes to configuration are still
...@@ -449,10 +476,16 @@ the public IP address with relabeling. ...@@ -449,10 +476,16 @@ the public IP address with relabeling.
The following meta labels are available on targets during relabeling: The following meta labels are available on targets during relabeling:
* `__meta_ec2_availability_zone`: the availability zone in which the instance is running
* `__meta_ec2_instance_id`: the EC2 instance ID * `__meta_ec2_instance_id`: the EC2 instance ID
* `__meta_ec2_public_ip`: the public IP address of the instance
* `__meta_ec2_private_ip`: the private IP address of the instance, if present * `__meta_ec2_private_ip`: the private IP address of the instance, if present
* `__meta_ec2_public_dns_name`: the public DNS name of the instance, if available
* `__meta_ec2_public_ip`: the public IP address of the instance, if available
* `__meta_ec2_subnet_id`: comma separated list of subnets IDs in which the instance is running, if available
* `__meta_ec2_tag_<tagkey>`: each tag value of the instance * `__meta_ec2_tag_<tagkey>`: each tag value of the instance
* `__meta_ec2_vpc_id`: the ID of the VPC in which the instance is running, if available
See below for the configuration options for EC2 discovery: See below for the configuration options for EC2 discovery:
...@@ -503,7 +536,7 @@ As a fallback, the file contents are also re-read periodically at the specified ...@@ -503,7 +536,7 @@ As a fallback, the file contents are also re-read periodically at the specified
refresh interval. refresh interval.
Each target has a meta label `__meta_filepath` during the Each target has a meta label `__meta_filepath` during the
[relabeling phase](#target-relabeling-relabel_config). Its value is set to the [relabeling phase](#relabel_config). Its value is set to the
filepath from which the target was extracted. filepath from which the target was extracted.
``` ```
...@@ -579,7 +612,8 @@ the `replace`, `keep`, `drop` and `labelmap` actions. The regex is fully anchore ...@@ -579,7 +612,8 @@ the `replace`, `keep`, `drop` and `labelmap` actions. The regex is fully anchore
* `replace`: Match `regex` against the concatenated `source_labels`. Then, set * `replace`: Match `regex` against the concatenated `source_labels`. Then, set
`target_label` to `replacement`, with match group references `target_label` to `replacement`, with match group references
(`${1}`, `${2}`, ...) in `replacement` substituted by their value. (`${1}`, `${2}`, ...) in `replacement` substituted by their value. If `regex`
does not match, no replacement takes place.
* `keep`: Drop targets for which `regex` does not match the concatenated `source_labels`. * `keep`: Drop targets for which `regex` does not match the concatenated `source_labels`.
* `drop`: Drop targets for which `regex` matches the concatenated `source_labels`. * `drop`: Drop targets for which `regex` matches the concatenated `source_labels`.
* `hashmod`: Set `target_label` to the `modulus` of a hash of the concatenated `source_labels`. * `hashmod`: Set `target_label` to the `modulus` of a hash of the concatenated `source_labels`.
...@@ -587,7 +621,7 @@ the `replace`, `keep`, `drop` and `labelmap` actions. The regex is fully anchore ...@@ -587,7 +621,7 @@ the `replace`, `keep`, `drop` and `labelmap` actions. The regex is fully anchore
to label names given by `replacement` with match group references to label names given by `replacement` with match group references
(`${1}`, `${2}`, ...) in `replacement` substituted by their value. (`${1}`, `${2}`, ...) in `replacement` substituted by their value.
### `<metric_relabel_configs>` ### `<metric_relabel_config>`
Metric relabeling is applied to samples as the last step before ingestion. It Metric relabeling is applied to samples as the last step before ingestion. It
has the same configuration format and actions as target relabeling. Metric has the same configuration format and actions as target relabeling. Metric
......
...@@ -175,7 +175,7 @@ the number of chunks waiting for persistence in relation to the ...@@ -175,7 +175,7 @@ the number of chunks waiting for persistence in relation to the
chunks in memory exceeds the `storage.local.memory-chunks` value (if at all, chunks in memory exceeds the `storage.local.memory-chunks` value (if at all,
and only if there is a minimum number of chunks waiting for persistence so that and only if there is a minimum number of chunks waiting for persistence so that
faster persisting of chunks can help at all). The score is between 0 and 1, faster persisting of chunks can help at all). The score is between 0 and 1,
where 1 corresponds to the highest unrgency. Depending on the score, Prometheus where 1 corresponds to the highest urgency. Depending on the score, Prometheus
will write to disk more frequently. Should the score ever pass the threshold will write to disk more frequently. Should the score ever pass the threshold
of 0.8, Prometheus enters “rushed mode” (which you can see in the logs). In of 0.8, Prometheus enters “rushed mode” (which you can see in the logs). In
rushed mode, the following strategies are applied to speed up persisting chunks: rushed mode, the following strategies are applied to speed up persisting chunks:
......
...@@ -221,10 +221,9 @@ Two rules of thumb: ...@@ -221,10 +221,9 @@ Two rules of thumb:
## What can I do if my client library does not support the metric type I need? ## What can I do if my client library does not support the metric type I need?
Implement it! [Code contributions are welcome](/community/). In Implement it! [Code contributions are welcome](/community/). In general, we
general, we expect histograms to be more urgently needed than expect histograms to be more urgently needed than summaries. Histograms are
summaries. Histograms are also easier to implement in a client also easier to implement in a client library, so we recommend to implement
library, so we recommend to implement histograms first, if in histograms first, if in doubt. The reason why some libraries offer summaries
doubt. The reason why some libraries offer summaries but not but not histograms (such as the Ruby client) is that histograms are a more
histograms (the Ruby client and the legacy Java client) is that recent feature of Prometheus.
histograms are a more recent feature of Prometheus.
...@@ -202,7 +202,7 @@ the value can go down, it is a gauge. ...@@ -202,7 +202,7 @@ the value can go down, it is a gauge.
Counters can only go up (and reset, such as when a process restarts). They are Counters can only go up (and reset, such as when a process restarts). They are
useful for accumulating the number of events, or the amount of something at useful for accumulating the number of events, or the amount of something at
each event. For example, the total number of HTTP requests, or the total number of each event. For example, the total number of HTTP requests, or the total number of
of bytes sent in HTTP requests. Raw counters are rarely useful. Use the bytes sent in HTTP requests. Raw counters are rarely useful. Use the
`rate()` function to get the per-second rate at which they are increasing. `rate()` function to get the per-second rate at which they are increasing.
Gauges can be set, go up, and go down. They are useful for snapshots of state, Gauges can be set, go up, and go down. They are useful for snapshots of state,
...@@ -230,7 +230,7 @@ For code which is performance-critical or called more than 100k times a second ...@@ -230,7 +230,7 @@ For code which is performance-critical or called more than 100k times a second
inside a given process, you may wish to take some care as to how many metrics inside a given process, you may wish to take some care as to how many metrics
you update. you update.
A Java Simpleclient counter takes A Java counter takes
[12-17ns](https://github.com/prometheus/client_java/blob/master/benchmark/README.md) [12-17ns](https://github.com/prometheus/client_java/blob/master/benchmark/README.md)
to increment depending on contention. Other languages will have similar to increment depending on contention. Other languages will have similar
performance. If that amount of time is significant for your inner loop, limit performance. If that amount of time is significant for your inner loop, limit
...@@ -250,5 +250,5 @@ to correctly handle them. To avoid this, export `0` (or `NaN`, if `0` ...@@ -250,5 +250,5 @@ to correctly handle them. To avoid this, export `0` (or `NaN`, if `0`
would be misleading) for any time series you know may exist in would be misleading) for any time series you know may exist in
advance. advance.
Most Prometheus client libraries (including Go and Java Simpleclient) will Most Prometheus client libraries (including Go, Java, and Python) will
automatically export a `0` for you for metrics with no labels. automatically export a `0` for you for metrics with no labels.
---
title: When to use the Pushgateway
sort_rank: 7
---
# When to use the Pushgateway
The Pushgateway is an intermediary service which allows you to push metrics
from jobs which cannot be scraped. For details, see [Pushing metrics](/docs/instrumenting/pushing/).
## Should I be using the Pushgateway?
**We only recommend using the Pushgateway in certain limited cases.** There are
several pitfalls when blindly using the Pushgateway instead of Prometheus's
usual pull model for general metrics collection:
* When monitoring multiple instances through a single Pushgateway, the
Pushgateway becomes both a single point of failure and a potential
bottleneck.
* You lose Prometheus's automatic instance health monitoring via the `up`
metric (generated on every scrape).
* The Pushgateway never forgets series pushed to it and will expose them to
Prometheus forever unless those series are manually deleted via the
Pushgateway's API.
The latter point is especially relevant when multiple instances of a job
differentiate their metrics in the Pushgateway via an `instance` label or
similar. Metrics for an instance will then remain in the Pushgateway even if
the originating instance is renamed or removed. This is because the lifecycle
of the Pushgateway as a metrics cache is fundamentally separate from the
lifecycle of the processes that push metrics to it. Contrast this to
Prometheus's usual pull-style monitoring: when an instance disappears
(intentional or not), its metrics will automatically disappear along with it.
When using the Pushgateway, this is not the case, and you would now have to
delete any stale metrics manually or automate this lifecycle synchronization
yourself.
**Usually, the only valid use case for the Pushgateway is for capturing the
outcome of a service-level batch job**. A "service-level" batch job is one
which is not semantically related to a specific machine or job instance (for
example, a batch job that deletes a number of users for an entire service).
Such a job's metrics should not include a machine or instance label to decouple
the lifecycle of specific machines or instances from the pushed metrics. This
decreases the burden for managing stale metrics in the Pushgateway. See also
the [best practices for monitoring batch jobs](https://prometheus.io/docs/practices/instrumentation/#batch-jobs).
## Alternative strategies
If an inbound firewall or NAT is preventing you from pulling metrics from
targets, consider moving the Prometheus server behind the network barrier as
well. We generally recommend running Prometheus servers on the same network as
the monitored instances.
For batch jobs that are related to a machine (such as automatic
security update cronjobs or configuration management client runs), expose the
resulting metrics using the [Node Exporter's](https://github.com/prometheus/node_exporter)
textfile module instead of the Pushgateway.
...@@ -158,6 +158,16 @@ for quantiles located in the lowest bucket. ...@@ -158,6 +158,16 @@ for quantiles located in the lowest bucket.
If `b` contains fewer than two buckets, `NaN` is returned. For φ < 0, `-Inf` is If `b` contains fewer than two buckets, `NaN` is returned. For φ < 0, `-Inf` is
returned. For φ > 1, `+Inf` is returned. returned. For φ > 1, `+Inf` is returned.
## `holt_winters()`
`holt_winters(v range-vector, sf scalar, tf scalar)` produces a smoothed value
for time series based on the range in `v`. The lower the smoothing factor `sf`,
the more importance is given to old data. The higher the trend factor `tf`, the
more trends in the data is considered. Both `sf` and `tf` must be between 0 and
1.
`holt_winters` should only be used with gauges.
## `increase()` ## `increase()`
`increase(v range-vector)` calculates the increase in the `increase(v range-vector)` calculates the increase in the
......
...@@ -102,17 +102,20 @@ matching behavior: ...@@ -102,17 +102,20 @@ matching behavior:
**One-to-one** finds a unique pair of entries from each side of the operation. **One-to-one** finds a unique pair of entries from each side of the operation.
In the default case, that is an operation following the format `vector1 <operator> vector2`. In the default case, that is an operation following the format `vector1 <operator> vector2`.
Two entries match if they have the exact same set of labels and corresponding values. Two entries match if they have the exact same set of labels and corresponding values.
The `on` keyword allows reducing the set of considered labels to a provided list: The `ignoring` keyword allows ignoring certain labels when matching, while the
`on` keyword allows reducing the set of considered labels to a provided list:
<vector expr> <bin-op> ignoring(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) <vector expr>
Example input: Example input:
method:http_errors:rate5m{source="internal", method="get", code="500"} 24 method_code:http_errors:rate5m{method="get", code="500"} 24
method:http_errors:rate5m{source="external", method="get", code="404"} 30 method_code:http_errors:rate5m{method="get", code="404"} 30
method:http_errors:rate5m{source="internal", method="put", code="501"} 3 method_code:http_errors:rate5m{method="put", code="501"} 3
method:http_errors:rate5m{source="internal", method="post", code="500"} 6 method_code:http_errors:rate5m{method="post", code="500"} 6
method:http_errors:rate5m{source="external", method="post", code="404"} 21 method_code:http_errors:rate5m{method="post", code="404"} 21
method:http_requests:rate5m{method="get"} 600 method:http_requests:rate5m{method="get"} 600
method:http_requests:rate5m{method="del"} 34 method:http_requests:rate5m{method="del"} 34
...@@ -120,35 +123,41 @@ Example input: ...@@ -120,35 +123,41 @@ Example input:
Example query: Example query:
method:http_errors:rate5m{code="500"} / on(method) method:http_requests:rate5m method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
This returns a result vector containing the fraction of HTTP requests with status code This returns a result vector containing the fraction of HTTP requests with status code
of 500 for each method, as measured over the last 5 minutes. Without `on(method)` there of 500 for each method, as measured over the last 5 minutes. Without `ignoring(code)` there
would have been no match as the metrics do not share the same set of labels. would have been no match as the metrics do not share the same set of labels.
The entries with methods `put` and `del` have no match and will not show up in the result: The entries with methods `put` and `del` have no match and will not show up in the result:
{method="get"} 0.04 // 24 / 600 {method="get"} 0.04 // 24 / 600
{method="post"} 0.1 // 12 / 120 {method="post"} 0.1 // 12 / 120
**Many-to-one** and **one-to-many** matchings refer to the case where each vector element on **Many-to-one** and **one-to-many** matchings refer to the case where each vector element on
the "one"-side can match with multiple elements on the "many"-side. This has to the "one"-side can match with multiple elements on the "many"-side. This has to
be explicitly requested using the `group_left` or `group_right` modifier, where be explicitly requested using the `group_left` or `group_right` modifier, where
left/right determines which vector has the higher cardinality. left/right determines which vector has the higher cardinality.
<vector expr> <bin-op> ignoring(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> ignoring(<label list>) group_right(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr>
The label list provided with the group modifier contains additional labels from the "many"-side The label list provided with the group modifier contains additional labels from
to be included in the result metrics. A label can only appear in one of the lists. Every time the "one"-side to be included in the result metrics. For `on` a label can only
series of the result vector must be uniquely identifiable by the labels from both lists combined. appear in one of the lists. Every time series of the result vector must be
uniquely identifiable.
_Grouping modifiers can only be used for [comparison/filtering](#comparison-/-filter-binary-operators) _Grouping modifiers can only be used for
and [arithmetic](#arithmetic-binary-operators) operations as `and` and `or` operations [comparison](#comparison-binary-operators) and
match with all possible entries in the right vector by default._ [arithmetic](#arithmetic-binary-operators). Operations as `and`, `unless` and
`or` operations match with all possible entries in the right vector by
default._
Example query: Example query:
method:http_errors:rate5m / on(method) group_left(code,source) method:http_requests:rate5m method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m
In this case the left vector contains more than one entry per `method` label value. Thus, In this case the left vector contains more than one entry per `method` label value. Thus,
we indicate this using `group_left`. To ensure that the result vector entries are unique, additional we indicate this using `group_left`. To ensure that the result vector entries are unique, additional
...@@ -156,14 +165,13 @@ labels have to be provided. Either `code` or `source` satisfy this requirement, ...@@ -156,14 +165,13 @@ labels have to be provided. Either `code` or `source` satisfy this requirement,
can be added for a more detailed result. The elements from the right side can be added for a more detailed result. The elements from the right side
are now matched with multiple elements with the same `method` label on the left: are now matched with multiple elements with the same `method` label on the left:
{source="internal", method="get", code="500"} 0.04 // 24 / 600 {method="get", code="500"} 0.04 // 24 / 600
{source="external", method="get", code="404"} 0.05 // 30 / 600 {method="get", code="404"} 0.05 // 30 / 600
{source="internal", method="post", code="500"} 0.1 // 12 / 120 {method="post", code="500"} 0.1 // 12 / 120
{source="external", method="post", code="404"} 0.175 // 21 / 120 {method="post", code="404"} 0.175 // 21 / 120
_Many-to-one and one-to-many matching are advanced use cases that should be carefully considered. _Many-to-one and one-to-many matching are advanced use cases that should be carefully considered.
Often a proper use of `on(<labels>)` provides the desired outcome._ Often a proper use of `ignoring(<labels>)` provides the desired outcome._
## Aggregation operators ## Aggregation operators
...@@ -187,7 +195,7 @@ or preserve distinct dimensions by including a `without` or `by` clause. ...@@ -187,7 +195,7 @@ or preserve distinct dimensions by including a `without` or `by` clause.
`without` removes the listed labels from the result vector, while all other `without` removes the listed labels from the result vector, while all other
labels are preserved the output. `by` does the opposite and drops labels that labels are preserved the output. `by` does the opposite and drops labels that
are not listed in the `by` clause, even if their label values are identical are not listed in the `by` clause, even if their label values are identical
between all elements of the vector. The `keep_common` clause allows to keep between all elements of the vector. The `keep_common` clause allows keeping
those extra labels (labels that are identical between elements, but not in the those extra labels (labels that are identical between elements, but not in the
`by` clause). `by` clause).
......
--- ---
title: Grafana title: Grafana
sort_rank: 7 sort_rank: 2
--- ---
# Grafana support for Prometheus # Grafana support for Prometheus
......
--- ---
title: PromDash title: PromDash
sort_rank: 2 sort_rank: 7
toc: full-width
--- ---
# PromDash # PromDash
CAUTION: <b>NOTE:</b> We recommend [Grafana](/docs/visualization/grafana) for
visualization of Prometheus metrics nowadays, as it has native Prometheus
support and is widely adopted and powerful. There will be less focus on
PromDash development in the future.
## Overview ## Overview
PromDash is a browser-based dashboard builder for Prometheus. It is a PromDash is a browser-based dashboard builder for Prometheus. It is a
......
...@@ -39,7 +39,7 @@ If functions are used in a pipeline, the pipeline value is passed as the last ar ...@@ -39,7 +39,7 @@ If functions are used in a pipeline, the pipeline value is passed as the last ar
| Name | Arguments | Returns | Notes | | Name | Arguments | Returns | Notes |
| ------------- | ------------- | -------- | -------- | | ------------- | ------------- | -------- | -------- |
| query | query string | []sample | Queries the databases, does not support returning range vectors. | | query | query string | []sample | Queries the database, does not support returning range vectors. |
| first | []sample | sample | Equivalent to `index a 0` | | first | []sample | sample | Equivalent to `index a 0` |
| label | label, sample | string | Equivalent to `index sample.Labels label` | | label | label, sample | string | Equivalent to `index sample.Labels label` |
| value | sample | float64 | Equivalent to `sample.Value` | | value | sample | float64 | Equivalent to `sample.Value` |
......
---
title: Download
---
<div class="row">
<div class="col-md-12 doc-content download">
<h1>Download</h1>
<div class="toc toc-right">
<ul>
<% Downloads.repositories.each do |repo| %>
<li><a href="#<%= repo.name %>"><%= repo.name %></a></li>
<% end %>
</ul>
</div>
<p>
We provide precompiled binaries and <a href="https://hub.docker.com/r/prom/">Docker images</a>
for most officially maintained Prometheus components. If a component is
not listed here, check the respective repository on Github for further
instructions.
</p>
<p>
There is also a constantly growing number of independently maintained
exporters listed at <a href="/docs/instrumenting/exporters/">Exporters
and integrations</a>.
</p>
<p>
After downloading a binary release suitable for your system, please follow
the <a href="/docs/introduction/getting_started/">installation instructions</a>.
</p>
<div class="alert alert-info" role="alert">
<strong>Work in progress!</strong>
We will provide more precompiled binary versions as well as checksums soon.
</div>
<div class="panel panel-default download-selection">
<div class="panel-body">
Operating system <%= dropdown(:os, Downloads.operating_systems, :popular, popular: %w(darwin linux windows)) %>
Architecture <%= dropdown(:arch, Downloads.architectures, :amd64) %>
</div>
</div>
<% Downloads.repositories.each do |repository| %>
<h2 id="<%= repository.name %>"><%= repository.name %></h2>
<p><%= repository.description %> <a href="<%= repository.url %>"><i class="fa fa-github"></i> <%= repository.full_name %></a></p>
<table class="table table-bordered downloads">
<% repository.releases.each do |release| %>
<thead>
<tr>
<td colspan="5">
<strong><%= release.name %></strong>
<%= %(<span class="label label-primary">Pre-release</span>) if release.prerelease %>
<small><a href="<%= release.url %>">Release notes</a></small>
</td>
</tr>
<tr class="first">
<th>File name</th>
<th>OS</th>
<th>Arch</th>
<th>Size</th>
<th>SHA256 Checksum</th>
</tr>
</thead>
<tbody>
<% release.assets.each do |asset| %>
<tr data-os="<%= asset.os %>" data-arch="<%= asset.arch %>">
<td class="filename"><a class="download" href="<%= asset.url %>"><%= asset.name %></a></td>
<td><%= asset.os %></td>
<td><%= asset.arch %></td>
<td><%= format_bytes asset.size %></td>
<td class="checksum">not available yet</td>
</tr>
<% end %>
</tbody>
<% end %>
</table>
<% end %>
</div>
</div>
--- ---
layout: jumbotron layout: jumbotron
--- ---
<div class="main"> <div class="jumbotron">
<div class="container">
<h1>From metrics to insight</h1>
<p class="subtitle">Power your metrics and alerting with a leading<br>open-source monitoring solution.</p>
<p>
<a class="btn btn-default btn-lg" href="/docs/introduction/getting_started/" role="button">Get Started</a>
<a class="btn btn-default btn-lg" href="/download" role="button">Download</a>
</p>
</div>
</div>
<div class="container">
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<h2><i class="fa fa-flask"></i> Data model</h2> <a href="/docs/concepts/data_model/">
<p class="desc">Prometheus implements a highly dimensional data model. Time series are identified by a metric name and a set of key-value pairs.</p> <h2><i class="fa fa-flask"></i> Dimensional data</h2>
<p><a class="btn btn-default" href="/docs/concepts/data_model/" role="button">View details &raquo;</a></p> <p>Prometheus implements a highly dimensional data model. Time series are identified by a metric name and a set of key-value pairs.</p>
</div> </a>
<div class="col-md-3"> </div>
<h2><i class="fa fa-search"></i> Query language</h2> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<p class="desc">A flexible query language allows slicing and dicing of collected time series data in order to generate ad-hoc graphs, tables, and alerts.</p> <a href="/docs/querying/basics/">
<p><a class="btn btn-default" href="/docs/querying/basics/" role="button">View details &raquo;</a></p> <h2><i class="fa fa-search"></i> Powerful queries</h2>
</div> <p>A flexible query language allows slicing and dicing of collected time series data in order to generate ad-hoc graphs, tables, and alerts.</p>
<div class="col-md-3"> </a>
<h2><i class="fa fa-line-chart"></i> Visualization</h2> </div>
<p class="desc">Prometheus has multiple modes for visualizing data: a built-in expression browser, a GUI-based dashboard builder, and a console template language.</p> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<p><a class="btn btn-default" href="/docs/visualization/promdash/" role="button">View details &raquo;</a></p> <a href="/docs/visualization/grafana/">
</div> <h2><i class="fa fa-line-chart"></i> Great visualization</h2>
<div class="col-md-3"> <p>Prometheus has multiple modes for visualizing data: a built-in expression browser, Grafana integration, and a console template language.</p>
<h2><i class="fa fa-database"></i> Storage</h2> </a>
<p class="desc">Prometheus stores time series in memory and on local disk in an efficient custom format. Scaling is achieved by functional sharding and federation.</p> </div>
<p><a class="btn btn-default" href="/docs/operating/storage/" role="button">View details &raquo;</a></p> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<a href="/docs/operating/storage/">
<h2><i class="fa fa-database"></i> Efficient storage</h2>
<p>Prometheus stores time series in memory and on local disk in an efficient custom format. Scaling is achieved by functional sharding and federation.</p>
</a>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<h2><i class="fa fa-cog"></i> Operation</h2> <a href="/docs/operating/configuration/">
<p class="desc">Each server is independent for reliability, relying only on local storage. Written in Go, all binaries are statically linked and easy to deploy.</p> <h2><i class="fa fa-cog"></i> Simple operation</h2>
<p><a class="btn btn-default" href="/docs/operating/configuration/" role="button">View details &raquo;</a></p> <p>Each server is independent for reliability, relying only on local storage. Written in Go, all binaries are statically linked and easy to deploy.</p>
</div> </a>
<div class="col-md-3"> </div>
<h2><i class="fa fa-code"></i> Client libraries</h2> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<p class="desc">Client libraries allow easy instrumentation of services. Currently, Go, Java, and Ruby are supported. Custom libraries are easy to implement.</p> <a href="/docs/alerting/rules/">
<p><a class="btn btn-default" href="/docs/instrumenting/clientlibs/" role="button">View details &raquo;</a></p> <h2><i class="fa fa-warning"></i> Precise alerting</h2>
</div> <p>Alerts are defined based on Prometheus's flexible query language and maintain dimensional information. An alertmanager handles notifications and silencing.</p>
<div class="col-md-3"> </a>
<h2><i class="fa fa-warning"></i> Alerting</h2> </div>
<p class="desc">Alerts are defined based on Prometheus's flexible query language and maintain dimensional information. An alertmanager handles notifications and silencing.</p> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<p><a class="btn btn-default" href="/docs/alerting/rules/" role="button">View details &raquo;</a></p> <a href="/docs/instrumenting/clientlibs/">
</div> <h2><i class="fa fa-code"></i> Many client libraries</h2>
<div class="col-md-3"> <p>Client libraries allow easy instrumentation of services. Currently, Go, Java, and Ruby are supported. Custom libraries are easy to implement.</p>
<h2><i class="fa fa-cloud-upload"></i> Exporters</h2> </a>
<p class="desc">Existing exporters allow bridging of third-party data into Prometheus. Examples: system statistics, as well as Docker, HAProxy, StatsD, and JMX metrics. </p> </div>
<p><a class="btn btn-default" href="/docs/instrumenting/exporters/" role="button">View details &raquo;</a></p> <div class="col-md-3 col-sm-6 col-xs-12 feature-item">
<a href="/docs/instrumenting/exporters/">
<h2><i class="fa fa-cloud-upload"></i> Many integrations</h2>
<p>Existing exporters allow bridging of third-party data into Prometheus. Examples: system statistics, as well as Docker, HAProxy, StatsD, and JMX metrics. </p>
</a>
</div> </div>
</div> </div>
<div class="container text-center">
<div class="row"> <div class="row top-hr">
<div class="col-md-12">
<hr> <hr>
<div class="col-md-12">Trusted by:</div>
</div> </div>
</div> </div>
<div class="container">
<div class="row quote">
<div class="col-md-12">
<p class="quote-text">
«Even though Borgmon remains internal to Google, the idea of treating time-series data as a data source for generating alerts is now accessible to everyone through those open source tools like Prometheus [...]»
</p>
<p class="quote-source">
<a href="http://shop.oreilly.com/product/0636920041528.do"><b>Site Reliability Engineering:</b> How Google Runs Production Systems</a> (O'Reilly Media)
</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<hr>
</div>
</div>
<div class="row open-source">
<div class="col-md-12">
<h1><i class="fa fa-github"></i> Open Source</h1>
<p>Prometheus is 100% open source and community-driven. All components are available under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2 License</a> on <a href="https://github.com/prometheus">GitHub</a>.</p>
<iframe src="https://ghbtns.com/github-btn.html?user=prometheus&repo=prometheus&type=star&count=true&size=large" frameborder="0" scrolling="0" class="github-stars"></iframe>
</div>
</div>
<div class="row">
<div class="col-md-12">
<hr>
</div>
</div>
<div class="container text-center">
<div class="row"> <div class="row">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <div class="col-md-12 trusted-by">Some of our users include:</div>
<a href="http://argus-sec.com/"><img src="assets/company-logos/Argus.png"/></a> </div>
</div> </div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="http://www.boxever.com/"><img src="assets/company-logos/Boxever.png"/></a> <div class="row">
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://argus-sec.com/"><img src="assets/company-logos/Argus.png"/></a>
<a href="https://www.cesanta.com/"><img src="assets/company-logos/cesanta.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://www.boxever.com/"><img src="assets/company-logos/Boxever.png"/></a>
<a href="https://www.coreos.com/"><img src="assets/company-logos/CoreOS.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="https://www.cesanta.com/"><img src="assets/company-logos/cesanta.png"/></a>
<a href="http://www.crononauta.com/"><img src="assets/company-logos/crononauta.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="https://www.coreos.com/"><img src="assets/company-logos/CoreOS.png"/></a>
<a href="http://www.ericsson.com/"><img src="assets/company-logos/Ericsson.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://www.crononauta.com/"><img src="assets/company-logos/crononauta.png"/></a>
<a href="http://www.eurotech.com/"><img src="assets/company-logos/Eurotech.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="https://www.digitalocean.com/"><img src="assets/company-logos/digitalocean.png"/></a>
<a href="http://improbable.io/"><img src="assets/company-logos/Improbable.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="https://www.docker.com/"><img src="assets/company-logos/Docker.png"/></a>
<a href="http://www.mavensecurities.com/"><img src="assets/company-logos/MavenSecurities.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://www.ericsson.com/"><img src="assets/company-logos/Ericsson.png"/></a>
<a href="http://www.outbrain.com/"><img src="assets/company-logos/Outbrain.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://www.eurotech.com/"><img src="assets/company-logos/Eurotech.png"/></a>
<a href="https://www.percona.com/"><img src="assets/company-logos/Percona.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="https://giantswarm.io/"><img src="assets/company-logos/giantswarm.png"/></a>
<a href="http://www.robustperception.io/"><img src="assets/company-logos/RobustPerception.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://improbable.io/"><img src="assets/company-logos/Improbable.png"/></a>
<a href="http://www.showmax.com/"><img src="assets/company-logos/ShowMax.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="https://www.jodel-app.com/"><img src="assets/company-logos/Jodel.png"/></a>
<a href="https://www.shuttlecloud.com/"><img src="assets/company-logos/shuttlecloud.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://www.mavensecurities.com/"><img src="assets/company-logos/MavenSecurities.png"/></a>
<a href="https://www.soundcloud.com/"><img src="assets/company-logos/soundcloud.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://www.outbrain.com/"><img src="assets/company-logos/Outbrain.png"/></a>
<a href="https://www.uda100.com/"><img src="assets/company-logos/WeLearn.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="https://www.percona.com/"><img src="assets/company-logos/Percona.png"/></a>
<a href="https://www.unosoft.hu/"><img src="assets/company-logos/unosoft.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<div class="col-md-2 col-sm-4 col-xs-6 logos"> <a href="http://www.quobyte.com/"><img src="assets/company-logos/quobyte.png"/></a>
<a href="https://www.variomedia.de/"><img src="assets/company-logos/variomedia.png"/></a> </div>
</div> <div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="http://www.robustperception.io/"><img src="assets/company-logos/RobustPerception.png"/></a>
</div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="http://www.showmax.com/"><img src="assets/company-logos/ShowMax.png"/></a>
</div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="https://www.shuttlecloud.com/"><img src="assets/company-logos/shuttlecloud.png"/></a>
</div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="https://www.soundcloud.com/"><img src="assets/company-logos/soundcloud.png"/></a>
</div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="https://www.uda100.com/"><img src="assets/company-logos/WeLearn.png"/></a>
</div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="https://www.unosoft.hu/"><img src="assets/company-logos/unosoft.png"/></a>
</div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="https://www.variomedia.de/"><img src="assets/company-logos/variomedia.png"/></a>
</div>
<div class="col-md-2 col-sm-4 col-xs-6 logos">
<a href="https://www.weave.works/"><img src="assets/company-logos/weave.png"/></a>
</div> </div>
</div> </div>
<%= render 'container_footer' %>
</div> </div>
---
title: Routing tree editor
---
<h1 id="routing-tree-editor" class="page-header">
Routing tree editor
<a class="header-anchor" href="#routing-tree-editor" name="routing-tree-editor"></a>
</h1>
<div class="form-group">
<p class="block">Copy and paste your Alertmanager config.yml:</p>
<div class="form-group">
<textarea class="js-config-yml form-control" cols="50" rows="10"></textarea>
</div>
<button type="button" class="js-parse-and-draw btn btn-default">Draw Routing Tree</button>
</div>
<div class="form-inline">
<div class="form-group">
<div class="form-group">
<input class="js-label-set-input label-input form-control" type="text" placeholder='{service="foo-service", severity="critical"}' \>
<button type="button" class="js-find-match btn btn-default">Match Label Set</button>
</div>
</div>
</div>
<script src="/assets/d3.v3.min.js"></script>
<script src="/assets/js-yaml.min.js"></script>
<script src="/assets/routing-tree.js"></script>
<% render 'default' do %> <% render 'default' do %>
<div class="row">
<div class="col-md-9 blog doc-content"> <div class="col-md-9 blog doc-content">
<h1><%= item[:title] %></h1> <h1><%= item[:title] %></h1>
<aside>Posted at: <%= get_pretty_date(item) %> by <%= item[:author_name]%></aside> <aside>Posted at: <%= get_pretty_date(item) %> by <%= item[:author_name]%></aside>
...@@ -22,4 +23,5 @@ ...@@ -22,4 +23,5 @@
</div> </div>
<%= render 'blog_sidebar' %> <%= render 'blog_sidebar' %>
</div>
<% end %> <% end %>
...@@ -2,10 +2,11 @@ ...@@ -2,10 +2,11 @@
<ul class="nav navbar-nav side-nav"> <ul class="nav navbar-nav side-nav">
<li> <li>
<span class="nav-header">Blog posts</span> <span class="nav-header">Blog posts</span>
<ul class="nav"> <ul class="nav active">
<% sorted_articles.each do |post| %> <% sorted_articles.each do |post| %>
<li><%= link_to post[:title], post.path %></li> <li><%= link_to post[:title], post.path %></li>
<% end %> <% end %>
</ul> </ul>
</li>
</ul> </ul>
</div> </div>
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
<footer> <footer>
<p class="pull-left"> <p class="pull-left">
&copy; Prometheus Authors 2015 &copy; Prometheus Authors 2016
</p> </p>
</footer> </footer>
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
<div class="container"> <div class="container">
<%= yield %> <%= yield %>
<%= render 'container_footer' %>
</div> </div>
<%= render 'footer' %> <%= render 'footer' %>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<div class="col-md-3 side-nav-col"> <div class="col-md-3 side-nav-col">
<ul class="nav navbar-nav side-nav"> <ul class="nav navbar-nav side-nav">
<% @items['/docs/'].children.sort_by { |i| i[:sort_rank] || 0 }.each do |i| %> <% @items['/docs/'].children.sort_by { |i| i[:sort_rank] || 0 }.each do |i| %>
<%= nav(i, @item) %> <%= nav(i) %>
<% end %> <% end %>
</ul> </ul>
</div> </div>
......
<!-- Bootstrap core JavaScript <!-- Bootstrap core JavaScript
================================================== --> ================================================== -->
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossorigin="anonymous"></script>
<script src="/assets/bootstrap-3.3.1/js/bootstrap.min.js"></script> <script src="/assets/bootstrap-3.3.1/js/bootstrap.min.js"></script>
<script src="/assets/docs.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug --> <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="/assets/ie10-viewport-bug-workaround.js"></script> <script src="/assets/ie10-viewport-bug-workaround.js"></script>
<!-- Google Analytics --> <!-- Google Analytics -->
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Prometheus monitoring system and time series database"> <meta name="description" content="An open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.">
<meta name="keywords" content="prometheus, monitoring, monitoring system, time series, time series database, alerting, metrics, telemetry"> <meta name="keywords" content="prometheus, monitoring, monitoring system, time series, time series database, alerting, metrics, telemetry">
<meta name="author" content="Prometheus"> <meta name="author" content="Prometheus">
...@@ -32,28 +32,26 @@ ...@@ -32,28 +32,26 @@
<% if @item[:title] %> <% if @item[:title] %>
<title><%= @item[:title] %> | Prometheus</title> <title><%= @item[:title] %> | Prometheus</title>
<% else %> <% else %>
<title>Prometheus</title> <title>Prometheus - Monitoring system &amp; time series database</title>
<% end %> <% end %>
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link href="/assets/bootstrap-3.3.1/css/bootstrap.min.css" rel="stylesheet"> <link href="/assets/bootstrap-3.3.1/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template --> <!-- Custom styles for this template -->
<link href="/assets/docs.css" rel="stylesheet"> <link href="/css/docs.css" rel="stylesheet">
<link href="/css/routing-tree-editor.css" rel="stylesheet">
<!-- Custom Fonts --> <!-- Custom Fonts -->
<link href="/assets/font-awesome-4.2.0/css/font-awesome.min.css" rel="stylesheet" type="text/css"> <link href="/assets/font-awesome-4.2.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans"> <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Lato:300,300italic,400' rel='stylesheet' type='text/css'>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head> </head>
<body> <body>
<div class="<%= @item[:layout] == 'jumbotron' ? 'navbar-jumbotron' : '' %>">
<nav class="navbar navbar-inverse navbar-static-top" role="navigation"> <nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
...@@ -67,12 +65,14 @@ ...@@ -67,12 +65,14 @@
</div> </div>
<div class="collapse navbar-collapse" id="navbar"> <div class="collapse navbar-collapse" id="navbar">
<ul class="nav navbar-nav navbar-right main-nav"> <ul class="nav navbar-nav navbar-right main-nav">
<li><a href="/">Overview</a></li> <li><a href="/docs/introduction/overview/">Docs</a></li>
<li><a href="/docs/introduction/overview/">Documentation</a></li> <li><a href="/download/">Download</a></li>
<li><a href="/community/">Community</a></li> <li><a href="/community/">Community</a></li>
<li><a href="/blog/">Blog</a></li> <li><a href="/blog/">Blog</a></li>
<li><a href="https://github.com/prometheus"><i class="fa fa-github"></i> Github</a></li> <li><a href="https://github.com/prometheus"><i class="fa fa-github"></i></a></li>
<li><a href="https://twitter.com/PrometheusIO"><i class="fa fa-twitter"></i></a></li>
</ul> </ul>
</div> </div>
</div> </div>
</nav> </nav>
</div>
<%= render 'header' %> <%= render 'header' %>
<%= yield %>
<div class="jumbotron">
<div class="container">
<h1><img src="/assets/prometheus_logo.svg" alt="Prometheus logo"> Prometheus</h1>
<p class="subtitle">An open-source service monitoring system and time series database.</p>
<p><a class="btn btn-default btn-lg" href="/docs/introduction/getting_started/" role="button">Get Started</a></p>
</div>
</div>
<div class="container">
<%= yield %>
<%= render 'container_footer' %>
</div>
<%= render 'footer' %> <%= render 'footer' %>
# All files in the 'lib' directory will be loaded # All files in the 'lib' directory will be loaded
# before nanoc starts compiling. # before nanoc starts compiling.
require 'nanoc/cachebuster'
include Nanoc::Helpers::LinkTo include Nanoc::Helpers::LinkTo
include Nanoc::Helpers::Rendering include Nanoc::Helpers::Rendering
include Nanoc3::Helpers::Blogging include Nanoc3::Helpers::Blogging
include Nanoc3::Helpers::Tagging include Nanoc3::Helpers::Tagging
include Nanoc::Helpers::CacheBusting
module BlogHelper module BlogHelper
def get_pretty_date(post) def get_pretty_date(post)
...@@ -15,10 +18,9 @@ module BlogHelper ...@@ -15,10 +18,9 @@ module BlogHelper
content = post.compiled_content content = post.compiled_content
if content =~ /\s<!-- more -->\s/ if content =~ /\s<!-- more -->\s/
content = content.partition('<!-- more -->').first + content = content.partition('<!-- more -->').first +
"<div class='read-more'><a href='#{post.path}'>Continue reading &rsaquo;</a></div>" "<div class='read-more'><a class='btn btn-primary' href='#{post.path}'>Continue reading &raquo;</a></div>"
end end
return content return content
end end
end end
include BlogHelper include BlogHelper
require 'json'
module Downloads
# repositories returns a list of all repositories with releases.
def self.repositories
@repositories ||= begin
repos = Dir.glob('downloads/*').map { |dir| Repository.new(dir) }
repos.sort_by { |r| r.name == 'prometheus' ? '0' : r.name }
end
end
# operating_systems returns a list of all operating systems downloads can be
# provided for.
def self.operating_systems
repositories.inject([]) do |list, repo|
list += repo.releases.map { |r| r.assets.map(&:os) }.flatten
end.uniq.sort
end
# architectures returns a list of all architectures downloads can be
# provided for.
def self.architectures
repositories.inject([]) do |list, repo|
list += repo.releases.map { |r| r.assets.map(&:arch) }.flatten
end.uniq.sort
end
class Repository
def initialize(dir)
@repo = JSON.parse(File.read(File.join(dir, 'repo.json')))
@releases = JSON.parse(File.read(File.join(dir, 'releases.json')))
end
def name
@repo['name']
end
def full_name
@repo['full_name']
end
def description
@repo['description']
end
def url
@repo['html_url']
end
def releases
releases = []
@releases.each do |r|
if r['prerelease']
releases << r if releases.empty?
else
releases << r
break
end
end
releases.map { |r| Release.new(r) }
end
end
class Release
def initialize(data)
@data = data
end
def name
@data['name']
end
def url
@data['html_url']
end
def prerelease
@data['prerelease']
end
def assets
@data['assets'].map { |d| Asset.new(d) }
end
end
class Asset
def initialize(data)
@data = data
end
def name
@data['name']
end
def url
@data['browser_download_url']
end
def kind
'Binary'
end
# TODO(ts): validate
def os
name.split('.')[3].split('-').first
end
# TODO(ts): validate
def arch
name.split('.')[3].split('-').last
end
def size
@data['size']
end
end
module Helper
def format_bytes(bytes)
'%.2f MiB' % (bytes.to_f / 1024 / 1024)
end
def dropdown(name, items, default, groups = {})
additional = groups.map do |name, items|
%(<li data-group="#{items.join(' ')}"><a href="#">#{name}</a></li>)
end.join('')
caption = %(<span class="caption">#{default}</span> <span class="caret"></span>)
button = %(<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">#{caption}</button>)
header = %(<li><a href="#">all</a></li>#{additional}<li role="separator" class="divider"></li>)
list = %(<ul class="dropdown-menu">#{header} #{items.map { |i| %(<li><a href="#">#{i}</a></li>) }.join('') }</ul>)
%(<div class="btn-group #{name}">#{button} #{list}</div>)
end
end
end
include Downloads::Helper
def nav_title_of(i) def nav(root_item, buffer='', layer=0)
i[:nav_title] || i[:title] || ''
end
def nav(root_item, focused_item, buffer='', layer=0)
# Skip non-written or hidden items
return buffer if root_item.nil? || root_item.path.nil? || root_item[:is_hidden] return buffer if root_item.nil? || root_item.path.nil? || root_item[:is_hidden]
# Open list element children = nav_children(root_item)
is_active = @item_rep && @item_rep.path == root_item.path
if is_active if nav_active?(root_item)
buffer << "<li class=\"active\">" buffer << "<li class=\"active\">"
else else
buffer << "<li>" buffer << "<li>"
...@@ -16,29 +11,36 @@ def nav(root_item, focused_item, buffer='', layer=0) ...@@ -16,29 +11,36 @@ def nav(root_item, focused_item, buffer='', layer=0)
title = nav_title_of(root_item) title = nav_title_of(root_item)
if layer == 0 if layer == 0
# Add section header. buffer << "<span class=\"nav-header\"><i class=\"fa fa-#{root_item[:nav_icon]}\"></i> <span>#{title}</span></span>"
buffer << "<span class=\"nav-header\"><i class=\"fa fa-#{root_item[:nav_icon]}\"></i> #{title}</span>"
else else
# Add link.
buffer << link_to(title, root_item.path) buffer << link_to(title, root_item.path)
end end
# Add children to sitemap, recursively if children.any?
visible_children = root_item.children.select { |child| !child[:is_hidden] && child.path } buffer << %(<ul class="nav #{nav_active?(root_item) ? 'active' : ''}">)
visible_children = visible_children.sort_by { |child| child[:sort_rank] || 0 }
if visible_children.size > 0
buffer << '<ul class="nav">'
visible_children.each do |child| children.each do |child|
nav(child, focused_item, buffer, layer + 1) nav(child, buffer, layer + 1)
end end
buffer << '</ul>' buffer << '</ul>'
end end
# Close list element
buffer << '</li>' buffer << '</li>'
# Return sitemap
buffer buffer
end end
def nav_active?(item)
active = @item_rep.respond_to?(:path) && @item_rep.path == item.path
active || nav_children(item).any? { |child| nav_active?(child) }
end
def nav_title_of(i)
i[:nav_title] || i[:title] || ''
end
def nav_children(item)
item.children
.select { |child| !child[:is_hidden] && child.path }
.sort_by { |child| child[:sort_rank] || 0 }
end
...@@ -77,4 +77,4 @@ checks: ...@@ -77,4 +77,4 @@ checks:
exclude: [] exclude: []
# The base url required by atom_feed # The base url required by atom_feed
base_url: "http://prometheus.io" base_url: "https://prometheus.io"
This source diff could not be displayed because it is too large. You can view the blob instead.
// Use CSS to hide elements without a delay during page load.
$('head').append('<style type="text/css"> \
.side-nav ul { display: none; } \
.side-nav ul.active { display: block; } \
</style>');
$(document).ready(function() {
var navToggle = function(event) {
event.preventDefault();
var visible = $(this).closest('li').children('ul.nav').is(':visible');
$(this).closest('ul').find('ul.nav').slideUp(200);
if (!visible) {
$(this).closest('li').children('ul.nav').slideDown(200);
}
};
$('.nav-header span').each(function() {
var link = $('<a href="#">').text($(this).text()).click(navToggle);
$(this).replaceWith(link);
});
var selected = function(value, want, group) {
switch(want) {
case 'all':
return true;
default:
if (group.length > 0) {
return group.indexOf(value) > -1;
}
return value === want;
}
}
var selectDownloads = function() {
var os = $('.download-selection .os .caption').text();
var osGroup = $('.download-selection .os li:contains("'+os+'")').data("group");
var arch = $('.download-selection .arch .caption').text();
$('.downloads tbody tr').each(function() {
if (selected($(this).data('os').toString(), os, osGroup !== undefined ? osGroup.split(' ') : [])
&& selected($(this).data('arch').toString(), arch, [])) {
$(this).show();
} else {
$(this).hide();
}
});
};
selectDownloads();
$('.download-selection a').on('click', function() {
event.preventDefault();
$(this).parents('.btn-group').find('button .caption').text($(this).text());
selectDownloads();
});
});
/* js-yaml 3.5.5 https://github.com/nodeca/js-yaml */
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.jsyaml=e()}}(function(){return function e(t,n,i){function r(a,s){if(!n[a]){if(!t[a]){var c="function"==typeof require&&require;if(!s&&c)return c(a,!0);if(o)return o(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[a]={exports:{}};t[a][0].call(l.exports,function(e){var n=t[a][1][e];return r(n?n:e)},l,l.exports,e,t,n,i)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;a<i.length;a++)r(i[a]);return r}({1:[function(e,t,n){"use strict";function i(e){return function(){throw new Error("Function "+e+" is deprecated and cannot be used.")}}var r=e("./js-yaml/loader"),o=e("./js-yaml/dumper");t.exports.Type=e("./js-yaml/type"),t.exports.Schema=e("./js-yaml/schema"),t.exports.FAILSAFE_SCHEMA=e("./js-yaml/schema/failsafe"),t.exports.JSON_SCHEMA=e("./js-yaml/schema/json"),t.exports.CORE_SCHEMA=e("./js-yaml/schema/core"),t.exports.DEFAULT_SAFE_SCHEMA=e("./js-yaml/schema/default_safe"),t.exports.DEFAULT_FULL_SCHEMA=e("./js-yaml/schema/default_full"),t.exports.load=r.load,t.exports.loadAll=r.loadAll,t.exports.safeLoad=r.safeLoad,t.exports.safeLoadAll=r.safeLoadAll,t.exports.dump=o.dump,t.exports.safeDump=o.safeDump,t.exports.YAMLException=e("./js-yaml/exception"),t.exports.MINIMAL_SCHEMA=e("./js-yaml/schema/failsafe"),t.exports.SAFE_SCHEMA=e("./js-yaml/schema/default_safe"),t.exports.DEFAULT_SCHEMA=e("./js-yaml/schema/default_full"),t.exports.scan=i("scan"),t.exports.parse=i("parse"),t.exports.compose=i("compose"),t.exports.addConstructor=i("addConstructor")},{"./js-yaml/dumper":3,"./js-yaml/exception":4,"./js-yaml/loader":5,"./js-yaml/schema":7,"./js-yaml/schema/core":8,"./js-yaml/schema/default_full":9,"./js-yaml/schema/default_safe":10,"./js-yaml/schema/failsafe":11,"./js-yaml/schema/json":12,"./js-yaml/type":13}],2:[function(e,t,n){"use strict";function i(e){return"undefined"==typeof e||null===e}function r(e){return"object"==typeof e&&null!==e}function o(e){return Array.isArray(e)?e:i(e)?[]:[e]}function a(e,t){var n,i,r,o;if(t)for(o=Object.keys(t),n=0,i=o.length;i>n;n+=1)r=o[n],e[r]=t[r];return e}function s(e,t){var n,i="";for(n=0;t>n;n+=1)i+=e;return i}function c(e){return 0===e&&Number.NEGATIVE_INFINITY===1/e}t.exports.isNothing=i,t.exports.isObject=r,t.exports.toArray=o,t.exports.repeat=s,t.exports.isNegativeZero=c,t.exports.extend=a},{}],3:[function(e,t,n){"use strict";function i(e,t){var n,i,r,o,a,s,c;if(null===t)return{};for(n={},i=Object.keys(t),r=0,o=i.length;o>r;r+=1)a=i[r],s=String(t[a]),"!!"===a.slice(0,2)&&(a="tag:yaml.org,2002:"+a.slice(2)),c=e.compiledTypeMap[a],c&&F.call(c.styleAliases,s)&&(s=c.styleAliases[s]),n[a]=s;return n}function r(e){var t,n,i;if(t=e.toString(16).toUpperCase(),255>=e)n="x",i=2;else if(65535>=e)n="u",i=4;else{if(!(4294967295>=e))throw new I("code point within a string may not be greater than 0xFFFFFFFF");n="U",i=8}return"\\"+n+j.repeat("0",i-t.length)+t}function o(e){this.schema=e.schema||S,this.indent=Math.max(1,e.indent||2),this.skipInvalid=e.skipInvalid||!1,this.flowLevel=j.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=i(this.schema,e.styles||null),this.sortKeys=e.sortKeys||!1,this.lineWidth=e.lineWidth||80,this.noRefs=e.noRefs||!1,this.noCompatMode=e.noCompatMode||!1,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function a(e,t){for(var n,i=j.repeat(" ",t),r=0,o=-1,a="",s=e.length;s>r;)o=e.indexOf("\n",r),-1===o?(n=e.slice(r),r=s):(n=e.slice(r,o+1),r=o+1),n.length&&"\n"!==n&&(a+=i),a+=n;return a}function s(e,t){return"\n"+j.repeat(" ",e.indent*t)}function c(e,t){var n,i,r;for(n=0,i=e.implicitTypes.length;i>n;n+=1)if(r=e.implicitTypes[n],r.resolve(t))return!0;return!1}function u(e){this.source=e,this.result="",this.checkpoint=0}function l(e,t,n,i){var r,o,s,l,f,m,g,y,x,v,A,b,w,C,k,j,I,S,E,O,F;if(0===t.length)return void(e.dump="''");if(!e.noCompatMode&&-1!==te.indexOf(t))return void(e.dump="'"+t+"'");for(r=!0,o=t.length?t.charCodeAt(0):0,s=M===o||M===t.charCodeAt(t.length-1),P!==o&&W!==o&&G!==o&&z!==o||(r=!1),s||e.flowLevel>-1&&e.flowLevel<=n?(s&&(r=!1),l=!1,f=!1):(l=!i,f=!i),m=!0,g=new u(t),y=!1,x=0,v=0,A=e.indent*n,b=e.lineWidth,-1===b&&(b=9007199254740991),40>A?b-=A:b=40,C=0;C<t.length;C++){if(w=t.charCodeAt(C),r){if(h(w))continue;r=!1}m&&w===R&&(m=!1),k=ee[w],j=d(w),(k||j)&&(w!==T&&w!==D&&w!==R?(l=!1,f=!1):w===T&&(y=!0,m=!1,C>0&&(I=t.charCodeAt(C-1),I===M&&(f=!1,l=!1)),l&&(S=C-x,x=C,S>v&&(v=S))),w!==D&&(m=!1),g.takeUpTo(C),g.escapeChar())}if(r&&c(e,t)&&(r=!1),E="",(l||f)&&(O=0,t.charCodeAt(t.length-1)===T&&(O+=1,t.charCodeAt(t.length-2)===T&&(O+=1)),0===O?E="-":2===O&&(E="+")),(f&&b>v||null!==e.tag)&&(l=!1),y||(f=!1),r)e.dump=t;else if(m)e.dump="'"+t+"'";else if(l)F=p(t,b),e.dump=">"+E+"\n"+a(F,A);else if(f)E||(t=t.replace(/\n$/,"")),e.dump="|"+E+"\n"+a(t,A);else{if(!g)throw new Error("Failed to dump scalar value");g.finish(),e.dump='"'+g.result+'"'}}function p(e,t){var n,i="",r=0,o=e.length,a=/\n+$/.exec(e);for(a&&(o=a.index+1);o>r;)n=e.indexOf("\n",r),n>o||-1===n?(i&&(i+="\n\n"),i+=f(e.slice(r,o),t),r=o):(i&&(i+="\n\n"),i+=f(e.slice(r,n),t),r=n+1);return a&&"\n"!==a[0]&&(i+=a[0]),i}function f(e,t){if(""===e)return e;for(var n,i,r,o=/[^\s] [^\s]/g,a="",s=0,c=0,u=o.exec(e);u;)n=u.index,n-c>t&&(i=s!==c?s:n,a&&(a+="\n"),r=e.slice(c,i),a+=r,c=i+1),s=n+1,u=o.exec(e);return a&&(a+="\n"),a+=c!==s&&e.length-c>t?e.slice(c,s)+"\n"+e.slice(s+1):e.slice(c)}function h(e){return N!==e&&T!==e&&_!==e&&B!==e&&V!==e&&Z!==e&&J!==e&&X!==e&&U!==e&&Y!==e&&$!==e&&L!==e&&Q!==e&&H!==e&&R!==e&&D!==e&&q!==e&&K!==e&&!ee[e]&&!d(e)}function d(e){return!(e>=32&&126>=e||133===e||e>=160&&55295>=e||e>=57344&&65533>=e||e>=65536&&1114111>=e)}function m(e,t,n){var i,r,o="",a=e.tag;for(i=0,r=n.length;r>i;i+=1)A(e,t,n[i],!1,!1)&&(0!==i&&(o+=", "),o+=e.dump);e.tag=a,e.dump="["+o+"]"}function g(e,t,n,i){var r,o,a="",c=e.tag;for(r=0,o=n.length;o>r;r+=1)A(e,t+1,n[r],!0,!0)&&(i&&0===r||(a+=s(e,t)),a+="- "+e.dump);e.tag=c,e.dump=a||"[]"}function y(e,t,n){var i,r,o,a,s,c="",u=e.tag,l=Object.keys(n);for(i=0,r=l.length;r>i;i+=1)s="",0!==i&&(s+=", "),o=l[i],a=n[o],A(e,t,o,!1,!1)&&(e.dump.length>1024&&(s+="? "),s+=e.dump+": ",A(e,t,a,!1,!1)&&(s+=e.dump,c+=s));e.tag=u,e.dump="{"+c+"}"}function x(e,t,n,i){var r,o,a,c,u,l,p="",f=e.tag,h=Object.keys(n);if(e.sortKeys===!0)h.sort();else if("function"==typeof e.sortKeys)h.sort(e.sortKeys);else if(e.sortKeys)throw new I("sortKeys must be a boolean or a function");for(r=0,o=h.length;o>r;r+=1)l="",i&&0===r||(l+=s(e,t)),a=h[r],c=n[a],A(e,t+1,a,!0,!0,!0)&&(u=null!==e.tag&&"?"!==e.tag||e.dump&&e.dump.length>1024,u&&(l+=e.dump&&T===e.dump.charCodeAt(0)?"?":"? "),l+=e.dump,u&&(l+=s(e,t)),A(e,t+1,c,!0,u)&&(l+=e.dump&&T===e.dump.charCodeAt(0)?":":": ",l+=e.dump,p+=l));e.tag=f,e.dump=p||"{}"}function v(e,t,n){var i,r,o,a,s,c;for(r=n?e.explicitTypes:e.implicitTypes,o=0,a=r.length;a>o;o+=1)if(s=r[o],(s.instanceOf||s.predicate)&&(!s.instanceOf||"object"==typeof t&&t instanceof s.instanceOf)&&(!s.predicate||s.predicate(t))){if(e.tag=n?s.tag:"?",s.represent){if(c=e.styleMap[s.tag]||s.defaultStyle,"[object Function]"===O.call(s.represent))i=s.represent(t,c);else{if(!F.call(s.represent,c))throw new I("!<"+s.tag+'> tag resolver accepts not "'+c+'" style');i=s.represent[c](t,c)}e.dump=i}return!0}return!1}function A(e,t,n,i,r,o){e.tag=null,e.dump=n,v(e,n,!1)||v(e,n,!0);var a=O.call(e.dump);i&&(i=e.flowLevel<0||e.flowLevel>t);var s,c,u="[object Object]"===a||"[object Array]"===a;if(u&&(s=e.duplicates.indexOf(n),c=-1!==s),(null!==e.tag&&"?"!==e.tag||c||2!==e.indent&&t>0)&&(r=!1),c&&e.usedDuplicates[s])e.dump="*ref_"+s;else{if(u&&c&&!e.usedDuplicates[s]&&(e.usedDuplicates[s]=!0),"[object Object]"===a)i&&0!==Object.keys(e.dump).length?(x(e,t,e.dump,r),c&&(e.dump="&ref_"+s+e.dump)):(y(e,t,e.dump),c&&(e.dump="&ref_"+s+" "+e.dump));else if("[object Array]"===a)i&&0!==e.dump.length?(g(e,t,e.dump,r),c&&(e.dump="&ref_"+s+e.dump)):(m(e,t,e.dump),c&&(e.dump="&ref_"+s+" "+e.dump));else{if("[object String]"!==a){if(e.skipInvalid)return!1;throw new I("unacceptable kind of an object to dump "+a)}"?"!==e.tag&&l(e,e.dump,t,o)}null!==e.tag&&"?"!==e.tag&&(e.dump="!<"+e.tag+"> "+e.dump)}return!0}function b(e,t){var n,i,r=[],o=[];for(w(e,r,o),n=0,i=o.length;i>n;n+=1)t.duplicates.push(r[o[n]]);t.usedDuplicates=new Array(i)}function w(e,t,n){var i,r,o;if(null!==e&&"object"==typeof e)if(r=t.indexOf(e),-1!==r)-1===n.indexOf(r)&&n.push(r);else if(t.push(e),Array.isArray(e))for(r=0,o=e.length;o>r;r+=1)w(e[r],t,n);else for(i=Object.keys(e),r=0,o=i.length;o>r;r+=1)w(e[i[r]],t,n)}function C(e,t){t=t||{};var n=new o(t);return n.noRefs||b(e,n),A(n,0,e,!0,!0)?n.dump+"\n":""}function k(e,t){return C(e,j.extend({schema:E},t))}var j=e("./common"),I=e("./exception"),S=e("./schema/default_full"),E=e("./schema/default_safe"),O=Object.prototype.toString,F=Object.prototype.hasOwnProperty,N=9,T=10,_=13,M=32,L=33,D=34,U=35,q=37,Y=38,R=39,$=42,B=44,P=45,K=58,H=62,W=63,G=64,V=91,Z=93,z=96,J=123,Q=124,X=125,ee={};ee[0]="\\0",ee[7]="\\a",ee[8]="\\b",ee[9]="\\t",ee[10]="\\n",ee[11]="\\v",ee[12]="\\f",ee[13]="\\r",ee[27]="\\e",ee[34]='\\"',ee[92]="\\\\",ee[133]="\\N",ee[160]="\\_",ee[8232]="\\L",ee[8233]="\\P";var te=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"];u.prototype.takeUpTo=function(e){var t;if(e<this.checkpoint)throw t=new Error("position should be > checkpoint"),t.position=e,t.checkpoint=this.checkpoint,t;return this.result+=this.source.slice(this.checkpoint,e),this.checkpoint=e,this},u.prototype.escapeChar=function(){var e,t;return e=this.source.charCodeAt(this.checkpoint),t=ee[e]||r(e),this.result+=t,this.checkpoint+=1,this},u.prototype.finish=function(){this.source.length>this.checkpoint&&this.takeUpTo(this.source.length)},t.exports.dump=C,t.exports.safeDump=k},{"./common":2,"./exception":4,"./schema/default_full":9,"./schema/default_safe":10}],4:[function(e,t,n){"use strict";function i(e,t){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||"",this.name="YAMLException",this.reason=e,this.mark=t,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():"")}i.prototype=Object.create(Error.prototype),i.prototype.constructor=i,i.prototype.toString=function(e){var t=this.name+": ";return t+=this.reason||"(unknown reason)",!e&&this.mark&&(t+=" "+this.mark.toString()),t},t.exports=i},{}],5:[function(e,t,n){"use strict";function i(e){return 10===e||13===e}function r(e){return 9===e||32===e}function o(e){return 9===e||32===e||10===e||13===e}function a(e){return 44===e||91===e||93===e||123===e||125===e}function s(e){var t;return e>=48&&57>=e?e-48:(t=32|e,t>=97&&102>=t?t-97+10:-1)}function c(e){return 120===e?2:117===e?4:85===e?8:0}function u(e){return e>=48&&57>=e?e-48:-1}function l(e){return 48===e?"\x00":97===e?"":98===e?"\b":116===e?" ":9===e?" ":110===e?"\n":118===e?"\x0B":102===e?"\f":114===e?"\r":101===e?"":32===e?" ":34===e?'"':47===e?"/":92===e?"\\":78===e?"…":95===e?" ":76===e?"\u2028":80===e?"\u2029":""}function p(e){return 65535>=e?String.fromCharCode(e):String.fromCharCode((e-65536>>10)+55296,(e-65536&1023)+56320)}function f(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||K,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function h(e,t){return new $(t,new B(e.filename,e.input,e.position,e.line,e.position-e.lineStart))}function d(e,t){throw h(e,t)}function m(e,t){e.onWarning&&e.onWarning.call(null,h(e,t))}function g(e,t,n,i){var r,o,a,s;if(n>t){if(s=e.input.slice(t,n),i)for(r=0,o=s.length;o>r;r+=1)a=s.charCodeAt(r),9===a||a>=32&&1114111>=a||d(e,"expected valid JSON character");else X.test(s)&&d(e,"the stream contains non-printable characters");e.result+=s}}function y(e,t,n,i){var r,o,a,s;for(R.isObject(n)||d(e,"cannot merge mappings; the provided source object is unacceptable"),r=Object.keys(n),a=0,s=r.length;s>a;a+=1)o=r[a],H.call(t,o)||(t[o]=n[o],i[o]=!0)}function x(e,t,n,i,r,o){var a,s;if(r=String(r),null===t&&(t={}),"tag:yaml.org,2002:merge"===i)if(Array.isArray(o))for(a=0,s=o.length;s>a;a+=1)y(e,t,o[a],n);else y(e,t,o,n);else e.json||H.call(n,r)||!H.call(t,r)||d(e,"duplicated mapping key"),t[r]=o,delete n[r];return t}function v(e){var t;t=e.input.charCodeAt(e.position),10===t?e.position++:13===t?(e.position++,10===e.input.charCodeAt(e.position)&&e.position++):d(e,"a line break is expected"),e.line+=1,e.lineStart=e.position}function A(e,t,n){for(var o=0,a=e.input.charCodeAt(e.position);0!==a;){for(;r(a);)a=e.input.charCodeAt(++e.position);if(t&&35===a)do a=e.input.charCodeAt(++e.position);while(10!==a&&13!==a&&0!==a);if(!i(a))break;for(v(e),a=e.input.charCodeAt(e.position),o++,e.lineIndent=0;32===a;)e.lineIndent++,a=e.input.charCodeAt(++e.position)}return-1!==n&&0!==o&&e.lineIndent<n&&m(e,"deficient indentation"),o}function b(e){var t,n=e.position;return t=e.input.charCodeAt(n),(45===t||46===t)&&t===e.input.charCodeAt(n+1)&&t===e.input.charCodeAt(n+2)&&(n+=3,t=e.input.charCodeAt(n),0===t||o(t))}function w(e,t){1===t?e.result+=" ":t>1&&(e.result+=R.repeat("\n",t-1))}function C(e,t,n){var s,c,u,l,p,f,h,d,m,y=e.kind,x=e.result;if(m=e.input.charCodeAt(e.position),o(m)||a(m)||35===m||38===m||42===m||33===m||124===m||62===m||39===m||34===m||37===m||64===m||96===m)return!1;if((63===m||45===m)&&(c=e.input.charCodeAt(e.position+1),o(c)||n&&a(c)))return!1;for(e.kind="scalar",e.result="",u=l=e.position,p=!1;0!==m;){if(58===m){if(c=e.input.charCodeAt(e.position+1),o(c)||n&&a(c))break}else if(35===m){if(s=e.input.charCodeAt(e.position-1),o(s))break}else{if(e.position===e.lineStart&&b(e)||n&&a(m))break;if(i(m)){if(f=e.line,h=e.lineStart,d=e.lineIndent,A(e,!1,-1),e.lineIndent>=t){p=!0,m=e.input.charCodeAt(e.position);continue}e.position=l,e.line=f,e.lineStart=h,e.lineIndent=d;break}}p&&(g(e,u,l,!1),w(e,e.line-f),u=l=e.position,p=!1),r(m)||(l=e.position+1),m=e.input.charCodeAt(++e.position)}return g(e,u,l,!1),e.result?!0:(e.kind=y,e.result=x,!1)}function k(e,t){var n,r,o;if(n=e.input.charCodeAt(e.position),39!==n)return!1;for(e.kind="scalar",e.result="",e.position++,r=o=e.position;0!==(n=e.input.charCodeAt(e.position));)if(39===n){if(g(e,r,e.position,!0),n=e.input.charCodeAt(++e.position),39!==n)return!0;r=o=e.position,e.position++}else i(n)?(g(e,r,o,!0),w(e,A(e,!1,t)),r=o=e.position):e.position===e.lineStart&&b(e)?d(e,"unexpected end of the document within a single quoted scalar"):(e.position++,o=e.position);d(e,"unexpected end of the stream within a single quoted scalar")}function j(e,t){var n,r,o,a,u,l;if(l=e.input.charCodeAt(e.position),34!==l)return!1;for(e.kind="scalar",e.result="",e.position++,n=r=e.position;0!==(l=e.input.charCodeAt(e.position));){if(34===l)return g(e,n,e.position,!0),e.position++,!0;if(92===l){if(g(e,n,e.position,!0),l=e.input.charCodeAt(++e.position),i(l))A(e,!1,t);else if(256>l&&re[l])e.result+=oe[l],e.position++;else if((u=c(l))>0){for(o=u,a=0;o>0;o--)l=e.input.charCodeAt(++e.position),(u=s(l))>=0?a=(a<<4)+u:d(e,"expected hexadecimal character");e.result+=p(a),e.position++}else d(e,"unknown escape sequence");n=r=e.position}else i(l)?(g(e,n,r,!0),w(e,A(e,!1,t)),n=r=e.position):e.position===e.lineStart&&b(e)?d(e,"unexpected end of the document within a double quoted scalar"):(e.position++,r=e.position)}d(e,"unexpected end of the stream within a double quoted scalar")}function I(e,t){var n,i,r,a,s,c,u,l,p,f,h,m=!0,g=e.tag,y=e.anchor,v={};if(h=e.input.charCodeAt(e.position),91===h)a=93,u=!1,i=[];else{if(123!==h)return!1;a=125,u=!0,i={}}for(null!==e.anchor&&(e.anchorMap[e.anchor]=i),h=e.input.charCodeAt(++e.position);0!==h;){if(A(e,!0,t),h=e.input.charCodeAt(e.position),h===a)return e.position++,e.tag=g,e.anchor=y,e.kind=u?"mapping":"sequence",e.result=i,!0;m||d(e,"missed comma between flow collection entries"),p=l=f=null,s=c=!1,63===h&&(r=e.input.charCodeAt(e.position+1),o(r)&&(s=c=!0,e.position++,A(e,!0,t))),n=e.line,_(e,t,W,!1,!0),p=e.tag,l=e.result,A(e,!0,t),h=e.input.charCodeAt(e.position),!c&&e.line!==n||58!==h||(s=!0,h=e.input.charCodeAt(++e.position),A(e,!0,t),_(e,t,W,!1,!0),f=e.result),u?x(e,i,v,p,l,f):s?i.push(x(e,null,v,p,l,f)):i.push(l),A(e,!0,t),h=e.input.charCodeAt(e.position),44===h?(m=!0,h=e.input.charCodeAt(++e.position)):m=!1}d(e,"unexpected end of the stream within a flow collection")}function S(e,t){var n,o,a,s,c=z,l=!1,p=t,f=0,h=!1;if(s=e.input.charCodeAt(e.position),124===s)o=!1;else{if(62!==s)return!1;o=!0}for(e.kind="scalar",e.result="";0!==s;)if(s=e.input.charCodeAt(++e.position),43===s||45===s)z===c?c=43===s?Q:J:d(e,"repeat of a chomping mode identifier");else{if(!((a=u(s))>=0))break;0===a?d(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):l?d(e,"repeat of an indentation width identifier"):(p=t+a-1,l=!0)}if(r(s)){do s=e.input.charCodeAt(++e.position);while(r(s));if(35===s)do s=e.input.charCodeAt(++e.position);while(!i(s)&&0!==s)}for(;0!==s;){for(v(e),e.lineIndent=0,s=e.input.charCodeAt(e.position);(!l||e.lineIndent<p)&&32===s;)e.lineIndent++,s=e.input.charCodeAt(++e.position);if(!l&&e.lineIndent>p&&(p=e.lineIndent),i(s))f++;else{if(e.lineIndent<p){c===Q?e.result+=R.repeat("\n",f):c===z&&l&&(e.result+="\n");break}for(o?r(s)?(h=!0,e.result+=R.repeat("\n",f+1)):h?(h=!1,e.result+=R.repeat("\n",f+1)):0===f?l&&(e.result+=" "):e.result+=R.repeat("\n",f):l?e.result+=R.repeat("\n",f+1):e.result+=R.repeat("\n",f),l=!0,f=0,n=e.position;!i(s)&&0!==s;)s=e.input.charCodeAt(++e.position);g(e,n,e.position,!1)}}return!0}function E(e,t){var n,i,r,a=e.tag,s=e.anchor,c=[],u=!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=c),r=e.input.charCodeAt(e.position);0!==r&&45===r&&(i=e.input.charCodeAt(e.position+1),o(i));)if(u=!0,e.position++,A(e,!0,-1)&&e.lineIndent<=t)c.push(null),r=e.input.charCodeAt(e.position);else if(n=e.line,_(e,t,V,!1,!0),c.push(e.result),A(e,!0,-1),r=e.input.charCodeAt(e.position),(e.line===n||e.lineIndent>t)&&0!==r)d(e,"bad indentation of a sequence entry");else if(e.lineIndent<t)break;return u?(e.tag=a,e.anchor=s,e.kind="sequence",e.result=c,!0):!1}function O(e,t,n){var i,a,s,c,u=e.tag,l=e.anchor,p={},f={},h=null,m=null,g=null,y=!1,v=!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=p),c=e.input.charCodeAt(e.position);0!==c;){if(i=e.input.charCodeAt(e.position+1),s=e.line,63!==c&&58!==c||!o(i)){if(!_(e,n,G,!1,!0))break;if(e.line===s){for(c=e.input.charCodeAt(e.position);r(c);)c=e.input.charCodeAt(++e.position);if(58===c)c=e.input.charCodeAt(++e.position),o(c)||d(e,"a whitespace character is expected after the key-value separator within a block mapping"),y&&(x(e,p,f,h,m,null),h=m=g=null),v=!0,y=!1,a=!1,h=e.tag,m=e.result;else{if(!v)return e.tag=u,e.anchor=l,!0;d(e,"can not read an implicit mapping pair; a colon is missed")}}else{if(!v)return e.tag=u,e.anchor=l,!0;d(e,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else 63===c?(y&&(x(e,p,f,h,m,null),h=m=g=null),v=!0,y=!0,a=!0):y?(y=!1,a=!0):d(e,"incomplete explicit mapping pair; a key node is missed"),e.position+=1,c=i;if((e.line===s||e.lineIndent>t)&&(_(e,t,Z,!0,a)&&(y?m=e.result:g=e.result),y||(x(e,p,f,h,m,g),h=m=g=null),A(e,!0,-1),c=e.input.charCodeAt(e.position)),e.lineIndent>t&&0!==c)d(e,"bad indentation of a mapping entry");else if(e.lineIndent<t)break}return y&&x(e,p,f,h,m,null),v&&(e.tag=u,e.anchor=l,e.kind="mapping",e.result=p),v}function F(e){var t,n,i,r,a=!1,s=!1;if(r=e.input.charCodeAt(e.position),33!==r)return!1;if(null!==e.tag&&d(e,"duplication of a tag property"),r=e.input.charCodeAt(++e.position),60===r?(a=!0,r=e.input.charCodeAt(++e.position)):33===r?(s=!0,n="!!",r=e.input.charCodeAt(++e.position)):n="!",t=e.position,a){do r=e.input.charCodeAt(++e.position);while(0!==r&&62!==r);e.position<e.length?(i=e.input.slice(t,e.position),r=e.input.charCodeAt(++e.position)):d(e,"unexpected end of the stream within a verbatim tag")}else{for(;0!==r&&!o(r);)33===r&&(s?d(e,"tag suffix cannot contain exclamation marks"):(n=e.input.slice(t-1,e.position+1),ne.test(n)||d(e,"named tag handle cannot contain such characters"),s=!0,t=e.position+1)),r=e.input.charCodeAt(++e.position);i=e.input.slice(t,e.position),te.test(i)&&d(e,"tag suffix cannot contain flow indicator characters")}return i&&!ie.test(i)&&d(e,"tag name cannot contain such characters: "+i),a?e.tag=i:H.call(e.tagMap,n)?e.tag=e.tagMap[n]+i:"!"===n?e.tag="!"+i:"!!"===n?e.tag="tag:yaml.org,2002:"+i:d(e,'undeclared tag handle "'+n+'"'),!0}function N(e){var t,n;if(n=e.input.charCodeAt(e.position),38!==n)return!1;for(null!==e.anchor&&d(e,"duplication of an anchor property"),n=e.input.charCodeAt(++e.position),t=e.position;0!==n&&!o(n)&&!a(n);)n=e.input.charCodeAt(++e.position);return e.position===t&&d(e,"name of an anchor node must contain at least one character"),e.anchor=e.input.slice(t,e.position),!0}function T(e){var t,n,i;if(i=e.input.charCodeAt(e.position),42!==i)return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!o(i)&&!a(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&d(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),e.anchorMap.hasOwnProperty(n)||d(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],A(e,!0,-1),!0}function _(e,t,n,i,r){var o,a,s,c,u,l,p,f,h=1,m=!1,g=!1;if(null!==e.listener&&e.listener("open",e),e.tag=null,e.anchor=null,e.kind=null,e.result=null,o=a=s=Z===n||V===n,i&&A(e,!0,-1)&&(m=!0,e.lineIndent>t?h=1:e.lineIndent===t?h=0:e.lineIndent<t&&(h=-1)),1===h)for(;F(e)||N(e);)A(e,!0,-1)?(m=!0,s=o,e.lineIndent>t?h=1:e.lineIndent===t?h=0:e.lineIndent<t&&(h=-1)):s=!1;if(s&&(s=m||r),1!==h&&Z!==n||(p=W===n||G===n?t:t+1,f=e.position-e.lineStart,1===h?s&&(E(e,f)||O(e,f,p))||I(e,p)?g=!0:(a&&S(e,p)||k(e,p)||j(e,p)?g=!0:T(e)?(g=!0,null===e.tag&&null===e.anchor||d(e,"alias node should not have any properties")):C(e,p,W===n)&&(g=!0,null===e.tag&&(e.tag="?")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===h&&(g=s&&E(e,f))),null!==e.tag&&"!"!==e.tag)if("?"===e.tag){for(c=0,u=e.implicitTypes.length;u>c;c+=1)if(l=e.implicitTypes[c],l.resolve(e.result)){e.result=l.construct(e.result),e.tag=l.tag,null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);break}}else H.call(e.typeMap,e.tag)?(l=e.typeMap[e.tag],null!==e.result&&l.kind!==e.kind&&d(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+l.kind+'", not "'+e.kind+'"'),l.resolve(e.result)?(e.result=l.construct(e.result),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):d(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")):d(e,"unknown tag !<"+e.tag+">");return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||g}function M(e){var t,n,a,s,c=e.position,u=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap={},e.anchorMap={};0!==(s=e.input.charCodeAt(e.position))&&(A(e,!0,-1),s=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==s));){for(u=!0,s=e.input.charCodeAt(++e.position),t=e.position;0!==s&&!o(s);)s=e.input.charCodeAt(++e.position);for(n=e.input.slice(t,e.position),a=[],n.length<1&&d(e,"directive name must not be less than one character in length");0!==s;){for(;r(s);)s=e.input.charCodeAt(++e.position);if(35===s){do s=e.input.charCodeAt(++e.position);while(0!==s&&!i(s));break}if(i(s))break;for(t=e.position;0!==s&&!o(s);)s=e.input.charCodeAt(++e.position);a.push(e.input.slice(t,e.position))}0!==s&&v(e),H.call(se,n)?se[n](e,n,a):m(e,'unknown document directive "'+n+'"')}return A(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,A(e,!0,-1)):u&&d(e,"directives end mark is expected"),_(e,e.lineIndent-1,Z,!1,!0),A(e,!0,-1),e.checkLineBreaks&&ee.test(e.input.slice(c,e.position))&&m(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&b(e)?void(46===e.input.charCodeAt(e.position)&&(e.position+=3,A(e,!0,-1))):void(e.position<e.length-1&&d(e,"end of the stream or a document separator is expected"))}function L(e,t){e=String(e),t=t||{},0!==e.length&&(10!==e.charCodeAt(e.length-1)&&13!==e.charCodeAt(e.length-1)&&(e+="\n"),65279===e.charCodeAt(0)&&(e=e.slice(1)));var n=new f(e,t);for(n.input+="\x00";32===n.input.charCodeAt(n.position);)n.lineIndent+=1,n.position+=1;for(;n.position<n.length-1;)M(n);return n.documents}function D(e,t,n){var i,r,o=L(e,n);for(i=0,r=o.length;r>i;i+=1)t(o[i])}function U(e,t){var n=L(e,t);if(0!==n.length){if(1===n.length)return n[0];throw new $("expected a single document in the stream, but found more")}}function q(e,t,n){D(e,t,R.extend({schema:P},n))}function Y(e,t){return U(e,R.extend({schema:P},t))}for(var R=e("./common"),$=e("./exception"),B=e("./mark"),P=e("./schema/default_safe"),K=e("./schema/default_full"),H=Object.prototype.hasOwnProperty,W=1,G=2,V=3,Z=4,z=1,J=2,Q=3,X=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,ee=/[\x85\u2028\u2029]/,te=/[,\[\]\{\}]/,ne=/^(?:!|!!|![a-z\-]+!)$/i,ie=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i,re=new Array(256),oe=new Array(256),ae=0;256>ae;ae++)re[ae]=l(ae)?1:0,oe[ae]=l(ae);var se={YAML:function(e,t,n){var i,r,o;null!==e.version&&d(e,"duplication of %YAML directive"),1!==n.length&&d(e,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),null===i&&d(e,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),o=parseInt(i[2],10),1!==r&&d(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=2>o,1!==o&&2!==o&&m(e,"unsupported YAML version of the document")},TAG:function(e,t,n){var i,r;2!==n.length&&d(e,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],ne.test(i)||d(e,"ill-formed tag handle (first argument) of the TAG directive"),H.call(e.tagMap,i)&&d(e,'there is a previously declared suffix for "'+i+'" tag handle'),ie.test(r)||d(e,"ill-formed tag prefix (second argument) of the TAG directive"),e.tagMap[i]=r}};t.exports.loadAll=D,t.exports.load=U,t.exports.safeLoadAll=q,t.exports.safeLoad=Y},{"./common":2,"./exception":4,"./mark":6,"./schema/default_full":9,"./schema/default_safe":10}],6:[function(e,t,n){"use strict";function i(e,t,n,i,r){this.name=e,this.buffer=t,this.position=n,this.line=i,this.column=r}var r=e("./common");i.prototype.getSnippet=function(e,t){var n,i,o,a,s;if(!this.buffer)return null;for(e=e||4,t=t||75,n="",i=this.position;i>0&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(i-1));)if(i-=1,this.position-i>t/2-1){n=" ... ",i+=5;break}for(o="",a=this.position;a<this.buffer.length&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(a));)if(a+=1,a-this.position>t/2-1){o=" ... ",a-=5;break}return s=this.buffer.slice(i,a),r.repeat(" ",e)+n+s+o+"\n"+r.repeat(" ",e+this.position-i+n.length)+"^"},i.prototype.toString=function(e){var t,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet(),t&&(n+=":\n"+t)),n},t.exports=i},{"./common":2}],7:[function(e,t,n){"use strict";function i(e,t,n){var r=[];return e.include.forEach(function(e){n=i(e,t,n)}),e[t].forEach(function(e){n.forEach(function(t,n){t.tag===e.tag&&r.push(n)}),n.push(e)}),n.filter(function(e,t){return-1===r.indexOf(t)})}function r(){function e(e){i[e.tag]=e}var t,n,i={};for(t=0,n=arguments.length;n>t;t+=1)arguments[t].forEach(e);return i}function o(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach(function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new s("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=i(this,"implicit",[]),this.compiledExplicit=i(this,"explicit",[]),this.compiledTypeMap=r(this.compiledImplicit,this.compiledExplicit)}var a=e("./common"),s=e("./exception"),c=e("./type");o.DEFAULT=null,o.create=function(){var e,t;switch(arguments.length){case 1:e=o.DEFAULT,t=arguments[0];break;case 2:e=arguments[0],t=arguments[1];break;default:throw new s("Wrong number of arguments for Schema.create function")}if(e=a.toArray(e),t=a.toArray(t),!e.every(function(e){return e instanceof o}))throw new s("Specified list of super schemas (or a single Schema object) contains a non-Schema object.");if(!t.every(function(e){return e instanceof c}))throw new s("Specified list of YAML types (or a single Type object) contains a non-Type object.");return new o({include:e,explicit:t})},t.exports=o},{"./common":2,"./exception":4,"./type":13}],8:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./json")]})},{"../schema":7,"./json":12}],9:[function(e,t,n){"use strict";var i=e("../schema");t.exports=i.DEFAULT=new i({include:[e("./default_safe")],explicit:[e("../type/js/undefined"),e("../type/js/regexp"),e("../type/js/function")]})},{"../schema":7,"../type/js/function":18,"../type/js/regexp":19,"../type/js/undefined":20,"./default_safe":10}],10:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./core")],implicit:[e("../type/timestamp"),e("../type/merge")],explicit:[e("../type/binary"),e("../type/omap"),e("../type/pairs"),e("../type/set")]})},{"../schema":7,"../type/binary":14,"../type/merge":22,"../type/omap":24,"../type/pairs":25,"../type/set":27,"../type/timestamp":29,"./core":8}],11:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({explicit:[e("../type/str"),e("../type/seq"),e("../type/map")]})},{"../schema":7,"../type/map":21,"../type/seq":26,"../type/str":28}],12:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./failsafe")],implicit:[e("../type/null"),e("../type/bool"),e("../type/int"),e("../type/float")]})},{"../schema":7,"../type/bool":15,"../type/float":16,"../type/int":17,"../type/null":23,"./failsafe":11}],13:[function(e,t,n){"use strict";function i(e){var t={};return null!==e&&Object.keys(e).forEach(function(n){e[n].forEach(function(e){t[String(e)]=n})}),t}function r(e,t){if(t=t||{},Object.keys(t).forEach(function(t){if(-1===a.indexOf(t))throw new o('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')}),this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.defaultStyle=t.defaultStyle||null,this.styleAliases=i(t.styleAliases||null),-1===s.indexOf(this.kind))throw new o('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}var o=e("./exception"),a=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],s=["scalar","sequence","mapping"];t.exports=r},{"./exception":4}],14:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;var t,n,i=0,r=e.length,o=u;for(n=0;r>n;n++)if(t=o.indexOf(e.charAt(n)),!(t>64)){if(0>t)return!1;i+=6}return i%8===0}function r(e){var t,n,i=e.replace(/[\r\n=]/g,""),r=i.length,o=u,a=0,c=[];for(t=0;r>t;t++)t%4===0&&t&&(c.push(a>>16&255),c.push(a>>8&255),c.push(255&a)),a=a<<6|o.indexOf(i.charAt(t));return n=r%4*6,0===n?(c.push(a>>16&255),c.push(a>>8&255),c.push(255&a)):18===n?(c.push(a>>10&255),c.push(a>>2&255)):12===n&&c.push(a>>4&255),s?new s(c):c}function o(e){var t,n,i="",r=0,o=e.length,a=u;for(t=0;o>t;t++)t%3===0&&t&&(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+e[t];return n=o%3,
0===n?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}function a(e){return s&&s.isBuffer(e)}var s=e("buffer").Buffer,c=e("../type"),u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";t.exports=new c("tag:yaml.org,2002:binary",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../type":13,buffer:30}],15:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)}function r(e){return"true"===e||"True"===e||"TRUE"===e}function o(e){return"[object Boolean]"===Object.prototype.toString.call(e)}var a=e("../type");t.exports=new a("tag:yaml.org,2002:bool",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},{"../type":13}],16:[function(e,t,n){"use strict";function i(e){return null===e?!1:!!u.test(e)}function r(e){var t,n,i,r;return t=e.replace(/_/g,"").toLowerCase(),n="-"===t[0]?-1:1,r=[],"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:t.indexOf(":")>=0?(t.split(":").forEach(function(e){r.unshift(parseFloat(e,10))}),t=0,i=1,r.forEach(function(e){t+=e*i,i*=60}),n*t):n*parseFloat(t,10)}function o(e,t){var n;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(s.isNegativeZero(e))return"-0.0";return n=e.toString(10),l.test(n)?n.replace("e",".e"):n}function a(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!==0||s.isNegativeZero(e))}var s=e("../common"),c=e("../type"),u=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?|\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),l=/^[-+]?[0-9]+e/;t.exports=new c("tag:yaml.org,2002:float",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o,defaultStyle:"lowercase"})},{"../common":2,"../type":13}],17:[function(e,t,n){"use strict";function i(e){return e>=48&&57>=e||e>=65&&70>=e||e>=97&&102>=e}function r(e){return e>=48&&55>=e}function o(e){return e>=48&&57>=e}function a(e){if(null===e)return!1;var t,n=e.length,a=0,s=!1;if(!n)return!1;if(t=e[a],"-"!==t&&"+"!==t||(t=e[++a]),"0"===t){if(a+1===n)return!0;if(t=e[++a],"b"===t){for(a++;n>a;a++)if(t=e[a],"_"!==t){if("0"!==t&&"1"!==t)return!1;s=!0}return s}if("x"===t){for(a++;n>a;a++)if(t=e[a],"_"!==t){if(!i(e.charCodeAt(a)))return!1;s=!0}return s}for(;n>a;a++)if(t=e[a],"_"!==t){if(!r(e.charCodeAt(a)))return!1;s=!0}return s}for(;n>a;a++)if(t=e[a],"_"!==t){if(":"===t)break;if(!o(e.charCodeAt(a)))return!1;s=!0}return s?":"!==t?!0:/^(:[0-5]?[0-9])+$/.test(e.slice(a)):!1}function s(e){var t,n,i=e,r=1,o=[];return-1!==i.indexOf("_")&&(i=i.replace(/_/g,"")),t=i[0],"-"!==t&&"+"!==t||("-"===t&&(r=-1),i=i.slice(1),t=i[0]),"0"===i?0:"0"===t?"b"===i[1]?r*parseInt(i.slice(2),2):"x"===i[1]?r*parseInt(i,16):r*parseInt(i,8):-1!==i.indexOf(":")?(i.split(":").forEach(function(e){o.unshift(parseInt(e,10))}),i=0,n=1,o.forEach(function(e){i+=e*n,n*=60}),r*i):r*parseInt(i,10)}function c(e){return"[object Number]"===Object.prototype.toString.call(e)&&e%1===0&&!u.isNegativeZero(e)}var u=e("../common"),l=e("../type");t.exports=new l("tag:yaml.org,2002:int",{kind:"scalar",resolve:a,construct:s,predicate:c,represent:{binary:function(e){return"0b"+e.toString(2)},octal:function(e){return"0"+e.toString(8)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return"0x"+e.toString(16).toUpperCase()}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},{"../common":2,"../type":13}],18:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;try{var t="("+e+")",n=s.parse(t,{range:!0});return"Program"===n.type&&1===n.body.length&&"ExpressionStatement"===n.body[0].type&&"FunctionExpression"===n.body[0].expression.type}catch(i){return!1}}function r(e){var t,n="("+e+")",i=s.parse(n,{range:!0}),r=[];if("Program"!==i.type||1!==i.body.length||"ExpressionStatement"!==i.body[0].type||"FunctionExpression"!==i.body[0].expression.type)throw new Error("Failed to resolve function");return i.body[0].expression.params.forEach(function(e){r.push(e.name)}),t=i.body[0].expression.body.range,new Function(r,n.slice(t[0]+1,t[1]-1))}function o(e){return e.toString()}function a(e){return"[object Function]"===Object.prototype.toString.call(e)}var s;try{var c=e;s=c("esprima")}catch(u){"undefined"!=typeof window&&(s=window.esprima)}var l=e("../../type");t.exports=new l("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13}],19:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;if(0===e.length)return!1;var t=e,n=/\/([gim]*)$/.exec(e),i="";if("/"===t[0]){if(n&&(i=n[1]),i.length>3)return!1;if("/"!==t[t.length-i.length-1])return!1}return!0}function r(e){var t=e,n=/\/([gim]*)$/.exec(e),i="";return"/"===t[0]&&(n&&(i=n[1]),t=t.slice(1,t.length-i.length-1)),new RegExp(t,i)}function o(e){var t="/"+e.source+"/";return e.global&&(t+="g"),e.multiline&&(t+="m"),e.ignoreCase&&(t+="i"),t}function a(e){return"[object RegExp]"===Object.prototype.toString.call(e)}var s=e("../../type");t.exports=new s("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13}],20:[function(e,t,n){"use strict";function i(){return!0}function r(){}function o(){return""}function a(e){return"undefined"==typeof e}var s=e("../../type");t.exports=new s("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13}],21:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return null!==e?e:{}}})},{"../type":13}],22:[function(e,t,n){"use strict";function i(e){return"<<"===e||null===e}var r=e("../type");t.exports=new r("tag:yaml.org,2002:merge",{kind:"scalar",resolve:i})},{"../type":13}],23:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t=e.length;return 1===t&&"~"===e||4===t&&("null"===e||"Null"===e||"NULL"===e)}function r(){return null}function o(e){return null===e}var a=e("../type");t.exports=new a("tag:yaml.org,2002:null",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})},{"../type":13}],24:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n,i,r,o,c=[],u=e;for(t=0,n=u.length;n>t;t+=1){if(i=u[t],o=!1,"[object Object]"!==s.call(i))return!1;for(r in i)if(a.call(i,r)){if(o)return!1;o=!0}if(!o)return!1;if(-1!==c.indexOf(r))return!1;c.push(r)}return!0}function r(e){return null!==e?e:[]}var o=e("../type"),a=Object.prototype.hasOwnProperty,s=Object.prototype.toString;t.exports=new o("tag:yaml.org,2002:omap",{kind:"sequence",resolve:i,construct:r})},{"../type":13}],25:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n,i,r,o,s=e;for(o=new Array(s.length),t=0,n=s.length;n>t;t+=1){if(i=s[t],"[object Object]"!==a.call(i))return!1;if(r=Object.keys(i),1!==r.length)return!1;o[t]=[r[0],i[r[0]]]}return!0}function r(e){if(null===e)return[];var t,n,i,r,o,a=e;for(o=new Array(a.length),t=0,n=a.length;n>t;t+=1)i=a[t],r=Object.keys(i),o[t]=[r[0],i[r[0]]];return o}var o=e("../type"),a=Object.prototype.toString;t.exports=new o("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:i,construct:r})},{"../type":13}],26:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return null!==e?e:[]}})},{"../type":13}],27:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n=e;for(t in n)if(a.call(n,t)&&null!==n[t])return!1;return!0}function r(e){return null!==e?e:{}}var o=e("../type"),a=Object.prototype.hasOwnProperty;t.exports=new o("tag:yaml.org,2002:set",{kind:"mapping",resolve:i,construct:r})},{"../type":13}],28:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return null!==e?e:""}})},{"../type":13}],29:[function(e,t,n){"use strict";function i(e){return null===e?!1:null!==s.exec(e)?!0:null!==c.exec(e)}function r(e){var t,n,i,r,o,a,u,l,p,f,h=0,d=null;if(t=s.exec(e),null===t&&(t=c.exec(e)),null===t)throw new Error("Date resolve error");if(n=+t[1],i=+t[2]-1,r=+t[3],!t[4])return new Date(Date.UTC(n,i,r));if(o=+t[4],a=+t[5],u=+t[6],t[7]){for(h=t[7].slice(0,3);h.length<3;)h+="0";h=+h}return t[9]&&(l=+t[10],p=+(t[11]||0),d=6e4*(60*l+p),"-"===t[9]&&(d=-d)),f=new Date(Date.UTC(n,i,r,o,a,u,h)),d&&f.setTime(f.getTime()-d),f}function o(e){return e.toISOString()}var a=e("../type"),s=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),c=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");t.exports=new a("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:i,construct:r,instanceOf:Date,represent:o})},{"../type":13}],30:[function(e,t,n){},{}],"/":[function(e,t,n){"use strict";var i=e("./lib/js-yaml.js");t.exports=i},{"./lib/js-yaml.js":1}]},{},[])("/")});
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="115.333px" height="114px" viewBox="0 0 115.333 114" enable-background="new 0 0 115.333 114" xml:space="preserve">
<g id="Layer_2">
</g>
<g>
<path fill="#FFFFFF" d="M56.667,0.667C25.372,0.667,0,26.036,0,57.332c0,31.295,25.372,56.666,56.667,56.666
s56.666-25.371,56.666-56.666C113.333,26.036,87.961,0.667,56.667,0.667z M56.667,106.722c-8.904,0-16.123-5.948-16.123-13.283
H72.79C72.79,100.773,65.571,106.722,56.667,106.722z M83.297,89.04H30.034v-9.658h53.264V89.04z M83.106,74.411h-52.92
c-0.176-0.203-0.356-0.403-0.526-0.609c-5.452-6.62-6.736-10.076-7.983-13.598c-0.021-0.116,6.611,1.355,11.314,2.413
c0,0,2.42,0.56,5.958,1.205c-3.397-3.982-5.414-9.044-5.414-14.218c0-11.359,8.712-21.285,5.569-29.308
c3.059,0.249,6.331,6.456,6.552,16.161c3.252-4.494,4.613-12.701,4.613-17.733c0-5.21,3.433-11.262,6.867-11.469
c-3.061,5.045,0.793,9.37,4.219,20.099c1.285,4.03,1.121,10.812,2.113,15.113C63.797,33.534,65.333,20.5,71,16
c-2.5,5.667,0.37,12.758,2.333,16.167c3.167,5.5,5.087,9.667,5.087,17.548c0,5.284-1.951,10.259-5.242,14.148
c3.742-0.702,6.326-1.335,6.326-1.335l12.152-2.371C91.657,60.156,89.891,67.418,83.106,74.411z"/>
</g>
</svg>
// Setup
// Modify the diameter to expand/contract space between nodes.
var anchor = document.querySelector(".page-header").parentElement;
var diameter = anchor.clientWidth;
var color = "#e6522c";
var tree = d3.layout.tree()
.size([360, diameter / 2 - 120])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
var svg;
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("background-color", "white")
.style("border", "1px solid #ddd")
.style("font", "9px monospace")
.style("padding", "4px 2px")
.style("z-index", "10")
.style("visibility", "hidden");
function parseSearch(searchString) {
var labels = searchString.replace(/{|}|\"|\s/g, "").split(",");
var o = {};
labels.forEach(function(label) {
var l = label.split("=");
o[l[0]] = l[1];
});
return o;
}
function resetSVG() {
d3.select(anchor).select("svg").remove()
svg = d3.select(anchor).append("svg")
.classed("routing-table", true)
.attr("width", diameter)
.attr("height", diameter - 150)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + (diameter / 2 - 200) + ")");
}
// Click handler for reading config.yml
d3.select(".js-parse-and-draw").on("click", function() {
var config = document.querySelector(".js-config-yml").value;
var parsedConfig = jsyaml.load(config);
// Create a new SVG for each time a config is loaded.
resetSVG();
loadConfig(parsedConfig);
});
// Click handler for input labelSet
d3.select(".js-find-match").on("click", function() {
labelServiceHandler();
});
$(document).on("keyup", function(e) {
if (e.keyCode != 13) {
return;
}
labelServiceHandler();
});
function labelServiceHandler() {
var searchValue = document.querySelector(".js-label-set-input").value
var labelSet = parseSearch(searchValue);
var matches = match(root, labelSet)
var nodes = tree.nodes(root);
var idx = nodes.map(function(n) { return n.id }).indexOf(matches[0].id)
nodes.forEach(function(n) { n.matched = false });
nodes[idx].matched = true;
update(root);
}
// Match does a depth-first left-to-right search through the route tree
// and returns the matching routing nodes.
function match(root, labelSet) {
// See if the node is a match. If it is, recurse through the children.
if (!matchLabels(root.matchers, labelSet)) {
return [];
}
var all = []
if (root.children) {
for (var j = 0; j < root.children.length; j++) {
child = root.children[j];
matches = match(child, labelSet)
all = all.concat(matches);
if (matches && !child.continue) {
break;
}
}
}
// If no child nodes were matches, the current node itself is a match.
if (all.length === 0) {
all.push(root);
}
return all
}
// Compare set of matchers to labelSet
function matchLabels(matchers, labelSet) {
for (var j = 0; j < matchers.length; j++) {
if (!matchLabel(matchers[j], labelSet)) {
return false;
}
}
return true;
}
// Compare single matcher to labelSet
function matchLabel(matcher, labelSet) {
var v = labelSet[matcher.name];
if (matcher.isRegex) {
return matcher.value.test(v)
}
return matcher.value === v;
}
// Load the parsed config and create the tree
function loadConfig(config) {
root = config.route;
root.parent = null;
massage(root)
update(root);
}
// Translate AlertManager names to expected d3 tree names, convert AlertManager
// Match and MatchRE objects to js objects.
function massage(root) {
if (!root) return;
root.children = root.routes
if (root.continue != false) {
root.continue = true;
}
var matchers = []
if (root.match) {
for (var key in root.match) {
var o = {};
o.isRegex = false;
o.value = root.match[key];
o.name = key;
matchers.push(o);
}
}
if (root.match_re) {
for (var key in root.match_re) {
var o = {};
o.isRegex = true;
o.value = new RegExp(root.match_re[key]);
o.name = key;
matchers.push(o);
}
}
root.matchers = matchers;
if (!root.children) return;
root.children.forEach(function(child) {
child.parent = root;
massage(child)
});
}
// Update the tree based on root.
function update(root) {
var i = 0;
var nodes = tree.nodes(root);
var links = tree.links(nodes);
var matchedNode = nodes.find(function(n) { return n.matched })
var highlight = [];
if (matchedNode) {
highlight = [matchedNode]
while (matchedNode.parent) {
highlight.push(matchedNode.parent);
matchedNode = matchedNode.parent;
}
}
var link = svg.selectAll(".link").data(links);
link.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
if (highlight.length) {
link.style("stroke", function(d) {
if (highlight.indexOf(d.source) > -1 && highlight.indexOf(d.target) > -1) {
return color
}
return "#ccc"
});
}
var node = svg.selectAll(".node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
})
nodeEnter.append("circle")
.attr("r", 4.5);
nodeEnter.append("text")
.attr("dy", ".31em")
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; })
.text(function(d) { return d.receiver; });
node.select(".node circle").style("fill", function(d) {
return d.matched ? color : "#fff";
})
.on("mouseover", function(d) {
d3.select(this).style("fill", color);
// Show all matchers for node and ancestors
matchers = aggregateMatchers(d);
text = formatMatcherText(matchers);
text.forEach(function(t) {
tooltip.append("div").text(t);
});
if (text.length) {
return tooltip.style("visibility", "visible");
}
})
.on("mousemove", function() {
return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");
})
.on("mouseout", function(d) {
d3.select(this).style("fill", d.matched ? color : "#fff");
tooltip.text("");
return tooltip.style("visibility", "hidden");
});
}
function formatMatcherText(matchersArray) {
return matchersArray.map(function(m) {
return m.name + ": " + m.value;
});
}
function aggregateMatchers(node) {
var n = node
matchers = [];
while (n.parent) {
matchers = matchers.concat(n.matchers);
n = n.parent;
}
return matchers
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment