Fix color and opacity legend clip

This commit is contained in:
Ivan Mikheykin
2018-10-04 19:21:49 +03:00
parent cf31fded9d
commit af55883905
5 changed files with 97 additions and 36 deletions
+8 -1
View File
@@ -17,7 +17,7 @@
## Installation
Plugin can be installed via Git:
Plugin can be installed with GF_INSTALL_PLUGINS="flant-statusmap-panel" or you can use Git and clone this repo:
```
git clone git@github.com:flant/grafana-statusmap.git /var/lib/grafana/plugins/flant-statusmap-panel
@@ -26,6 +26,7 @@ git clone git@github.com:flant/grafana-statusmap.git /var/lib/grafana/plugins/fl
Alternatively, you can download [ZIP archive](https://github.com/flant/grafana-statusmap/archive/master.zip)
of this repo and unpack it into /var/lib/grafana/plugins directory.
## Motivation
We had a desperate need to show a set of timeseries statuses over time period, so we can see
@@ -137,7 +138,13 @@ This plugin is based on "Heatmap" panel by Grafana and partly inspired by ideas
#### Changelog
##### v0.0.2
- Install with GF_INSTALL_PLUGINS
- Fix legend overlap
##### v0.0.1
- First public release
+77 -25
View File
@@ -4,11 +4,11 @@
## Features
* Group values into rows and buckets by query's legend
* Grouping values into rows and buckets using legend from query
* User defined color mapping
* Multiple values in bucket display in tooltip
* Interval shaping to better visual representation
* Represent null values as empty bucket or as zero value
* Multiple values in bucket can be displayed via tooltip
* Increasing rows/buckets' interval for better visual representation
* Representing null values as empty bucket or zero value
### Supported environment
@@ -17,59 +17,104 @@
## Installation
Plugin can be installed with git:
Plugin can be installed with GF_INSTALL_PLUGINS="flant-statusmap-panel" or you can use Git and clone this repo:
```
git clone git@github.com:flant/grafana-statusmap.git /var/lib/grafana/plugins/flant-statusmap-panel
```
Or you can download ZIP archive of this repo and unpack it into /var/lib/grafana/plugins directory.
Alternatively, you can download [ZIP archive](https://github.com/flant/grafana-statusmap/archive/master.zip)
of this repo and unpack it into /var/lib/grafana/plugins directory.
## Motivation
This plugin emerges from our needs to visually represent history of changes for a set of objects
with discrete statuses.
_Objects_ can be hosts, Kubernetes pods or coffee makers and _discrete statuses_ are a set
of predefined values: something like `ok` = 0, `off` = 1, `fail` = 2.
We had a desperate need to show a set of timeseries statuses over time period, so we can see
a history of changes for objects' statuses. Since we maintain a lot of Kubernetes clusters
(and related infrastructure), our main cases for that are visualization of servers & Kubernetes
pods health states as well as HTTP services health checks. We've tried a variety of Grafana
plugins available (they are listed in *Acknowledgements* below) but none of them could provide
the features and visualization really close to what we've been looking for.
_Objects_ being visualized with this plugin may be different: not only IT components (e.g. server
hosts and Kubernetes pods) but just anything you can imagine like coffee makers on the picture
above. These objects should have _discrete statuses_ which are sets of predefined values, e.g.
`ok` = 0, `off` = 1, `fail` = 2, etc.
## Configuration
Recommended setup is to create query for each possible status value with similar legend:
### Prometheus
To work with data from Prometheus you will need to setup discrete statuses of your objects.
Requirements to store these statuses in metrics are as follows:
* metrics should have two values: `0` and `1`;
* there should be a label with status' value.
When it's done, you can collect all the data via query, e.g.:
```
(max_over_time(coffee_maker_status{status="<STATUS_VALUE>"}[$__interval]) == 1) * <STATUS_VALUE>
```
If there was no such status (`<STATUS_VALUE>`) during query's interval, Prometheus would
return nothing. Otherwise, status' value will be returned.
For example, if you have 5 statuses and a metric (`coffee_maker_status`) with 5 allowed
values (`0`, `1`, `2`, `3`, `4`), you should transform this metric using the following rule:
```
- record: coffee_maker_status:discrete
expr: |
count_values("status", coffee_maker_status)
```
That's how `coffee_maker_status` metric with value `3` will be transformed into new metric:
```
coffee_maker_status:discrete{status="3"} 1
```
Now, when Prometheus has `0` and `1` values for each status, all these metrics can be
aggregated, so you will get all available statuses of your objects over time.
### Panel
First of all, an individual query for each possible status' value should be created.
Each query should also have similar legend for grouping:
![Query setup](https://raw.githubusercontent.com/flant/grafana-statusmap/master/src/img/queries-example.png)
Next define color mapping for status values in __Discrete__ color mode.
Then, color mapping for status' values should be defined in __Discrete__ color mode:
![Color mapping](https://raw.githubusercontent.com/flant/grafana-statusmap/master/src/img/color-mapping.png)
__Spectrum__ and __Opacity__ color modes works as in a [Heatmap](https://grafana.com/plugins/heatmap) plugin.
_Note: __Spectrum__ and __Opacity__ color modes function the same way they do in [Heatmap](https://grafana.com/plugins/heatmap) plugin._
### More options
![Bucket options](https://raw.githubusercontent.com/flant/grafana-statusmap/master/src/img/options-bucket.png)
__Multiple values__ check determine multiple values display mode. If check is unset then multiple values
for one bucket treated as error. If check is on then color for bucket determined
by value with least index in color mapping.
__Multiple values__ checkbox specifies how they should be displayed:
* If it's off, multiple values for one bucket are treated as error;
* If it's on, color for such bucket would be determined by the value having least index in color mapping.
![Color mapping](https://raw.githubusercontent.com/flant/grafana-statusmap/master/src/img/multiple-values-error.png)
__Null values__ can be treated as empty buckets or displayed as color of 0 value.
__Null values__ can be treated as empty buckets or displayed with the color of `0` value.
![Color mapping](https://raw.githubusercontent.com/flant/grafana-statusmap/master/src/img/null-as-empty.png)
__Min width__ and __spacing__ are determine minimal bucket width and spacing between buckets.
__Rounding__ is for round edges.
__Min width__ and __spacing__ are used to specify minimal bucket width and spacing between buckets.
__Rounding__ may be used to round edges.
![Min width, spacing, rounding](https://raw.githubusercontent.com/flant/grafana-statusmap/master/src/img/min-width-spacing-rounding.png)
## Development
The easy way to test and develop plugin is to run Grafana instance in docker with following command in the directory containing the plugin.
This will expose the local plugin on your machine to the Grafana container.
To test and improve the plugin you can run Grafana instance in Docker using following command (in
the directory containing Statusmap plugin):
```
docker run --rm -it -v $PWD:/var/lib/grafana/plugins/flant-statusmap-panel \
@@ -78,7 +123,8 @@ docker run --rm -it -v $PWD:/var/lib/grafana/plugins/flant-statusmap-panel \
grafana/grafana:5.1.3
```
Now run `grunt` to compile dist directory and start changes watcher:
This will expose local plugin from your machine to Grafana container. Now run `grunt` to compile
dist directory and start changes watcher:
```
grunt watch
@@ -86,13 +132,19 @@ grunt watch
## Acknowledgements
Idea of a plugin comes from Dmitry Stolyarov @distol, initial version written by Sergey Gnuskov @gsmetal and final changes made by Ivan Mikheykin @diafour.
The first public release of this plugin has been fully made by [Flant](https://flant.com/) engineers. The whole idea has come from Dmitry Stolyarov ([@distol](https://github.com/distol)), initial version has been written by Sergey Gnuskov ([@gsmetal](https://github.com/gsmetal)) and final changes has been made by Ivan Mikheykin ([@diafour](https://github.com/diafour)).
This plugin is based on a "Heatmap" panel by Grafana and inspired by ideas from Carpet plot, Discrete panel, Status Panel, Status Dot, Status By Group.
This plugin is based on "Heatmap" panel by Grafana and partly inspired by ideas from Carpet plot, Discrete panel, Status Panel, Status Dot, Status By Group.
#### Changelog
##### v0.0.2
- Install with GF_INSTALL_PLUGINS
- Fix legend overlap
##### v0.0.1
- First public release
+8 -6
View File
@@ -10,7 +10,7 @@ System.register(['angular', 'lodash', 'jquery', 'd3', './libs/d3-scale-chromatic
var legend = d3.select(legendElem.get(0));
clearLegend(elem);
var legendWidth = Math.floor(legendElem.outerWidth()) - 30;
var legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
var legendHeight = legendElem.attr("height");
var rangeStep = 1;
@@ -27,8 +27,9 @@ System.register(['angular', 'lodash', 'jquery', 'd3', './libs/d3-scale-chromatic
var colorScale = getColorScale(colorScheme, maxValue, minValue);
legend.selectAll(".status-heatmap-color-legend-rect").data(valuesRange).enter().append("rect").attr("x", function (d) {
return d * widthFactor;
}).attr("y", 0).attr("width", rangeStep * widthFactor + 1) // Overlap rectangles to prevent gaps
return d * widthFactor + 10;
}) // shift all color rectangles to the right
.attr("y", 0).attr("width", rangeStep * widthFactor + 1) // Overlap rectangles to prevent gaps
.attr("height", legendHeight).attr("stroke-width", 0).attr("fill", function (d) {
return colorScale(d);
});
@@ -41,7 +42,7 @@ System.register(['angular', 'lodash', 'jquery', 'd3', './libs/d3-scale-chromatic
var legend = d3.select(legendElem.get(0));
clearLegend(elem);
var legendWidth = Math.floor(legendElem.outerWidth()) - 30;
var legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
var legendHeight = legendElem.attr("height");
var rangeStep = 10;
@@ -55,8 +56,9 @@ System.register(['angular', 'lodash', 'jquery', 'd3', './libs/d3-scale-chromatic
var opacityScale = getOpacityScale(options, maxValue, minValue);
legend.selectAll(".status-heatmap-opacity-legend-rect").data(valuesRange).enter().append("rect").attr("x", function (d) {
return d * widthFactor;
}).attr("y", 0).attr("width", rangeStep * widthFactor).attr("height", legendHeight).attr("stroke-width", 0).attr("fill", options.cardColor).style("opacity", function (d) {
return d * widthFactor + 10;
}) // shift all opacity rectangles to the right
.attr("y", 0).attr("width", rangeStep * widthFactor).attr("height", legendHeight).attr("stroke-width", 0).attr("fill", options.cardColor).style("opacity", function (d) {
return opacityScale(d);
});
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 64 KiB

+4 -4
View File
@@ -89,7 +89,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal
let legend = d3.select(legendElem.get(0));
clearLegend(elem);
let legendWidth = Math.floor(legendElem.outerWidth()) - 30;
let legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
let legendHeight = legendElem.attr("height");
let rangeStep = 1;
@@ -108,7 +108,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal
legend.selectAll(".status-heatmap-color-legend-rect")
.data(valuesRange)
.enter().append("rect")
.attr("x", d => d * widthFactor)
.attr("x", d => d * widthFactor + 10) // shift all color rectangles to the right
.attr("y", 0)
.attr("width", rangeStep * widthFactor + 1) // Overlap rectangles to prevent gaps
.attr("height", legendHeight)
@@ -123,7 +123,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue
let legend = d3.select(legendElem.get(0));
clearLegend(elem);
let legendWidth = Math.floor(legendElem.outerWidth()) - 30;
let legendWidth = Math.floor(legendElem.outerWidth()) - 30; // narrow legendWidth by 30px to get space for first and last tick values
let legendHeight = legendElem.attr("height");
let rangeStep = 10;
@@ -139,7 +139,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue
legend.selectAll(".status-heatmap-opacity-legend-rect")
.data(valuesRange)
.enter().append("rect")
.attr("x", d => d * widthFactor)
.attr("x", d => d * widthFactor + 10) // shift all opacity rectangles to the right
.attr("y", 0)
.attr("width", rangeStep * widthFactor)
.attr("height", legendHeight)