This project allows exporting Grafana dashboards to PDF using Puppeteer. The project uses a Node.js server to handle HTTP requests and launch Puppeteer to generate the PDFs.
It is possible to inject a button into Grafana to generate a PDF directly from the interface.
- Docker
- Docker Compose
Clone this repository and navigate to the project directory:
git clone https://github.com/arthur-mdn/grafana-export-to-pdf/
cd grafana-export-to-pdf
Duplicate the .env.example
file and rename it to .env
.
cp .env.template .env
nano .env
Modify the values according to your configuration.
GRAFANA_USER=pdf_export
GRAFANA_PASSWORD=pdf_export
GRAFANA_USER
and GRAFANA_PASSWORD
are the credentials used to authenticate to the Grafana instance.
To start the project, run the following command:
docker compose up -d --build
The server will be accessible on port 3001.
To generate a PDF, send a POST request to the /generate-pdf API with the Grafana dashboard URL as a parameter. The server will respond with the URL of the generated PDF.
curl \
-H "Content-Type: application/json" \
-X POST \
-d '{ "url": "http://your-grafana-server/d/your-dashboard-id?orgId=1&kiosk"}' \
http://localhost:3001/generate-pdf
docker compose exec grafana-export-to-pdf /usr/src/app/generate-pdf.sh GF_DASH_URL 'http://your-grafana-server/d/your-dashboard-id?orgId=1&kiosk'
You must ensure that the
disable_sanitize_html
parameter is set totrue
in the Grafana configuration file to be able to inject HTML and Javascript code.
To inject a button directly into Grafana, add the content of the grafana-button.html
file to the "Text" field of a Grafana text panel.
Make sure to modify the server URL if necessary. You can easily deactivate the button injection by setting the pdfGeneration
variable to false
.
const pdfGeneration = true;
const pdfGenerationServerUrl = 'http://localhost:3001/';
The button should now be displayed in the native Grafana share menu.
In the examples below, the time range is
now-1y/y
, which corresponds to last year.
See more details on supported time ranges in the Grafana documentation.
To generate a PDF with a time range, you can simply add the native Grafana time range parameters to the URL.
http://your-grafana-server/d/your-dashboard-id?orgId=1&kiosk&from=now-1y%2Fy&to=now-1y%2Fy
But you can also specify the time range manually by specifying the from
and to
parameters in the request.
curl \
-H "Content-Type: application/json" \
-X POST \
-d '{ "url": "http://your-grafana-server/d/your-dashboard-id?orgId=1&kiosk", "from": "now-1y/y", "to": "now-1y/y"}' \
http://localhost:3001/generate-pdf
docker compose exec server /usr/src/app/generate-pdf.sh GF_DASH_URL 'http://your-grafana-server/d/your-dashboard-id?orgId=1&kiosk' GF_FROM 'now-1y/y' GF_TO 'now-1y/y'
The injected HTML button already retrieves the values of the selected time range in Grafana. You do not need to specify them manually. It also retrieves the theme selected from the share panel.
By default, the server exports the entire dashboard. If you want to export a single panel, you can add the viewPanel
parameter to the URL.
http://your-grafana-server/d/your-dashboard-id?orgId=1&kiosk&viewPanel=2
By using the HTML button injected into Grafana, you can simply click the share button of the panel you want to export, and then you will find the Export to PDF button.
The script will try to extract the panel title and use it in the PDF filename.
To avoid fetching the dashboard name and the time range from the URL (that are sometimes not user-friendly), you can extract the values directly from HTML elements in the Grafana dashboard with a better display format.
For this URL: http://localhost/d/ID/stats?orgId=1&from=now-1y%2Fy&to=now-1y%2Fy
- The initial PDF filename will be:
stats_now-1y_y_to_now-1y_y.pdf
- With the custom configuration, the PDF filename could be:
Stats_Sunday_January_1_2023_-_Sunday_December_31_2023.pdf
To activate this feature, set the following variable to true
in your .env
file:
EXTRACT_DATE_AND_DASHBOARD_NAME_FROM_HTML_PANEL_ELEMENTS=true
And then add the following code to the Grafana panel where you want to display the dashboard name and the time range. You can customize the display format by modifying the formatTimestamp
function in the script below:
<div>
<p id="display_actual_dashboard_title">${__dashboard}</p>
<p id="display_actual_date" style="text-transform:capitalize;"></p>
</div>
<script>
(function() {
function formatTimestamp(timestamp) {
const date = new Date(timestamp);
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
return date.toLocaleDateString('en-US', options);
}
let fromTimestampGrafana = ${__from};
let toTimestampGrafana = ${__to};
document.getElementById("display_actual_date").innerHTML = formatTimestamp(fromTimestampGrafana) + " - " + formatTimestamp(toTimestampGrafana);
})();
</script>
By default, FORCE_KIOSK_MODE
is set to true
. This means that if the url does not contain the kiosk
parameter, the server will add it to the URL to ensure that the PDF is generated without any elements overlapping the dashboard content .
You can disable this behavior by setting the following variable to false
in your .env
file:
FORCE_KIOSK_MODE=false
Disabling this feature would have no effect if the
kiosk
parameter is already present in the URL given to the server.
By default, DEBUG_MODE
is set to false
. When activated, the server will save the HTML content of the page to a file in the debug
folder. This can be useful for debugging purposes.
You can enable this behavior by setting the following variable to true
in your .env
file:
DEBUG_MODE=true