Getting Started with grafanalib

Do you like Grafana but wish you could version your dashboard configuration? Do you find yourself repeating common patterns? If so, grafanalib is for you.

grafanalib lets you generate Grafana dashboards from simple Python scripts.

Grafana migrates dashboards to the latest Grafana schema version on import, meaning that dashboards created with grafanalib are supported by all versions of Grafana. You may find that some of the latest features are missing from grafanalib, please refer to the module documentation for information about supported features. If you find a missing feature please raise an issue or submit a PR to the GitHub repository

Writing dashboards

The following will configure a dashboard with a single row, with one QPS graph broken down by status code and another latency graph showing median and 99th percentile latency:

from grafanalib.core import (
    Alert, AlertCondition, Dashboard, Graph, GridPos,
    GreaterThan, Notification, OP_AND, OPS_FORMAT, RowPanel, RTYPE_SUM, SECONDS_FORMAT,
    SHORT_FORMAT, single_y_axis, Target, TimeRange, YAxes, YAxis
)


dashboard = Dashboard(
    title="Frontend Stats",
    panels=[
        RowPanel(
            title="New row",
            gridPos=GridPos(h=1, w=24, x=0, y=8)
        ),
        Graph(
            title="Frontend QPS",
            dataSource='My Prometheus',
            targets=[
                Target(
                    expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"1.."}[1m]))',
                    legendFormat="1xx",
                    refId='A',
                ),
                Target(
                    expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"2.."}[1m]))',
                    legendFormat="2xx",
                    refId='B',
                ),
                Target(
                    expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"3.."}[1m]))',
                    legendFormat="3xx",
                    refId='C',
                ),
                Target(
                    expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"4.."}[1m]))',
                    legendFormat="4xx",
                    refId='D',
                ),
                Target(
                    expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"5.."}[1m]))',
                    legendFormat="5xx",
                    refId='E',
                ),
            ],
            yAxes=YAxes(
                YAxis(format=OPS_FORMAT),
                YAxis(format=SHORT_FORMAT),
            ),
            alert=Alert(
                name="Too many 500s on Nginx",
                message="More than 5 QPS of 500s on Nginx for 5 minutes",
                alertConditions=[
                    AlertCondition(
                        Target(
                            expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"5.."}[1m]))',
                            legendFormat="5xx",
                            refId='A',
                        ),
                        timeRange=TimeRange("5m", "now"),
                        evaluator=GreaterThan(5),
                        operator=OP_AND,
                        reducerType=RTYPE_SUM,
                    ),
                ],
                notifications=[
                    Notification("notification_channel_uid"),
                ]
            ),
            gridPos=GridPos(h=1, w=12, x=0, y=9)
        ),
        Graph(
            title="Frontend latency",
            dataSource='My Prometheus',
            targets=[
                Target(
                    expr='histogram_quantile(0.5, sum(irate(nginx_http_request_duration_seconds_bucket{job="default/frontend"}[1m])) by (le))',
                    legendFormat="0.5 quantile",
                    refId='A',
                ),
                Target(
                    expr='histogram_quantile(0.99, sum(irate(nginx_http_request_duration_seconds_bucket{job="default/frontend"}[1m])) by (le))',
                    legendFormat="0.99 quantile",
                    refId='B',
                ),
            ],
            yAxes=single_y_axis(format=SECONDS_FORMAT),
            gridPos=GridPos(h=8, w=12, x=0, y=0)
        )
    ],
).auto_panel_ids()

There is a fair bit of repetition here, but once you figure out what works for your needs, you can factor that out. See our Weave-specific customizations for inspiration.

Generating dashboards

If you save the above as example.dashboard.py (the suffix must be .dashboard.py), you can then generate the JSON dashboard with:

$ generate-dashboard -o frontend.json example.dashboard.py

Generating dashboards from code

Sometimes you may need to generate and upload dashboard directly from Python code. The following example provides minimal code boilerplate for it:

from grafanalib.core import Dashboard
from grafanalib._gen import DashboardEncoder
import json
import requests
from os import getenv


def get_dashboard_json(dashboard):
    '''
    get_dashboard_json generates JSON from grafanalib Dashboard object

    :param dashboard - Dashboard() created via grafanalib
    '''

    # grafanalib generates json which need to pack to "dashboard" root element
    return json.dumps({"dashboard": dashboard.to_json_data()}, sort_keys=True, indent=2, cls=DashboardEncoder)


def upload_to_grafana(json, server, api_key):
    '''
    upload_to_grafana tries to upload dashboard to grafana and prints response

    :param json - dashboard json generated by grafanalib
    :param server - grafana server name
    :param api_key - grafana api key with read and write privileges
    '''

    headers = {'Authorization': f"Bearer {api_key}", 'Content-Type': 'application/json'}
    r = requests.post(f"https://{server}/api/dashboards/db", data=json, headers=headers)
    # TODO: add error handling
    print(f"{r.status_code} - {r.content}")


grafana_api_key = getenv("GRAFANA_API_KEY")
grafana_server = getenv("GRAFANA_SERVER")

my_dashboard = Dashboard(title="My awesome dashboard")
my_dashboard_json = get_dashboard_json(my_dashboard)
upload_to_grafana(my_dashboard_json, grafana_server, grafana_api_key)

Installation

grafanalib is just a Python package, so:

$ pip install grafanalib

Support

This library is in its very early stages. We’ll probably make changes that break backwards compatibility, although we’ll try hard not to.

grafanalib works with Python 3.6, 3.7, 3.8 and 3.9.

Developing

If you’re working on the project, and need to build from source, it’s done as follows:

$ virtualenv .env
$ . ./.env/bin/activate
$ pip install -e .

Configuring Grafana Datasources

This repo used to contain a program gfdatasource for configuring Grafana data sources, but it has been retired since Grafana now has a built-in way to do it. See https://grafana.com/docs/administration/provisioning/#datasources