[Home] Edit Article
Author Information
File Uploads
Edit Article XML Content
<document> <header> <issuecode /> <articlecode /> <zone /> <title>Developing Dashboards Using Grafana</title> <authors /> <copyright>CODE Magazine</copyright> <owner>CODE Magazine</owner> </header> <body> <p id="0">In my earlier article on Building Dashboards Using Bokeh (https://www.codemag.com/Article/2111061/Building-Dashboards-Using-Bokeh), I talked about how you can build dashboards programmatically using the Bokeh Python library. What if you want to create dashboards but don’t want to spend too much time coding them? The good news is that you can do that using a tool called <b>Grafana</b>. </p> <p id="1">In this article, I’ll walk you through how to get started with Grafana, and how you can use it to build interesting dashboards. I’ll also describe two projects that you can build using Grafana:</p> <list type="bulleted"> <bulletedlist>How to create dynamic auto-updated time series charts</bulletedlist> <bulletedlist>How to display real-time sensor data using MQTT</bulletedlist> </list> <h2>Installing Grafana</h2> <p id="2">The first good news about Grafana is that it’s free to use for most use cases, except where you need enterprise features such as advanced security, reporting, etc. If you want to modify the source code of Grafana, be sure to check the licensing info at https://grafana.com/licensing/. </p> <p id="3">To download the free enterprise version of Grafana, go to https://grafana.com/grafana/download (see <b>Figure 1</b>). There, you’ll find the instructions to download Grafana for the OS that you’re running. </p> <figure id="1" src="image1.png"> <b>Figure 1</b> <b>:</b> Instructions for downloading Grafana for the various OSes</figure> <p id="4">For Windows, you can download the Windows installer (https://dl.grafana.com/enterprise/release/grafana-enterprise-8.5.2.windows-amd64.msi) and then run it. </p> <p id="5">For Mac, you can download and install Grafana directly using Terminal:</p> <codesnippet>$»curl»-O»</codesnippet> <codesnippet>https://dl.grafana.com/enterprise/release/grafana-</codesnippet> <codesnippet>enterprise-8.5.2.darwin-amd64.tar.gz</codesnippet> <codesnippet>$»tar»-zxvf»grafana-enterprise-8.5.2.darwin-</codesnippet> <codesnippet>amd64.tar.gz</codesnippet> <p id="6">Once the above steps are done, you should now find a folder named <b>Grafana-8.5.2</b> in your home directory. On Windows, Grafana will be installed in the following directory: <b>C:\Program Files\GrafanaLabs\grafana\</b>.</p> <h3>Starting the Grafana Service</h3> <p id="7">On the Mac, you need to manually start the Grafana service by using the following commands in Terminal:</p> <codesnippet>$»cd»~</codesnippet> <codesnippet>$»cd»grafana-8.5.2</codesnippet> <codesnippet>$»./bin/grafana-server»web</codesnippet> <p id="8">On Windows, Grafana is installed as a Windows service and is automatically started after you have installed it. </p> <h3>Logging in to Grafana</h3> <p id="9">Once Grafana has been installed on your computer, open it using a Web browser using the following URL: http://localhost:3000/login (see <b>Figure 2</b>).</p> <figure id="2" src="image2.png"> <b>Figure 2</b> <b>:</b> Logging in to Grafana</figure> <p id="10">The default username is <b>admin</b> and password is also <b>admin</b>. Once you’ve logged in, you’ll be asked to change your default password. After that, you’ll see the page shown in <b>Figure 3</b>. </p> <figure id="3" src="image3.png"> <b>Figure 3</b> <b>:</b> The home page of Grafana</figure> <pullquote>If you forget your password, you can reset the admin password using the following command: ./bin/grafana-cli admin reset-admin-password password</pullquote> <h2>Creating Data Sources</h2> <p id="11">Grafana can load data from a variety of data sources, such as Prometheus, MySQL, InfluxDB, PostgreSQL, Microsoft SQL Server, Oracle, MongoDB, and more. If the data source that you need isn’t available, you can locate additional data sources via plug-ins (<b>Settings > Plugins</b>). A good example of a data source that a lot of data analyst use is CSV, which is supported in Grafana through the <b>CSV</b> data source plug-in. </p> <p id="12">Although you can use CSV as a data source in Grafana, querying your data (and manipulating it) is much easier if you use a database data source, such as <b>MySQL</b>. For this article, I’ll show you how to import your CSV file into a MySQL database server and then use MySQL as the data source in Grafana. </p> <h3>Preparing the MySQL databases</h3> <p id="13">For this article, I’m going to assume that you already have MySQL and MySQL Workbench installed. In the MySQL Workbench, log in to your local database server. Create a new query and enter the following SQL statements to create a new account named <b>user1</b> with <b>password</b> as the password:</p> <codesnippet>CREATE»USER»'user1'@'localhost'»IDENTIFIED»BY»</codesnippet> <codesnippet>'password';</codesnippet> <codesnippet>GRANT»ALL»ON»*.*»TO»'user1'@'localhost'</codesnippet> <p id="14">Next, create a new databased named <b>Insurance</b>:</p> <codesnippet>CREATE»DATABASE»Insurance;</codesnippet> <codesnippet>USE»Insurance;</codesnippet> <p id="15">Once the <b>Insurance</b> database is created on MySQL, right-click on <b>Tables</b> and select <b>Table Data Import Wizard</b> (see <b>Figure 4</b>):</p> <figure id="4" src="image4.png"> <b>Figure 4</b> <b>:</b> Import data into the table.</figure> <p id="16">You’ll be loading a CSV file into the <b>Insurance</b> database. For this example, I’ll use the insurance dataset from: https://www.kaggle.com/datasets/teertha/ushealthinsurancedataset. </p> <p id="17">In the Table Data Import wizard that appears, enter the path of the CSV file that you’ve downloaded and follow the steps. Specify that the data of the CSV file be loaded onto the <b>Insurance</b> database and you also have the option to name the table. If you take the default options, the content of the CSV file is loaded onto a table named <b>Insurance</b>. You should now be able to see the imported table and its content (see <b>Figure 5</b>).</p> <figure id="5" src="image5.png"> <b>Figure 5</b> <b>:</b> Viewing the imported Insurance dataset</figure> <h3>Using the MySQL Data Source</h3> <p id="18">With the database prepared, it’s now time to configure the MySQL data source in Grafana. In Grafana, click <b>Configuration > Data sources</b> (see <b>Figure 6</b>).</p> <figure id="6" src="image6.png"> <b>Figure 6</b> <b>:</b> Configuring a new data source</figure> <p id="19">Click <b>Add data source</b> (see <b>Figure 7</b>).</p> <figure id="7" src="image7.png"> <b>Figure 7</b> <b>:</b> Add a new data source.</figure> <p id="20">Type in <b>MySQL</b> and then click on the <b>MySQL</b> data source that appears (see <b>Figure 8</b>).</p> <figure id="8" src="image8.png"> <b>Figure 8</b> <b>:</b> Locate the MySQL data source.</figure> <p id="21">Enter the details of the MySQL connection as shown in <b>Figure 9</b>.</p> <figure id="9" src="image9.png"> <b>Figure 9</b> <b>:</b> <b> </b>Configure the MySQL data source to connect to the Insurance database.</figure> <p id="22">On the bottom of the page, click <b>Save & test</b> to ensure that the connection to the MySQL database server is working correctly.</p> <h2>Creating a Dashboard</h2> <p id="23">Now that the data source configuration is done, it’s time to get into the meat of what we want to do! In Grafana, select <b>+ > Dashboard</b> to create a new dashboard (see <b>Figure 10</b>). </p> <figure id="10" src="image10.png"> <b>Figure 10</b> <b>:</b> Add a new dashboard.</figure> <pullquote>In Grafana, a dashboard is a set of one or more panels organized and arranged into one or more rows.</pullquote> <p id="24">Click on the <b>Save</b> icon and give a name to your dashboard (see <b>Figure 11</b>). Click <b>Save</b>.</p> <figure id="11" src="image11.png"> <b>Figure 11</b> <b>:</b> Name and save the dashboard.</figure> <h3>Creating Panels</h3> <p id="25">With the dashboard created, you’re now ready to add a <b>panel</b> to your dashboard. </p> <pullquote>The panel is the basic visualization building block in Grafana. A panel can display a bar chart, pie chart, time series, map, histogram, and more.</pullquote> <p id="26">Click the <b>Add Panel</b> icon and then the <b>Add a new panel </b>button to add a panel to your dashboard (see <b>Figure 12</b>). </p> <figure id="12" src="image12.png"> <b>Figure 12</b> <b>:</b> Add a new panel to the dashboard.</figure> <p id="27">You should see the default panel, as shown in <b>Figure 13</b>. </p> <figure id="13" src="image13.png"> <b>Figure 13</b> <b>:</b> The default panel added to the dashboard</figure> <p id="28">Let’s try to create a pie chart. Enter the details shown in <b>Figure 14</b>.</p> <figure id="14" src="image14.png"> <b>Figure 14</b> <b>:</b> Display a pie chart in the current panel.</figure> <p id="29">In the above, you set:</p> <list type="bulleted"> <bulletedlist>Visualization type to Pie chart</bulletedlist> <bulletedlist>Data source to MySQL</bulletedlist> <bulletedlist>Manually set the SQL query to:</bulletedlist> </list> <codesnippet>SELECT»</codesnippet> <codesnippet>»»»»now()»as»time,</codesnippet> <codesnippet>»»»»count(region)»AS»value,</codesnippet> <codesnippet>»»»»region»as»metric</codesnippet> <codesnippet>FROM»Insurance»</codesnippet> <codesnippet>GROUP»BY»region</codesnippet> <list type="bulleted"> <bulletedlist>Format to Time Series</bulletedlist> </list> <pullquote>Time series visualization is the default and primary way to visualize data in Grafana. Your SQL query’s result should have a field named Time.</pullquote> <p id="30">In addition to the pie chart, you can also display bar chart (see <b>Figure 15</b>).</p> <figure id="15" src="image15.png"> <b>Figure 15</b> <b>:</b> Displaying a bar chart in the current panel</figure> <p id="31">You can also display the data as <b>Stat</b> (statistics; see <b>Figure 16</b>).</p> <figure id="16" src="image16.png"> <b>Figure 16</b> <b>:</b> Displaying the result of the query as statistics</figure> <p id="32">If you want to display a histogram showing the distribution of the various age range, you can set the panel as shown in <b>Figure 17</b>. </p> <figure id="17" src="image17.png"> <b>Figure 17</b> <b>:</b> Displaying a histogram showing the age distribution</figure> <p id="33">When you’re done with the panel, clicking on the <b>Apply</b> button at the top of the page returns you to the dashboard. Your dashboard now has one panel (see <b>Figure 18</b>).</p> <p id="34" /> <figure id="18" src=""> <b>Figure 18</b> <b>:</b> The dashboard with one panel</figure> <p id="35">From this point, you can add additional panels to your dashboard. </p> <h3>Exporting and Importing Dashboards</h3> <p id="36">Once you’ve created your dashboard, you can export it so that you can back them up or load them into Grafana on another computer. To export a dashboard, click the <b>Share</b> icon and then click the <b>Export</b> tab (see <b>Figure 19</b>). Then, click the <b>Save to file</b> button.</p> <figure id="19" src="image19.png"> <b>Figure 19</b> <b>:</b> Exporting a dashboard</figure> <p id="37">A JSON file will now be downloaded to your computer. The JSON file now contains all the details of your dashboards and can be sent to another computer. To import the exported dashboard, select <b>+ > Import</b> (see <b>Figure 20</b>) and load the JSON file that you saved earlier. The dashboard will now be imported into Grafana.</p> <figure id="20" src="image20.png"> <b>Figure 20</b> <b>:</b> Importing a dashboard</figure> <h2>Creating Dynamic Auto-Updated Time Series Charts</h2> <p id="38">So far in this article, you’ve seen how to load data from a MySQL database using the MySQL Data Source. However, in real-life, a lot of times the data is from Web APIs, and so it makes sense to be able to use Web APIs in Grafana. Although this looks like a straight-forward affair, it’s more involved than you would imagine. For this section, you’ll: </p> <list type="bulleted"> <bulletedlist>Build a REST API back-end containing stock prices so that it can be used by Grafana’s<b> SimpleJson</b> data source.</bulletedlist> <bulletedlist>Build the front-end using Grafana. The chart that you’ll build shows the stock price of either AAPL or GOOG (fetched from the back-end REST API) and will be automatically updated every five seconds.</bulletedlist> </list> <h3>Creating the Sample Data</h3> <p id="39">First, let’s start off with the server side. For this project, you’ll build a REST API that allows a client to retrieve the stock prices of either AAPL or GOOG based on a specified range of dates. For the stock prices, I’m going to simulate the prices by randomly generating them and then saving them in CSV files.</p> <pullquote>In the real-world, your stock data will more likely come from databases that are updated dynamically.</pullquote> <p id="40">For this article, I’ll pre-generate the prices for four days, from one day before the current day, to three days after the current day. First, generate the simulated stock prices for <b>GOOG</b>:</p> <codesnippet> <font color="#C586C0">from</font>»datetime»<font color="#C586C0">import</font>»timedelta,»timezone</codesnippet> <codesnippet> <font color="#C586C0">import</font>»datetime</codesnippet> <codesnippet> <font color="#C586C0">import</font>»numpy»<font color="#C586C0">as</font>»np</codesnippet> <codesnippet> <font color="#C586C0">import</font>»pandas»<font color="#C586C0">as</font>»pddate_today»=»\</codesnippet> <codesnippet> <font color="#C586C0">»»»»</font>datetime.datetime.now()</codesnippet> <codesnippet>days»=»pd.date_range(date_today»-»timedelta(<font color="#B5CEA8">1</font>),»</codesnippet> <codesnippet>»»»»»»»»»date_today»+»timedelta(<font color="#B5CEA8">3</font>),»<font color="#9CDCFE">freq</font>»=»<font color="#CE9178">'5s'</font>,»</codesnippet> <codesnippet>»»»»»»»»»<font color="#9CDCFE">tz</font>»=»timezone.utc)</codesnippet> <codesnippet>df»=»pd.DataFrame(</codesnippet> <codesnippet>»»»»{</codesnippet> <codesnippet>»»»»»»»»<font color="#CE9178">'value'</font>:np.random.uniform(</codesnippet> <codesnippet> <font color="#CE9178">»»»»»»»»»»»»»»»»</font> <font color="#B5CEA8">2500</font>,<font color="#B5CEA8">3200</font>,<font color="#DCDCAA">len</font>(days))»</codesnippet> <codesnippet>»»»»},»<font color="#9CDCFE">index</font>»=»days)</codesnippet> <codesnippet>display(df)</codesnippet> <codesnippet>df.to_csv(<font color="#CE9178">'stocks_goog.csv'</font>)</codesnippet> <p id="41">The simulated stock price is saved as <b>stocks_goog.csv</b>. Next, generate the prices for AAPL:</p> <codesnippet> <font color="#C586C0">from</font>»datetime»<font color="#C586C0">import</font>»timedelta,»timezone</codesnippet> <codesnippet> <font color="#C586C0">import</font>»datetime</codesnippet> <codesnippet> <font color="#C586C0">import</font>»numpy»<font color="#C586C0">as</font>»np</codesnippet> <codesnippet> <font color="#C586C0">import</font>»pandas»<font color="#C586C0">as</font>»pddate_today»=»\</codesnippet> <codesnippet> <font color="#C586C0">»»»»</font>datetime.datetime.now()</codesnippet> <codesnippet>days»=»pd.date_range(date_today»-»timedelta(<font color="#B5CEA8">1</font>),»</codesnippet> <codesnippet>»»»»»»»»»date_today»+»timedelta(<font color="#B5CEA8">3</font>),»<font color="#9CDCFE">freq</font>»=»<font color="#CE9178">'5s'</font>,»</codesnippet> <codesnippet>»»»»»»»»»<font color="#9CDCFE">tz</font>»=»timezone.utc)</codesnippet> <codesnippet>df»=»pd.DataFrame(</codesnippet> <codesnippet>»»»»{</codesnippet> <codesnippet>»»»»»»»»<font color="#CE9178">'value'</font>:np.random.uniform(</codesnippet> <codesnippet> <font color="#CE9178">»»»»»»»»»»»»»»»»</font> <font color="#B5CEA8">150</font>,<font color="#B5CEA8">190</font>,<font color="#DCDCAA">len</font>(days))»</codesnippet> <codesnippet>»»»»},»<font color="#9CDCFE">index</font>»=»days)</codesnippet> <codesnippet>display(df)</codesnippet> <codesnippet>df.to_csv(<font color="#CE9178">'stocks_aapl.csv'</font>)</codesnippet> <p id="42">The simulated stock price is saved as <b>stocks_aapl.csv</b>. Here is a sample output for the <b>stocks_aapl.csv</b> file:</p> <codesnippet>,value</codesnippet> <codesnippet>2022-03-16»11:19:56.209523+00:00,184.55338767944096</codesnippet> <codesnippet>2022-03-16»11:20:01.209523+00:00,168.76885410294773</codesnippet> <codesnippet>2022-03-16»11:20:06.209523+00:00,188.02816186918278</codesnippet> <codesnippet>2022-03-16»11:20:11.209523+00:00,164.63482117646518</codesnippet> <codesnippet>2022-03-16»11:20:16.209523+00:00,161.33806737466773</codesnippet> <codesnippet>2022-03-16»11:20:21.209523+00:00,169.10779687119663</codesnippet> <codesnippet>2022-03-16»11:20:26.209523+00:00,169.90405158220707</codesnippet> <codesnippet>2022-03-16»11:20:31.209523+00:00,189.30501099950166</codesnippet> <codesnippet>...</codesnippet> <h3>Creating the REST API</h3> <p id="43">Let’s now focus attention on creating the REST API, which is the more challenging aspect of this project. As mentioned earlier, you can use the <b>SimpleJson</b> data source on Grafana to connect to a REST API. However, it requires the REST API to implement specific URLs (see https://grafana.com/grafana/plugins/grafana-simple-json-datasource/ for more details). This means that your REST API must be written specially for the<b> SimpleJson</b> data source.</p> <p id="44">To make my life simpler, I decided to use the <b>Grafana pandas datasource</b> module (https://github.com/panodata/grafana-pandas-datasource) to create my REST API. This module runs an HTTP API based on Flask, and it returns Panda’s dataframes to Grafana, which is what <b>SimpleJson </b>data source can work with. This module also provides samples where you can see how you can implement your own REST API. I’ve adapted one of the provided samples (sinewave-midnights) and modified it for my purposes.</p> <p id="45">Create a new text file and name it <b>demo.py</b>. Populate it with the following statements, shown in <b>Listing 1</b>.</p> <p id="46">Observe the following:</p> <list type="bulleted"> <bulletedlist>The <b>define_and_register_data()</b> function contains another function called <b>get_stock().</b></bulletedlist> <bulletedlist>The <b>add_metric_reader()</b> function links the query sent by Grafana with the function that handles the query. In this case, it will link the "<b>symbol</b>" query with the <b>get_stock()</b> function.</bulletedlist> <bulletedlist>The query sent by Grafana includes a value. For example, you can configure Grafana to send a query "<b>symbol:AAPL</b>". The value of "<b>AAPL</b>" will be passed to the first parameter of the <b>get_stock()</b> function (see <b>Figure 21</b>).</bulletedlist> </list> <figure id="21" src="image21.png"> <b>Figure 21</b> <b>:</b> Understanding how the REST API works with Grafana</figure> <list type="bulleted"> <bulletedlist>The second parameter of the <b>get_stock()</b> function—a <b>ts_range—</b>gets its value from Grafana. You’ll see this later.</bulletedlist> <bulletedlist>In the <b>get_stock()</b> function, you load the required CSV file and then perform a filter to only return the rows that match the time range (<b>ts_range</b>) passed in by Grafana. In real-life, you should load the data from a database server.</bulletedlist> <bulletedlist>The REST API listens at port 3003 on the local computer.</bulletedlist> </list> <p id="47">Before you can run the REST API, you need to install the <b>grafana-pandas-</b>datasource module:</p> <codesnippet>$»pip»install»grafana-pandas-datasource</codesnippet> <p id="48">You now can run the REST API:</p> <codesnippet>$»python»demo.py</codesnippet> <p id="49">You should see the following:</p> <codesnippet>*»Serving»Flask»app»"grafana_pandas_datasource"»</codesnippet> <codesnippet>»(lazy»loading)</codesnippet> <codesnippet>*»Environment:»production</codesnippet> <codesnippet>WARNING:»This»is»a»development»server.»Do»not»use</codesnippet> <codesnippet>»»it»in»a»production»deployment.Use»a»production»</codesnippet> <codesnippet>»»WSGI»server»instead.</codesnippet> <codesnippet>*»Debug»mode:»on</codesnippet> <codesnippet>2022-03-17»13:37:38,454.454»[werkzeug»»»»»»»»»]»</codesnippet> <codesnippet>INFO»»»:»»*»Running»on»http://127.0.0.1:3003/»</codesnippet> <codesnippet>»»(Press»CTRL+C»to»quit)</codesnippet> <codesnippet>2022-03-17»13:37:38,455.455»[werkzeug»»»»»»»»»]»</codesnippet> <codesnippet>INFO»»»:»»*»Restarting»with»stat</codesnippet> <codesnippet>2022-03-17»13:37:38,895.895»[werkzeug»»»»»»»»»]</codesnippet> <codesnippet>WARNING:»»*»Debugger»is»active!</codesnippet> <codesnippet>2022-03-17»13:37:38,902.902»[werkzeug»»»»»»»»»]</codesnippet> <codesnippet>INFO»»»:»»*»Debugger»PIN:»301-215-354</codesnippet> <h3>Testing the REST API</h3> <p id="50">You can now test the REST API. You can issue the following command using the curl utility:</p> <codesnippet> <font>$»</font> <font>curl»-H»"Content-Type:»application/json"»-X»POST»</font> </codesnippet> <codesnippet> <font>»»</font>http://127.0.0.1:3003/query<font>»-d»â€˜{"targets":»</font></codesnippet> <codesnippet> <font>»»[{"target":»"symbol:GOOG"}],»"range":»{"from":</font> </codesnippet> <codesnippet> <font>»»"2022–03–17»03:00:49.110000+00:00",»</font> </codesnippet> <codesnippet> <font>»»"to":»"2022–03–17»03:05:49.110000+00:00"}}’</font> </codesnippet> <p id="51">The result looks like this:</p> <codesnippet>[</codesnippet> <codesnippet>»»{</codesnippet> <codesnippet>»»»»"datapoints":»[</codesnippet> <codesnippet>»»»»»»[</codesnippet> <codesnippet>»»»»»»»»3169.441653300435,»</codesnippet> <codesnippet>»»»»»»»»1647486052594</codesnippet> <codesnippet>»»»»»»],»</codesnippet> <codesnippet>»»»»»»[</codesnippet> <codesnippet>»»»»»»»»2748.501265212758,»</codesnippet> <codesnippet>»»»»»»»»1647486057594</codesnippet> <codesnippet>»»»»»»],»</codesnippet> <codesnippet>»»»»»»[</codesnippet> <codesnippet>»»»»»»»»3195.3559754568632,»</codesnippet> <codesnippet>»»»»»»»»1647486062594</codesnippet> <codesnippet>»»»»»»],»</codesnippet> <codesnippet>»»»»»»...»</codesnippet> <codesnippet>»»»»»»[</codesnippet> <codesnippet>»»»»»»»»2744.0582066482057,»</codesnippet> <codesnippet>»»»»»»»»1647486342594</codesnippet> <codesnippet>»»»»»»],»</codesnippet> <codesnippet>»»»»»»[</codesnippet> <codesnippet>»»»»»»»»3098.521949302881,»</codesnippet> <codesnippet>»»»»»»»»1647486347594</codesnippet> <codesnippet>»»»»»»]</codesnippet> <codesnippet>»»»»],»</codesnippet> <codesnippet>»»»»"target":»"value"</codesnippet> <codesnippet>»»}</codesnippet> <codesnippet>]</codesnippet> <pullquote>Each value in the datapoint array contains the stock price, as well as the epoch time. You can use the Epoch time converter at https://www.epochconverter.com to convert the value of 1647486052594, which yields the date of Thursday, 17 March 2022 03:00:52.594 (GMT).</pullquote> <p id="52">Now that the REST API is up and running, the next thing is to work on the Grafana side.</p> <h3>Adding the SimpleJson Data Source</h3> <p id="53">The next step is to add and configure the <b>SimpleJSON</b> data source in Grafana. Using the <b>SimpleJSON</b> data source, you can connect to REST APIs and download the data in JSON format. </p> <p id="54">In Grafana, first add a new Data Source (<b>Configuration > Data sources</b>). Click on the <b>Add data source</b> button. Search for<b> SimpleJson</b> and double click on it (see <b>Figure 22</b>).</p> <figure id="22" src="image22.png"> <b>Figure 22</b> <b>:</b> Add the SimpleJson data source to the project.</figure> <p id="55">For the <b>URL</b>, enter http://localhost:3003 and click <b>Save & test</b> (see <b>Figure 23</b>).</p> <figure id="23" src="image23.png"> <b>Figure 23</b> <b>:</b> Configure the SimpleJson data source.</figure> <h3>Creating the Dashboard</h3> <p id="56">Create a new dashboard in Grafana. Then add a new Panel by clicking <b>Add a new panel</b> (see <b>Figure 24</b>).</p> <figure id="24" src="image24.png"> <b>Figure 24</b> <b>:</b> Add a new panel to the dashboard.</figure> <p id="57">Using the default <b>Time series</b> visualization, configure the <b>Data source</b> as shown in <b>Figure 25</b>. Also, enter <b>symbol:AAPL</b> for the <b>timeserie</b> query. You should now see the chart plotted.</p> <figure id="25" src="image25.png"> <b>Figure 25</b> <b>:</b> Configure the SimpleJson data source in the Panel.</figure> <p id="58">Earlier I mentioned that Grafana will send a query to the REST API. This is the query: <b>symbol:AAPL</b>.</p> <p id="59">You can control how often you want Grafana to fetch the data for you by selecting the date range (see <b>Figure 26</b>).</p> <figure id="26" src="image26.png"> <b>Figure 26</b> <b>:</b> Select how often you want Grafana to fetch your data for you.</figure> <p id="60">For example, if the current date and time is <b>17th March 2022, 5.53am</b> (UTC-time), Grafana will send the following time range to the back-end REST API if you select the <b>Last 5 minutes</b> option:</p> <codesnippet> <font>{</font> </codesnippet> <codesnippet> <font>»»'$gt':»datetime.datetime(2022,»3,»17,»5,»48,»</font> </codesnippet> <codesnippet> <font>»»»»»»»»»</font> <font>18,»555000,»</font> </codesnippet> <codesnippet> <font>»»»»»»»»»tzinfo=<UTC>),»</font> </codesnippet> <codesnippet> <font>»»'$lte':»datetime.datetime(2022,»3,»17,»5,»53,»</font> </codesnippet> <codesnippet> <font>»»»»»»»»»»</font> <font>18,»555000,»</font> </codesnippet> <codesnippet> <font>»</font> <font>»</font> <font>»»»»»»»»tzinfo=<UTC>)</font> </codesnippet> <codesnippet> <font>}</font> </codesnippet> <p id="61">The chart will be updated with the data for the last five minutes (see <b>Figure 27</b>).</p> <figure id="27" src="image27.png"> <b>Figure 27</b> <b>:</b> Display the data fetched from the REST API.</figure> <p id="62">Click the Save button at the top right corner to name and save the dashboard (see <b>Figure 28</b>).</p> <figure id="28" src="image28.png"> <b>Figure 28</b> <b>:</b> Save the dashboard.</figure> <p id="63">You’ll now be returned to the dashboard.</p> <h3>Configuring a Variable</h3> <p id="64">You can configure a <b>variable</b> in Grafana for your dashboard to allow users to display the chart for different stock symbols.</p> <pullquote>In Grafana, a variable is a placeholder for a value that you can reference in your queries and panels.</pullquote> <p id="65">Click on the <b>Settings</b> button for your current dashboard (see <b>Figure 29</b>).</p> <figure id="29" src="image29.png"> <b>Figure 29</b> <b>:</b> Configure the dashboard</figure> <p id="66">Click the <b>Variable</b>s section on the left and then click <b>Add variable</b> (see <b>Figure 30</b>).</p> <figure id="30" src="image30.png"> <b>Figure 30</b> <b>:</b> Add a variable to the current dashboard.</figure> <p id="67">Enter the following information for the variable and then click <b>Update</b> (see <b>Figure 31</b>).</p> <figure id="31" src="image31.png"> <b>Figure 31</b> <b>:</b> Configure the variable to be added to the dashboard.</figure> <p id="68">Back in the dashboard, select <b>Edit</b> for the Panel (see <b>Figure 32</b>).</p> <figure id="32" src="image32.png"> <b>Figure 32</b> <b>:</b> Editing the panel</figure> <p id="69">Set the title of the Panel as <b>Company: $stock</b> and update the query to <b>symbol:$stock</b> (see <b>Figure 33</b>). Prefixing the variable name with the <b>$</b> character allows you to retrieve the value of the variable and use it in your query and panel.</p> <figure id="33" src="image33.png"> <b>Figure 33</b> <b>:</b> Using the newly added variable in the panel</figure> <p id="70">Click<b> Apply</b>. In the textbox next to the stock variable, enter <b>AAPL</b>. You should now see the chart for AAPL (see <b>Figure 34</b>).</p> <figure id="34" src="image34.png"> <b>Figure 34</b> <b>:</b> Viewing the chart for AAPL</figure> <p id="71">In the textbox next to the stock variable, enter <b>GOOG</b>. You should now see the chart for GOOG (see <b>Figure 35</b>).</p> <figure id="35" src="image35.png"> <b>Figure 35</b> <b>:</b> Viewing the chart for GOOG</figure> <h3>Auto-Updating the Chart</h3> <p id="72">To automatically update the chart, select the drop-down list next to the <b>Refresh</b> button and select how often you want to refresh the chart (see <b>Figure 36</b>).</p> <figure id="36" src="image36.png"> <b>Figure 36</b> <b>:</b> Select how often you want the chart to refresh.</figure> <p id="73">If you select <b>5s</b>, the chart will now be updated every five seconds.</p> <h2>Displaying Real-Time Sensor Data Using MQTT</h2> <p id="74">In Grafana 8.0, as part of the Grafana Live feature, it’s now possible to perform real-time data updates using a new streaming API. This means that it’s now possible for you to create charts that update in real-time and on-demand.</p> <p id="75">To make use of this feature, you can make use of the <b>MQTT</b> data source (https://github.com/svet-b/grafana-mqtt-datasource), a plug-in that allows Grafana users to visualize MQTT data in real-time. In this section, you’ll learn how to use the <b>MQTT</b> data source to display sensor data in real-time.</p> <pullquote>In order to use the MQTT data source, you need to use Grafana 8.0, which makes it possible to perform real-time data updates using a new streaming API that’s part of the Grafana Live feature.</pullquote> <p id="76">Using MQTT is ideal for projects such as those involving the Raspberry Pi (see <b>Figure 37</b>).</p> <figure id="37" src="image37.png"> <b>Figure 37</b> <b>:</b> A Raspberry Pi</figure> <h3>Installing the MQTT Data Source into Grafana</h3> <p id="77">Although there is the <b>MQTT</b> data source built for Grafana, it doesn’t come bundled with Grafana—you need to build and install it yourself. This is where the complexity comes in. In this section, I’ll show you how to install the <b>MQTT</b> Data Source on Windows. You’ll need a few tools/language:</p> <list type="bulleted"> <bulletedlist>Node.js</bulletedlist> <bulletedlist>yarn</bulletedlist> <bulletedlist>Go</bulletedlist> <bulletedlist>Mage</bulletedlist> </list> <p id="78">Don’t worry if you’re not familiar with these tools and language. I’ll show you how to install them in the following sections.</p> <h3>Install Node.js</h3> <p id="79">Download and install Node.js from https://nodejs.org/en/download/. Once Node.js is installed on your system, type the following command in Command Prompt to install <b>yarn</b>:</p> <codesnippet>npm»install»â€”g»yarn</codesnippet> <pullquote>Yarn is a package manager that doubles down as a project manager.</pullquote> <h3>Install Go</h3> <p id="80">Download and install Go from https://go.dev/dl/. Follow the instructions in the installer.</p> <pullquote>Go is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson.</pullquote> <h3>Download Mage</h3> <p id="81">Go to the source of Mage at HYPERLINK "https://github.com/magefile/mage" https://github.com/magefile/mage. Click on the <b>Code</b> button and select <b>Download ZIP</b> (see <b>Figure 38</b>).</p> <figure id="38" src="image38.png"> <b>Figure 38</b> <b>:</b> Download the source code for Mage.</figure> <pullquote>Mage is a build tool like Make, but instead of writing bash code, Mage lets you write the logic in Go.</pullquote> <p id="82">Once downloaded, extract the content of the zip file onto the Desktop. Next, find out the location of your <b>GOPATH</b> by typing this command in Command Prompt:</p> <codesnippet>go»env</codesnippet> <p id="83">You can locate the path for <b>GOPATH</b> from the output:</p> <codesnippet>...</codesnippet> <codesnippet>set»GOOS=windows</codesnippet> <codesnippet>set»GOPATH=C:\Users\Wei-Meng»Lee\go</codesnippet> <codesnippet>set»GOPRIVATE=</codesnippet> <codesnippet>...</codesnippet> <p id="84">Type the following command to create a path call Go (if this path does not already exist on your computer; remember to use your own path):</p> <codesnippet>cd»c:\users\Wei»Meng»Lee\</codesnippet> <codesnippet>mkdir»Go</codesnippet> <p id="85">In the Command Prompt, change the directory to the <b>mage-master</b> folder:</p> <codesnippet>cd»C:\Users\Wei-Meng»Lee\Desktop\mage-master</codesnippet> <p id="86">Type the following command:</p> <codesnippet>go»run»bootstrap.go</codesnippet> <p id="87">You should see something like this:</p> <codesnippet>Running»target:»Install</codesnippet> <codesnippet>exec:»go»"env"»"GOBIN"</codesnippet> <codesnippet>exec:»go»"env"»"GOPATH"</codesnippet> <codesnippet>exec:»git»"rev-parse"»"»â€”»short"»"HEAD"</codesnippet> <codesnippet>exec:»git»"describe"»"»â€”»tags"</codesnippet> <codesnippet>exec:»go»"build"»"-o"»"C:\\Users\\Wei-Meng»</codesnippet> <codesnippet>Lee\\go\\bin\\mage.exe"»"-ldflags=-X»</codesnippet> <codesnippet>\"github.com/magefile/mage/mage.timestamp=2022–03–</codesnippet> <codesnippet>18T12:44:11+08:00\"»-X»</codesnippet> <codesnippet>\"github.com/magefile/mage/mage.commitHash=\"»-X»</codesnippet> <codesnippet>\"github.com/magefile/mage/mage.gitTag=dev\""»</codesnippet> <codesnippet>"github.com/magefile/mage"</codesnippet> <h3>Download the Source for the MQTT Data Source</h3> <p id="88">The next step is to download the source code of the MQTT data source. Go to https://github.com/grafana/mqtt-datasource and download the ZIP file (see <b>Figure 39</b>).</p> <figure id="39" src="image39.png"> <b>Figure 39</b> <b>:</b> Download the source code of the MQTT Data Source.</figure> <p id="89">Once the file is downloaded, extract the content of the zip file to the Desktop. Edit the file named <b>package.go</b> in the <b>mqtt-datasource-main folder</b> using a code editor. Replace "<b>rm -rf dist && ...</b>" with <b>"del /F /Q dist && ...</b>" (the command you replaced is to ensure that it works on Windows):</p> <codesnippet>{</codesnippet> <codesnippet>»»"name":»"grafana-mqtt-datasource",</codesnippet> <codesnippet>»»"version":»"0.0.1-dev",</codesnippet> <codesnippet>»»"description":»"MQTT»Datasource»Plugin",</codesnippet> <codesnippet>»»"scripts":»{»"build":»"del»/F»/Q»dist»&&»grafana-</codesnippet> <codesnippet>toolkit»plugin:build»&&»mage»build:backend",</codesnippet> <codesnippet>»»...</codesnippet> <p id="90">In the Command Prompt, cd to the <b>mqtt-datasource-main</b> folder:</p> <codesnippet>cd»C:\Users\Wei-Meng»Lee\Desktop\mqtt-datasource-main</codesnippet> <p id="91">And type in the following commands:</p> <codesnippet>yarn»build</codesnippet> <codesnippet>yarn»install</codesnippet> <h3>Configuring the Plugin</h3> <p id="92">Move the <b>mqtt-datasource-main</b> folder into the <b>C:\Program Files\GrafanaLabs\grafana\data\plugins</b> folder.</p> <pullquote>The C:\Program Files\GrafanaLabs\grafana\data\plugins folder is where Grafana stores the various plug-ins.</pullquote> <p id="93">Next, load the <b>defaults.ini</b> file in the <b>C:\Program Files\GrafanaLabs\grafana\conf</b> folder and add in the following statement in bold:</p> <codesnippet>[plugins]</codesnippet> <codesnippet>enable_alpha»=»false</codesnippet> <codesnippet>app_tls_skip_verify_insecure»=»false</codesnippet> <codesnippet>#»Enter»a»comma-separated»list»of»plugin»identifiers»</codesnippet> <codesnippet>to»identify»plugins»to»load»even»if»they»are»</codesnippet> <codesnippet>unsigned.»Plugins»with»modified»signatures»are»never</codesnippet> <codesnippet>loaded.</codesnippet> <codesnippet>allow_loading_unsigned_plugins»=»grafana-mqtt-</codesnippet> <codesnippet>datasource</codesnippet> <codesnippet>#»Enable»or»disable»installing»/»uninstalling»/»</codesnippet> <codesnippet>updating»plugins»directly»from»within»Grafana.</codesnippet> <codesnippet>plugin_admin_enabled»=»true</codesnippet> <p id="94">The above addition indicates to Grafana to allow unsigned plug-ins (which, in this case, is the <b>MQTT Data Source</b>). Restart Grafana in Windows Services. If you have performed the above steps in building the MQTT Data Source for Grafana, you should now be ready to use it.</p> <h3>Publishing Data to a MQTT Broker</h3> <p id="95">Before you start to build a Panel to display data using the MQTT Data Source, you need to write data to a MQTT broker so that you can subscribe to it. For this, I’ll write a Python program to simulate some sensor data.</p> <pullquote>If you’re not familiar with a MQTT broker, refer to my article on MQTT at: https://weimenglee.medium.com/using-mqtt-to-push-messages-across-devices-1465855b36e5</pullquote> <p id="96">Create a text file and name it as <b>publish.py</b>. Populate it with the following statements:</p> <codesnippet> <font color="Green">#»pip»install»paho-mqtt</font> </codesnippet> <codesnippet> <font color="Blue">import</font> <font>»paho.mqtt.client»</font> <font color="Blue">as</font> <font>»mqtt</font> </codesnippet> <codesnippet> <font color="Blue">import</font> <font>»numpy»</font> <font color="Blue">as</font> <font>»np</font> </codesnippet> <codesnippet> <font color="Blue">import</font> <font>»time</font> </codesnippet> <codesnippet> <font>MQTTBROKER»=»</font> <font color="#A31515">'test.mosquitto.org'</font> </codesnippet> <codesnippet> <font>PORT»=»1883</font> </codesnippet> <codesnippet> <font>TOPIC»=»</font> <font color="#A31515">"home/temp/room1/storeroom"</font> </codesnippet> <codesnippet> <font>mqttc»=»mqtt.Client(</font> <font color="#A31515">"python_pub"</font> <font>)</font> </codesnippet> <codesnippet> <font>mqttc.connect(MQTTBROKER,»PORT)</font> </codesnippet> <codesnippet> <font color="Blue">while</font> <font>»</font> <font color="#A31515">True</font> <font>:</font> </codesnippet> <codesnippet> <font>»»»»MESSAGE»=»str(np.random.uniform(20,30))</font> </codesnippet> <codesnippet> <font>»»»»mqttc.publish(TOPIC,»MESSAGE)</font> </codesnippet> <codesnippet> <font>»»»»print(</font> <font color="#A31515">"Published»to»"</font> <font>»+»MQTTBROKER»+»</font> <font color="#A31515">':»'</font> <font>»+»</font> </codesnippet> <codesnippet> <font>»»»»»»»»»»TOPIC»+»</font> <font color="#A31515">':'</font> <font>»+»MESSAGE)</font> </codesnippet> <codesnippet> <font>»»»»time.sleep(3)</font> </codesnippet> <p id="97">The above Python program sends some data to the public MQTT broker every three seconds. To run the <b>publish.py</b> file, type the following commands in Command Prompt:</p> <codesnippet>pip»install»paho-mqtt</codesnippet> <codesnippet>python»publish.py</codesnippet> <h3>Subscribe Using the MQTT Data Source</h3> <p id="98">In Grafana, add a new data source by searching for <b>mqtt</b> (see <b>Figure 40</b>).</p> <figure id="40" src="image40.png"> <b>Figure 40</b> <b>:</b> Add the MQTT data source to Grafana.</figure> <p id="99">Configure the <b>MQTT</b> data source as follows (see also <b>Figure 41</b>):</p> <list type="bulleted"> <bulletedlist> <b>Host:</b> test.mosquitto.org</bulletedlist> <bulletedlist> <b>Port:</b> 1883</bulletedlist> </list> <p id="100">You can leave the fields under the <b>Authentication</b> section empty. Click <b>Save & test</b> once you’re done.</p> <figure id="41" src="image41.png"> <b>Figure 41</b> <b>:</b> Configure the MQTT data source.</figure> <p id="101">Next, create a new <b>Dashboard</b> and add a new Panel <b>(see</b> <b>Figure 42):</b></p> <figure id="42" src="image42.png"> <b>Figure 4</b> <b>2:</b> Add a new panel to the dashboard.</figure> <p id="102">Configure the panel with the following (see also <b>Figure 43</b>):</p> <list type="bulleted"> <bulletedlist> <b>Visualization:</b> Time Series</bulletedlist> <bulletedlist> <b>Data source:</b> MQTT</bulletedlist> <bulletedlist> <b>Topic:</b> home/temp/room1/storeroom</bulletedlist> </list> <p id="103">Click <b>Apply</b> when done.</p> <figure id="43" src="image43.png"> <b>Figure 43</b> <b>:</b> Configuring the panel</figure> <p id="104">You should now see the chart plotted and updated every three seconds (see <b>Figure 44</b>). If you can see the chart, this means that you’re able to receive the data through the MQTT data source.</p> <figure id="44" src="image44.png"> <b>Figure 44</b> <b>:</b> Display the data from MQTT as a line chart.</figure> <pullquote>Hint: Switch the Dashboard to view the Last 5 minutes to see a close-up of the chart.</pullquote> <h2>Summary</h2> <p id="105">In this article, you’ve seen how to use Grafana to build dashboards with a minimal amount of code needed. Instead of code, all you need is some basic SQL skills (of course, there will be cases where you need to write complex SQL queries to extract your data). In addition, I’ve walked you through two projects where you learned how to dynamically update your chart from a REST API, as well as fetch data from a MQTT data source. Have fun!</p> </body> <sidebars> <sidebar title="Sponsored Sidebar" /> <sidebar title="Need FREE Project Advice? CODE Can Help!"> <p id="106">No strings free advice on a new or existing software development projects. CODE Consulting experts have experience in cloud, Web, desktop, mobile, microservices, containers, and DevOps projects. Schedule your free hour of CODE call with our expert consultants today. For more information visit www.codemag.com/consulting or email us at info@codemag.com.</p> </sidebar> </sidebars> <tables /> <codelistings> <codelisting id="1" header="Listing 1. The REST API for stock prices"> <code> <font color="#569CD6">import</font>»numpy»<font color="#569CD6">as</font>»np</code> <code> <font color="#569CD6">import</font>»pandas»<font color="#569CD6">as</font>»pd</code> <code> <font color="#569CD6">from</font>»grafana_pandas_datasource»<font color="#569CD6">import</font>»create_app</code> <code> <font color="#569CD6">from</font>»grafana_pandas_datasource.registry»<font color="#569CD6">import</font>»data_generators»</code> <code> <font color="#569CD6">»»as</font>»dg</code> <code> <font color="#569CD6">from</font>»grafana_pandas_datasource.service»<font color="#569CD6">import</font>»pandas_component</code> <code /> <code> <font color="#569CD6">from</font>»datetime»<font color="#569CD6">import</font>»datetime,»timedelta</code> <code> <font color="#569CD6">from</font>»pytz»<font color="#569CD6">import</font>»timezone»</code> <code /> <code> <font color="#569CD6">def</font>»define_and_register_data():»»»»</code> <code>»»»»<font color="#569CD6">def</font>»get_stock(stock_symbol,»ts_range):</code> <code>»»»»»»»»<font color="#569CD6">if</font>»stock_symbol»==»<font color="#CE9178">'AAPL'</font>:</code> <code>»»»»»»»»»»»»df»=»pd.read_csv(<font color="#CE9178">'stocks_aapl.csv'</font>,»</code> <code>»»»»»»»»»»»»»»»»parse_dates=<font color="#569CD6">True</font>,»index_col=<font color="#B5CEA8">0</font>)</code> <code>»»»»»»»»<font color="#569CD6">if</font>»stock_symbol»==»<font color="#CE9178">'GOOG'</font>:</code> <code>»»»»»»»»»»»»df»=»pd.read_csv(<font color="#CE9178">'stocks_goog.csv'</font>,»</code> <code>»»»»»»»»»»»»»»»»parse_dates=<font color="#569CD6">True</font>,»index_col=<font color="#B5CEA8">0</font>)»»»»»»»»</code> <code>»»»»»»»»»»»»»»»»</code> <code>»»»»»»»»<font color="#6A9955">#»return»the»rows»that»falls»within»the»specified»dates</font></code> <code>»»»»»»»»<font color="#569CD6">return</font>»df[(df.index»>»ts_range[<font color="#CE9178">'$gt'</font>])»&</code> <code>»»»»»»»»»»»»»»»»»»(df.index»<»ts_range[<font color="#CE9178">'$lte'</font>])]</code> <code /> <code>»»»»<font color="#6A9955">#»Register»data»generators</font></code> <code>»»»»dg.add_metric_reader(<font color="#CE9178">"symbol"</font>,»get_stock)</code> <code>»»»»</code> <code> <font color="#569CD6">def</font>»main():</code> <code>»»»»<font color="#6A9955">#»Define»and»register»data»generators.</font></code> <code>»»»»define_and_register_data()</code> <code>»»»»</code> <code>»»»»<font color="#6A9955">#»Create»Flask»application.</font></code> <code>»»»»app»=»create_app()»»»»</code> <code>»»»»</code> <code>»»»»<font color="#6A9955">#»Register»pandas»component.</font></code> <code>»»»»app.register_blueprint(pandas_component,»url_prefix=<font color="#CE9178">"/"</font>)»»»»</code> <code>»»»»</code> <code>»»»»<font color="#6A9955">#»Invoke»Flask»application.</font></code> <code>»»»»app.run(host=<font color="#CE9178">"127.0.0.1"</font>,»port=<font color="#B5CEA8">3003</font>,»debug=<font color="#569CD6">True</font>)</code> <code /> <code> <font color="#569CD6">if</font>»__name__»==»<font color="#CE9178">"__main__"</font>:</code> <code>»»»»main()</code> </codelisting> </codelistings> <Errors> <Error>Actual figure not found for this caption: Figure 18: The dashboard with one panel</Error> </Errors> </document>