diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/dashboard.jsonnet b/content/posts/build-a-grafana-dashboard-generator/examples/dashboard.jsonnet new file mode 100644 index 00000000..c6e2e6d3 --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/dashboard.jsonnet @@ -0,0 +1,69 @@ +local grafana = import 'grafonnet/grafana.libsonnet'; +local dashboard = grafana.dashboard; +local annotation = grafana.annotation; +local template = grafana.template; +local graphPanel = grafana.graphPanel; +local prometheus = grafana.prometheus; + +dashboard.new( + 'title', + editable=true, + refresh='5s', + time_from='now-6h', + time_to='now', + timepicker={}, + schemaVersion=27, + uid='uid', +) +.addAnnotation(annotation.default) +.addTemplate( + template.datasource( + name='cluster', + query='prometheus', + current='Thanos', + hide='', + ) +) +.addTemplate( + template.new( + name='namespace', + datasource='${cluster}', + query={ + query: 'label_values(grpc_io_server_completed_rpcs, namespace)', + refId: 'StandardVariableQuery' + }, + label='Namespace', + hide='', + refresh='load', + definition='label_values(grpc_io_server_completed_rpcs, namespace)' + ) +) +.addPanel( + graphPanel.new( + title='Number of requests', + datasource='${cluster}', + fill=1, + legend_show=true, + lines=true, + linewidth=1, + pointradius=2, + stack=true, + shared_tooltip=true, + value_type='individual', + ).resetYaxes(). + addYaxis( + format='none', + ).addYaxis( + format='short', + ).addTarget( + prometheus.custom_target( + expr='expr', + legendFormat='legendFormat', + ) + ), gridPos={ + x: 0, + y: 1, + w: 18, + h: 10, + } +) \ No newline at end of file diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/go.mod b/content/posts/build-a-grafana-dashboard-generator/examples/go.mod new file mode 100644 index 00000000..b065ca48 --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/go.mod @@ -0,0 +1,15 @@ +module github.com/manabie-com/manabie-com.github.io/content/posts/build-a-grafana-dashboard-generator/examples + +go 1.18 + +require ( + github.com/google/go-jsonnet v0.18.0 + github.com/stretchr/testify v1.4.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v2 v2.2.7 // indirect + sigs.k8s.io/yaml v1.1.0 // indirect +) diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/go.sum b/content/posts/build-a-grafana-dashboard-generator/examples/go.sum new file mode 100644 index 00000000..824546c8 --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/go.sum @@ -0,0 +1,31 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= +github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/annotation.libsonnet b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/annotation.libsonnet new file mode 100644 index 00000000..955b029c --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/annotation.libsonnet @@ -0,0 +1,40 @@ +{ + default:: + { + builtIn: 1, + datasource: '-- Grafana --', + enable: true, + hide: true, + iconColor: 'rgba(0, 211, 255, 1)', + name: 'Annotations & Alerts', + type: 'dashboard', + }, + + /** + * @name annotation.datasource + */ + + datasource( + name, + datasource, + expr=null, + enable=true, + hide=false, + iconColor='rgba(255, 96, 96, 1)', + tags=[], + type='tags', + builtIn=null, + ):: + { + datasource: datasource, + enable: enable, + [if expr != null then 'expr']: expr, + hide: hide, + iconColor: iconColor, + name: name, + showIn: 0, + tags: tags, + type: type, + [if builtIn != null then 'builtIn']: builtIn, + }, +} diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/dashboard.libsonnet b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/dashboard.libsonnet new file mode 100644 index 00000000..1cc1bf32 --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/dashboard.libsonnet @@ -0,0 +1,181 @@ +local timepickerlib = import 'timepicker.libsonnet'; + +{ + /** + * Creates a [dashboard](https://grafana.com/docs/grafana/latest/features/dashboard/dashboards/) + * + * @name dashboard.new + * + * @param title The title of the dashboard + * @param editable (default: `false`) Whether the dashboard is editable via Grafana UI. + * @param style (default: `'dark'`) Theme of dashboard, `'dark'` or `'light'` + * @param tags (optional) Array of tags associated to the dashboard, e.g.`['tag1','tag2']` + * @param time_from (default: `'now-6h'`) + * @param time_to (default: `'now'`) + * @param timezone (default: `'browser'`) Timezone of the dashboard, `'utc'` or `'browser'` + * @param refresh (default: `''`) Auto-refresh interval, e.g. `'30s'` + * @param timepicker (optional) See timepicker API + * @param graphTooltip (default: `'default'`) `'default'` : no shared crosshair or tooltip (0), `'shared_crosshair'`: shared crosshair (1), `'shared_tooltip'`: shared crosshair AND shared tooltip (2) + * @param hideControls (default: `false`) + * @param schemaVersion (default: `14`) Version of the Grafana JSON schema, incremented each time an update brings changes. `26` for Grafana 7.1.5, `22` for Grafana 6.7.4, `16` for Grafana 5.4.5, `14` for Grafana 4.6.3. etc. + * @param uid (default: `''`) Unique dashboard identifier as a string (8-40), that can be chosen by users. Used to identify a dashboard to update when using Grafana REST API. + * @param description (optional) + * + * @method addTemplate(template) Add a template variable + * @method addTemplates(templates) Adds an array of template variables + * @method addAnnotation(annotation) Add an [annotation](https://grafana.com/docs/grafana/latest/dashboards/annotations/) + * @method addPanel(panel,gridPos) Appends a panel, with an optional grid position in grid coordinates, e.g. `gridPos={'x':0, 'y':0, 'w':12, 'h': 9}` + * @method addPanels(panels) Appends an array of panels + * @method addLink(link) Adds a [dashboard link](https://grafana.com/docs/grafana/latest/linking/dashboard-links/) + * @method addLinks(dashboardLink) Adds an array of [dashboard links](https://grafana.com/docs/grafana/latest/linking/dashboard-links/) + * @method addRequired(type, name, id, version) + * @method addInput(name, label, type, pluginId, pluginName, description, value) + * @method addRow(row) Adds a row. This is the legacy row concept from Grafana < 5, when rows were needed for layout. Rows should now be added via `addPanel`. + */ + new( + title, + editable=false, + style='dark', + tags=[], + time_from='now-6h', + time_to='now', + timezone='browser', + refresh='', + timepicker=timepickerlib.new(), + graphTooltip='default', + hideControls=false, + schemaVersion=14, + uid='', + description=null, + ):: { + local it = self, + _annotations:: [], + [if uid != '' then 'uid']: uid, + editable: editable, + [if description != null then 'description']: description, + gnetId: null, + graphTooltip: + if graphTooltip == 'shared_tooltip' then 2 + else if graphTooltip == 'shared_crosshair' then 1 + else if graphTooltip == 'default' then 0 + else graphTooltip, + hideControls: hideControls, + id: null, + links: [], + panels:: [], + refresh: refresh, + rows: [], + schemaVersion: schemaVersion, + style: style, + tags: tags, + time: { + from: time_from, + to: time_to, + }, + timezone: timezone, + timepicker: timepicker, + title: title, + version: 0, + addAnnotations(annotations):: self { + _annotations+:: annotations, + }, + addAnnotation(a):: self.addAnnotations([a]), + addTemplates(templates):: self { + templates+: templates, + }, + addTemplate(t):: self.addTemplates([t]), + templates:: [], + annotations: { list: it._annotations }, + templating: { list: it.templates }, + _nextPanel:: 2, + addRow(row):: + self { + // automatically number panels in added rows. + // https://github.com/kausalco/public/blob/master/klumps/grafana.libsonnet + local n = std.length(row.panels), + local nextPanel = super._nextPanel, + local panels = std.makeArray(n, function(i) + row.panels[i] { id: nextPanel + i }), + + _nextPanel: nextPanel + n, + rows+: [row { panels: panels }], + }, + addPanels(newpanels):: + self { + // automatically number panels in added rows. + // https://github.com/kausalco/public/blob/master/klumps/grafana.libsonnet + local n = std.foldl(function(numOfPanels, p) + (if 'panels' in p then + numOfPanels + 1 + std.length(p.panels) + else + numOfPanels + 1), newpanels, 0), + local nextPanel = super._nextPanel, + local _panels = std.makeArray( + std.length(newpanels), function(i) + newpanels[i] { + id: nextPanel + ( + if i == 0 then + 0 + else + if 'panels' in _panels[i - 1] then + (_panels[i - 1].id - nextPanel) + 1 + std.length(_panels[i - 1].panels) + else + (_panels[i - 1].id - nextPanel) + 1 + + ), + [if 'panels' in newpanels[i] then 'panels']: std.makeArray( + std.length(newpanels[i].panels), function(j) + newpanels[i].panels[j] { + id: 1 + j + + nextPanel + ( + if i == 0 then + 0 + else + if 'panels' in _panels[i - 1] then + (_panels[i - 1].id - nextPanel) + 1 + std.length(_panels[i - 1].panels) + else + (_panels[i - 1].id - nextPanel) + 1 + + ), + } + ), + } + ), + + _nextPanel: nextPanel + n, + panels+::: _panels, + }, + addPanel(panel, gridPos):: self.addPanels([panel { gridPos: gridPos }]), + addRows(rows):: std.foldl(function(d, row) d.addRow(row), rows, self), + addLink(link):: self { + links+: [link], + }, + addLinks(dashboardLinks):: std.foldl(function(d, t) d.addLink(t), dashboardLinks, self), + required:: [], + __requires: it.required, + addRequired(type, name, id, version):: self { + required+: [{ type: type, name: name, id: id, version: version }], + }, + inputs:: [], + __inputs: it.inputs, + addInput( + name, + label, + type, + pluginId=null, + pluginName=null, + description='', + value=null, + ):: self { + inputs+: [{ + name: name, + label: label, + type: type, + [if pluginId != null then 'pluginId']: pluginId, + [if pluginName != null then 'pluginName']: pluginName, + [if value != null then 'value']: value, + description: description, + }], + }, + }, +} diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/grafana.libsonnet b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/grafana.libsonnet new file mode 100644 index 00000000..7b1ac1cd --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/grafana.libsonnet @@ -0,0 +1,7 @@ +{ + dashboard:: import 'dashboard.libsonnet', + template:: import 'template.libsonnet', + annotation:: import 'annotation.libsonnet', + graphPanel:: import 'graph_panel.libsonnet', + prometheus:: import 'prometheus.libsonnet', +} diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/graph_panel.libsonnet b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/graph_panel.libsonnet new file mode 100644 index 00000000..c501ce9e --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/graph_panel.libsonnet @@ -0,0 +1,344 @@ +{ + /** + * Creates a [graph panel](https://grafana.com/docs/grafana/latest/panels/visualizations/graph-panel/). + * It requires the graph panel plugin in grafana, which is built-in. + * + * @name graphPanel.new + * + * @param title The title of the graph panel. + * @param description (optional) The description of the panel + * @param span (optional) Width of the panel + * @param datasource (optional) Datasource + * @param fill (default `1`) , integer from 0 to 10 + * @param fillGradient (default `0`) , integer from 0 to 10 + * @param linewidth (default `1`) Line Width, integer from 0 to 10 + * @param decimals (optional) Override automatic decimal precision for legend and tooltip. If null, not added to the json output. + * @param decimalsY1 (optional) Override automatic decimal precision for the first Y axis. If null, use decimals parameter. + * @param decimalsY2 (optional) Override automatic decimal precision for the second Y axis. If null, use decimals parameter. + * @param min_span (optional) Min span + * @param format (default `short`) Unit of the Y axes + * @param formatY1 (optional) Unit of the first Y axis + * @param formatY2 (optional) Unit of the second Y axis + * @param min (optional) Min of the Y axes + * @param max (optional) Max of the Y axes + * @param maxDataPoints (optional) If the data source supports it, sets the maximum number of data points for each series returned. + * @param labelY1 (optional) Label of the first Y axis + * @param labelY2 (optional) Label of the second Y axis + * @param x_axis_mode (default `'time'`) X axis mode, one of [time, series, histogram] + * @param x_axis_values (default `'total'`) Chosen value of series, one of [avg, min, max, total, count] + * @param x_axis_buckets (optional) Restricts the x axis to this amount of buckets + * @param x_axis_min (optional) Restricts the x axis to display from this value if supplied + * @param x_axis_max (optional) Restricts the x axis to display up to this value if supplied + * @param lines (default `true`) Display lines + * @param points (default `false`) Display points + * @param pointradius (default `5`) Radius of the points, allowed values are 0.5 or [1 ... 10] with step 1 + * @param bars (default `false`) Display bars + * @param staircase (default `false`) Display line as staircase + * @param dashes (default `false`) Display line as dashes + * @param stack (default `false`) Whether to stack values + * @param repeat (optional) Name of variable that should be used to repeat this panel. + * @param repeatDirection (default `'h'`) 'h' for horizontal or 'v' for vertical. + * @param legend_show (default `true`) Show legend + * @param legend_values (default `false`) Show values in legend + * @param legend_min (default `false`) Show min in legend + * @param legend_max (default `false`) Show max in legend + * @param legend_current (default `false`) Show current in legend + * @param legend_total (default `false`) Show total in legend + * @param legend_avg (default `false`) Show average in legend + * @param legend_alignAsTable (default `false`) Show legend as table + * @param legend_rightSide (default `false`) Show legend to the right + * @param legend_sideWidth (optional) Legend width + * @param legend_sort (optional) Sort order of legend + * @param legend_sortDesc (optional) Sort legend descending + * @param aliasColors (optional) Define color mappings for graphs + * @param thresholds (optional) An array of graph thresholds + * @param logBase1Y (default `1`) Value of logarithm base of the first Y axis + * @param logBase2Y (default `1`) Value of logarithm base of the second Y axis + * @param transparent (default `false`) Whether to display the panel without a background. + * @param value_type (default `'individual'`) Type of tooltip value + * @param shared_tooltip (default `true`) Allow to group or spit tooltips on mouseover within a chart + * @param percentage (defaut: false) show as percentages + * @param interval (defaut: null) A lower limit for the interval. + + * + * @method addTarget(target) Adds a target object. + * @method addTargets(targets) Adds an array of targets. + * @method addSeriesOverride(override) + * @method addYaxis(format,min,max,label,show,logBase,decimals) Adds a Y axis to the graph + * @method addAlert(alert) Adds an alert + * @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, + span=null, + fill=1, + fillGradient=0, + linewidth=1, + decimals=null, + decimalsY1=null, + decimalsY2=null, + description=null, + min_span=null, + format='short', + formatY1=null, + formatY2=null, + min=null, + max=null, + labelY1=null, + labelY2=null, + x_axis_mode='time', + x_axis_values='total', + x_axis_buckets=null, + x_axis_min=null, + x_axis_max=null, + lines=true, + datasource=null, + points=false, + pointradius=5, + bars=false, + staircase=false, + height=null, + nullPointMode='null', + dashes=false, + stack=false, + repeat=null, + repeatDirection=null, + sort=0, + show_xaxis=true, + legend_show=true, + legend_values=false, + legend_min=false, + legend_max=false, + legend_current=false, + legend_total=false, + legend_avg=false, + legend_alignAsTable=false, + legend_rightSide=false, + legend_sideWidth=null, + legend_hideEmpty=null, + legend_hideZero=null, + legend_sort=null, + legend_sortDesc=null, + aliasColors={}, + thresholds=[], + links=[], + logBase1Y=1, + logBase2Y=1, + transparent=false, + value_type='individual', + shared_tooltip=true, + percentage=false, + maxDataPoints=null, + time_from=null, + time_shift=null, + interval=null, + hiddenSeries=false, + unit=null, + ):: { + title: title, + [if span != null then 'span']: span, + [if min_span != null then 'minSpan']: min_span, + [if decimals != null then 'decimals']: decimals, + type: 'graph', + datasource: datasource, + targets: [ + ], + [if description != null then 'description']: description, + [if height != null then 'height']: height, + renderer: 'flot', + yaxes: [ + self.yaxe( + if formatY1 != null then formatY1 else format, + min, + max, + decimals=(if decimalsY1 != null then decimalsY1 else decimals), + logBase=logBase1Y, + label=labelY1 + ), + self.yaxe( + if formatY2 != null then formatY2 else format, + min, + max, + decimals=(if decimalsY2 != null then decimalsY2 else decimals), + logBase=logBase2Y, + label=labelY2 + ), + ], + xaxis: { + show: show_xaxis, + mode: x_axis_mode, + name: null, + values: if x_axis_mode == 'series' then [x_axis_values] else [], + buckets: if x_axis_mode == 'histogram' then x_axis_buckets else null, + [if x_axis_min != null then 'min']: x_axis_min, + [if x_axis_max != null then 'max']: x_axis_max, + }, + lines: lines, + fill: fill, + fillGradient: fillGradient, + linewidth: linewidth, + dashes: dashes, + dashLength: 10, + spaceLength: 10, + points: points, + pointradius: pointradius, + bars: bars, + stack: stack, + percentage: percentage, + [if maxDataPoints != null then 'maxDataPoints']: maxDataPoints, + legend: { + show: legend_show, + values: legend_values, + min: legend_min, + max: legend_max, + current: legend_current, + total: legend_total, + alignAsTable: legend_alignAsTable, + rightSide: legend_rightSide, + sideWidth: legend_sideWidth, + avg: legend_avg, + [if legend_hideEmpty != null then 'hideEmpty']: legend_hideEmpty, + [if legend_hideZero != null then 'hideZero']: legend_hideZero, + [if legend_sort != null then 'sort']: legend_sort, + [if legend_sortDesc != null then 'sortDesc']: legend_sortDesc, + }, + nullPointMode: nullPointMode, + steppedLine: staircase, + tooltip: { + value_type: value_type, + shared: shared_tooltip, + sort: if sort == 'decreasing' then 2 else if sort == 'increasing' then 1 else sort, + }, + timeFrom: time_from, + timeShift: time_shift, + [if interval != null then 'interval']: interval, + [if transparent == true then 'transparent']: transparent, + aliasColors: aliasColors, + repeat: repeat, + [if repeatDirection != null then 'repeatDirection']: repeatDirection, + seriesOverrides: [], + thresholds: thresholds, + links: links, + hiddenSeries: hiddenSeries, + yaxis: { + align: false, + alignLevel: null + }, + yaxe( + format='short', + min=null, + max=null, + label=null, + show=true, + logBase=1, + decimals=null, + ):: { + label: label, + show: show, + logBase: logBase, + min: min, + max: max, + format: format, + [if decimals != null then 'decimals']: decimals, + }, + _nextTarget:: 0, + [if unit != null then 'fieldConfig']: { + defaults: { + unit: unit + }, + }, + addTarget(target):: self { + // automatically ref id in added targets. + // https://github.com/kausalco/public/blob/master/klumps/grafana.libsonnet + 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), + addSeriesOverride(override):: self { + seriesOverrides+: [override], + }, + resetYaxes():: self { + yaxes: [], + }, + addYaxis( + format='short', + min=null, + max=null, + label=null, + show=true, + logBase=1, + decimals=null, + ):: self { + yaxes+: [self.yaxe(format, min, max, label, show, logBase, decimals)], + }, + addAlert( + name, + executionErrorState='alerting', + forDuration='5m', + frequency='60s', + handler=1, + message='', + noDataState='no_data', + notifications=[], + alertRuleTags={}, + ):: self { + local it = self, + _conditions:: [], + alert: { + name: name, + conditions: it._conditions, + executionErrorState: executionErrorState, + 'for': forDuration, + frequency: frequency, + handler: handler, + noDataState: noDataState, + notifications: notifications, + message: message, + alertRuleTags: alertRuleTags, + }, + addCondition(condition):: self { + _conditions+: [condition], + }, + addConditions(conditions):: std.foldl(function(p, c) p.addCondition(c), conditions, it), + }, + addLink(link):: self { + links+: [link], + }, + addLinks(links):: std.foldl(function(p, t) p.addLink(t), links, self), + addOverride( + matcher=null, + properties=null, + ):: self { + fieldConfig+: { + overrides+: [ + { + [if matcher != null then 'matcher']: matcher, + [if properties != null then 'properties']: properties, + }, + ], + }, + }, + addOverrides(overrides):: std.foldl(function(p, o) p.addOverride(o.matcher, o.properties), overrides, self), + addThreshold( + mode = 'absolute', + step, + ):: self { + fieldConfig+: { + defaults+: { + steps+: [step] + }, + }, + }, + addFieldConfigColorMode( + color_mode = 'thresholds' + ):: self { + fieldConfig+: { + color: { + mode: color_mode + }, + }, + }, + }, +} diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/prometheus.libsonnet b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/prometheus.libsonnet new file mode 100644 index 00000000..b4945acd --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/prometheus.libsonnet @@ -0,0 +1,58 @@ +{ + /** + * Creates a [Prometheus target](https://grafana.com/docs/grafana/latest/datasources/prometheus/) + * to be added to panels. + * + * @name prometheus.target + * + * @param expr PromQL query to be exercised against Prometheus. Checkout [Prometheus documentation](https://prometheus.io/docs/prometheus/latest/querying/basics/). + * @param format (default `'time_series'`) Switch between `'table'`, `'time_series'` or `'heatmap'`. Table will only work in the Table panel. Heatmap is suitable for displaying metrics of the Histogram type on a Heatmap panel. Under the hood, it converts cumulative histograms to regular ones and sorts series by the bucket bound. + * @param intervalFactor (default `2`) + * @param legendFormat (default `''`) Controls the name of the time series, using name or pattern. For example `{{hostname}}` is replaced with the label value for the label `hostname`. + * @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 hide (optional) Set to `true` to hide the target from the panel. + * + * @return A Prometheus target to be added to panels. + */ + target( + expr, + format='time_series', + intervalFactor=2, + legendFormat='', + datasource=null, + interval=null, + instant=null, + hide=null, + ):: { + [if hide != null then 'hide']: hide, + [if datasource != null then 'datasource']: datasource, + expr: expr, + format: format, + intervalFactor: intervalFactor, + legendFormat: legendFormat, + [if interval != null then 'interval']: interval, + [if instant != null then 'instant']: instant, + }, + custom_target( + expr='', + format=null, + intervalFactor=null, + legendFormat='', + datasource=null, + interval=null, + instant=null, + hide=null, + exemplar=true, + ):: { + [if hide != null then 'hide']: hide, + [if datasource != null then 'datasource']: datasource, + expr: expr, + [if format != null then 'format']: format, + [if intervalFactor != null then 'intervalFactor']: intervalFactor, + legendFormat: legendFormat, + [if interval != null then 'interval']: interval, + [if instant != null then 'instant']: instant, + }, +} diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/template.libsonnet b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/template.libsonnet new file mode 100644 index 00000000..1e0900f9 --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/grafonnet/template.libsonnet @@ -0,0 +1,291 @@ +{ + /** + * Creates a [template](https://grafana.com/docs/grafana/latest/variables/#templates) that can be added to a dashboard. + * + * @name template.new + * + * @param name Name of variable. + * @param datasource Template [datasource](https://grafana.com/docs/grafana/latest/variables/variable-types/add-data-source-variable/) + * @param query [Query expression](https://grafana.com/docs/grafana/latest/variables/variable-types/add-query-variable/) for the datasource. + * @param label (optional) Display name of the variable dropdown. If null, then the dropdown label will be the variable name. + * @param allValues (optional) Formatting for [multi-value variables](https://grafana.com/docs/grafana/latest/variables/formatting-multi-value-variables/#formatting-multi-value-variables) + * @param tagValuesQuery (default `''`) Group values into [selectable tags](https://grafana.com/docs/grafana/latest/variables/variable-value-tags/) + * @param current (default `null`) Can be `null`, `'all'` for all, or any other custom text value. + * @param hide (default `''`) `''`: the variable dropdown displays the variable Name or Label value. `'label'`: the variable dropdown only displays the selected variable value and a down arrow. Any other value: no variable dropdown is displayed on the dashboard. + * @param regex (default `''`) Regex expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with regex](https://grafana.com/docs/grafana/latest/variables/filter-variables-with-regex/). + * @param refresh (default `'never'`) `'never'`: variables queries are cached and values are not updated. This is fine if the values never change, but problematic if they are dynamic and change a lot. `'load'`: Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized. `'time'`: Queries the data source when the dashboard time range changes. Only use this option if your variable options query contains a time range filter or is dependent on the dashboard time range. + * @param includeAll (default `false`) Whether all value option is available or not. + * @param multi (default `false`) Whether multiple values can be selected or not from variable value list. + * @param sort (default `0`) `0`: Without Sort, `1`: Alphabetical (asc), `2`: Alphabetical (desc), `3`: Numerical (asc), `4`: Numerical (desc). + * + * @return A [template](https://grafana.com/docs/grafana/latest/variables/#templates) + */ + new( + name, + datasource, + query, + label=null, + allValues=null, + tagValuesQuery='', + current=null, + hide='', + regex='', + refresh='never', + includeAll=false, + multi=false, + sort=0, + definition, + ):: + { + allValue: allValues, + current: $.current(current), + datasource: datasource, + includeAll: includeAll, + hide: $.hide(hide), + label: label, + multi: multi, + name: name, + options: [], + query: query, + refresh: $.refresh(refresh), + regex: regex, + sort: sort, + tagValuesQuery: tagValuesQuery, + tags: [], + tagsQuery: '', + type: 'query', + useTags: false, + definition: definition, + }, + /** + * Use an [interval variable](https://grafana.com/docs/grafana/latest/variables/variable-types/add-interval-variable/) to represent time spans such as '1m', '1h', '1d'. You can think of them as a dashboard-wide "group by time" command. Interval variables change how the data is grouped in the visualization. You can also use the Auto Option to return a set number of data points per time span. + * You can use an interval variable as a parameter to group by time (for InfluxDB), date histogram interval (for Elasticsearch), or as a summarize function parameter (for Graphite). + * + * @name template.interval + * + * @param name Variable name + * @param query Comma separated values without spacing of intervals available for selection. Add `'auto'` in the query to turn on the Auto Option. Ex: `'auto,5m,10m,20m'`. + * @param current Currently selected interval. Must be one of the values in the query. `'auto'` is allowed if defined in the query. + * @param hide (default `''`) `''`: the variable dropdown displays the variable Name or Label value. `'label'`: the variable dropdown only displays the selected variable value and a down arrow. Any other value: no variable dropdown is displayed on the dashboard. + * @param label (optional) Display name of the variable dropdown. If null, then the dropdown label will be the variable name. + * @param auto_count (default `300`) Valid only if `'auto'` is defined in query. Number of times the current time range will be divided to calculate the value, similar to the Max data points query option. For example, if the current visible time range is 30 minutes, then the auto interval groups the data into 30 one-minute increments. The default value is 30 steps. + * @param auto_min (default `'10s'`) Valid only if `'auto'` is defined in query. The minimum threshold below which the step count intervals will not divide the time. To continue the 30 minute example, if the minimum interval is set to `'2m'`, then Grafana would group the data into 15 two-minute increments. + * + * @return A new interval variable for templating. + */ + interval( + name, + query, + current, + hide='', + label=null, + auto_count=300, + auto_min='10s', + ):: + { + current: $.current(current), + hide: $.hide(hide), + label: label, + name: name, + query: std.join(',', std.filter($.filterAuto, std.split(query, ','))), + refresh: 2, + type: 'interval', + auto: std.count(std.split(query, ','), 'auto') > 0, + auto_count: auto_count, + auto_min: auto_min, + }, + hide(hide):: + if hide == '' then 0 else if hide == 'label' then 1 else 2, + current(current):: { + [if current != null then 'text']: current, + [if current != null then 'value']: if current == 'auto' then + '$__auto_interval' + else if current == 'all' then + '$__all' + else + current, + }, + /** + * Data [source variables](https://grafana.com/docs/grafana/latest/variables/variable-types/add-data-source-variable/) + * allow you to quickly change the data source for an entire dashboard. + * They are useful if you have multiple instances of a data source, perhaps in different environments. + * + * @name template.datasource + * + * @param name Data source variable name. Ex: `'PROMETHEUS_DS'`. + * @param query Type of data source. Ex: `'prometheus'`. + * @param current Ex: `'Prometheus'`. + * @param hide (default `''`) `''`: the variable dropdown displays the variable Name or Label value. `'label'`: the variable dropdown only displays the selected variable value and a down arrow. Any other value: no variable dropdown is displayed on the dashboard. + * @param label (optional) Display name of the variable dropdown. If null, then the dropdown label will be the variable name. + * @param regex (default `''`) Regex filter for which data source instances to choose from in the variable value drop-down list. Leave this field empty to display all instances. + * @param refresh (default `'load'`) `'never'`: Variables queries are cached and values are not updated. This is fine if the values never change, but problematic if they are dynamic and change a lot. `'load'`: Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized. `'time'`: Queries the data source when the dashboard time range changes. Only use this option if your variable options query contains a time range filter or is dependent on the dashboard time range. + * + * @return A [data source variable](https://grafana.com/docs/grafana/latest/variables/variable-types/add-data-source-variable/). + */ + datasource( + name, + query, + current, + hide='', + label=null, + regex='', + refresh='load', + ):: { + current: $.current(current), + hide: $.hide(hide), + label: label, + name: name, + options: [], + query: query, + refresh: $.refresh(refresh), + regex: regex, + type: 'datasource', + }, + refresh(refresh):: if refresh == 'never' + then + 0 + else if refresh == 'load' + then + 1 + else if refresh == 'time' + then + 2 + else + refresh, + filterAuto(str):: str != 'auto', + /** + * Use a [custom variable](https://grafana.com/docs/grafana/latest/variables/variable-types/add-custom-variable/) + * for values that do not change. + * + * @name template.custom + * This might be numbers, strings, or even other variables. + * @param name Variable name + * @param query Comma separated without spacing list of selectable values. + * @param current Selected value + * @param refresh (default `'never'`) `'never'`: Variables queries are cached and values are not updated. This is fine if the values never change, but problematic if they are dynamic and change a lot. `'load'`: Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized. `'time'`: Queries the data source when the dashboard time range changes. Only use this option if your variable options query contains a time range filter or is dependent on the dashboard time range. + * @param label (default `''`) Display name of the variable dropdown. If you don’t enter a display name, then the dropdown label will be the variable name. + * @param valuelabels (default `{}`) Display names for values defined in query. For example, if `query='new,old'`, then you may display them as follows `valuelabels={new: 'nouveau', old: 'ancien'}`. + * @param multi (default `false`) Whether multiple values can be selected or not from variable value list. + * @param allValues (optional) Formatting for [multi-value variables](https://grafana.com/docs/grafana/latest/variables/formatting-multi-value-variables/#formatting-multi-value-variables) + * @param includeAll (default `false`) Whether all value option is available or not. + * @param hide (default `''`) `''`: the variable dropdown displays the variable Name or Label value. `'label'`: the variable dropdown only displays the selected variable value and a down arrow. Any other value: no variable dropdown is displayed on the dashboard. + * + * @return A custom variable. + */ + custom( + name, + query, + current, + refresh='never', + label='', + valuelabels={}, + multi=false, + allValues=null, + includeAll=false, + hide='', + ):: + { + // self has dynamic scope, so self may not be myself below. + // '$' can't be used neither as this object is not top-level object. + local custom = self, + + allValue: allValues, + current: { + // Both 'all' and 'All' are accepted for consistency. + value: if includeAll && (current == 'All' || current == 'all') then + if multi then ['$__all'] else '$__all' + else + current, + text: if std.isArray(current) then + std.join(' + ', std.map(custom.valuelabel, current)) + else + custom.valuelabel(current), + [if multi then 'selected']: true, + }, + options: std.map(self.option, self.query_array(query)), + hide: $.hide(hide), + includeAll: includeAll, + label: label, + refresh: $.refresh(refresh), + multi: multi, + name: name, + query: query, + type: 'custom', + + valuelabel(value):: if value in valuelabels then + valuelabels[value] + else value, + + option(option):: { + text: custom.valuelabel(option), + value: if includeAll && option == 'All' then '$__all' else option, + [if multi then 'selected']: if multi && std.isArray(current) then + std.member(current, option) + else if multi then + current == option + else + null, + }, + query_array(query):: std.split( + if includeAll then 'All,' + query else query, ',' + ), + }, + /** + * [Text box variables](https://grafana.com/docs/grafana/latest/variables/variable-types/add-text-box-variable/) + * display a free text input field with an optional default value. + * This is the most flexible variable, because you can enter any value. + * Use this type of variable if you have metrics with high cardinality or if you want to + * update multiple panels in a dashboard at the same time. + * + * @name template.text + * + * @param name Variable name. + * @param label (default `''`) Display name of the variable dropdown. If you don’t enter a display name, then the dropdown label will be the variable name. + * + * @return A text box variable. + */ + text( + name, + label='' + ):: + { + current: { + selected: false, + text: '', + value: '', + }, + name: name, + label: label, + query: '', + type: 'textbox', + }, + /** + * [Ad hoc filters](https://grafana.com/docs/grafana/latest/variables/variable-types/add-ad-hoc-filters/) + * allow you to add key/value filters that are automatically added to all metric queries + * that use the specified data source. Unlike other variables, you do not use ad hoc filters in queries. + * Instead, you use ad hoc filters to write filters for existing queries. + * Note: Ad hoc filter variables only work with InfluxDB, Prometheus, and Elasticsearch data sources. + * + * @name template.adhoc + * + * @param name Variable name. + * @param datasource Target data source + * @param label (optional) Display name of the variable dropdown. If you don’t enter a display name, then the dropdown label will be the variable name. + * @param hide (default `''`) `''`: the variable dropdown displays the variable Name or Label value. `'label'`: the variable dropdown only displays the selected variable value and a down arrow. Any other value: no variable dropdown is displayed on the dashboard. + * + * @return An ad hoc filter + */ + adhoc( + name, + datasource, + label=null, + hide='', + ):: + { + datasource: datasource, + hide: $.hide(hide), + label: label, + name: name, + type: 'adhoc', + }, +} diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/main.go b/content/posts/build-a-grafana-dashboard-generator/examples/main.go new file mode 100644 index 00000000..6070ee5d --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/main.go @@ -0,0 +1,17 @@ +package examples + +import ( + "fmt" + + "github.com/google/go-jsonnet" +) + +func MakeConfig() error { + vm := jsonnet.MakeVM() + jsonData, err := vm.EvaluateFile(`dashboard.jsonnet`) + if err != nil { + return err + } + fmt.Println(jsonData) + return nil +} diff --git a/content/posts/build-a-grafana-dashboard-generator/examples/main_test.go b/content/posts/build-a-grafana-dashboard-generator/examples/main_test.go new file mode 100644 index 00000000..2530b74f --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/examples/main_test.go @@ -0,0 +1,12 @@ +package examples + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMakeConfig(t *testing.T) { + err := MakeConfig() + require.NoError(t, err) +} diff --git a/content/posts/build-a-grafana-dashboard-generator/graph.png b/content/posts/build-a-grafana-dashboard-generator/graph.png new file mode 100644 index 00000000..8b8c92a7 Binary files /dev/null and b/content/posts/build-a-grafana-dashboard-generator/graph.png differ diff --git a/content/posts/build-a-grafana-dashboard-generator/index.md b/content/posts/build-a-grafana-dashboard-generator/index.md new file mode 100644 index 00000000..14d228c9 --- /dev/null +++ b/content/posts/build-a-grafana-dashboard-generator/index.md @@ -0,0 +1,403 @@ ++++ +title = "Build a Grafana dashboard using Grafonnet" +date = "2022-08-22T16:19:32+07:00" +author = "buivuanh" +description = "This tutorial helps you build Grafana dashboard config by using Grafonnet and golang." +categories = ["Monitoring", "Automation"] +tags = ["Grafana", "configure-Grafana-as-code", "Grafonnet"] +slug = "build-a-grafana-dashboard-using-grafonnet" ++++ + +## Overview + +- Currently, if we want to create a Grafana dashboard, we often use UI and copy json config into our repo. +- And We have to manage folder which contains dashboard configs JSON files. + +=> Maintain a set of dashboards for people with conflicting preferences and goals. Or you’re dealing with confusion +because one graph shows errors in one color and a new one uses a different color. Or there’s a new feature in Grafana +and you need to change 50 dashboards to use it. As a result, making sure your dashboards work and look good is no longer +a simple process. + +=> That’s why we want to use your dashboard as code. + +## [Jsonnet](https://github.com/google/go-jsonnet) + +A data templating language for app and tool developers + +- Generate config data +- Side-effect free +- Organize, simplify, unify +- Manage sprawling config + +The example: + +- You define a local variable and then that same local variable is referenced later: + +``` +local greeting = "hello world!"; + +{ + foo: "bar", + dict: { + nested: greeting + }, +} +``` + +=> + +``` +{ + "foo": "bar", + "dict": { + "nested": "hello world!" + } +} +``` + +- Or, use functions: + +``` +// A function that returns an object. +local Person(name='Alice') = { + name: name, + welcome: 'Hello ' + name + '!', +}; +{ + person1: Person(), + person2: Person('Bob'), +} +``` + +=> + +``` +{ + "person1": { + "name": "Alice", + "welcome": "Hello Alice!" + }, + "person2": { + "name": "Bob", + "welcome": "Hello Bob!" + } +} +``` + +- Patches: You call your Jsonnet function and append to it the snippet of JSON, and it simply adds — overwrites: like + so: + +``` +dashboard: { + new(title, uid): {...} +} + +dashboard.new(...) + { + schemaVersion: 22, +} +``` + +=> + +``` +{ + "title": "super dash", + "uid": "my-dash", + "Panels": [], + "schemaVersion": 22 +} +``` + +- Imports: Not only can you create functions, but you also can put those functions into files. + +``` +{ + alertlist:: import 'alertlist.libsonnet', + dashboard:: import 'dashboard.libsonnet', + gauge:: error 'gauge is removed, migrate to gaugePanel', + gaugePanel:: import 'gauge_panel.libsonnet', +} +``` + +## [Grafonnet](https://github.com/grafana/grafonnet-lib) + +- Is a very simple library that provides you with the basics: creating a dashboard, creating a panel, creating a single + stat panel, and so on. +- See more examples: https://github.com/grafana/grafonnet-lib/tree/master/examples +- E.g: we have one `dashboard.jsonnet` jsonnet file, maybe call it is main file which will evaluates it to json file + +``` +local grafana = import 'grafonnet/grafana.libsonnet'; +local dashboard = grafana.dashboard; +local annotation = grafana.annotation; +local template = grafana.template; +local graphPanel = grafana.graphPanel; +local prometheus = grafana.prometheus; + +dashboard.new( + 'title', + editable=true, + refresh='5s', + time_from='now-6h', + time_to='now', + timepicker={}, + schemaVersion=27, + uid='uid', +) +.addAnnotation(annotation.default) +.addTemplate( + template.datasource( + name='cluster', + query='prometheus', + current='Thanos', + hide='', + ) +) +.addTemplate( + template.new( + name='namespace', + datasource='${cluster}', + query={ + query: 'label_values(grpc_io_server_completed_rpcs, namespace)', + refId: 'StandardVariableQuery' + }, + label='Namespace', + hide='', + refresh='load', + definition='label_values(grpc_io_server_completed_rpcs, namespace)' + ) +) +.addPanel( + graphPanel.new( + title='Number of requests', + datasource='${cluster}', + fill=1, + legend_show=true, + lines=true, + linewidth=1, + pointradius=2, + stack=true, + shared_tooltip=true, + value_type='individual', + ).resetYaxes(). + addYaxis( + format='none', + ).addYaxis( + format='short', + ).addTarget( + prometheus.custom_target( + expr='expr', + legendFormat='legendFormat', + ) + ), gridPos={ + x: 0, + y: 1, + w: 18, + h: 10, + } +) +``` + +- And now, we are going to make config from above jsonnet file. + +``` +func MakeConfig() error { + vm := jsonnet.MakeVM() + jsonData, err := vm.EvaluateFile(`dashboard.jsonnet`) + if err != nil { + return err + } + fmt.Println(jsonData) + return nil +} +``` + +- Result will look like: +``` +{ + "__inputs": [ ], + "__requires": [ ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [ ], + "panels": [ + { + "aliasColors": { }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${cluster}", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 18, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "expr", + "legendFormat": "legendFormat", + "refId": "A" + } + ], + "thresholds": [ ], + "timeFrom": null, + "timeShift": null, + "title": "Number of requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ ] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "rows": [ ], + "schemaVersion": 27, + "style": "dark", + "tags": [ ], + "templating": { + "list": [ + { + "current": { + "text": "Thanos", + "value": "Thanos" + }, + "hide": 0, + "label": null, + "name": "cluster", + "options": [ ], + "query": "prometheus", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allValue": null, + "current": { }, + "datasource": "${cluster}", + "definition": "label_values(grpc_io_server_completed_rpcs, namespace)", + "hide": 0, + "includeAll": false, + "label": "Namespace", + "multi": false, + "name": "namespace", + "options": [ ], + "query": { + "query": "label_values(grpc_io_server_completed_rpcs, namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "sort": 0, + "tagValuesQuery": "", + "tags": [ ], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { }, + "timezone": "browser", + "title": "title", + "uid": "uid", + "version": 0 +} +``` +![Dashboard](graph.png) + +- Project's structure will look like: +``` +my-repo/ +├─ grafonnet/ +├─ dashboard.jsonnet +├─ main.go +``` + +### [Full source code](https://github.com/manabie-com/manabie-com.github.io/tree/main/content/posts/build-a-grafana-dashboard-generator/examples) + +## More +When you are able to config dashboard as code, in particular: using golang to make config base Grafonnet lib, you can totally add more feature such as : +- Create and manage dashboard config files with jsonnet format instead of raw json files. +- Can reuse one or more pannels for multiple dashboards. +- Build jsonnet template file which you can fill automatically params such metric names, grpc methods .... \ No newline at end of file diff --git a/data/authors/buivuanh.json b/data/authors/buivuanh.json new file mode 100644 index 00000000..34225b33 --- /dev/null +++ b/data/authors/buivuanh.json @@ -0,0 +1,7 @@ +{ + "name": "buivuanh", + "bio": "", + "social": { + "github": "https://github.com/buivuanh" + } +} \ No newline at end of file