Plugins on Grafana Labshttps://grafana.com/categories/plugins/
Recent content in Plugins on Grafana LabsHugo -- gohugo.ioen-usMon, 13 Apr 2020 00:00:00 -0400Build a panel pluginhttps://grafana.com/tutorials/build-a-panel-plugin/
Mon, 01 Jan 0001 00:00:00 +0000https://grafana.com/tutorials/build-a-panel-plugin/<p>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Introduction</h2>
<p>Panels are the building blocks of Grafana. They allow you to visualize data in different ways. While Grafana has several types of panels already built-in, you can also build your own panel, to add support other visualizations.</p>
<p>For more information about panels, refer to the documentation on <a href="https://grafana.com/docs/grafana/latest/features/panels/panels/">Panels</a>.</p>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>Grafana 7.0</li>
<li>NodeJS</li>
<li>yarn</li>
</ul>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Set up your environment</h2>
<p>Before you can get started building plugins, you need to set up your environment for plugin development.</p>
<p>To discover plugins, Grafana scans a <em>plugin directory</em>, the location of which depends on your operating system.</p>
<ol>
<li>
<p>Create a directory called <code>grafana-plugins</code> in your preferred workspace.</p>
</li>
<li>
<p>Find the <code>plugins</code> property in the Grafana configuration file and set the <code>plugins</code> property to the path of your <code>grafana-plugins</code> directory. Refer to the <a href="https://grafana.com/docs/grafana/latest/installation/configuration/#plugins">Grafana configuration documentation</a> for more information.</p>
<pre><code class="language-ini">[paths]
plugins = &quot;/path/to/grafana-plugins&quot;
</code></pre>
</li>
<li>
<p>Restart Grafana if it&rsquo;s already running, to load the new configuration.</p>
</li>
</ol>
<h3 id="alternative-method-docker">Alternative method: Docker</h3>
<p>If you don&rsquo;t want to install Grafana on your local machine, you can use <a href="https://www.docker.com">Docker</a>.</p>
<p>To set up Grafana for plugin development using Docker, run the following command:</p>
<pre><code>docker run -d -p 3000:3000 -v &quot;$(pwd)&quot;/grafana-plugins:/var/lib/grafana/plugins --name=grafana grafana/grafana
</code></pre>
<p>Since Grafana only loads plugins on start-up, you need to restart the container whenever you add or remove a plugin.</p>
<pre><code>docker restart grafana
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Create a new plugin</h2>
<p>Tooling for modern web development can be tricky to wrap your head around. While you certainly can write your own webpack configuration, for this guide, you&rsquo;ll be using <em>grafana-toolkit</em>.</p>
<p><a href="https://github.com/grafana/grafana/tree/master/packages/grafana-toolkit">grafana-toolkit</a> is a CLI application that simplifies Grafana plugin development, so that you can focus on code. The toolkit takes care of building and testing for you.</p>
<ol>
<li>
<p>In the plugin directory, create a plugin from template using the <code>plugin:create</code> command:</p>
<pre><code>npx @grafana/toolkit plugin:create my-plugin
</code></pre>
</li>
<li>
<p>Change directory.</p>
<pre><code>cd my-plugin
</code></pre>
</li>
<li>
<p>Download necessary dependencies:</p>
<pre><code>yarn install
</code></pre>
</li>
<li>
<p>Build the plugin:</p>
<pre><code>yarn dev
</code></pre>
</li>
<li>
<p>Restart the Grafana server for Grafana to discover your plugin.</p>
</li>
<li>
<p>Open Grafana and go to <strong>Configuration</strong> -&gt; <strong>Plugins</strong>. Make sure that your plugin is there.</p>
</li>
</ol>
<p>By default, Grafana logs whenever it discovers a plugin:</p>
<pre><code>INFO[01-01|12:00:00] Registering plugin logger=plugins name=my-plugin
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Anatomy of a plugin</h2>
<p>Plugins come in different shapes and sizes. Before we dive deeper, let&rsquo;s look at some of the properties that are shared by all of them.</p>
<p>Every plugin you create will require at least two files: <code>plugin.json</code> and <code>module.ts</code>.</p>
<h3 id="plugin-json">plugin.json</h3>
<p>When Grafana starts, it scans the plugin directory for any subdirectory that contains a <code>plugin.json</code> file. The <code>plugin.json</code> file contains information about your plugin, and tells Grafana about what capabilities and dependencies your plugin needs.</p>
<p>While certain plugin types can have specific configuration options, let&rsquo;s look at the mandatory ones:</p>
<ul>
<li><code>type</code> tells Grafana what type of plugin to expect. Grafana supports three types of plugins: <code>panel</code>, <code>datasource</code>, and <code>app</code>.</li>
<li><code>name</code> is what users will see in the list of plugins. If you&rsquo;re creating a data source, this is typically the name of the database it connects to, such as Prometheus, PostgreSQL, or Stackdriver.</li>
<li><code>id</code> uniquely identifies your plugin, and should start with your GitHub username, to avoid clashing with other plugins.</li>
</ul>
<p>To see all the available configuration settings for the <code>plugin.json</code>, refer to the <a href="https://grafana.com/docs/grafana/latest/plugins/developing/plugin.json/">plugin.json Schema</a>.</p>
<h3 id="module-ts">module.ts</h3>
<p>After discovering your plugin, Grafana loads the <code>module.ts</code> file, the entrypoint for your plugin. <code>module.ts</code> exposes the implementation of your plugin, which depends on the type of plugin you&rsquo;re building.</p>
<p>Specifically, <code>module.ts</code> needs to expose an object that extends <a href="https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/plugin.ts#L124">GrafanaPlugin</a>, and can be any of the following:</p>
<ul>
<li><a href="https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/panel.ts#L73">PanelPlugin</a></li>
<li><a href="https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/datasource.ts#L33">DataSourcePlugin</a></li>
<li><a href="https://github.com/grafana/grafana/blob/45b7de1910819ad0faa7a8aeac2481e675870ad9/packages/grafana-data/src/types/app.ts#L27">AppPlugin</a></li>
</ul>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Panel plugins</h2>
<p>Since Grafana 6.x, panels are <a href="https://reactjs.org/docs/components-and-props.html">ReactJS components</a>.</p>
<p>Prior to Grafana 6.0, plugins were written in <a href="https://angular.io/">AngularJS</a>. Even though we still support plugins written in AngularJS, we highly recommend that you write new plugins using ReactJS.</p>
<h3 id="panel-properties">Panel properties</h3>
<p>The <a href="https://github.com/grafana/grafana/blob/747b546c260f9a448e2cb56319f796d0301f4bb9/packages/grafana-data/src/types/panel.ts#L27-L40">PanelProps</a> interface exposes runtime information about the panel, such as panel dimensions, and the current time range.</p>
<p>You can access the panel properties through <code>props</code>, as seen in your plugin.</p>
<p><strong>src/SimplePanel.tsx</strong></p>
<pre><code class="language-js">const { options, data, width, height } = props;
</code></pre>
<h3 id="development-workflow">Development workflow</h3>
<p>Next, you&rsquo;ll learn the basic workflow of making a change to your panel, building it, and reloading Grafana to reflect the changes you made.</p>
<p>First, you need to add your panel to a dashboard:</p>
<ol>
<li>Open Grafana in your browser.</li>
<li>Create a new dashboard, and add a new panel.</li>
<li>Select your panel from the list of visualization types.</li>
<li>Save the dashboard.</li>
</ol>
<p>Now that you can view your panel, try making a change to the panel plugin:</p>
<ol>
<li>In <code>SimplePanel.tsx</code>, change the fill color of the circle.</li>
<li>Run <code>yarn dev</code> to build the plugin.</li>
<li>In the browser, reload Grafana with the new changes.</li>
</ol>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Add panel options</h2>
<p>Sometimes you want to offer the users of your panel an option to configure the behavior of your plugin. By configuring <em>panel options</em> for your plugin, your panel will be able to accept user input.</p>
<p>In the previous step, you changed the fill color of the circle in the code. Let&rsquo;s change the code so that the plugin user can configure the color from the panel editor.</p>
<h4 id="add-an-option">Add an option</h4>
<p>Panel options are defined in a <em>panel options object</em>. <code>SimpleOptions</code> is an interface that describes the options object.</p>
<ol>
<li>
<p>In <code>types.ts</code>, add a <code>CircleColor</code> type to hold the colors the users can choose from:</p>
<pre><code>type CircleColor = 'red' | 'green' | 'blue';
</code></pre>
</li>
<li>
<p>In the <code>SimpleOptions</code> interface, add a new option called <code>color</code>:</p>
<pre><code>color: CircleColor;
</code></pre>
</li>
</ol>
<p>Here&rsquo;s the updated options definition:</p>
<p><strong>src/types.ts</strong></p>
<pre><code class="language-ts">type SeriesSize = 'sm' | 'md' | 'lg';
type CircleColor = 'red' | 'green' | 'blue';
// interface defining panel options type
export interface SimpleOptions {
text: string;
showSeriesCount: boolean;
seriesCountSize: SeriesSize;
color: CircleColor;
}
</code></pre>
<h4 id="add-an-option-control">Add an option control</h4>
<p>To change the option from the panel editor, you need to bind the <code>color</code> option to an <em>option control</em>.</p>
<p>Grafana supports a range of option controls, such as text inputs, switches, and radio groups.</p>
<p>Let&rsquo;s create a radio control and bind it to the <code>color</code> option.</p>
<ol>
<li>
<p>In <code>src/module.ts</code>, add the control at the end of the builder:</p>
<pre><code class="language-ts">.addRadio({
path: 'color',
name: 'Circle color',
defaultValue: 'red',
settings: {
options: [
{
value: 'red',
label: 'Red',
},
{
value: 'green',
label: 'Green',
},
{
value: 'blue',
label: 'Blue',
},
],
}
});
</code></pre>
<p>The <code>path</code> is used to bind the control to an option. You can bind a control to nested option by specifying the full path within a options object, for example <code>colors.background</code>.</p>
</li>
</ol>
<p>Grafana builds an options editor for you and displays it in the panel editor sidebar in the <strong>Display</strong> section.</p>
<h4 id="use-the-new-option">Use the new option</h4>
<p>You&rsquo;re almost done. You&rsquo;ve added a new option and a corresponding control to change the value. But the plugin isn&rsquo;t using the option yet. Let&rsquo;s change that.</p>
<ol>
<li>
<p>To convert option value to the colors used by the current theme, add a <code>switch</code> statement right before the <code>return</code> statement in <code>SimplePanel.tsx</code>.</p>
<p><strong>src/SimplePanel.tsx</strong></p>
<pre><code class="language-ts">let color: string;
switch (options.color) {
case 'red':
color = theme.palette.redBase;
break;
case 'green':
color = theme.palette.greenBase;
break;
case 'blue':
color = theme.palette.blue95;
break;
}
</code></pre>
</li>
<li>
<p>Configure the circle to use the color.</p>
<pre><code class="language-ts">&lt;g&gt;
&lt;circle style={{ fill: color }} r={100} /&gt;
&lt;/g&gt;
</code></pre>
</li>
</ol>
<p>Now, when you change the color in the panel editor, the fill color of the circle changes as well.</p>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Create dynamic panels using data frames</h2>
<p>Most panels visualize dynamic data from a Grafana data source. In this step, you&rsquo;ll create one circle per series, each with a radius equal to the last value in the series.</p>
<blockquote>
<p>To use data from queries in your panel, you need to set up a data source. If you don&rsquo;t have one available, you can use the <a href="https://grafana.com/docs/grafana/latest/features/datasources/testdata">TestData DB</a> data source while developing.</p>
</blockquote>
<p>The results from a data source query within your panel are available in the <code>data</code> property inside your panel component.</p>
<pre><code class="language-ts">const { data } = props;
</code></pre>
<p><code>data.series</code> contains the series returned from a data source query. Each series is represented as a data structure called <em>data frame</em>. A data frame resembles a table, where data is stored by columns, or <em>fields</em>, instead of rows. Every value in a field share the same data type, such as string, number, or time.</p>
<p>Here&rsquo;s an example of a data frame with a time field, <code>Time</code>, and a number field, <code>Value</code>:</p>
<table>
<thead>
<tr>
<th>Time</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>1589189388597</td>
<td>32.4</td>
</tr>
<tr>
<td>1589189406480</td>
<td>27.2</td>
</tr>
<tr>
<td>1589189513721</td>
<td>15.0</td>
</tr>
</tbody>
</table>
<p>Let&rsquo;s see how you can retrieve data from a data frame and use it in your visualization.</p>
<ol>
<li>
<p>Get the last value of each field of type <code>number</code>, by adding the following to <code>SimplePanel.tsx</code>, before the <code>return</code> statement:</p>
<pre><code class="language-ts">const radii = data.series
.map(series =&gt; series.fields.find(field =&gt; field.type === 'number'))
.map(field =&gt; field?.values.get(field.values.length - 1));
</code></pre>
<p><code>radii</code> will contain the last values in each of the series that are returned from a data source query. You&rsquo;ll use these to set the radius for each circle.</p>
</li>
<li>
<p>Change the <code>svg</code> element to the following:</p>
<pre><code class="language-ts">&lt;svg
className={styles.svg}
width={width}
height={height}
xmlns=&quot;http://www.w3.org/2000/svg&quot;
xmlnsXlink=&quot;http://www.w3.org/1999/xlink&quot;
viewBox={`0 -${height / 2} ${width} ${height}`}
&gt;
&lt;g fill={color}&gt;
{radii.map((radius, index) =&gt; {
const step = width / radii.length;
return &lt;circle r={radius} transform={`translate(${index * step + step / 2}, 0)`} /&gt;;
})}
&lt;/g&gt;
&lt;/svg&gt;
</code></pre>
<p>Note how we&rsquo;re creating a <code>&lt;circle&gt;</code> element for each value in <code>radii</code>:</p>
<pre><code class="language-ts">{radii.map((radius, index) =&gt; {
const step = width / radii.length;
return &lt;circle r={radius} transform={`translate(${index * step + step / 2}, 0)`} /&gt;;
})}
</code></pre>
<p>We use the <code>transform</code> here to distribute the circle horizontally within the panel.</p>
</li>
<li>
<p>Rebuild your plugin and try it out by adding multiple queries to the panel. Refresh the dashboard.</p>
</li>
</ol>
<p>If you want to know more about data frames, check out our introduction to <a href="https://grafana.com/docs/grafana/latest/developers/plugins/data-frames/">Data frames</a>.</p>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Publish your plugin</h2>
<p>Once you&rsquo;re happy with your plugin, it&rsquo;s time to package it, and submit to the plugin repository.</p>
<p>For users to be able to use the plugin without building it themselves, you need to make a production build of the plugin, and commit to a release branch in your repository.</p>
<h4 id="create-a-plugin-release">Create a plugin release</h4>
<ol>
<li>
<p>Create a branch called <code>release-&lt;version&gt;</code>:</p>
<pre><code>git checkout -b release-&lt;version&gt;
</code></pre>
</li>
<li>
<p>Do a production build</p>
<pre><code>yarn build
</code></pre>
</li>
<li>
<p>Add the <code>dist</code> directory:</p>
<pre><code>git add -f dist
</code></pre>
</li>
<li>
<p>Create the release commit:</p>
<pre><code>git commit -m &quot;Release &lt;version&gt;&quot;
</code></pre>
</li>
<li>
<p>Create a release tag:</p>
<pre><code>git tag -a &lt;version&gt;
</code></pre>
</li>
<li>
<p>Push to GitHub:</p>
<pre><code>git push --follow-tags
</code></pre>
</li>
</ol>
<h4 id="submit-the-plugin">Submit the plugin</h4>
<p>For a plugin to be published on <a href="https://grafana.com/grafana/plugins">Grafana Plugins</a>, it needs to be added to the <a href="https://github.com/grafana/grafana-plugin-repository">grafana-plugin-repository</a>.</p>
<ol>
<li>
<p>Fork the <a href="https://github.com/grafana/grafana-plugin-repository">grafana-plugin-repository</a></p>
</li>
<li>
<p>Add your plugin to the <code>repo.json</code> file in the project root directory:</p>
<pre><code class="language-json">{
&quot;id&quot;: &quot;&lt;plugin id&gt;&quot;,
&quot;type&quot;: &quot;&lt;plugin type&gt;&quot;,
&quot;url&quot;: &quot;https://github.com/&lt;username&gt;/my-plugin&quot;,
&quot;versions&quot;: [
{
&quot;version&quot;: &quot;&lt;version&gt;&quot;,
&quot;commit&quot;: &quot;&lt;git sha&gt;&quot;,
&quot;url&quot;: &quot;https://github.com/&lt;username&gt;/my-plugin&quot;
}
]
}
</code></pre>
</li>
<li>
<p><a href="https://github.com/grafana/grafana-plugin-repository/pull/new/master">Create a pull request</a>.</p>
</li>
</ol>
<p>Once your plugin has been accepted, it&rsquo;ll be published on <a href="https://grafana.com/grafana/plugins">Grafana Plugin</a>, available for anyone to <a href="https://grafana.com/docs/grafana/latest/plugins/installation">install</a>!</p>
<blockquote>
<p>We&rsquo;re auditing every plugin that&rsquo;s added to make sure it&rsquo;s ready to be published. This means that it might take some time before your plugin is accepted. We&rsquo;re working on adding more automated tests to improve this process.</p>
</blockquote>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Congratulations</h2>
Congratulations, you made it to the end of this tutorial!
</div></p>
Build a panel plugin with D3.jshttps://grafana.com/tutorials/build-a-panel-plugin-with-d3/
Mon, 01 Jan 0001 00:00:00 +0000https://grafana.com/tutorials/build-a-panel-plugin-with-d3/<p>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Introduction</h2>
<p>Panels are the building blocks of Grafana, and allow you to visualize data in different ways. This tutorial gives you a hands-on walkthrough of creating your own panel using <a href="https://d3js.org/">D3.js</a>.</p>
<p>For more information about panels, refer to the documentation on <a href="https://grafana.com/docs/grafana/latest/features/panels/panels/">Panels</a>.</p>
<p>In this tutorial, you&rsquo;ll:</p>
<ul>
<li>Build a simple, but complete bar graph panel</li>
<li>Learn how to use D3.js to build a panel using data-driven transformations</li>
</ul>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>Grafana 7.0</li>
<li>NodeJS</li>
<li>yarn</li>
</ul>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Set up your environment</h2>
<p>Before you can get started building plugins, you need to set up your environment for plugin development.</p>
<p>To discover plugins, Grafana scans a <em>plugin directory</em>, the location of which depends on your operating system.</p>
<ol>
<li>
<p>Create a directory called <code>grafana-plugins</code> in your preferred workspace.</p>
</li>
<li>
<p>Find the <code>plugins</code> property in the Grafana configuration file and set the <code>plugins</code> property to the path of your <code>grafana-plugins</code> directory. Refer to the <a href="https://grafana.com/docs/grafana/latest/installation/configuration/#plugins">Grafana configuration documentation</a> for more information.</p>
<pre><code class="language-ini">[paths]
plugins = &quot;/path/to/grafana-plugins&quot;
</code></pre>
</li>
<li>
<p>Restart Grafana if it&rsquo;s already running, to load the new configuration.</p>
</li>
</ol>
<h3 id="alternative-method-docker">Alternative method: Docker</h3>
<p>If you don&rsquo;t want to install Grafana on your local machine, you can use <a href="https://www.docker.com">Docker</a>.</p>
<p>To set up Grafana for plugin development using Docker, run the following command:</p>
<pre><code>docker run -d -p 3000:3000 -v &quot;$(pwd)&quot;/grafana-plugins:/var/lib/grafana/plugins --name=grafana grafana/grafana
</code></pre>
<p>Since Grafana only loads plugins on start-up, you need to restart the container whenever you add or remove a plugin.</p>
<pre><code>docker restart grafana
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Create a new plugin</h2>
<p>Tooling for modern web development can be tricky to wrap your head around. While you certainly can write your own webpack configuration, for this guide, you&rsquo;ll be using <em>grafana-toolkit</em>.</p>
<p><a href="https://github.com/grafana/grafana/tree/master/packages/grafana-toolkit">grafana-toolkit</a> is a CLI application that simplifies Grafana plugin development, so that you can focus on code. The toolkit takes care of building and testing for you.</p>
<ol>
<li>
<p>In the plugin directory, create a plugin from template using the <code>plugin:create</code> command:</p>
<pre><code>npx @grafana/toolkit plugin:create my-plugin
</code></pre>
</li>
<li>
<p>Change directory.</p>
<pre><code>cd my-plugin
</code></pre>
</li>
<li>
<p>Download necessary dependencies:</p>
<pre><code>yarn install
</code></pre>
</li>
<li>
<p>Build the plugin:</p>
<pre><code>yarn dev
</code></pre>
</li>
<li>
<p>Restart the Grafana server for Grafana to discover your plugin.</p>
</li>
<li>
<p>Open Grafana and go to <strong>Configuration</strong> -&gt; <strong>Plugins</strong>. Make sure that your plugin is there.</p>
</li>
</ol>
<p>By default, Grafana logs whenever it discovers a plugin:</p>
<pre><code>INFO[01-01|12:00:00] Registering plugin logger=plugins name=my-plugin
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Data-driven transformations</h2>
<p><a href="https://d3js.org/">D3.js</a> is a JavaScript library for manipulating documents based on data. It lets you transform arbitrary data into HTML, and is commonly used for creating visualizations.</p>
<p>In fact, D3.js is already bundled with Grafana, and you can access it by importing the <code>d3</code> package.</p>
<ol>
<li>
<p>Install the D3 type definitions:</p>
<pre><code>yarn add @types/d3
</code></pre>
</li>
<li>
<p>Import the <code>select</code> function from <code>d3</code> in <strong>SimplePanel.tsx</strong>:</p>
<pre><code class="language-ts">import { select } from 'd3';
</code></pre>
</li>
<li>
<p>Import <code>useRef</code> and <code>useEffect</code> from <code>react</code>:</p>
<pre><code class="language-ts">import React, { useRef, useEffect } from 'react';
</code></pre>
</li>
<li>
<p>Replace the content of the SimplePanel component with the following:</p>
<pre><code class="language-ts">export const SimplePanel: React.FC&lt;Props&gt; = ({ options, data, width, height }) =&gt; {
const theme = useTheme();
const d3Container = useRef(null);
const values = [4, 8, 15, 16, 23, 42];
useEffect(() =&gt; {
if (d3Container.current) {
const chart = select(d3Container.current)
.attr('width', width)
.attr('height', height);
chart
.append('text')
.text('Hello world')
.attr('x', 10)
.attr('y', 10)
.attr('fill', theme.palette.greenBase);
}
}, [width, height, values, d3Container.current]);
return &lt;svg ref={d3Container}&gt;&lt;/svg&gt;;
};
</code></pre>
</li>
<li>
<p>Run <code>yarn dev</code>, and reload Grafana to reflect the changes you&rsquo;ve made.</p>
</li>
</ol>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Build a graph from data</h2>
<p>You&rsquo;ve seen how to use D3.js to create a container element with some hard-coded text in it. Next, you&rsquo;ll build the graph from actual data.</p>
<ol>
<li>
<p>Update the panel with the following code:</p>
<pre><code class="language-ts">export const SimplePanel: React.FC&lt;Props&gt; = ({ options, data, width, height }) =&gt; {
const theme = useTheme();
const d3Container = useRef(null);
const values = [4, 8, 15, 16, 23, 42];
useEffect(() =&gt; {
if (d3Container.current) {
const maxValue = Math.max.apply(Math, values);
const barHeight = height / values.length;
const chart = select(d3Container.current)
.html('')
.append('svg')
.attr('width', width)
.attr('height', height);
const bars = chart
.selectAll('rect')
.data(values)
.enter()
.append('rect');
bars
.attr('height', barHeight - 1)
.attr('width', d =&gt; (d / maxValue) * width)
.attr('transform', (d, i) =&gt; {
return 'translate(0,' + i * barHeight + ')';
})
.attr('fill', theme.palette.greenBase);
}
}, [width, height, values, d3Container.current]);
return &lt;div ref={d3Container}&gt;&lt;/div&gt;;
};
</code></pre>
</li>
<li>
<p>Run <code>yarn dev</code>, and reload Grafana to see a bar chart that dynamically resizes to fit the panel.</p>
</li>
</ol>
<p>Congratulations, you&rsquo;ve created a dynamic bar chart! Still, you&rsquo;ve only touched the surface of what&rsquo;s possible with D3. To learn more, check out the <a href="https://github.com/d3/d3/wiki/Gallery">D3 Gallery</a>.</p>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Complete example</h2>
<pre><code class="language-ts">import React, { useRef, useEffect } from 'react';
import { PanelProps } from '@grafana/data';
import { SimpleOptions } from 'types';
import { useTheme } from '@grafana/ui';
import { select } from 'd3';
interface Props extends PanelProps&lt;SimpleOptions&gt; {}
export const SimplePanel: React.FC&lt;Props&gt; = ({ options, data, width, height }) =&gt; {
const theme = useTheme();
const d3Container = useRef(null);
const values = [4, 8, 15, 16, 23, 42];
useEffect(() =&gt; {
if (d3Container.current) {
const maxValue = Math.max.apply(Math, values);
const barHeight = height / values.length;
const chart = select(d3Container.current)
.attr('width', width)
.attr('height', height);
const bars = chart
.selectAll('rect')
.data(values)
.enter()
.append('rect');
bars
.attr('height', barHeight - 1)
.attr('width', d =&gt; (d / maxValue) * width)
.attr('transform', (d, i) =&gt; {
return 'translate(0,' + i * barHeight + ')';
})
.attr('fill', theme.palette.greenBase);
}
}, [width, height, values, d3Container.current]);
return &lt;svg ref={d3Container}&gt;&lt;/svg&gt;;
};
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Congratulations</h2>
Congratulations, you made it to the end of this tutorial!
</div></p>
Build a data source pluginhttps://grafana.com/tutorials/build-a-data-source-plugin/
Mon, 01 Jan 0001 00:00:00 +0000https://grafana.com/tutorials/build-a-data-source-plugin/<p>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Introduction</h2>
<p>Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. There&rsquo;s a good chance you can already visualize metrics from the systems you have set up. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. This tutorial teaches you to build a support for your data source.</p>
<p>In this tutorial, you&rsquo;ll:</p>
<ul>
<li>Build a data source to visualize a sine wave</li>
<li>Construct queries using the query editor</li>
<li>Configure your data source using the config editor</li>
</ul>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>Grafana 7.0</li>
<li>NodeJS</li>
<li>yarn</li>
</ul>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Set up your environment</h2>
<p>Before you can get started building plugins, you need to set up your environment for plugin development.</p>
<p>To discover plugins, Grafana scans a <em>plugin directory</em>, the location of which depends on your operating system.</p>
<ol>
<li>
<p>Create a directory called <code>grafana-plugins</code> in your preferred workspace.</p>
</li>
<li>
<p>Find the <code>plugins</code> property in the Grafana configuration file and set the <code>plugins</code> property to the path of your <code>grafana-plugins</code> directory. Refer to the <a href="https://grafana.com/docs/grafana/latest/installation/configuration/#plugins">Grafana configuration documentation</a> for more information.</p>
<pre><code class="language-ini">[paths]
plugins = &quot;/path/to/grafana-plugins&quot;
</code></pre>
</li>
<li>
<p>Restart Grafana if it&rsquo;s already running, to load the new configuration.</p>
</li>
</ol>
<h3 id="alternative-method-docker">Alternative method: Docker</h3>
<p>If you don&rsquo;t want to install Grafana on your local machine, you can use <a href="https://www.docker.com">Docker</a>.</p>
<p>To set up Grafana for plugin development using Docker, run the following command:</p>
<pre><code>docker run -d -p 3000:3000 -v &quot;$(pwd)&quot;/grafana-plugins:/var/lib/grafana/plugins --name=grafana grafana/grafana
</code></pre>
<p>Since Grafana only loads plugins on start-up, you need to restart the container whenever you add or remove a plugin.</p>
<pre><code>docker restart grafana
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Create a new plugin</h2>
<p>Tooling for modern web development can be tricky to wrap your head around. While you certainly can write your own webpack configuration, for this guide, you&rsquo;ll be using <em>grafana-toolkit</em>.</p>
<p><a href="https://github.com/grafana/grafana/tree/master/packages/grafana-toolkit">grafana-toolkit</a> is a CLI application that simplifies Grafana plugin development, so that you can focus on code. The toolkit takes care of building and testing for you.</p>
<ol>
<li>
<p>In the plugin directory, create a plugin from template using the <code>plugin:create</code> command:</p>
<pre><code>npx @grafana/toolkit plugin:create my-plugin
</code></pre>
</li>
<li>
<p>Change directory.</p>
<pre><code>cd my-plugin
</code></pre>
</li>
<li>
<p>Download necessary dependencies:</p>
<pre><code>yarn install
</code></pre>
</li>
<li>
<p>Build the plugin:</p>
<pre><code>yarn dev
</code></pre>
</li>
<li>
<p>Restart the Grafana server for Grafana to discover your plugin.</p>
</li>
<li>
<p>Open Grafana and go to <strong>Configuration</strong> -&gt; <strong>Plugins</strong>. Make sure that your plugin is there.</p>
</li>
</ol>
<p>By default, Grafana logs whenever it discovers a plugin:</p>
<pre><code>INFO[01-01|12:00:00] Registering plugin logger=plugins name=my-plugin
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Anatomy of a plugin</h2>
<p>Plugins come in different shapes and sizes. Before we dive deeper, let&rsquo;s look at some of the properties that are shared by all of them.</p>
<p>Every plugin you create will require at least two files: <code>plugin.json</code> and <code>module.ts</code>.</p>
<h3 id="plugin-json">plugin.json</h3>
<p>When Grafana starts, it scans the plugin directory for any subdirectory that contains a <code>plugin.json</code> file. The <code>plugin.json</code> file contains information about your plugin, and tells Grafana about what capabilities and dependencies your plugin needs.</p>
<p>While certain plugin types can have specific configuration options, let&rsquo;s look at the mandatory ones:</p>
<ul>
<li><code>type</code> tells Grafana what type of plugin to expect. Grafana supports three types of plugins: <code>panel</code>, <code>datasource</code>, and <code>app</code>.</li>
<li><code>name</code> is what users will see in the list of plugins. If you&rsquo;re creating a data source, this is typically the name of the database it connects to, such as Prometheus, PostgreSQL, or Stackdriver.</li>
<li><code>id</code> uniquely identifies your plugin, and should start with your GitHub username, to avoid clashing with other plugins.</li>
</ul>
<p>To see all the available configuration settings for the <code>plugin.json</code>, refer to the <a href="https://grafana.com/docs/grafana/latest/plugins/developing/plugin.json/">plugin.json Schema</a>.</p>
<h3 id="module-ts">module.ts</h3>
<p>After discovering your plugin, Grafana loads the <code>module.ts</code> file, the entrypoint for your plugin. <code>module.ts</code> exposes the implementation of your plugin, which depends on the type of plugin you&rsquo;re building.</p>
<p>Specifically, <code>module.ts</code> needs to expose an object that extends <a href="https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/plugin.ts#L124">GrafanaPlugin</a>, and can be any of the following:</p>
<ul>
<li><a href="https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/panel.ts#L73">PanelPlugin</a></li>
<li><a href="https://github.com/grafana/grafana/blob/08bf2a54523526a7f59f7c6a8dafaace79ab87db/packages/grafana-data/src/types/datasource.ts#L33">DataSourcePlugin</a></li>
<li><a href="https://github.com/grafana/grafana/blob/45b7de1910819ad0faa7a8aeac2481e675870ad9/packages/grafana-data/src/types/app.ts#L27">AppPlugin</a></li>
</ul>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Data source plugins</h2>
<p>A data source in Grafana must extend the <code>DataSourceApi</code> interface, which requires you to defines two methods: <code>query</code> and <code>testDatasource</code>.</p>
<h3 id="the-query-method">The <code>query</code> method</h3>
<p>The <code>query</code> method is the heart of any data source plugin. It accepts a query from the user, retrieves the data from an external database, and returns the data in a format that Grafana recognizes.</p>
<pre><code>async query(options: DataQueryRequest&lt;MyQuery&gt;): Promise&lt;DataQueryResponse&gt;
</code></pre>
<p>The <code>options</code> object contains the queries, or <em>targets</em>, that the user made, along with context information, like the current time interval. Use this information to query an external database.</p>
<blockquote>
<p>The term <em>target</em> originates from Graphite, and the earlier days of Grafana when Graphite was the only supported data source. As Grafana gained support for more data sources, the term &ldquo;target&rdquo; became synonymous with any type of query.</p>
</blockquote>
<h3 id="test-your-data-source">Test your data source</h3>
<p><code>testDatasource</code> implements a health check for your data source. For example, Grafana calls this method whenever the user clicks the <strong>Save &amp; Test</strong> button, after changing the connection settings.</p>
<pre><code>async testDatasource()
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Data frames</h2>
<p>Nowadays there are countless of different databases, each with their own ways of querying data. To be able to support all the different data formats, Grafana consolidates the data into a unified data structure called <em>data frames</em>.</p>
<p>Let&rsquo;s see how to create and return a data frame from the <code>query</code> method. In this step, you&rsquo;ll change the code in the starter plugin to return a <a href="https://en.wikipedia.org/wiki/Sine_wave">sine wave</a>.</p>
<ol>
<li>
<p>In the current <code>query</code> method, remove the code inside the <code>map</code> function.</p>
<p>The <code>query</code> method now look like this:</p>
<pre><code class="language-ts">async query(options: DataQueryRequest&lt;MyQuery&gt;): Promise&lt;DataQueryResponse&gt; {
const { range } = options;
const from = range!.from.valueOf();
const to = range!.to.valueOf();
const data = options.targets.map(target =&gt; {
// Your code goes here.
});
return { data };
}
</code></pre>
</li>
<li>
<p>In the <code>map</code> function, use the <code>lodash/defaults</code> package to set default values for query properties that haven&rsquo;t been set:</p>
<pre><code class="language-ts">const query = defaults(target, defaultQuery);
</code></pre>
</li>
<li>
<p>Create a data frame with a time field and a number field:</p>
<pre><code class="language-ts">const frame = new MutableDataFrame({
refId: query.refId,
fields: [
{ name: 'time', type: FieldType.time },
{ name: 'value', type: FieldType.number },
],
});
</code></pre>
<p><code>refId</code> needs to be set to tell Grafana which query that generated this date frame.</p>
</li>
</ol>
<p>Next, we&rsquo;ll add the actual values to the data frame. Don&rsquo;t worry about the math used to calculate the values.</p>
<ol>
<li>
<p>Create a couple of helper variables:</p>
<pre><code class="language-ts">// duration of the time range, in milliseconds.
const duration = to - from;
// step determines how close in time (ms) the points will be to each other.
const step = duration / 1000;
</code></pre>
</li>
<li>
<p>Add the values to the data frame:</p>
<pre><code class="language-ts">for (let t = 0; t &lt; duration; t += step) {
frame.add({ time: from + t, value: Math.sin((2 * Math.PI * t) / duration) });
}
</code></pre>
<p>The <code>frame.add()</code> accepts an object where the keys corresponds to the name of each field in the data frame.</p>
</li>
<li>
<p>Return the data frame:</p>
<pre><code class="language-ts">return frame;
</code></pre>
</li>
<li>
<p>Rebuild the plugin and try it out.</p>
</li>
</ol>
<p>Your data source is now sending data frames that Grafana can visualize. Next, we&rsquo;ll look at how you can control the frequency of the sine wave by defining a <em>query</em>.</p>
<blockquote>
<p>In this example, we&rsquo;re generating timestamps from the current time range. This means that you&rsquo;ll get the same graph no matter what time range you&rsquo;re using. In practice, you&rsquo;d instead use the timestamps returned by your database.</p>
</blockquote>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Define a query</h2>
<p>Most data sources offer a way to query specific data. MySQL and PostgreSQL use SQL, while Prometheus has its own query language, called <em>PromQL</em>. No matter what query language your databases are using, Grafana lets you build support for it.</p>
<p>Add support for custom queries to your data source, by implementing a your own <em>query editor</em>, a React component that enables users to build their own queries, through a user-friendly graphical interface.</p>
<p>A query editor can be as simple as a text field where the user edits the raw query text, or it can provide a more user-friendly form with drop-down menus and switches, that later gets converted into the raw query text before it gets sent off to the database.</p>
<h3 id="define-the-query-model">Define the query model</h3>
<p>The first step in designing your query editor is to define its <em>query model</em>. The query model defines the user input to your data source.</p>
<p>We want to be able to control the frequency of the sine wave, so let&rsquo;s add another property.</p>
<ol>
<li>
<p>Add a new number property called <code>frequency</code> to the query model:</p>
<p><strong>src/types.ts</strong></p>
<pre><code class="language-ts">export interface MyQuery extends DataQuery {
queryText?: string;
constant: number;
frequency: number;
}
</code></pre>
</li>
<li>
<p>Set a default value to the new <code>frequency</code> property:</p>
<pre><code class="language-ts">export const defaultQuery: Partial&lt;MyQuery&gt; = {
constant: 6.5,
frequency: 1.0,
};
</code></pre>
</li>
</ol>
<h3 id="bind-the-model-to-a-form">Bind the model to a form</h3>
<p>Now that you&rsquo;ve defined the query model you wish to support, the next step is to bind the model to a form. The <code>FormField</code> is a text field component from <code>grafana/ui</code> that lets you register a listener which will be invoked whenever the form field value changes.</p>
<ol>
<li>
<p>Add a new form field to the query editor to control the new frequency property.</p>
<p><strong>QueryEditor.tsx</strong></p>
<pre><code class="language-ts">const { queryText, constant, frequency } = query;
</code></pre>
<pre><code class="language-ts">&lt;FormField
width={4}
value={frequency}
onChange={this.onFrequencyChange}
label=&quot;Frequency&quot;
type=&quot;number&quot;
/&gt;
</code></pre>
</li>
<li>
<p>Add a event listener for the new property.</p>
<pre><code class="language-ts">onFrequencyChange = (event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
const { onChange, query, onRunQuery } = this.props;
onChange({ ...query, frequency: parseFloat(event.target.value) });
// executes the query
onRunQuery();
};
</code></pre>
<p>The registered listener, <code>onFrequencyChange</code>, calls <code>onChange</code> to update the current query with the value from the form field.</p>
<p><code>onRunQuery();</code> tells Grafana to run the query after each change. For fast queries, this is recommended to provide a more responsive experience.</p>
</li>
</ol>
<h3 id="use-the-property">Use the property</h3>
<p>The new query model is now ready to use in our <code>query</code> method.</p>
<ol>
<li>
<p>In the <code>query</code> method, use the <code>frequency</code> property to adjust our equation.</p>
<pre><code class="language-ts">frame.add({ time: from + t, value: Math.sin((2 * Math.PI * query.frequency * t) / duration) });
</code></pre>
</li>
</ol>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Configure your data source</h2>
<p>To access a specific data source, you often need to configure things like hostname, credentials, or authentication method. A <em>config editor</em> lets your users configure your data source plugin to fit their needs.</p>
<p>The config editor looks similar to the query editor, in that it defines a model and binds it to a form.</p>
<p>Since we&rsquo;re not actually connecting to an external database in our sine wave example, we don&rsquo;t really need many options. To show you how you can add an option however, we&rsquo;re going to add the <em>wave resolution</em> as an option.</p>
<p>The resolution controls how close in time the data points are to each other. A higher resolution means more points closer together, at the cost of more data being processed.</p>
<h3 id="define-the-options-model">Define the options model</h3>
<ol>
<li>
<p>Add a new number property called <code>resolution</code> to the options model.</p>
<p><strong>types.ts</strong></p>
<pre><code class="language-ts">export interface MyDataSourceOptions extends DataSourceJsonData {
path?: string;
resolution?: number;
}
</code></pre>
</li>
</ol>
<h3 id="bind-the-model-to-a-form">Bind the model to a form</h3>
<p>Just like query editor, the form field in the config editor calls the registered listener whenever the value changes.</p>
<ol>
<li>
<p>Add a new form field to the query editor to control the new resolution option.</p>
<p><strong>ConfigEditor.tsx</strong></p>
<pre><code class="language-ts">&lt;div className=&quot;gf-form&quot;&gt;
&lt;FormField
label=&quot;Resolution&quot;
onChange={this.onResolutionChange}
value={jsonData.resolution || ''}
placeholder=&quot;Enter a number&quot;
/&gt;
&lt;/div&gt;
</code></pre>
</li>
<li>
<p>Add a event listener for the new option.</p>
<pre><code class="language-ts">onResolutionChange = (event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
const { onOptionsChange, options } = this.props;
const jsonData = {
...options.jsonData,
resolution: parseFloat(event.target.value),
};
onOptionsChange({ ...options, jsonData });
};
</code></pre>
<p>The <code>onResolutionChange</code> listener calls <code>onOptionsChange</code> to update the current options with the value from the form field.</p>
</li>
</ol>
<h3 id="use-the-option">Use the option</h3>
<ol>
<li>
<p>Create a property called <code>resolution</code> to the <code>DataSource</code> class.</p>
<pre><code class="language-ts">export class DataSource extends DataSourceApi&lt;MyQuery, MyDataSourceOptions&gt; {
resolution: number;
constructor(instanceSettings: DataSourceInstanceSettings&lt;MyDataSourceOptions&gt;) {
super(instanceSettings);
this.resolution = instanceSettings.jsonData.resolution || 1000.0;
}
// ...
</code></pre>
</li>
<li>
<p>In the <code>query</code> method, use the <code>resolution</code> property to calculate the step size.</p>
<p><strong>src/DataSource.ts</strong></p>
<pre><code class="language-ts">const step = duration / this.resolution;
</code></pre>
</li>
</ol>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Publish your plugin</h2>
<p>Once you&rsquo;re happy with your plugin, it&rsquo;s time to package it, and submit to the plugin repository.</p>
<p>For users to be able to use the plugin without building it themselves, you need to make a production build of the plugin, and commit to a release branch in your repository.</p>
<h4 id="create-a-plugin-release">Create a plugin release</h4>
<ol>
<li>
<p>Create a branch called <code>release-&lt;version&gt;</code>:</p>
<pre><code>git checkout -b release-&lt;version&gt;
</code></pre>
</li>
<li>
<p>Do a production build</p>
<pre><code>yarn build
</code></pre>
</li>
<li>
<p>Add the <code>dist</code> directory:</p>
<pre><code>git add -f dist
</code></pre>
</li>
<li>
<p>Create the release commit:</p>
<pre><code>git commit -m &quot;Release &lt;version&gt;&quot;
</code></pre>
</li>
<li>
<p>Create a release tag:</p>
<pre><code>git tag -a &lt;version&gt;
</code></pre>
</li>
<li>
<p>Push to GitHub:</p>
<pre><code>git push --follow-tags
</code></pre>
</li>
</ol>
<h4 id="submit-the-plugin">Submit the plugin</h4>
<p>For a plugin to be published on <a href="https://grafana.com/grafana/plugins">Grafana Plugins</a>, it needs to be added to the <a href="https://github.com/grafana/grafana-plugin-repository">grafana-plugin-repository</a>.</p>
<ol>
<li>
<p>Fork the <a href="https://github.com/grafana/grafana-plugin-repository">grafana-plugin-repository</a></p>
</li>
<li>
<p>Add your plugin to the <code>repo.json</code> file in the project root directory:</p>
<pre><code class="language-json">{
&quot;id&quot;: &quot;&lt;plugin id&gt;&quot;,
&quot;type&quot;: &quot;&lt;plugin type&gt;&quot;,
&quot;url&quot;: &quot;https://github.com/&lt;username&gt;/my-plugin&quot;,
&quot;versions&quot;: [
{
&quot;version&quot;: &quot;&lt;version&gt;&quot;,
&quot;commit&quot;: &quot;&lt;git sha&gt;&quot;,
&quot;url&quot;: &quot;https://github.com/&lt;username&gt;/my-plugin&quot;
}
]
}
</code></pre>
</li>
<li>
<p><a href="https://github.com/grafana/grafana-plugin-repository/pull/new/master">Create a pull request</a>.</p>
</li>
</ol>
<p>Once your plugin has been accepted, it&rsquo;ll be published on <a href="https://grafana.com/grafana/plugins">Grafana Plugin</a>, available for anyone to <a href="https://grafana.com/docs/grafana/latest/plugins/installation">install</a>!</p>
<blockquote>
<p>We&rsquo;re auditing every plugin that&rsquo;s added to make sure it&rsquo;s ready to be published. This means that it might take some time before your plugin is accepted. We&rsquo;re working on adding more automated tests to improve this process.</p>
</blockquote>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Congratulations</h2>
<p>Congratulations, you made it to the end of this tutorial!</p>
<p>You&rsquo;ve built a complete data source plugin for Grafana that uses a query editor to control what data to visualize. You&rsquo;ve added a data source option, commonly used to set connection options and more.</p>
<h3 id="learn-more">Learn more</h3>
<p>Learn how you can improve your plugin even further, by reading our advanced guides:</p>
<ul>
<li><a href="https://grafana.com/docs/grafana/latest/developers/plugins/add-support-for-variables/">Add support for variables</a></li>
<li><a href="https://grafana.com/docs/grafana/latest/developers/plugins/add-support-for-annotations/">Add support for annotations</a></li>
<li><a href="https://grafana.com/docs/grafana/latest/developers/plugins/add-support-for-explore-queries/">Add support for Explore queries</a></li>
<li><a href="https://grafana.com/docs/grafana/latest/developers/plugins/build-a-logs-data-source/">Build a logs data source</a></li>
</ul>
</div></p>
Build a data source backend pluginhttps://grafana.com/tutorials/build-a-data-source-backend-plugin/
Mon, 01 Jan 0001 00:00:00 +0000https://grafana.com/tutorials/build-a-data-source-backend-plugin/<p>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Introduction</h2>
<p>Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. There&rsquo;s a good chance you can already visualize metrics from the systems you have set up. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. This tutorial teaches you to build a support for your data source.</p>
<p>For more information about backend plugins, refer to the documentation on <a href="https://grafana.com/docs/grafana/latest/developers/plugins/backend/">Backend plugins</a>.</p>
<p>In this tutorial, you&rsquo;ll:</p>
<ul>
<li>Build a backend for your data source</li>
<li>Implement a health check for your data source</li>
<li>Enable Grafana Alerting for your data source</li>
</ul>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>Knowledge about how data sources are implemented in the frontend.</li>
<li>Grafana 7.0</li>
<li>Go 1.14+</li>
<li><a href="https://magefile.org/">Mage</a></li>
<li>NodeJS</li>
<li>yarn</li>
</ul>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Set up your environment</h2>
<p>Before you can get started building plugins, you need to set up your environment for plugin development.</p>
<p>To discover plugins, Grafana scans a <em>plugin directory</em>, the location of which depends on your operating system.</p>
<ol>
<li>
<p>Create a directory called <code>grafana-plugins</code> in your preferred workspace.</p>
</li>
<li>
<p>Find the <code>plugins</code> property in the Grafana configuration file and set the <code>plugins</code> property to the path of your <code>grafana-plugins</code> directory. Refer to the <a href="https://grafana.com/docs/grafana/latest/installation/configuration/#plugins">Grafana configuration documentation</a> for more information.</p>
<pre><code class="language-ini">[paths]
plugins = &quot;/path/to/grafana-plugins&quot;
</code></pre>
</li>
<li>
<p>Restart Grafana if it&rsquo;s already running, to load the new configuration.</p>
</li>
</ol>
<h3 id="alternative-method-docker">Alternative method: Docker</h3>
<p>If you don&rsquo;t want to install Grafana on your local machine, you can use <a href="https://www.docker.com">Docker</a>.</p>
<p>To set up Grafana for plugin development using Docker, run the following command:</p>
<pre><code>docker run -d -p 3000:3000 -v &quot;$(pwd)&quot;/grafana-plugins:/var/lib/grafana/plugins --name=grafana grafana/grafana
</code></pre>
<p>Since Grafana only loads plugins on start-up, you need to restart the container whenever you add or remove a plugin.</p>
<pre><code>docker restart grafana
</code></pre>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Create a new plugin</h2>
<p>To build a backend for your data source plugin, Grafana requires a binary that it can execute when it loads the plugin during start-up. In this guide, we will build a binary using the <a href="https://grafana.com/docs/grafana/latest/developers/plugins/backend/grafana-plugin-sdk-for-go/">Grafana plugin SDK for Go</a>.</p>
<p>The easiest way to get started is to clone one of our test data datasources. Navigate to the plugin folder that you configured in step 1 and type:</p>
<pre><code>npx @grafana/toolkit@next plugin:create my-plugin
</code></pre>
<p>Select <strong>Backend Datasource Plugin</strong> and follow the rest of the steps in the plugin scaffolding command.</p>
<pre><code class="language-bash">cd my-plugin
</code></pre>
<p>Install frontend dependencies and build frontend parts of the plugin to <em>dist</em> directory:</p>
<pre><code class="language-bash">yarn install
yarn build
</code></pre>
<p>Run the following to update <a href="https://grafana.com/docs/grafana/latest/developers/plugins/backend/grafana-plugin-sdk-for-go/">Grafana plugin SDK for Go</a> dependency to the latest minor version:</p>
<pre><code class="language-bash">go get -u github.com/grafana/grafana-plugin-sdk-go
</code></pre>
<p>Build backend plugin binaries for Linux, Windows and Darwin to <em>dist</em> directory:</p>
<pre><code class="language-bash">mage -v
</code></pre>
<p>Now, let&rsquo;s verify that the plugin you&rsquo;ve built so far can be used in Grafana when creating a new data source:</p>
<ol>
<li>Restart your Grafana instance.</li>
<li>Open Grafana in your web browser.</li>
<li>Navigate via the side-menu to <strong>Configuration</strong> -&gt; <strong>Data Sources</strong>.</li>
<li>Click <strong>Add data source</strong>.</li>
<li>Find your newly created plugin and select it.</li>
<li>Enter a name and then click <strong>Save &amp; Test</strong> (ignore any errors reported for now).</li>
</ol>
<p>You now have a new data source instance of your plugin that is ready to use in a dashboard:</p>
<ol>
<li>Navigate via the side-menu to <strong>Create</strong> -&gt; <strong>Dashboard</strong>.</li>
<li>Click <strong>Add new panel</strong>.</li>
<li>In the query tab, select the data source you just created.</li>
<li>A line graph is rendered with one series consisting of two data points.</li>
<li>Save the dashboard.</li>
</ol>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Anatomy of a backend plugin</h2>
<p>The folders and files used to build the backend for the data source are:</p>
<table>
<thead>
<tr>
<th>file/folder</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Magefile.go</code></td>
<td>It’s not a requirement to use mage build files, but we strongly recommend using it so that you can use the build targets provided by the plugin SDK.</td>
</tr>
<tr>
<td><code>/go.mod </code></td>
<td>Go modules dependencies, <a href="https://golang.org/cmd/go/#hdr-The_go_mod_file">reference</a></td>
</tr>
<tr>
<td><code>/src/plugin.json</code></td>
<td>A JSON file describing the backend plugin</td>
</tr>
<tr>
<td><code>/pkg/main.go</code></td>
<td>Starting point of the plugin binary.</td>
</tr>
</tbody>
</table>
<h4 id="plugin-json">plugin.json</h4>
<p>The <a href="https://grafana.com/docs/grafana/latest/developers/plugins/metadata/">plugin.json</a> file is required for all plugins. When building a backend plugin these properties are important:</p>
<table>
<thead>
<tr>
<th>property</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>backend</td>
<td>Should be set to <code>true</code> for backend plugins. This tells Grafana that it should start a binary when loading the plugin.</td>
</tr>
<tr>
<td>executable</td>
<td>This is the name of the executable that Grafana expects to start, see <a href="https://grafana.com/docs/grafana/latest/developers/plugins/metadata/">plugin.json reference</a> for details.</td>
</tr>
<tr>
<td>alerting</td>
<td>Should be set to <code>true</code> if your backend datasource supports alerting.</td>
</tr>
</tbody>
</table>
<p>In the next step we will look at the query endpoint!</p>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Implement data queries</h2>
<p>We begin by opening the file <code>/pkg/sample-plugin.go</code>. In this file you will see the <code>SampleDatasource</code> struct which implements the <a href="https://pkg.go.dev/github.com/grafana/grafana-plugin-sdk-go/backend?tab=doc#QueryDataHandler">backend.QueryDataHandler</a> interface. The <code>QueryData</code> method on this struct is where the data fetching happens for a data source plugin.</p>
<p>Each request contains multiple queries to reduce traffic between Grafana and plugins. So you need to loop over the slice of queries, process each query, and then return the results of all queries.</p>
<p>In the tutorial we have extracted a method named <code>query</code> to take care of each query model. Since each plugin has their own unique query model, Grafana sends it to the backend plugin as JSON. Therefore the plugin needs to <code>Unmarshal</code> the query model into something easier to work with.</p>
<p>As you can see the sample only returns static numbers. Try to extend the plugin to return other types of data.</p>
<p>You can read more about how to <a href="https://grafana.com/docs/grafana/latest/developers/plugins/data-frames/">build data frames in our docs</a>.</p>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Add support for health checks</h2>
<p>Implementing the health check handler allows Grafana to verify that a data source has been configured correctly.</p>
<p>When editing a data source in Grafana&rsquo;s UI, you can <strong>Save &amp; Test</strong> to verify that it works as expected.</p>
<p>In this sample data source, there is a 50% chance that the health check will be successful. Make sure to return appropriate error messages to the users, informing them about what is misconfigured in the data source.</p>
<p>Open <code>/pkg/sample-plugin.go</code>. In this file you&rsquo;ll see that the <code>SampleDatasource</code> struct also implements the <a href="https://pkg.go.dev/github.com/grafana/grafana-plugin-sdk-go/backend?tab=doc#CheckHealthHandler">backend.CheckHealthHandler</a> interface. Navigate to the <code>CheckHealth</code> method to see how the health check for this sample plugin is implemented.</p>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Enable Grafana Alerting</h2>
<ol>
<li>Open <em>src/plugin.json</em>.</li>
<li>Add the top level <code>backend</code> property with a value of <code>true</code> to specify that your plugin supports Grafana Alerting, e.g.
<pre><code class="language-json">{
...
&quot;backend&quot;: true,
&quot;executable&quot;: &quot;gpx_simple_datasource_backend&quot;,
&quot;alerting&quot;: true,
&quot;info&quot;: {
...
}
</code></pre>
</li>
<li>Rebuild frontend parts of the plugin to <em>dist</em> directory:</li>
</ol>
<pre><code class="language-bash">yarn build
</code></pre>
<ol>
<li>Restart your Grafana instance.</li>
<li>Open Grafana in your web browser.</li>
<li>Open the dashboard you created earlier in the <em>Create a new plugin</em> step.</li>
<li>Edit the existing panel.</li>
<li>Click on the <em>Alert</em> tab.</li>
<li>Click on <em>Create Alert</em> button.</li>
<li>Edit condition and specify <em>IS ABOVE 10</em>. Change <em>Evaluate every</em> to <em>10s</em> and clear the <em>For</em> field to make the alert rule evaluate quickly.</li>
<li>Save the dashboard.</li>
<li>After some time the alert rule evaluates and transitions into <em>Alerting</em> state.</li>
</ol>
</div>
<div class="tutorial-content__step" style="transform: translate3d(200%, 0, 0);">
<h2 class="tutorial-content__step-title">Congratulations</h2>
Congratulations, you made it to the end of this tutorial!
</div></p>
Pro tips for making the most of your Datadog metrics in Grafana with the enterprise pluginhttps://grafana.com/blog/2020/04/13/pro-tips-for-making-the-most-of-your-datadog-metrics-in-grafana-with-the-enterprise-plugin/
Mon, 13 Apr 2020 00:00:00 -0400https://grafana.com/blog/2020/04/13/pro-tips-for-making-the-most-of-your-datadog-metrics-in-grafana-with-the-enterprise-plugin/<p>Hello again! We are Eldin and Christine &ndash; or, as our lovely editor has dubbed us, Regis and Kelly &ndash; jumping back in for another post.</p>
<p>This week, to highlight the big tent and community theme, we are going to write about how adding the <a href="https://grafana.com/grafana/plugins/grafana-datadog-datasource">Datadog plugin</a> and visualizations alongside other sources (like Prometheus and Loki) helps provide a fuller picture of operations.</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin.png"
alt="Dashboard with multiple data sources"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin.png" alt="Dashboard with multiple data sources" />
</noscript></a>
</figure>
<p>Datadog is a popular monitoring and analytics platform that allows you to easily install an agent so you can get started with collecting metrics right away. Since we announced our Enterprise Datadog data source two years ago, the plugin has continued to evolve. We thought we were due for a blog post to show off some of our favorite features, ensuring you really can see it all in one place in no time.</p>
<h2 id="getting-started">Getting started</h2>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin1.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin1.png"
alt="Datadog dashboard"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin1.png" alt="Datadog dashboard" />
</noscript></a>
</figure>
<p>In our example dashboard, we are visualizing some infrastructure metrics with our graph, <a href="https://grafana.com/docs/grafana/latest/features/panels/stat/">stat</a>, and <a href="https://grafana.com/docs/grafana/latest/features/panels/bar_gauge/">bar gauge</a> panels. (As of the <a href="https://grafana.com/blog/2020/01/27/grafana-v6.6-released/">6.6 release</a>, we debuted the super flexible stat panel and the beloved bar gauge got a facelift!)</p>
<p>To start using Datadog as a data source, <a href="https://grafana.com/grafana/plugins/grafana-datadog-datasource">install</a> the Datadog plugin. After, add your data source to Grafana by going to Configuration &gt; Data Sources &gt; Add data source. Find the Datadog data source and fill out the connection details (you will need your API and App key &ndash; more information can be found <a href="https://docs.datadoghq.com/api/?lang=bash#authentication">here</a>):</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin2.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin2.png"
alt="Add data source"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin2.png" alt="Add data source" />
</noscript></a>
</figure>
<p><em>Tip: For large dashboards that will be making lots of queries, it is possible to be rate-limited by the Datadog API. The Cache Interval feature helps you bypass these limits by caching unique queries. The default is 60 seconds and can be adjusted in the configuration page.</em></p>
<h2 id="some-tips-on-querying">Some tips on querying</h2>
<p>Once you&rsquo;ve set up your data source, let&rsquo;s head over to a new dashboard and add a panel to take a look at the query editor.</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin3.gif"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin3.gif"
alt="Add a panel"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin3.gif" alt="Add a panel" />
</noscript></a>
</figure>
<p>If you are familiar with our other query editors, you&rsquo;ll notice the Datadog editor is just as easy! Select the metric, aggregation, and select one or more tags (if you want to filter results). The data source also supports all of the advanced functions with which you are familiar. Select it from the Functions dropdown and click the left/right arrows to order the functions.</p>
<p><strong>One of our go-tos is the Alias By field.</strong></p>
<p>Let’s say you have this metric:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin4.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin4.png"
alt="Datadog dashboard"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin4.png" alt="Datadog dashboard" />
</noscript></a>
</figure>
<p>You can leverage this scoped variable $__aggr($__display_name) to replace the metric name and aggregation:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin5.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin5.png"
alt="Replace metric name and aggregation"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin5.png" alt="Replace metric name and aggregation" />
</noscript></a>
</figure>
<p>Here’s a list of available variables that can be used:</p>
<ul>
<li>$__metric = replaced with metric name</li>
<li>$__display_name = replaced with metric name</li>
<li>$__expression = replaced with full metric expression</li>
<li>$__aggr = replaced with metric aggregation function (e.g. avg, max, min, sum)</li>
<li>$__scope = replaced with metric scope (e.g. region, site, env, host)</li>
</ul>
<p>We also support ever useful regular expressions!</p>
<p><strong>Tip for arithmetic queries:</strong> In the query type, switch it to Arithmetic. Use the pound sign (#) to reference a desired query. For example, #A * 2 will double the result of query A. Arithmetic between two metrics works the same way &ndash; you can perform calculations between two queries, such as #A / #B.</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin6.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin6.png"
alt="Arithmetic query"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin6.png" alt="Arithmetic query" />
</noscript></a>
</figure>
<h2 id="templating">Templating!</h2>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin7.gif"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin7.gif"
alt="Templating"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin7.gif" alt="Templating" />
</noscript></a>
</figure>
<p>There are a few options for getting values from <a href="https://grafana.com/docs/grafana/latest/reference/templating/#adding-a-variable">template variables</a>: metrics and tags. To fetch the list of available metrics, specify the star symbol (*) in the Query field (or to return all tags, input the value <code>tag</code> or <code>scope</code>).</p>
<p>To return tags for a specified tag group, use one of the following default category values:</p>
<ul>
<li>host</li>
<li>device</li>
<li>env</li>
<li>region</li>
<li>site</li>
<li>status</li>
<li>version</li>
</ul>
<p>Custom tag groups and filtered results are possible by using the Regex field.</p>
<h2 id="one-last-thing">One last thing</h2>
<p>Our last tip is to leverage <a href="https://grafana.com/docs/grafana/latest/reference/templating/#variable-types">ad-hoc filters</a>. This variable will apply to all the Datadog queries using the variable in a dashboard, so you can use it like a quick filter. An ad-hoc variable for Datadog fetches all the key-value pairs from tags (for example, region:east, region:west) and uses them as query tags. To create this variable, select the Ad-hoc filter type and choose your Datadog data source. You can set any name for this variable.</p>
<p>That’s all we have time for today, but please tweet at us to let us know the next plugin you’d like us to cover. Happy dashboarding and until next time! Kelly and Regis out!</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/datadog_plugin8.gif"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/datadog_plugin8.gif"
alt="Kelly and Regis out!"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/datadog_plugin8.gif" alt="Kelly and Regis out!" />
</noscript></a>
</figure>
How to pull Oracle data and stats directly into Grafana dashboards with the Oracle Enterprise pluginhttps://grafana.com/blog/2020/03/24/how-to-pull-oracle-data-and-stats-directly-into-grafana-dashboards-with-the-oracle-enterprise-plugin/
Tue, 24 Mar 2020 00:00:00 -0400https://grafana.com/blog/2020/03/24/how-to-pull-oracle-data-and-stats-directly-into-grafana-dashboards-with-the-oracle-enterprise-plugin/<p><em>Greetings! This is Eldin reporting from the Solutions Engineering team at Grafana Labs. In previous posts, you might have read about Grafana’s <a href="https://grafana.com/blog/2020/02/18/everything-you-need-to-know-about-the-splunk-plugin-for-grafana/">Splunk</a> or <a href="https://grafana.com/blog/2020/02/03/introducing-the-new-servicenow-plugin-for-grafana/">ServiceNow</a> plugins. In this week&rsquo;s post, I am introducing <a href="https://grafana.com/blog/2019/12/13/meet-the-grafana-labs-team-aengus-rooney/">Aengus</a>, who will be covering our Oracle Enterprise data source. &ndash; Eldin</em></p>
<p>Grafana’s plugins are a quick and simple way to extend Grafana’s dashboard plugins and data sources. Leveraging the GoLang oci8 library, the Oracle plugin allows you to pull Oracle data and stats directly into Grafana dashboards &ndash; without having to extract and load the data to “yet another data warehouse” &ndash; which means you can visualize the data either in isolation (one database) or blend the data with other data sources (e.g., other Oracle databases, other database technologies, MSSQL, MySQL, Maria, Postgres, RDS, etc.). This enables you to discover and visualize correlations and covariances in your data in minutes.</p>
<p>As someone who has worked with many databases in my past, I found that the ease of use, extensibility, and the ability to mix queries across databases in real time make this plugin a highly powerful tool to have in your analytics and visualization toolkit.</p>
<p>Alongside visualizing business data, we can also visualize system data in a number of ways. First, we can query the Oracle system tables to visualize system health, logins and sessions, blocked users, CPU, Memory and IO all in one place and create a DevOps dashboard similar to the following example:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin1.jpg"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin1.jpg"
alt="Oracle DevOps dashboard"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin1.jpg" alt="Oracle DevOps dashboard" />
</noscript></a>
</figure>
<p>Remember: If you are running multiple Oracle instances, you don’t need multiple dashboards. You can easily switch between instances by dropping down the <code>Oracle_Instance</code> variable list and selecting the instance you wish to monitor. Alternatively, you can run one dashboard and aggregate any metric across all of your instances in one panel!</p>
<p>We can also export OS-level data using the <a href="https://prometheus.io/docs/instrumenting/exporters/">Oracle DB Prometheus Exporter</a>, which provides statistics on database utilization, e.g., Database Up/Down, User Activity, Resource Utilization, Tablespace Capacity Utilization, Session Activity, and Process Counts.</p>
<p>Bringing the two types of data together (system data and business data) provides a powerful example of a mixed DevOps dashboard. For example, in the dashboard below, we are leveraging an HR sample schema along with the Oracle Database self-generated system time series using the Oracle DB Prometheus Exporter. Developers can now check the impact of their queries and applications on the database without having to raise a request for the DBA team to provide this information!</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin2.jpg"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin2.jpg"
alt="Oracle mixed DevOps dashboard"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin2.jpg" alt="Oracle mixed DevOps dashboard" />
</noscript></a>
</figure>
<p>With this quick view, we are getting the status, active sessions, user commits, wait time, and business table data out of the database. Furthermore, each “quick view” panel can link to additional panels with more detailed data.</p>
<p>So, how do you get this all up and running? Once you have the <a href="https://grafana.com/grafana/plugins/grafana-oracle-datasource/installation">datasource installed</a> on your Grafana server, we need to add it to your Grafana data sources in the Configuration section via “Configuration” =&gt; “Datasources” =&gt; “Add”. Find the Oracle data source and complete the connection details:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin3.jpg"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin3.jpg"
alt="Connecting the Oracle data source"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin3.jpg" alt="Connecting the Oracle data source" />
</noscript></a>
</figure>
<p><em>(Note: Installing the datasource has some dependencies such as the <a href="https://www.oracle.com/uk/database/technologies/instant-client/downloads.html">Oracle Instant Client</a>.)</em></p>
<p>Once the data source has been installed, you can start adding new dashboards with Oracle Database as the query source and use the SQL query editor to write your queries. You have two options in the editor for the type of data you wish to query and display: relational table data (for table data visualization) or time series data (for graph visualizations).</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin4.gif"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin4.gif"
alt="Writing queries"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin4.gif" alt="Writing queries" />
</noscript></a>
</figure>
<p>The query editor also has a useful SQL statement auto-complete:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin5.gif"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin5.gif"
alt="Writing queries"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin5.gif" alt="Writing queries" />
</noscript></a>
</figure>
<p>Now, let’s play with some queries to test the plugin. On our plugins page, <a href="https://grafana.com/grafana/plugins/grafana-oracle-datasource#macros">we offer some macros</a> that can be leveraged to simplify syntax and to allow for dynamic parts like date range filters.</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin6.jpg"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin6.jpg"
alt="Macros to simplify syntax"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin6.jpg" alt="Macros to simplify syntax" />
</noscript></a>
</figure>
<h2 id="setting-up-database-alerts">Setting up database alerts</h2>
<p>With the Oracle Datasource and a graph panel, you can set an alert in Grafana. You can set a threshold as shown in the example below or drag the slider to set an alert based on the data. Read more about alerting <a href="https://grafana.com/docs/grafana/latest/alerting/rules/">here</a>!</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin7.gif"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin7.gif"
alt="Setting an alert"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin7.gif" alt="Setting an alert" />
</noscript></a>
</figure>
<p>For example, alerting on Blocked Users or Sessions is a very useful feature to have and setting this up takes seconds! In the panel alert view, set the blocked users count to be above zero and set the notification you wish to send any time the rule is triggered &ndash; done!</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin8.jpg"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin8.jpg"
alt="Alerting on Blocked Users or Sessions"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin8.jpg" alt="Alerting on Blocked Users or Sessions" />
</noscript></a>
</figure>
<p>One final tip: When working with tables, my go-to is hiding columns that are not relevant:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin9.jpg"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin9.jpg"
alt="Hiding columns that aren’t relevant"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin9.jpg" alt="Hiding columns that aren’t relevant" />
</noscript></a>
</figure>
<p>Here is the output of all the columns, and here it is with a little bit of cleanup:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin10.jpg"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin10.jpg"
alt="Cleaned up output"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin10.jpg" alt="Cleaned up output" />
</noscript></a>
</figure>
<p>Visit the ‘options’ section and add a few rules to the columns for which you would like to hide:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/oracle_plugin11.gif"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/oracle_plugin11.gif"
alt="Adding rules to hide columns"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/oracle_plugin11.gif" alt="Adding rules to hide columns" />
</noscript></a>
</figure>
<p>Well, that’s all we have time for today, but please tweet us to let us know the next plugin you’d like to hear about. Happy dashboarding and until next time!</p>
An Inside Look at the Life of a Technical Writer at Grafana Labshttps://grafana.com/blog/2020/02/20/an-inside-look-at-the-life-of-a-technical-writer-at-grafana-labs/
Thu, 20 Feb 2020 00:00:00 -0400https://grafana.com/blog/2020/02/20/an-inside-look-at-the-life-of-a-technical-writer-at-grafana-labs/<p>People think technical writing is boring, but sometimes <a href="https://grafana.com/blog/2020/01/15/everything-you-need-to-know-about-the-grafana-prometheus-gitlab-integration/">documenting software</a> is an adventure.</p>
<p>It&rsquo;s not an adventure like &ldquo;whee, got my sword and shield, adventure time!” No, it&rsquo;s more like taking a nice stroll down a path to an unfamiliar-but-known destination when the ground suddenly opens up under your feet. As you’re falling down into the depths, that&rsquo;s when you realize you are about to have an adventure.</p>
<p>I&rsquo;m a technical writer at Grafana Labs. Writing, editing, and generally improving the <a href="https://grafana.com/docs/grafana/latest/">technical documentation</a> is what I do for a living. (Writing snarky blog posts is a fun hobby, though.)</p>
<p>So there I was, testing <a href="https://grafana.com/grafana/">Grafana</a> installation documentation on my <a href="https://grafana.com/docs/grafana/latest/installation/windows/">Windows machine</a> and <a href="https://grafana.com/docs/grafana/latest/installation/debian/">Linux VMs</a>. Before moving on to the next task, I decided to do a nice, simple task on both machines. Something quintessentially Grafana; something so simple that nothing could possibly go wrong.</p>
<p>Word of advice: Never say that. Not even in the privacy of your own head.</p>
<p>I decided to install a <a href="https://grafana.com/grafana/plugins?orderBy=weight&amp;direction=asc">plugin</a>.</p>
<p>No problem on Linux.</p>
<p>Windows, though…</p>
<pre><code>Error: ✗ Failed to extract plugin archive: Could not create ../data/plugins/sidewinder-datasource. Permission denied. Make sure you have write access to plugindir
</code></pre>
<p>&ldquo;No problem,&rdquo; I thought. &ldquo;Maybe it needs to be run in PowerShell.&rdquo;</p>
<p>Same result.</p>
<p>I glared at my computer, then remembered that running PowerShell as an Administrator was a thing. It didn&rsquo;t take long to figure out how to do that on Windows 10. And thankfully, it worked.</p>
<p>While I waited for the installation to finish, I had a stern discussion with my laptop. &ldquo;You only have one user: me. By definition, I am the administrator. That means <em>everything</em> I run on you is being run by an administrator. I <em>own</em> you!&rdquo;</p>
<p>The computer reacted about as one would expect.</p>
<p><code>Installation complete</code></p>
<p>I breathed a sigh of relief and entered the command <a href="https://grafana.com/docs/grafana/latest/administration/cli/">Grafana CLI</a> printed at the end: <code>service grafana-server restart</code>.</p>
<p>Response:</p>
<pre><code>.\service : The term '.\service' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ .\service grafana-server restart
</code></pre>
<p>After a few questions to my developer colleagues, I found out this was a Linux command, and I needed to enter a different command for Windows. That will be hopefully corrected in Grafana 6.7 <a href="https://github.com/grafana/grafana/issues/20983">issue #20983</a>.</p>
<p>The whole &ldquo;PowerShell as Admin&rdquo; requirement seemed like a pretty big gap in our documentation so, busy documentarian that I am, I decided to add a few paragraphs to address it.</p>
<p>I opened <a href="https://grafana.com/docs/grafana/latest/plugins/installation/">Install Plugins</a> and was greeted with a whole list of Grafana CLI plugins commands.</p>
<p>Oh, <em>hell</em> no.</p>
<p>For those of you who are unfamiliar with topic types, we technical communicators try to divide content into concept, task, and reference topics. This was a big chunk of reference content in what should have been a task topic.</p>
<p>&ldquo;No problem,&rdquo; I thought. &ldquo;I&rsquo;ll just copy and paste that content into <a href="https://grafana.com/docs/grafana/latest/administration/cli/">Grafana CLI</a> where it belongs and write an actual task for installing plugins.&rdquo;</p>
<p>I moved the content and then ran <code>grafana-cli --help</code> to make sure nothing had changed since the Grafana CLI had last been updated.</p>
<p>Amid the wall of text listing all the options, commands, and other useful things to know, there was one glaring surprise.</p>
<p><em>&ldquo;What do you mean Global Options?&quot;</em></p>
<p>Global Options&ndash;and several other commands listed in the Grafana CLI help output&ndash;weren&rsquo;t even documented.</p>
<p>Now, I could have moved back to the plugins docs, but that sort of documentation gap is like an itch. Once you know it&rsquo;s there, it bugs you. Haunts you.</p>
<p>I couldn&rsquo;t just move on. There was nothing to be done but to venture into the bowels of Grafana CLI and tap into developer brains to slay the beast.</p>
<p>Long story short (or as short as I can manage at this point) <a href="https://github.com/grafana/grafana/pull/21179">PR #21179</a> applied all the updates.</p>
<p>I hope you enjoyed this little glimpse into the Land of Software Documentation. Let us know if you have any suggestions for parts of the documentation that I should tackle. I&rsquo;m always up for another adventure!</p>
Introducing the New ServiceNow Plugin for Grafanahttps://grafana.com/blog/2020/02/03/introducing-the-new-servicenow-plugin-for-grafana/
Mon, 03 Feb 2020 00:00:00 -0400https://grafana.com/blog/2020/02/03/introducing-the-new-servicenow-plugin-for-grafana/<p>Greetings! This is Christine and Eldin reporting from Solutions Engineering, a team at Grafana Labs dedicated to helping users maximize what our <a href="https://grafana.com/products/enterprise/">Enterprise</a> and <a href="https://grafana.com/products/cloud/">Cloud</a> products can do for your orgs.</p>
<p>In a previous life, Eldin managed the customer experience department for several different companies and still remembers the pain of running daily reports so his team could have visibility into their ticket queues. Christine has also, as she puts it, “paid her dues as frontlines technical support,” weaving her way through tickets, SLAs, and waning customer patience.</p>
<p>So today, we want to talk about something clearly very near and dear to our hearts: our new <a href="https://grafana.com/grafana/plugins/grafana-servicenow-datasource">ServiceNow plugin</a>.</p>
<h2 id="we-servicenow-grafana">We ❤️ ServiceNow + Grafana</h2>
<p>The latest addition to our burgeoning assemblage of Enterprise plugins, our ServiceNow plugin enables Grafana users to visualize the data they’re already collecting with the popular workflow automation platform against any other metric data source.</p>
<p>Imagine this: Instead of facing an endless list of incidents, you can view meaningful and actionable dashboards when you want to understand the aggregated, top-level summary of the health of your IT service management.</p>
<p>The end goal is to take something like this:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/servicenow_plugin1.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/servicenow_plugin1.png"
alt="ServiceNow Dashboard"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/servicenow_plugin1.png" alt="ServiceNow Dashboard" />
</noscript></a>
</figure>
<p>And turn it into this:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/servicenow_plugin2.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/servicenow_plugin2.png"
alt="ServiceNow Grafana Dashboard"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/servicenow_plugin2.png" alt="ServiceNow Grafana Dashboard" />
</noscript></a>
</figure>
<p>The plugin currently supports ServiceNow’s Incident Management and Change Management solutions. When you can leverage your incident data with other metrics to gain deeper insight into all of your systems, your team can spend less time working on incidents and reduce response times. Say you suddenly receive multiple reports of an incident. Now, with dashboards that utilize data sources from your full technology <em>and</em> business processes stack, you’ll be able to, in real time, correlate the incident to an issue within your IT infrastructure.</p>
<h2 id="some-quick-tips">Some Quick Tips</h2>
<ul>
<li>
<p>Remember to take advantage of <a href="https://grafana.com/blog/2019/08/27/new-in-grafana-6.3-easy-to-use-data-links/">data links</a> so your troubleshooting workflow is seamless as you click through dashboards to uncover supporting data in addition to what ServiceNow collects.</p>
</li>
<li>
<p>With the ability to add and configure <a href="https://grafana.com/docs/grafana/latest/alerting/rules/">alerts</a>, you can notify any and all of your channels to prevent SLA breaches.</p>
</li>
<li>
<p>We are ultimately in the business of making you look good at work, so don’t forget to set up a daily <a href="https://grafana.com/docs/grafana/latest/features/reporting/">PDF report</a> so your bosses can have end-of-day stats delivered directly to their inboxes.</p>
</li>
</ul>
<p>One last goodie before we hang up our boots again: If you’ve installed last week’s <a href="https://grafana.com/docs/grafana/latest/guides/whats-new-in-v6-6/">Grafana 6.6 release</a>, be sure to test out the News Panel so you can integrate your internal status page into your dashboards:</p>
<figure
class="figure-wrapper figure-wrapper__lightbox"
style="max-width: 800px;"
itemprop="associatedMedia"
itemscope=""
itemtype="http://schema.org/ImageObject">
<a
href="https://grafana.com/static/assets/img/blog/servicenow_plugin3.png"
itemprop="contentUrl"><img
class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="https://grafana.com/static/assets/img/blog/servicenow_plugin3.png"
alt="News Panel"
/>
<noscript>
<img src="https://grafana.com/static/assets/img/blog/servicenow_plugin3.png" alt="News Panel" />
</noscript></a>
</figure>
<p>Please <a href="https://twitter.com/grafana">give us a shout</a> when you’ve set up your ServiceNow dashboards &ndash; we love seeing what the community comes up with. And, as always, happy dashboarding!</p>
Community Spotlight: BigQuery Pluginhttps://grafana.com/blog/2019/06/25/community-spotlight-bigquery-plugin/
Tue, 25 Jun 2019 00:00:00 -0400https://grafana.com/blog/2019/06/25/community-spotlight-bigquery-plugin/<p>The Grafana community comes up with some pretty cool stuff, and we’re hoping to spotlight some of it from time to time. Today, we’re starting with the <a href="https://github.com/doitintl/bigquery-grafana">BigQuery datasource plugin</a> developed by the team at <a href="https://www.doit-intl.com/labs/">DoiT International</a>.</p>
<p>DoiT is a reseller of Google Cloud and AWS that helps companies either move from on premise to cloud or move from one cloud provider to another. &ldquo;We mostly do architecture review for companies and help them solve problems,&rdquo; says <a href="https://github.com/avivl">Aviv Laufer</a>, a Principal Reliability Engineer at DoiT. &ldquo;When there is a problem that’s common to a few customers, I try to solve it in a general manner and provide it as an open source solution.&rdquo;</p>
<p>And that’s what led to the plugin for BigQuery, which many of DoiT’s customers use. &ldquo;We’re pretty involved in the Grafana community, and we saw that one of the most voted features of Grafana was the connector to BigQuery database,&rdquo; he says. &ldquo;BigQuery comes with the data visualization by Google, which is not as good as Grafana, at least in my experience. Some of our customers were using Google Sheets in order to do graphs in BigQuery! And a lot of our customers were already using Grafana for other purposes. I like the fact that they can use one visualization tool across multiple sources.&rdquo;</p>
<p>So Laufer decided to tackle the issue, and after about six weeks of work off and on, the plugin was released. It was the first time he had coded in TypeScript, and the first time he had built a plugin for Grafana. He says there’s &ldquo;room for improvement&rdquo; in the Grafana documentation about how to write plugins, so he set out to &ldquo;reverse engineer some of the other plugins and go through the Grafana code, figure out what I can do, and how I can do it. At the end of the day, it worked.&rdquo;</p>
<p><img src="https://grafana.com/static/assets/img/blog/BigQuery1.png" alt="Dashboard visualizing Google Cloud Billing data from BigQuery" /></p>
<p>Since the plugin’s release, &ldquo;more than a handful&rdquo; of DoiT’s customers &ndash; including some large companies in Israel &ndash; are using it, and many other people around the world are downloading it, opening issues on GitHub, and asking for features. (One common request: alerts.)</p>
<p>&ldquo;We are looking for more suggestions, more functionality,&rdquo; Laufer says. &ldquo;We want to hear feedback from the community. We are looking to make this a better product.&rdquo;</p>
<p>And it might not be the last Grafana plugin you’ll see from the DoiT team. &ldquo;If there’s something that will benefit our customers and the community, we probably will do it,&rdquo; he says. &ldquo;We first look for the problem and then find out whether or not we can provide a solution. There are lots of other solutions that we have, and all of them are open source.&rdquo;</p>
Grafana Tutorial: How to Create Kiosks to Display Dashboards on a TVhttps://grafana.com/blog/2019/05/02/grafana-tutorial-how-to-create-kiosks-to-display-dashboards-on-a-tv/
Thu, 02 May 2019 00:00:00 -0400https://grafana.com/blog/2019/05/02/grafana-tutorial-how-to-create-kiosks-to-display-dashboards-on-a-tv/<h1 id="grafana-kiosk-grafana-kiosk-https-github-com-grafana-grafana-kiosk">Grafana Kiosk <a href="https://github.com/grafana/grafana-kiosk">grafana-kiosk</a></h1>
<p>A very useful feature of Grafana is the ability to display dashboards and playlists on a large TV.</p>
<p>Documentation on how to do this is sparse, which inspired this tutorial and also led to automating the process.</p>
<ul>
<li>login
<ul>
<li>to a Grafana server (local account)</li>
<li>to a Grafana server with anonymous-mode enabled (same method used on <a href="https://play.grafana.org">https://play.grafana.org</a>)</li>
<li>to a Hosted Grafana instance (sign up for a free instance at <a href="https://grafana.com">https://grafana.com</a>)</li>
</ul>
</li>
<li>switch to kiosk or kiosk-tv mode</li>
<li>display the default dashboard set for the user</li>
<li>display a specified dashboard</li>
<li>start a playlist immediately (inactive mode enable)</li>
</ul>
<p>Additionally, an initialize option is provided to configure LXDE for Raspberry Pi Desktop.</p>
<p>Here&rsquo;s what we&rsquo;ll end up with (please note these screenshots include the virtualbox window):</p>
<p>A kiosk displaying the default dashboard on <a href="https://play.grafana.org">https://play.grafana.org</a> in full screen:
<img src="https://grafana.com/static/assets/img/blog/grafana-kiosk/play-grafana-org-1.png" alt="kiosk goal1" /></p>
<p>A kiosk displaying a specific dashboard on <a href="https://play.grafana.org">https://play.grafana.org</a> in TV mode:
<img src="https://grafana.com/static/assets/img/blog/grafana-kiosk/play-grafana-org-2.png" alt="kiosk goal2" /></p>
<p>A kiosk displaying another dashboard on <a href="https://play.grafana.org">https://play.grafana.org</a> in normal mode:
<img src="https://grafana.com/static/assets/img/blog/grafana-kiosk/play-grafana-org-3.png" alt="kiosk goal3" /></p>
<p>A kiosk with a playlist running on <a href="https://play.grafana.org">https://play.grafana.org</a> in full screen:
<img src="https://grafana.com/static/assets/img/blog/grafana-kiosk/play-grafana-org-4.png" alt="kiosk goal4" /></p>
<p>A kiosk using Hosted Grafana login:
<img src="https://grafana.com/static/assets/img/blog/grafana-kiosk/hosted-grafana-1.png" alt="kiosk goal5" /></p>
<p>A kiosk using local login:
<img src="https://grafana.com/static/assets/img/blog/grafana-kiosk/local-grafana-1.png" alt="kiosk goal6" /></p>
<h2 id="getting-started">Getting Started</h2>
<p>This setup uses a Raspberry Pi as the display for the kiosk. It is well-suited for displaying dashboards and is easy to set up.
The &ldquo;launcher&rdquo; can be used on other devices (Arduino, NUC, etc.) running Linux or MacOS X.</p>
<p>You can also try this out with a VM; RPD will run inside virtualbox. The command will also run on MacOS X.</p>
<h3 id="raspberry-pi-2-3">Raspberry Pi 2 &amp; 3</h3>
<p>Any Raspberry Pi can be used; however, this has only been tested with a v2 and v3b+ (and MacOS X Mojave).</p>
<p>To test with a VM, you can download a current version of <a href="https://www.raspberrypi.org/downloads/raspberry-pi-desktop">Debian Stretch with Raspberry Pi Desktop</a>.</p>
<p>For a Pi, use <a href="https://www.raspberrypi.org/downloads/raspbian/">Raspbian Stretch with desktop and recommended software</a>.</p>
<p><a href="https://downloads.raspberrypi.org/raspbian_latest">Raspbian Stretch with desktop</a> and <a href="https://downloads.raspberrypi.org/raspbian_lite_latest">Raspbian Stretch Lite</a> can also be used. The full install comes with everything you need to get running quickly.</p>
<p>Apply the iso to the sd-card as usual. For MacOS, <a href="https://www.balena.io/etcher/">Etcher.io</a> works great!</p>
<h3 id="install-unclutter-utility">Install &ldquo;Unclutter&rdquo; Utility</h3>
<p>Boot up your Pi, and start a terminal window.</p>
<p>The application &ldquo;unclutter&rdquo; is used to keep the mouse point hidden. Install it with this command:</p>
<pre><code class="language-bash">sudo apt-get install unclutter -y
</code></pre>
<h3 id="enable-and-start-ssh-on-pi">Enable and Start SSH on Pi</h3>
<pre><code class="language-bash">sudo systemctl sshd enable
sudo systemctl sshd start
ip addr
</code></pre>
<p>Note the ip address output from above.</p>
<h3 id="ssh-into-pi">SSH into Pi</h3>
<p>This step can be skipped, but it is much more convenient to interact with the Pi remotely over ssh.</p>
<p>Next &lsquo;ssh&rsquo; into the Pi:</p>
<pre><code class="language-bash">ssh pi@ipaddress
</code></pre>
<p>The default password is <code>raspberry</code> if you haven&rsquo;t changed it.</p>
<h3 id="clone-git-repository">Clone Git Repository</h3>
<p>Next, clone the git repository.</p>
<pre><code class="language-bash">git clone https://github.com/grafana/grafana-kiosk
</code></pre>
<p>Copy the appropriate binary from grafana-kiosk/bin. It will be one of these:</p>
<ul>
<li>grafana-kiosk.linux.armv5</li>
<li>grafana-kiosk.linux.armv6</li>
<li>grafana-kiosk.linux.armv7</li>
</ul>
<pre><code class="language-bash">sudo cp bin/grafana-kiosk.linux.armv7 /usr/bin/grafana-kiosk
</code></pre>
<p>Additional binaries are available to run on standard Linux and MacOS:</p>
<ul>
<li>grafana-kiosk.darwin</li>
<li>grafana-kiosk.linux.amd64</li>
<li>grafana-kiosk.linux.386</li>
</ul>
<h2 id="try-it-out">Try It Out!</h2>
<p>Here are some commands to take it for a spin.</p>
<p>This will take the browser to a playlist on <a href="https://play.grafana.org">https://play.grafana.org</a> in fullscreen kiosk mode:</p>
<pre><code class="language-bash">/usr/bin/grafana-kiosk --URL https://play.grafana.org/playlists/play/1 --login-method anon --kiosk-mode full
</code></pre>
<p>Running Grafana in Docker locally? Try this:</p>
<pre><code class="language-bash">/usr/bin/grafana-kiosk --URL https://localhost:3000 --login-method local --user admin --password admin --kiosk-mode tv
</code></pre>
<p>Have a Hosted Grafana account? Try this, replacing the name of the instance, username, and password as appropriate:</p>
<pre><code class="language-bash">/usr/bin/grafana-kiosk --URL https://GCOM_ACCOUNT.grafana.net --login-method gcom --user GCOM_USER --password GCOM_PASSWORD --kiosk-mode full
</code></pre>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Hopefully this tutorial and utility will spread more kiosks. Show off yours on Twitter!</p>
<h2 id="todo">TODO</h2>
<ul>
<li>Support for OAuth2 logins</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="https://grafana.com/docs/guides/whats-new-in-v5-3/#tv-and-kiosk-mode">https://grafana.com/docs/guides/whats-new-in-v5-3/#tv-and-kiosk-mode</a></li>
<li><a href="https://grafana.com/docs/reference/playlist/">https://grafana.com/docs/reference/playlist/</a></li>
</ul>