From 4a63f3adef18990c835a50c6168761147fe01534 Mon Sep 17 00:00:00 2001 From: Xavier Basty Date: Wed, 5 Apr 2023 01:22:57 +0200 Subject: [PATCH] fix: monitoring dashboard viz (#26) --- terraform/monitoring/dashboard.jsonnet | 62 +++++------ .../grafonnet/field_config.libsonnet | 104 +++++++++++++++++ .../monitoring/grafonnet/grafana.libsonnet | 45 ++++---- terraform/monitoring/grafonnet/link.libsonnet | 8 +- .../monitoring/grafonnet/panel.libsonnet | 74 ++++++++++++ .../monitoring/grafonnet/prometheus.libsonnet | 3 + .../grafonnet/timeseries_panel.libsonnet | 105 ++++++++++++++++++ 7 files changed, 344 insertions(+), 57 deletions(-) create mode 100644 terraform/monitoring/grafonnet/field_config.libsonnet create mode 100644 terraform/monitoring/grafonnet/panel.libsonnet create mode 100644 terraform/monitoring/grafonnet/timeseries_panel.libsonnet diff --git a/terraform/monitoring/dashboard.jsonnet b/terraform/monitoring/dashboard.jsonnet index 63adcd7..d879ea9 100644 --- a/terraform/monitoring/dashboard.jsonnet +++ b/terraform/monitoring/dashboard.jsonnet @@ -3,11 +3,11 @@ local full_width = layout.full_width; local half_width = layout.half_width; local height = 8; -local grafana = import 'grafonnet/grafana.libsonnet'; -local dashboard = grafana.dashboard; -local annotation = grafana.annotation; -local statPanel = grafana.statPanel; -local prometheus = grafana.prometheus; +local grafana = import 'grafonnet/grafana.libsonnet'; +local dashboard = grafana.dashboard; +local annotation = grafana.annotation; +local timeseriesPanel = grafana.timeseriesPanel; +local prometheus = grafana.prometheus; local ds_prometheus = { type: 'prometheus', @@ -17,7 +17,7 @@ local ds_prometheus = { dashboard.new( title = "%s - %s" % [std.extVar('stage'), std.extVar('name')], uid = "%s_%s" % [std.extVar('stage'), std.extVar('name')], - schemaVersion = 26, + schemaVersion = 35, editable = true, graphTooltip = 'shared_crosshair', ) @@ -28,73 +28,69 @@ dashboard.new( ) .addPanels( layout.generate_grid([ - statPanel.new( + timeseriesPanel.new( title = 'Received Items per Hour', - description = 'The number of items received from relay', datasource = ds_prometheus, - reducerFunction = 'lastNotNull', ) .addTarget(prometheus.target( - expr = 'sum(rate(received_items{}[1h]))', - legendFormat = 'Received Items', datasource = ds_prometheus, + expr = 'sum(rate(received_items{}[10m]))', + legendFormat = 'received items', + exemplar = true, )) { gridPos: { w: half_width, h: height } }, - statPanel.new( + timeseriesPanel.new( title = 'Stored Items per Hour', - description = 'The number of items actually stored in the database', datasource = ds_prometheus, - reducerFunction = 'lastNotNull', ) .addTarget(prometheus.target( - expr = 'sum(rate(stored_items{}[1h]))', - legendFormat = 'Stored Items', datasource = ds_prometheus, + expr = 'sum(rate(stored_items{}[10m]))', + legendFormat = 'stored items', + exemplar = true, )) { gridPos: { w: half_width, h: height } }, - statPanel.new( + timeseriesPanel.new( title = 'Get Queries per Hour', - description = 'The number of items retrieval queries', datasource = ds_prometheus, - reducerFunction = 'lastNotNull', ) .addTarget(prometheus.target( - expr = 'sum(rate(get_queries{}[1h]))', - legendFormat = '"Get" Queries', datasource = ds_prometheus, + expr = 'sum(rate(get_queries{}[10m]))', + legendFormat = 'get queries', + exemplar = true, )) { gridPos: { w: half_width, h: height } }, - statPanel.new( + timeseriesPanel.new( title = 'Served Items per Hour', - description = 'The number of messages served to clients', datasource = ds_prometheus, - reducerFunction = 'lastNotNull', ) .addTarget(prometheus.target( - expr = 'sum(rate(served_items{}[1h]))', - legendFormat = '"Get" Queries', datasource = ds_prometheus, + expr = 'sum(rate(served_items{}[10m]))', + legendFormat = 'served items', + exemplar = true, )) { gridPos: { w: half_width, h: height } }, - statPanel.new( + timeseriesPanel.new( title = 'Registrations per Hour', - description = 'The number of registrations retrievals', datasource = ds_prometheus, - reducerFunction = 'lastNotNull', ) .addTarget(prometheus.target( - expr = 'sum(rate(cached_registrations{}[1h]))', - legendFormat = 'Cache hits', datasource = ds_prometheus, + expr = 'sum(rate(cached_registrations{}[10m]))', + legendFormat = 'cached registrations', + exemplar = true, )) .addTarget(prometheus.target( - expr = 'sum(rate(fetched_registrations{}[1h]))', - legendFormat = 'Cache misses', datasource = ds_prometheus, + expr = 'sum(rate(fetched_registrations{}[10m]))', + legendFormat = 'fetched registrations', + exemplar = true, )) { gridPos: { w: full_width, h: height } }, ]) diff --git a/terraform/monitoring/grafonnet/field_config.libsonnet b/terraform/monitoring/grafonnet/field_config.libsonnet new file mode 100644 index 0000000..d8eb275 --- /dev/null +++ b/terraform/monitoring/grafonnet/field_config.libsonnet @@ -0,0 +1,104 @@ +local ThresholdMode = { + Absolute: 'absolute', + Percentage: 'percentage', +}; +local FieldColorModeId = { + ContinuousGrYlRd: 'continuous-GrYlRd', + Fixed: 'fixed', + PaletteClassic: 'palette-classic', + PaletteSaturated: 'palette-saturated', + Thresholds: 'thresholds', +}; +local FieldColorSeriesByMode = { + Min: 'min', + Max: 'max', + Last: 'last', +}; + +{ + /** + * Creates a default field config. + * + * @name field_config.default + * + * @param color (optional) Map values to a display color. + * @param custom (optional) ????. + * @param decimals (optional) Significant digits (for display). + * @param description (optional) Human readable field metadata. + * @param displayName (optional) The display value for this field. This supports template variables blank is auto. + * @param displayNameFromDS (optional) This can be used by data sources that return and explicit naming structure for values and labels. + * @param filterable (optional) True if data source field supports ad-hoc filters. + * @param min (optional) + * @param max (optional) + * @param noValue (optional) Alternative to empty string. + * @param path (optional) An explicit path to the field in the datasource. + * @param unit (optional) Numeric Options. + * @param writeable (optional) True if data source can write a value to the path. + * + * @method addThreshold(step) Adds a [threshold](https://grafana.com/docs/grafana/latest/panels/thresholds/) step. Argument format: `{ color: 'green', value: 0 }`. + * @method addThresholds(steps) Adds an array of threshold steps. + * @method addMapping(mapping) Adds a value mapping. + * @method addMappings(mappings) Adds an array of value mappings. + */ + default( + color=null, + custom={}, + decimals=null, + description=null, + displayName=null, + displayNameFromDS=null, + filterable=null, + min=null, + max=null, + noValue=null, + path=null, + unit=null, + thresholds=null, + writeable=null, + ):: { + color: if color != null then color else { + mode: FieldColorModeId.PaletteClassic + }, + custom: custom, + [if decimals != null then 'decimals']: decimals, + [if description != null then 'description']: description, + [if displayName != null then 'displayName']: displayName, + [if displayNameFromDS != null then 'displayNameFromDS']: displayNameFromDS, + [if filterable != null then 'filterable']: filterable, + [if min != null then 'min']: min, + [if max != null then 'max']: max, + [if noValue != null then 'noValue']: noValue, + [if path != null then 'path']: path, + [if unit != null then 'unit']: unit, + [if writeable != null then 'writeable']: writeable, + + thresholds: if thresholds != null then thresholds else { + mode: ThresholdMode.Absolute, + steps: [ + { + color: "green", + value: null + } + ] + }, + } + + + { + // Thresholds + addThreshold(step):: self { + thresholds+: { + steps+: [step], + } + }, + addThresholds(steps):: std.foldl(function(p, s) p.addThreshold(s), steps, self), + } + + + { + // Mappings + mappings: [], + addMapping(mapping):: self { + mappings+: [mapping], + }, + addMappings(mappings):: std.foldl(function(p, m) p.addMapping(m), mappings, self), + }, +} diff --git a/terraform/monitoring/grafonnet/grafana.libsonnet b/terraform/monitoring/grafonnet/grafana.libsonnet index b94ddf3..f2094cc 100644 --- a/terraform/monitoring/grafonnet/grafana.libsonnet +++ b/terraform/monitoring/grafonnet/grafana.libsonnet @@ -1,32 +1,35 @@ { + alertCondition:: import 'alert_condition.libsonnet', alertlist:: import 'alertlist.libsonnet', - dashboard:: import 'dashboard.libsonnet', - template:: import 'template.libsonnet', - text:: import 'text.libsonnet', - timepicker:: import 'timepicker.libsonnet', - row:: import 'row.libsonnet', - link:: import 'link.libsonnet', annotation:: import 'annotation.libsonnet', - graphPanel:: import 'graph_panel.libsonnet', - logPanel:: import 'log_panel.libsonnet', - tablePanel:: import 'table_panel.libsonnet', - singlestat:: import 'singlestat.libsonnet', - pieChartPanel:: import 'pie_chart_panel.libsonnet', - influxdb:: import 'influxdb.libsonnet', - prometheus:: import 'prometheus.libsonnet', - loki:: import 'loki.libsonnet', - sql:: import 'sql.libsonnet', - graphite:: import 'graphite.libsonnet', - alertCondition:: import 'alert_condition.libsonnet', + barGaugePanel:: import 'bar_gauge_panel.libsonnet', cloudmonitoring:: import 'cloudmonitoring.libsonnet', cloudwatch:: import 'cloudwatch.libsonnet', - elasticsearch:: import 'elasticsearch.libsonnet', - heatmapPanel:: import 'heatmap_panel.libsonnet', + dashboard:: import 'dashboard.libsonnet', dashlist:: import 'dashlist.libsonnet', - pluginlist:: import 'pluginlist.libsonnet', + elasticsearch:: import 'elasticsearch.libsonnet', + fieldConfig:: import 'field_config.libsonnet', gauge:: error 'gauge is removed, migrate to gaugePanel', gaugePanel:: import 'gauge_panel.libsonnet', - barGaugePanel:: import 'bar_gauge_panel.libsonnet', + graphPanel:: import 'graph_panel.libsonnet', + graphite:: import 'graphite.libsonnet', + heatmapPanel:: import 'heatmap_panel.libsonnet', + influxdb:: import 'influxdb.libsonnet', + legend:: import 'legend.libsonnet', + link:: import 'link.libsonnet', + logPanel:: import 'log_panel.libsonnet', + loki:: import 'loki.libsonnet', + pieChartPanel:: import 'pie_chart_panel.libsonnet', + pluginlist:: import 'pluginlist.libsonnet', + prometheus:: import 'prometheus.libsonnet', + row:: import 'row.libsonnet', + singlestat:: import 'singlestat.libsonnet', + sql:: import 'sql.libsonnet', statPanel:: import 'stat_panel.libsonnet', + tablePanel:: import 'table_panel.libsonnet', + template:: import 'template.libsonnet', + text:: import 'text.libsonnet', + timepicker:: import 'timepicker.libsonnet', + timeseriesPanel:: import 'timeseries_panel.libsonnet', transformation:: import 'transformation.libsonnet', } diff --git a/terraform/monitoring/grafonnet/link.libsonnet b/terraform/monitoring/grafonnet/link.libsonnet index 5e5ebd2..62f52ff 100644 --- a/terraform/monitoring/grafonnet/link.libsonnet +++ b/terraform/monitoring/grafonnet/link.libsonnet @@ -10,13 +10,14 @@ * @param icon (default: `'external link'`) Icon displayed with the link. * @param url (default: `''`) URL of the link * @param targetBlank (default: `false`) Whether the link will open in a new window. - * @param type (default: `'dashboards'`) + * @param type (default: `'dashboards'`) 'dashboards' or 'link' * - * @name link.dashboards + * @name link.new */ - dashboards( + new( title, tags, + tooltip='', asDropdown=true, includeVars=false, keepTime=false, @@ -32,6 +33,7 @@ keepTime: keepTime, tags: tags, title: title, + tooltip: tooltip, type: type, url: url, targetBlank: targetBlank, diff --git a/terraform/monitoring/grafonnet/panel.libsonnet b/terraform/monitoring/grafonnet/panel.libsonnet new file mode 100644 index 0000000..53d6946 --- /dev/null +++ b/terraform/monitoring/grafonnet/panel.libsonnet @@ -0,0 +1,74 @@ +{ + /** + * Dashboard panel. + * + * @name timeseries_panel.new + * + * @param title The title of the singlestat panel. + + + * @method addTarget(target) Adds a target object. + * @method addTargets(targets) Adds an array of targets. + * @method addLink(link) Adds a [panel link](https://grafana.com/docs/grafana/latest/linking/panel-links/). + * @method addLinks(links) Adds an array of links. + */ + new( + title, + description=null, + datasource=null, + fieldConfig=null, + interval=null, + libraryPanel=null, + maxDataPoints=null, + pluginVersion='8.4.7', + repeat=null, + repeatDirection=null, + repeatPanelId=null, + tags=null, + timeFrom=null, + timeRegions=null, + timeShift=null, + transformations=null, + transparent=null, + ):: + self + { + title: title, + [if description != '' then 'description']: description, + [if datasource != null then 'datasource']: datasource, + [if fieldConfig != null then 'fieldConfig']: fieldConfig, + [if interval != null then 'interval']: interval, + [if libraryPanel != null then 'libraryPanel']: libraryPanel, + [if maxDataPoints != null then 'maxDataPoints']: maxDataPoints, + options: {}, + pluginVersion: pluginVersion, + [if repeat != null then 'repeat']: repeat, + [if repeatDirection != null then 'repeatDirection']: repeatDirection, + [if repeatPanelId != null then 'repeatPanelId']: repeatPanelId, + [if tags != null then 'tags']: tags, + [if timeFrom != null then 'timeFrom']: timeFrom, + [if timeRegions != null then 'timeRegions']: timeRegions, + [if timeShift != null then 'timeShift']: timeShift, + [if transparent != null then 'transparent']: transparent, + } + + + { + // Targets + targets: [], + _nextTarget:: 0, + addTarget(target):: self { + local nextTarget = super._nextTarget, + _nextTarget: nextTarget + 1, + targets+: [target { refId: std.char(std.codepoint('A') + nextTarget) }], + }, + addTargets(targets):: std.foldl(function(p, t) p.addTarget(t), targets, self), + } + + + { + // Links + links: [], + addLink(link):: self { + links+: [link], + }, + addLinks(links):: std.foldl(function(p, l) p.addLink(l), links, self), + }, +} diff --git a/terraform/monitoring/grafonnet/prometheus.libsonnet b/terraform/monitoring/grafonnet/prometheus.libsonnet index 46b75b0..b9a6ec0 100644 --- a/terraform/monitoring/grafonnet/prometheus.libsonnet +++ b/terraform/monitoring/grafonnet/prometheus.libsonnet @@ -12,6 +12,7 @@ * @param datasource (optional) Name of the Prometheus datasource. Leave by default otherwise. * @param interval (optional) Time span used to aggregate or group data points by time. By default Grafana uses an automatic interval calculated based on the width of the graph. * @param instant (optional) Perform an "instant" query, to return only the latest value that Prometheus has scraped for the requested time series. Instant queries return results much faster than normal range queries. Use them to look up label sets. + * @param exemplar (optional) Enable "exemplar" tracking. * @param hide (optional) Set to `true` to hide the target from the panel. * * @return A Prometheus target to be added to panels. @@ -24,6 +25,7 @@ datasource=null, interval=null, instant=null, + exemplar=null, hide=null, ):: { [if hide != null then 'hide']: hide, @@ -34,5 +36,6 @@ legendFormat: legendFormat, [if interval != null then 'interval']: interval, [if instant != null then 'instant']: instant, + [if exemplar != null then 'exemplar']: exemplar, }, } diff --git a/terraform/monitoring/grafonnet/timeseries_panel.libsonnet b/terraform/monitoring/grafonnet/timeseries_panel.libsonnet new file mode 100644 index 0000000..8d5e481 --- /dev/null +++ b/terraform/monitoring/grafonnet/timeseries_panel.libsonnet @@ -0,0 +1,105 @@ +local panel = import 'panel.libsonnet'; +local fieldConfig = import 'field_config.libsonnet'; + + /** + * Timeseries panel panel. + * + * Options: + * @name timeseries_panel.new + * @name axisGridShow (optional) True to always show the axis grid. + * @name axisLabel (optional) Set a Y-axis text label. If you have more than one Y-axis, then you can assign different labels using an override. + * @name axisPlacement (default `auto`) Select the placement of the Y-axis. 'auto', 'left', 'right', 'hidden'. + * @name axisSoftMax (optional) + * @name axisSoftMin (optional) + * @name axisWidth (optional) Set a fixed width of the axis. By default, Grafana dynamically calculates the width of an axis. + * @name barAlignment (default 0) Set the position of the bar relative to a data point + * @name drawStyle (default `'line'`) + * @name fillOpacity (default 0) + * @name gradientMode (default `'none'`) 'none' | 'opacity' | 'hue' + * @name hideFrom (optional) + * @name lineInterpolation (default `'linear'`) 'linear' | 'smooth' + * @name lineStyle (optional) + * @name lineWidth (default 1) + * @name pointSize (default 5) + * @name scaleDistribution (optional) + * @name showPoints (default `'auto'`) 'auto' | 'always' | 'never' + * @name spanNulls (default `false`) + * @name stacking (optional) + * @name thresholdsStyle (optional) + * + * @param title The title of the singlestat panel. + + + * @method addTarget(target) Adds a target object. + * @method addTargets(targets) Adds an array of targets. + * @method addLink(link) Adds a [panel link](https://grafana.com/docs/grafana/latest/linking/panel-links/). + * @method addLinks(links) Adds an array of links. + */ +{ + type: 'timeseries', + fieldConfig: fieldConfig.default() + { + defaults: { + custom: { + axisPlacement: 'auto', + barAlignment: 0, + drawStyle: 'line', + fillOpacity: 0, + gradientMode: 'none', + lineInterpolation: 'linear', + lineWidth: 1, + pointSize: 5, + showPoints: 'auto', + spanNulls: false, + thresholdsStyle: { + mode: 'off' + } + } + } + }, + + options( + axisGridShow=null, + axisLabel=null, + axisPlacement=null, + axisSoftMax=null, + axisSoftMin=null, + axisWidth=null, + barAlignment=null, + drawStyle=null, + fillOpacity=null, + gradientMode=null, + hideFrom=null, + lineInterpolation=null, + lineStyle=null, + lineWidth=null, + pointSize=null, + scaleDistribution=null, + showPoints=null, + spanNulls=null, + stacking=null, + thresholdsStyle=null, + ):: self { + custom+: { + [if axisGridShow != null then 'axisGridShow']: axisGridShow, + [if axisLabel != null then 'axisLabel']: axisLabel, + [if axisPlacement != null then 'axisPlacement']: axisPlacement, + [if axisSoftMax != null then 'axisSoftMax']: axisSoftMax, + [if axisSoftMin != null then 'axisSoftMin']: axisSoftMin, + [if axisWidth != null then 'axisWidth']: axisWidth, + [if barAlignment != null then 'barAlignment']: barAlignment, + [if drawStyle != null then 'drawStyle']: drawStyle, + [if fillOpacity != null then 'fillOpacity']: fillOpacity, + [if gradientMode != null then 'gradientMode']: gradientMode, + [if hideFrom != null then 'hideFrom']: hideFrom, + [if hideFrom != null then 'hideFrom']: hideFrom, + [if lineStyle != null then 'lineStyle']: lineStyle, + [if lineWidth != null then 'lineWidth']: lineWidth, + [if pointSize != null then 'pointSize']: pointSize, + [if scaleDistribution != null then 'scaleDistribution']: scaleDistribution, + [if showPoints != null then 'showPoints']: showPoints, + [if spanNulls != null then 'spanNulls']: spanNulls, + [if stacking != null then 'stacking']: stacking, + [if thresholdsStyle != null then 'thresholdsStyle']: thresholdsStyle, + }, + }, +} + panel