ruby-grafana-reporter 0.5.3 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +20 -20
- data/README.md +337 -336
- data/bin/ruby-grafana-reporter +5 -5
- data/lib/VERSION.rb +2 -2
- data/lib/grafana/abstract_datasource.rb +8 -1
- data/lib/grafana/dashboard.rb +8 -1
- data/lib/grafana/grafana.rb +15 -1
- data/lib/grafana/grafana_alerts_datasource.rb +0 -0
- data/lib/grafana/grafana_annotations_datasource.rb +0 -0
- data/lib/grafana/panel.rb +1 -1
- data/lib/grafana/prometheus_datasource.rb +61 -13
- data/lib/grafana/unsupported_datasource.rb +0 -0
- data/lib/grafana/variable.rb +1 -1
- data/lib/grafana_reporter/abstract_query.rb +8 -2
- data/lib/grafana_reporter/application/application.rb +0 -0
- data/lib/grafana_reporter/application/errors.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/help.rb +44 -2
- data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/report.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/show_help_include_processor.rb +0 -0
- data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +0 -0
- data/lib/grafana_reporter/erb/demo_report_builder.rb +0 -0
- data/lib/grafana_reporter/erb/report.rb +0 -0
- data/lib/grafana_reporter/erb/report_jail.rb +0 -0
- data/lib/grafana_reporter/logger/two_way_delegate_logger.rb +0 -0
- data/lib/grafana_reporter/panel_property_query.rb +0 -0
- data/lib/grafana_reporter/report_webhook.rb +0 -0
- metadata +20 -7
- data/lib/ruby_grafana_extension.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '00837acead340b75df1bc4dcdf93c05c8014c1317aae288191b592b77a660a7a'
|
4
|
+
data.tar.gz: 2e072de19e45e7fe5f167f37ec4a88be2a482bf7843df96f059a9489e48760b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eec08117ef1a83e7c0a016e80cce31c67fcf552253b63141a440951fa94f1168531b31131ae5cc79e50ac14bf4161a867b717f1c2bfb77ed6d0edac985b7cc38
|
7
|
+
data.tar.gz: fe05d5a7baeb91411fb5942602cd66a66ae8dfcc65eedb7cf421617ffa6c827f5d2a11306dc33ffae9370033a35c10883c17c11f25d8a431bebaf7ae476af4ef
|
data/LICENSE
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
MIT License
|
2
|
-
|
3
|
-
Copyright (c) 2020 Christian Kohlmeyer
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
-
this software and associated documentation files (the "Software"), to deal in
|
7
|
-
the Software without restriction, including without limitation the rights to
|
8
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
-
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
-
so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
13
|
-
copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESs
|
17
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2020 Christian Kohlmeyer
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESs
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,336 +1,337 @@
|
|
1
|
-
[![MIT License](https://img.shields.io/github/license/divinity666/ruby-grafana-reporter.svg?style=flat-square)](https://github.com/divinity666/ruby-grafana-reporter/blob/master/LICENSE)
|
2
|
-
[![Build Status](https://travis-ci.com/divinity666/ruby-grafana-reporter.svg?branch=master)](https://travis-ci.com/github/divinity666/ruby-grafana-reporter?branch=master)
|
3
|
-
[![Coverage Status](https://coveralls.io/repos/github/divinity666/ruby-grafana-reporter/badge.svg?branch=master)](https://coveralls.io/github/divinity666/ruby-grafana-reporter?branch=master)
|
4
|
-
[![Gem Version](https://badge.fury.io/rb/ruby-grafana-reporter.svg)](https://badge.fury.io/rb/ruby-grafana-reporter)
|
5
|
-
|
6
|
-
# Ruby Grafana Reporter
|
7
|
-
Reporting Service for Grafana
|
8
|
-
|
9
|
-
## Table of Contents
|
10
|
-
|
11
|
-
* [About the project](#about-the-project)
|
12
|
-
* [Features](#features)
|
13
|
-
* [Supported datasources](#supported-datasources)
|
14
|
-
* [Quick Start](#quick-start)
|
15
|
-
* [Setup](#setup)
|
16
|
-
* [Grafana integration](#grafana-integration)
|
17
|
-
* [Advanced information](#advanced-information)
|
18
|
-
* [Webservice](#webservice)
|
19
|
-
* [Using ERB templates](#using-erb-templates)
|
20
|
-
* [Using webhooks](#using-webhooks)
|
21
|
-
* [Developing your own plugin](#developing-your-own-plugin)
|
22
|
-
* [Roadmap](#roadmap)
|
23
|
-
* [Donations](#donations)
|
24
|
-
|
25
|
-
## About the project
|
26
|
-
|
27
|
-
[Grafana](https://github.com/grafana/grafana) is a great tool for monitoring and
|
28
|
-
visualizing data from different sources. Anyway the free version is lacking a
|
29
|
-
professional reporting functionality. And this is, where the ruby grafana reporter
|
30
|
-
steps in.
|
31
|
-
|
32
|
-
The key functionality of the reporter is to capture data and images from grafana
|
33
|
-
dashboards and to use it in your custom templates to finally create reports in PDF
|
34
|
-
(default), HTML, or any other format.
|
35
|
-
|
36
|
-
By default (an extended version of) Asciidoctor is enabled as template language.
|
37
|
-
|
38
|
-
## Features
|
39
|
-
|
40
|
-
* Supports creation of reports for multiple [grafana](https://github.com/grafana/grafana)
|
41
|
-
dashboards (and also multiple grafana installations!) in one resulting report
|
42
|
-
* PDF (default), HTML and many other report formats are supported
|
43
|
-
* Easy-to-use configuration wizard, including fully automated functionality to create a
|
44
|
-
demo report for your dashboard
|
45
|
-
* Include dynamic content from grafana (find here a reference for all
|
46
|
-
[asciidcotor reporter calls](FUNCTION_CALLS.md)):
|
47
|
-
* panels as images
|
48
|
-
* tables based on grafana panel queries or custom database queries (no images!)
|
49
|
-
* single values to be integrated in text, based on grafana panel queries or custom
|
50
|
-
database queries
|
51
|
-
* Runs as
|
52
|
-
* webservice to be called directly from grafana
|
53
|
-
* standalone command line tool, e.g. to be automated with `cron` or `bash` scrips
|
54
|
-
* microservice from standard asciidoctor docker container without any dependencies
|
55
|
-
* Supports webhook callbacks on before, on cancel and on finishing a report (see
|
56
|
-
configuration file)
|
57
|
-
* Solid as a rock, also in case of template errors and whatever else may happen
|
58
|
-
* Full [API documentation](https://rubydoc.info/gems/ruby-grafana-reporter) available
|
59
|
-
|
60
|
-
## Supported datasources
|
61
|
-
|
62
|
-
Functionalities are provided as shown here:
|
63
|
-
|
64
|
-
Database | Image rendering | Raw queries | Composed queries
|
65
|
-
------------------------- | :-------------: | :-----------: | :------------:
|
66
|
-
all SQL based datasources | supported | supported | supported
|
67
|
-
Graphite | supported | supported | supported
|
68
|
-
InfluxDB | supported | supported | supported
|
69
|
-
Prometheus | supported | supported | n/a in grafana
|
70
|
-
other datasources | supported | not supported | not supported
|
71
|
-
|
72
|
-
The characteristics of a raw query are, that the query is either specified manually in
|
73
|
-
the panel specification or in the calling template.
|
74
|
-
|
75
|
-
Composed queries are all kinds of query, where the grafana UI feature (aka visual editor
|
76
|
-
mode) for query specifications are used. In this case grafana is translating the UI query
|
77
|
-
specification to a raw query, which then in fact is sent to the database.
|
78
|
-
|
79
|
-
## Quick Start
|
80
|
-
|
81
|
-
|
82
|
-
### Setup
|
83
|
-
|
84
|
-
You don't have a grafana setup runnning already? No worries, just configure
|
85
|
-
`https://play.grafana.org` in the configuration wizard and see the magic
|
86
|
-
happen!
|
87
|
-
|
88
|
-
If your grafana setup requires a login, you'll have to setup an api key for
|
89
|
-
the reporter. Please follow the steps
|
90
|
-
[described here](https://github.com/divinity666/ruby-grafana-reporter/issues/2#issuecomment-811836757)
|
91
|
-
first.
|
92
|
-
|
93
|
-
**Windows:**
|
94
|
-
|
95
|
-
* [Download latest Windows executable](https://github.com/divinity666/ruby-grafana-reporter/releases/latest)
|
96
|
-
* `ruby-grafana-reporter -w`
|
97
|
-
|
98
|
-
**Raspberry Pi:**
|
99
|
-
|
100
|
-
* `sudo apt-get install ruby`
|
101
|
-
* `gem install ruby-grafana-reporter`
|
102
|
-
* `ruby-grafana-reporter -w`
|
103
|
-
|
104
|
-
**Ruby environment:**
|
105
|
-
|
106
|
-
* `gem install ruby-grafana-reporter`
|
107
|
-
* `ruby-grafana-reporter -w`
|
108
|
-
|
109
|
-
**Docker environment** (advanced users):
|
110
|
-
|
111
|
-
* [Download latest single-rb file](https://github.com/divinity666/ruby-grafana-reporter/releases/latest)
|
112
|
-
to an empty folder
|
113
|
-
* create a configuration file by calling `ruby ruby-grafana-reporter -w` (if in doubt,
|
114
|
-
run the command within your docker container)
|
115
|
-
* create file `/<<path-to-single-rb-file-folder>>/startup.sh` with the following
|
116
|
-
content:
|
117
|
-
|
118
|
-
```
|
119
|
-
cd /documents
|
120
|
-
ruby bin/ruby-grafana-reporter
|
121
|
-
```
|
122
|
-
* add the startup script to your asciidoctor section in your docker-compose.yaml:
|
123
|
-
|
124
|
-
```
|
125
|
-
asciidoctor:
|
126
|
-
image: asciidoctor/docker-asciidoctor
|
127
|
-
container_name: asciidoctor
|
128
|
-
hostname: asciidoctor
|
129
|
-
volumes:
|
130
|
-
- /<<path-to-single-rb-file-folder>>:/documents
|
131
|
-
command:
|
132
|
-
sh /documents/startup.sh
|
133
|
-
restart: unless-stopped
|
134
|
-
```
|
135
|
-
* start/restart the asciidoctor docker container
|
136
|
-
|
137
|
-
### Grafana integration
|
138
|
-
|
139
|
-
For using the reporter directly from grafana, you need to simply add a link to your
|
140
|
-
grafana dashboard:
|
141
|
-
|
142
|
-
* Open the dashboard configuration
|
143
|
-
* Select `Links`
|
144
|
-
* Select `Add`
|
145
|
-
* Fill out as following:
|
146
|
-
* Type: `link`
|
147
|
-
* Url: `http://<<your-server-url>>:<<your-webservice-port>>/render?var-template=demo_report`
|
148
|
-
* Title: `Demo Report`
|
149
|
-
* Select `Time range`
|
150
|
-
* Select `Variable values`
|
151
|
-
* Select `Add`
|
152
|
-
|
153
|
-
Now go back to your dashboard and click the newly generated `Demo Report`
|
154
|
-
link on it. Now the renderer should start it's task and show you the expected
|
155
|
-
results.
|
156
|
-
|
157
|
-
Please note, that the reporter won't automatically refresh your screen to update
|
158
|
-
the progress. Simply hit `F5` to refresh your browser. After the report has been
|
159
|
-
successfully built, it will show the PDF after the next refresh automatically.
|
160
|
-
|
161
|
-
You want to select a template in grafana, which shall then be rendered?
|
162
|
-
Piece of cake: Just add a dashboard variable to your grafana dashboard named
|
163
|
-
`template` and let the user select or enter a template name. To make use of it,
|
164
|
-
you should change the link of the `Demo Report` link to
|
165
|
-
`http://<<your-server-url>>:<<your-webservice-port>>/render?`. On
|
166
|
-
hitting the new link in the dashboard, grafana will add the selected template as
|
167
|
-
a variable and forward it to the reporter.
|
168
|
-
|
169
|
-
## Advanced information
|
170
|
-
|
171
|
-
### Webservice
|
172
|
-
|
173
|
-
Running the reporter as a webservice provides the following URLs
|
174
|
-
|
175
|
-
/overview - for all running or retained renderings
|
176
|
-
/render - for rendering a template, 'var-template' is the only mandatory GET parameter, all parameters will be passed to the report templates as attributes
|
177
|
-
/view_report - for viewing the status or receving the result of a specific rendering, is automatically called after a successfull /render call
|
178
|
-
/cancel_report - for cancelling the rendering of a specific report, normally not called manually, but on user interaction in the /view_report or /overview URL
|
179
|
-
|
180
|
-
The main endpoint to call for report generation is configured in the previous chapter [Grafana integration](#grafana-integration).
|
181
|
-
|
182
|
-
However, if you would like to see, currently running report generations and previously generated reports, you may want to call the endpoint `/overview`.
|
183
|
-
|
184
|
-
### Using ERB templates
|
185
|
-
|
186
|
-
By default the configuration wizard will setup the reporter with the asciidoctor
|
187
|
-
template language enabled. For several reasons, you may want to take advantage of
|
188
|
-
the ruby included
|
189
|
-
[ERB template language](https://docs.ruby-lang.org/en/master/ERB.html).
|
190
|
-
|
191
|
-
Anyway you should consider, that ERB templates can include harmful code. So make
|
192
|
-
sure, that you will only use ERB templates in a safe environment.
|
193
|
-
|
194
|
-
To enable the ERB template language, you need to modify your configuration file
|
195
|
-
in the section `grafana-reporter`:
|
196
|
-
|
197
|
-
````
|
198
|
-
grafana-reporter:
|
199
|
-
report-class: GrafanaReporter::ERB::Report
|
200
|
-
````
|
201
|
-
|
202
|
-
Restart the grafana reporter instance, if running as webservice. That's all.
|
203
|
-
|
204
|
-
In ERB templates, you have access to the variables `report`, which is a reference
|
205
|
-
to the currently executed
|
206
|
-
[ERB Report object](https://rubydoc.info/gems/ruby-grafana-reporter/GrafanaReporter/ERB/Report)
|
207
|
-
and `attributes`, which contains a hash
|
208
|
-
of variables, which have been handed over to the report generations, e.g. from
|
209
|
-
a webservice call.
|
210
|
-
|
211
|
-
To test the configuration, you may want to run the configuration wizard again,
|
212
|
-
which will create an ERB template for you.
|
213
|
-
|
214
|
-
### Using webhooks
|
215
|
-
|
216
|
-
Webhooks provide an easy way to get automatically informed about the progress
|
217
|
-
of a report. The nice thing is, that this is completely independent from
|
218
|
-
running the reporter as webservice, i.e. these callbacks are also called if you
|
219
|
-
run the reporter standalone.
|
220
|
-
|
221
|
-
To use webhooks, you have to specify, in which progress states of a report you
|
222
|
-
are interested. Therefore you have to configure it in the `grafana-reporter`
|
223
|
-
section of your configuration file, e.g.
|
224
|
-
|
225
|
-
````
|
226
|
-
grafana-reporter:
|
227
|
-
callbacks:
|
228
|
-
all:
|
229
|
-
- http://<<your_callback_url>>
|
230
|
-
````
|
231
|
-
|
232
|
-
Remember to restart the reporter, if it is running as a webservice.
|
233
|
-
|
234
|
-
After having done so, your callback url will be called for each event with
|
235
|
-
a JSON body including all necessary information of the report. For details see
|
236
|
-
[callback](https://rubydoc.info/gems/ruby-grafana-reporter/GrafanaReporter/ReportWebhook#callback-instance_method).
|
237
|
-
|
238
|
-
### Developing your own plugin
|
239
|
-
|
240
|
-
The reporter is designed to allow easy integration of your own plugins,
|
241
|
-
without having to modify the reporter base source on github (or anywhere
|
242
|
-
else). This section shows how to implement and load a custom datasource.
|
243
|
-
|
244
|
-
Implementing a custom datasource is needed, if you use a custom datasource
|
245
|
-
grafana plugin, which is not yet supported by the reporter. In that case you
|
246
|
-
can build your own custom datasource for the reporter and load it on demand
|
247
|
-
with a command line parameter, without having to build your own fork of this
|
248
|
-
project.
|
249
|
-
|
250
|
-
This documentation will provide a simple, but mocked implementation of an
|
251
|
-
imagined grafana datasource.
|
252
|
-
|
253
|
-
First of all, let's create a new text file, e.g. `my_datasource.rb` with the
|
254
|
-
following content:
|
255
|
-
|
256
|
-
````
|
257
|
-
class MyDatasource < ::Grafana::AbstractDatasource
|
258
|
-
def self.handles?(model)
|
259
|
-
tmp = new(model)
|
260
|
-
tmp.type == 'my_datasource'
|
261
|
-
end
|
262
|
-
|
263
|
-
def request(query_description)
|
264
|
-
# see https://rubydoc.info/gems/ruby-grafana-reporter/Grafana/AbstractDatasource#request-instance_method
|
265
|
-
# for detailed information of given parameters and expected return format
|
266
|
-
|
267
|
-
# TODO: call your datasource, e.g. via REST call
|
268
|
-
# TODO: return the value in the needed format
|
269
|
-
end
|
270
|
-
|
271
|
-
def raw_query_from_panel_model(panel_query_target)
|
272
|
-
# TODO: extract or build the query from the given grafana panel query target hash
|
273
|
-
end
|
274
|
-
|
275
|
-
def default_variable_format
|
276
|
-
# TODO, specify the default variable format
|
277
|
-
# see https://rubydoc.info/gems/ruby-grafana-reporter/Grafana/Variable#value_formatted-instance_method
|
278
|
-
# for detailed information.
|
279
|
-
end
|
280
|
-
end
|
281
|
-
````
|
282
|
-
|
283
|
-
The only thing left to do now, is to make this datasource known to the
|
284
|
-
reporter. This can be done with the `-r` command line flag, e.g.
|
285
|
-
|
286
|
-
````
|
287
|
-
ruby-grafana-reporter -r my_datasource.rb
|
288
|
-
````
|
289
|
-
|
290
|
-
The reporter implemented some magic, to automatically register datasource
|
291
|
-
implementations on load, if they inherit from `::Grafana::AbstractDatasource`.
|
292
|
-
This means, that you don't have to do anything else here.
|
293
|
-
|
294
|
-
Now the reporter knows about your datasource implementation and will use it,
|
295
|
-
if you request information from a panel, which is linked to the type
|
296
|
-
`my_datasource` as specified in the `handles?` method above. If any errors
|
297
|
-
occur during execution, the reporter will catch them and show them in the error
|
298
|
-
log.
|
299
|
-
|
300
|
-
Registering a custom ruby file is independent from running the reporter as a
|
301
|
-
webservice or as a standalone executable. In any case the reporter will apply
|
302
|
-
the file.
|
303
|
-
|
304
|
-
Technically, loading your own plugin will call require for your ruby file,
|
305
|
-
_after_ all reporter files have been loaded and _before_ the execution of the
|
306
|
-
webservice or a rendering process starts.
|
307
|
-
|
308
|
-
## Roadmap
|
309
|
-
|
310
|
-
This is just a collection of things, I am heading for in future, without a schedule.
|
311
|
-
|
312
|
-
* Support grafana internal datasources
|
313
|
-
*
|
314
|
-
*
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
* [asciidoctor
|
328
|
-
* [
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
1
|
+
[![MIT License](https://img.shields.io/github/license/divinity666/ruby-grafana-reporter.svg?style=flat-square)](https://github.com/divinity666/ruby-grafana-reporter/blob/master/LICENSE)
|
2
|
+
[![Build Status](https://travis-ci.com/divinity666/ruby-grafana-reporter.svg?branch=master)](https://travis-ci.com/github/divinity666/ruby-grafana-reporter?branch=master)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/github/divinity666/ruby-grafana-reporter/badge.svg?branch=master)](https://coveralls.io/github/divinity666/ruby-grafana-reporter?branch=master)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/ruby-grafana-reporter.svg)](https://badge.fury.io/rb/ruby-grafana-reporter)
|
5
|
+
|
6
|
+
# Ruby Grafana Reporter
|
7
|
+
Reporting Service for Grafana
|
8
|
+
|
9
|
+
## Table of Contents
|
10
|
+
|
11
|
+
* [About the project](#about-the-project)
|
12
|
+
* [Features](#features)
|
13
|
+
* [Supported datasources](#supported-datasources)
|
14
|
+
* [Quick Start](#quick-start)
|
15
|
+
* [Setup](#setup)
|
16
|
+
* [Grafana integration](#grafana-integration)
|
17
|
+
* [Advanced information](#advanced-information)
|
18
|
+
* [Webservice](#webservice)
|
19
|
+
* [Using ERB templates](#using-erb-templates)
|
20
|
+
* [Using webhooks](#using-webhooks)
|
21
|
+
* [Developing your own plugin](#developing-your-own-plugin)
|
22
|
+
* [Roadmap](#roadmap)
|
23
|
+
* [Donations](#donations)
|
24
|
+
|
25
|
+
## About the project
|
26
|
+
|
27
|
+
[Grafana](https://github.com/grafana/grafana) is a great tool for monitoring and
|
28
|
+
visualizing data from different sources. Anyway the free version is lacking a
|
29
|
+
professional reporting functionality. And this is, where the ruby grafana reporter
|
30
|
+
steps in.
|
31
|
+
|
32
|
+
The key functionality of the reporter is to capture data and images from grafana
|
33
|
+
dashboards and to use it in your custom templates to finally create reports in PDF
|
34
|
+
(default), HTML, or any other format.
|
35
|
+
|
36
|
+
By default (an extended version of) Asciidoctor is enabled as template language.
|
37
|
+
|
38
|
+
## Features
|
39
|
+
|
40
|
+
* Supports creation of reports for multiple [grafana](https://github.com/grafana/grafana)
|
41
|
+
dashboards (and also multiple grafana installations!) in one resulting report
|
42
|
+
* PDF (default), HTML and many other report formats are supported
|
43
|
+
* Easy-to-use configuration wizard, including fully automated functionality to create a
|
44
|
+
demo report for your dashboard
|
45
|
+
* Include dynamic content from grafana (find here a reference for all
|
46
|
+
[asciidcotor reporter calls](FUNCTION_CALLS.md)):
|
47
|
+
* panels as images
|
48
|
+
* tables based on grafana panel queries or custom database queries (no images!)
|
49
|
+
* single values to be integrated in text, based on grafana panel queries or custom
|
50
|
+
database queries
|
51
|
+
* Runs as
|
52
|
+
* webservice to be called directly from grafana
|
53
|
+
* standalone command line tool, e.g. to be automated with `cron` or `bash` scrips
|
54
|
+
* microservice from standard asciidoctor docker container without any dependencies
|
55
|
+
* Supports webhook callbacks on before, on cancel and on finishing a report (see
|
56
|
+
configuration file)
|
57
|
+
* Solid as a rock, also in case of template errors and whatever else may happen
|
58
|
+
* Full [API documentation](https://rubydoc.info/gems/ruby-grafana-reporter) available
|
59
|
+
|
60
|
+
## Supported datasources
|
61
|
+
|
62
|
+
Functionalities are provided as shown here:
|
63
|
+
|
64
|
+
Database | Image rendering | Raw queries | Composed queries
|
65
|
+
------------------------- | :-------------: | :-----------: | :------------:
|
66
|
+
all SQL based datasources | supported | supported | supported
|
67
|
+
Graphite | supported | supported | supported
|
68
|
+
InfluxDB | supported | supported | supported
|
69
|
+
Prometheus | supported | supported | n/a in grafana
|
70
|
+
other datasources | supported | not supported | not supported
|
71
|
+
|
72
|
+
The characteristics of a raw query are, that the query is either specified manually in
|
73
|
+
the panel specification or in the calling template.
|
74
|
+
|
75
|
+
Composed queries are all kinds of query, where the grafana UI feature (aka visual editor
|
76
|
+
mode) for query specifications are used. In this case grafana is translating the UI query
|
77
|
+
specification to a raw query, which then in fact is sent to the database.
|
78
|
+
|
79
|
+
## Quick Start
|
80
|
+
|
81
|
+
|
82
|
+
### Setup
|
83
|
+
|
84
|
+
You don't have a grafana setup runnning already? No worries, just configure
|
85
|
+
`https://play.grafana.org` in the configuration wizard and see the magic
|
86
|
+
happen!
|
87
|
+
|
88
|
+
If your grafana setup requires a login, you'll have to setup an api key for
|
89
|
+
the reporter. Please follow the steps
|
90
|
+
[described here](https://github.com/divinity666/ruby-grafana-reporter/issues/2#issuecomment-811836757)
|
91
|
+
first.
|
92
|
+
|
93
|
+
**Windows:**
|
94
|
+
|
95
|
+
* [Download latest Windows executable](https://github.com/divinity666/ruby-grafana-reporter/releases/latest)
|
96
|
+
* `ruby-grafana-reporter -w`
|
97
|
+
|
98
|
+
**Raspberry Pi:**
|
99
|
+
|
100
|
+
* `sudo apt-get install ruby`
|
101
|
+
* `gem install ruby-grafana-reporter`
|
102
|
+
* `ruby-grafana-reporter -w`
|
103
|
+
|
104
|
+
**Ruby environment:**
|
105
|
+
|
106
|
+
* `gem install ruby-grafana-reporter`
|
107
|
+
* `ruby-grafana-reporter -w`
|
108
|
+
|
109
|
+
**Docker environment** (advanced users):
|
110
|
+
|
111
|
+
* [Download latest single-rb file](https://github.com/divinity666/ruby-grafana-reporter/releases/latest)
|
112
|
+
to an empty folder
|
113
|
+
* create a configuration file by calling `ruby ruby-grafana-reporter -w` (if in doubt,
|
114
|
+
run the command within your docker container)
|
115
|
+
* create file `/<<path-to-single-rb-file-folder>>/startup.sh` with the following
|
116
|
+
content:
|
117
|
+
|
118
|
+
```
|
119
|
+
cd /documents
|
120
|
+
ruby bin/ruby-grafana-reporter
|
121
|
+
```
|
122
|
+
* add the startup script to your asciidoctor section in your docker-compose.yaml:
|
123
|
+
|
124
|
+
```
|
125
|
+
asciidoctor:
|
126
|
+
image: asciidoctor/docker-asciidoctor
|
127
|
+
container_name: asciidoctor
|
128
|
+
hostname: asciidoctor
|
129
|
+
volumes:
|
130
|
+
- /<<path-to-single-rb-file-folder>>:/documents
|
131
|
+
command:
|
132
|
+
sh /documents/startup.sh
|
133
|
+
restart: unless-stopped
|
134
|
+
```
|
135
|
+
* start/restart the asciidoctor docker container
|
136
|
+
|
137
|
+
### Grafana integration
|
138
|
+
|
139
|
+
For using the reporter directly from grafana, you need to simply add a link to your
|
140
|
+
grafana dashboard:
|
141
|
+
|
142
|
+
* Open the dashboard configuration
|
143
|
+
* Select `Links`
|
144
|
+
* Select `Add`
|
145
|
+
* Fill out as following:
|
146
|
+
* Type: `link`
|
147
|
+
* Url: `http://<<your-server-url>>:<<your-webservice-port>>/render?var-template=demo_report`
|
148
|
+
* Title: `Demo Report`
|
149
|
+
* Select `Time range`
|
150
|
+
* Select `Variable values`
|
151
|
+
* Select `Add`
|
152
|
+
|
153
|
+
Now go back to your dashboard and click the newly generated `Demo Report`
|
154
|
+
link on it. Now the renderer should start it's task and show you the expected
|
155
|
+
results.
|
156
|
+
|
157
|
+
Please note, that the reporter won't automatically refresh your screen to update
|
158
|
+
the progress. Simply hit `F5` to refresh your browser. After the report has been
|
159
|
+
successfully built, it will show the PDF after the next refresh automatically.
|
160
|
+
|
161
|
+
You want to select a template in grafana, which shall then be rendered?
|
162
|
+
Piece of cake: Just add a dashboard variable to your grafana dashboard named
|
163
|
+
`template` and let the user select or enter a template name. To make use of it,
|
164
|
+
you should change the link of the `Demo Report` link to
|
165
|
+
`http://<<your-server-url>>:<<your-webservice-port>>/render?`. On
|
166
|
+
hitting the new link in the dashboard, grafana will add the selected template as
|
167
|
+
a variable and forward it to the reporter.
|
168
|
+
|
169
|
+
## Advanced information
|
170
|
+
|
171
|
+
### Webservice
|
172
|
+
|
173
|
+
Running the reporter as a webservice provides the following URLs
|
174
|
+
|
175
|
+
/overview - for all running or retained renderings
|
176
|
+
/render - for rendering a template, 'var-template' is the only mandatory GET parameter, all parameters will be passed to the report templates as attributes
|
177
|
+
/view_report - for viewing the status or receving the result of a specific rendering, is automatically called after a successfull /render call
|
178
|
+
/cancel_report - for cancelling the rendering of a specific report, normally not called manually, but on user interaction in the /view_report or /overview URL
|
179
|
+
|
180
|
+
The main endpoint to call for report generation is configured in the previous chapter [Grafana integration](#grafana-integration).
|
181
|
+
|
182
|
+
However, if you would like to see, currently running report generations and previously generated reports, you may want to call the endpoint `/overview`.
|
183
|
+
|
184
|
+
### Using ERB templates
|
185
|
+
|
186
|
+
By default the configuration wizard will setup the reporter with the asciidoctor
|
187
|
+
template language enabled. For several reasons, you may want to take advantage of
|
188
|
+
the ruby included
|
189
|
+
[ERB template language](https://docs.ruby-lang.org/en/master/ERB.html).
|
190
|
+
|
191
|
+
Anyway you should consider, that ERB templates can include harmful code. So make
|
192
|
+
sure, that you will only use ERB templates in a safe environment.
|
193
|
+
|
194
|
+
To enable the ERB template language, you need to modify your configuration file
|
195
|
+
in the section `grafana-reporter`:
|
196
|
+
|
197
|
+
````
|
198
|
+
grafana-reporter:
|
199
|
+
report-class: GrafanaReporter::ERB::Report
|
200
|
+
````
|
201
|
+
|
202
|
+
Restart the grafana reporter instance, if running as webservice. That's all.
|
203
|
+
|
204
|
+
In ERB templates, you have access to the variables `report`, which is a reference
|
205
|
+
to the currently executed
|
206
|
+
[ERB Report object](https://rubydoc.info/gems/ruby-grafana-reporter/GrafanaReporter/ERB/Report)
|
207
|
+
and `attributes`, which contains a hash
|
208
|
+
of variables, which have been handed over to the report generations, e.g. from
|
209
|
+
a webservice call.
|
210
|
+
|
211
|
+
To test the configuration, you may want to run the configuration wizard again,
|
212
|
+
which will create an ERB template for you.
|
213
|
+
|
214
|
+
### Using webhooks
|
215
|
+
|
216
|
+
Webhooks provide an easy way to get automatically informed about the progress
|
217
|
+
of a report. The nice thing is, that this is completely independent from
|
218
|
+
running the reporter as webservice, i.e. these callbacks are also called if you
|
219
|
+
run the reporter standalone.
|
220
|
+
|
221
|
+
To use webhooks, you have to specify, in which progress states of a report you
|
222
|
+
are interested. Therefore you have to configure it in the `grafana-reporter`
|
223
|
+
section of your configuration file, e.g.
|
224
|
+
|
225
|
+
````
|
226
|
+
grafana-reporter:
|
227
|
+
callbacks:
|
228
|
+
all:
|
229
|
+
- http://<<your_callback_url>>
|
230
|
+
````
|
231
|
+
|
232
|
+
Remember to restart the reporter, if it is running as a webservice.
|
233
|
+
|
234
|
+
After having done so, your callback url will be called for each event with
|
235
|
+
a JSON body including all necessary information of the report. For details see
|
236
|
+
[callback](https://rubydoc.info/gems/ruby-grafana-reporter/GrafanaReporter/ReportWebhook#callback-instance_method).
|
237
|
+
|
238
|
+
### Developing your own plugin
|
239
|
+
|
240
|
+
The reporter is designed to allow easy integration of your own plugins,
|
241
|
+
without having to modify the reporter base source on github (or anywhere
|
242
|
+
else). This section shows how to implement and load a custom datasource.
|
243
|
+
|
244
|
+
Implementing a custom datasource is needed, if you use a custom datasource
|
245
|
+
grafana plugin, which is not yet supported by the reporter. In that case you
|
246
|
+
can build your own custom datasource for the reporter and load it on demand
|
247
|
+
with a command line parameter, without having to build your own fork of this
|
248
|
+
project.
|
249
|
+
|
250
|
+
This documentation will provide a simple, but mocked implementation of an
|
251
|
+
imagined grafana datasource.
|
252
|
+
|
253
|
+
First of all, let's create a new text file, e.g. `my_datasource.rb` with the
|
254
|
+
following content:
|
255
|
+
|
256
|
+
````
|
257
|
+
class MyDatasource < ::Grafana::AbstractDatasource
|
258
|
+
def self.handles?(model)
|
259
|
+
tmp = new(model)
|
260
|
+
tmp.type == 'my_datasource'
|
261
|
+
end
|
262
|
+
|
263
|
+
def request(query_description)
|
264
|
+
# see https://rubydoc.info/gems/ruby-grafana-reporter/Grafana/AbstractDatasource#request-instance_method
|
265
|
+
# for detailed information of given parameters and expected return format
|
266
|
+
|
267
|
+
# TODO: call your datasource, e.g. via REST call
|
268
|
+
# TODO: return the value in the needed format
|
269
|
+
end
|
270
|
+
|
271
|
+
def raw_query_from_panel_model(panel_query_target)
|
272
|
+
# TODO: extract or build the query from the given grafana panel query target hash
|
273
|
+
end
|
274
|
+
|
275
|
+
def default_variable_format
|
276
|
+
# TODO, specify the default variable format
|
277
|
+
# see https://rubydoc.info/gems/ruby-grafana-reporter/Grafana/Variable#value_formatted-instance_method
|
278
|
+
# for detailed information.
|
279
|
+
end
|
280
|
+
end
|
281
|
+
````
|
282
|
+
|
283
|
+
The only thing left to do now, is to make this datasource known to the
|
284
|
+
reporter. This can be done with the `-r` command line flag, e.g.
|
285
|
+
|
286
|
+
````
|
287
|
+
ruby-grafana-reporter -r my_datasource.rb
|
288
|
+
````
|
289
|
+
|
290
|
+
The reporter implemented some magic, to automatically register datasource
|
291
|
+
implementations on load, if they inherit from `::Grafana::AbstractDatasource`.
|
292
|
+
This means, that you don't have to do anything else here.
|
293
|
+
|
294
|
+
Now the reporter knows about your datasource implementation and will use it,
|
295
|
+
if you request information from a panel, which is linked to the type
|
296
|
+
`my_datasource` as specified in the `handles?` method above. If any errors
|
297
|
+
occur during execution, the reporter will catch them and show them in the error
|
298
|
+
log.
|
299
|
+
|
300
|
+
Registering a custom ruby file is independent from running the reporter as a
|
301
|
+
webservice or as a standalone executable. In any case the reporter will apply
|
302
|
+
the file.
|
303
|
+
|
304
|
+
Technically, loading your own plugin will call require for your ruby file,
|
305
|
+
_after_ all reporter files have been loaded and _before_ the execution of the
|
306
|
+
webservice or a rendering process starts.
|
307
|
+
|
308
|
+
## Roadmap
|
309
|
+
|
310
|
+
This is just a collection of things, I am heading for in future, without a schedule.
|
311
|
+
|
312
|
+
* Support grafana internal datasources
|
313
|
+
* Support additional templating variable types
|
314
|
+
* Solve code TODOs
|
315
|
+
* Become [rubocop](https://rubocop.org/) ready
|
316
|
+
|
317
|
+
## Contributing
|
318
|
+
|
319
|
+
If you'd like to contribute, please fork the repository and use a feature
|
320
|
+
branch. Pull requests are warmly welcome.
|
321
|
+
|
322
|
+
## Licensing
|
323
|
+
|
324
|
+
The code in this project is licensed under MIT license.
|
325
|
+
|
326
|
+
## Acknowledgements
|
327
|
+
* [asciidoctor](https://github.com/asciidoctor/asciidoctor)
|
328
|
+
* [asciidoctor-pdf](https://github.com/asciidoctor/asciidoctor-pdf)
|
329
|
+
* [grafana](https://github.com/grafana/grafana)
|
330
|
+
|
331
|
+
Inspired by [Izak Marai's grafana reporter](https://github.com/IzakMarais/reporter)
|
332
|
+
|
333
|
+
## Donations
|
334
|
+
|
335
|
+
If you like this project and you would like to support my work, feel free to donate. :)
|
336
|
+
|
337
|
+
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=35LH6JNLPHPHQ)
|
data/bin/ruby-grafana-reporter
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative '../lib/ruby_grafana_reporter'
|
5
|
-
GrafanaReporter::Application::Application.new.configure_and_run(ARGV) unless defined?(Ocra)
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../lib/ruby_grafana_reporter'
|
5
|
+
GrafanaReporter::Application::Application.new.configure_and_run(ARGV) unless defined?(Ocra)
|
data/lib/VERSION.rb
CHANGED
@@ -82,6 +82,7 @@ module Grafana
|
|
82
82
|
# }
|
83
83
|
#
|
84
84
|
# @param query_description [Hash] query description, which will requested:
|
85
|
+
# @option query_description [String] :grafana_version grafana version, for which the request is to be prepared
|
85
86
|
# @option query_description [String] :from +from+ timestamp
|
86
87
|
# @option query_description [String] :to +to+ timestamp
|
87
88
|
# @option query_description [Integer] :timeout expected timeout for the request
|
@@ -170,7 +171,13 @@ module Grafana
|
|
170
171
|
data = data['frames']
|
171
172
|
headers = []
|
172
173
|
data.first['schema']['fields'].each do |headline|
|
173
|
-
|
174
|
+
use_name_only = true
|
175
|
+
if not headline['config'].nil?
|
176
|
+
if not headline['config']['displayNameFromDS'].nil?
|
177
|
+
use_name_only = false
|
178
|
+
end
|
179
|
+
end
|
180
|
+
header = use_name_only ? headline['name'] : headline['config']['displayNameFromDS']
|
174
181
|
headers << header
|
175
182
|
end
|
176
183
|
content = data.first['data']['values'][0].zip(data.first['data']['values'][1])
|
data/lib/grafana/dashboard.rb
CHANGED
@@ -58,7 +58,14 @@ module Grafana
|
|
58
58
|
list = @model['templating']['list']
|
59
59
|
return unless list.is_a? Array
|
60
60
|
|
61
|
-
list.each
|
61
|
+
list.each do |item|
|
62
|
+
begin
|
63
|
+
@variables << Variable.new(item, self)
|
64
|
+
rescue => e
|
65
|
+
# TODO: show this message as a warning - needs test cleanup
|
66
|
+
@grafana.logger.debug(e.message)
|
67
|
+
end
|
68
|
+
end
|
62
69
|
end
|
63
70
|
|
64
71
|
# read panels
|
data/lib/grafana/grafana.rb
CHANGED
@@ -86,7 +86,15 @@ module Grafana
|
|
86
86
|
# @param datasource_uid [String] unique id of the searched datasource
|
87
87
|
# @return [Datasource] Datasource for the specified datasource unique id
|
88
88
|
def datasource_by_uid(datasource_uid)
|
89
|
-
datasource = @datasources.select
|
89
|
+
datasource = @datasources.select do |ds_name, ds|
|
90
|
+
if (ds.nil?)
|
91
|
+
# print debug info for https://github.com/divinity666/ruby-grafana-reporter/issues/29
|
92
|
+
@logger.warn("Datasource with name #{ds_name} is nil, which should never happen. Check logs for details.")
|
93
|
+
false
|
94
|
+
else
|
95
|
+
ds.uid == datasource_uid
|
96
|
+
end
|
97
|
+
end.values.first
|
90
98
|
raise DatasourceDoesNotExistError.new('uid', datasource_uid) unless datasource
|
91
99
|
|
92
100
|
datasource
|
@@ -156,6 +164,12 @@ module Grafana
|
|
156
164
|
json = JSON.parse(settings.body)
|
157
165
|
json['datasources'].select { |_k, v| v['id'].to_i.positive? }.each do |ds_name, ds_value|
|
158
166
|
@datasources[ds_name] = AbstractDatasource.build_instance(ds_value)
|
167
|
+
|
168
|
+
# print debug info for https://github.com/divinity666/ruby-grafana-reporter/issues/29
|
169
|
+
if @datasources[ds_name].nil?
|
170
|
+
@logger.error("Datasource with name '#{ds_name}' and configuration: '#{ds_value}' could not be initialized.")
|
171
|
+
@datasources.delete(ds_name)
|
172
|
+
end
|
159
173
|
end
|
160
174
|
@datasources['default'] = @datasources[json['defaultDatasource']]
|
161
175
|
end
|
File without changes
|
File without changes
|
data/lib/grafana/panel.rb
CHANGED
@@ -37,7 +37,7 @@ module Grafana
|
|
37
37
|
#
|
38
38
|
# @param variables [Hash] variables hash, which should be use to resolve variable datasource
|
39
39
|
def resolve_variable_datasource(variables)
|
40
|
-
@datasource_uid_or_name = AbstractDatasource.new(nil).replace_variables(@datasource_uid_or_name, variables, 'raw')
|
40
|
+
@datasource_uid_or_name = AbstractDatasource.new(nil).replace_variables(@datasource_uid_or_name, variables, 'raw') if @datasource_uid_or_name.is_a?(String)
|
41
41
|
end
|
42
42
|
|
43
43
|
# @return [Datasource] datasource object specified for the current panel
|
@@ -24,21 +24,15 @@ module Grafana
|
|
24
24
|
interval = interval.raw_value if interval.is_a?(Variable)
|
25
25
|
query = query_hash[:query] || query_description[:raw_query]
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
ver = query_description[:grafana_version].split('.').map{|x| x.to_i}
|
28
|
+
request = nil
|
29
|
+
if (ver[0] == 7 and ver[1] < 5) or ver[0] < 7
|
30
|
+
request = prepare_get_request({query_description: query_description, instant: instant, interval: interval, query: query})
|
30
31
|
else
|
31
|
-
|
32
|
-
"&end=#{query_description[:to]}"\
|
33
|
-
"&query=#{CGI.escape(replace_variables(query, query_description[:variables]))}"\
|
34
|
-
"&step=#{interval}"
|
32
|
+
request = prepare_post_request({query_description: query_description, instant: instant, interval: interval, query: query})
|
35
33
|
end
|
36
34
|
|
37
|
-
|
38
|
-
webrequest.relative_url = url
|
39
|
-
webrequest.options.merge!({ request: Net::HTTP::Get })
|
40
|
-
|
41
|
-
result = webrequest.execute(query_description[:timeout])
|
35
|
+
result = request.execute(query_description[:timeout])
|
42
36
|
preformat_response(result.body)
|
43
37
|
end
|
44
38
|
|
@@ -54,13 +48,67 @@ module Grafana
|
|
54
48
|
end
|
55
49
|
|
56
50
|
private
|
51
|
+
def prepare_get_request(hash)
|
52
|
+
url = if hash[:instant]
|
53
|
+
"/api/datasources/proxy/#{id}/api/v1/query?time=#{hash[:query_description][:to]}&query="\
|
54
|
+
"#{CGI.escape(replace_variables(hash[:query], hash[:query_description][:variables]))}"
|
55
|
+
else
|
56
|
+
"/api/datasources/proxy/#{id}/api/v1/query_range?start=#{hash[:query_description][:from]}"\
|
57
|
+
"&end=#{hash[:query_description][:to]}"\
|
58
|
+
"&query=#{CGI.escape(replace_variables(hash[:query], hash[:query_description][:variables]))}"\
|
59
|
+
"&step=#{hash[:interval]}"
|
60
|
+
end
|
61
|
+
|
62
|
+
webrequest = hash[:query_description][:prepared_request]
|
63
|
+
webrequest.relative_url = url
|
64
|
+
webrequest.options.merge!({ request: Net::HTTP::Get })
|
65
|
+
|
66
|
+
webrequest
|
67
|
+
end
|
68
|
+
|
69
|
+
def prepare_post_request(hash)
|
70
|
+
webrequest = hash[:query_description][:prepared_request]
|
71
|
+
webrequest.relative_url = '/api/ds/query'
|
72
|
+
|
73
|
+
params = {
|
74
|
+
from: hash[:query_description][:from],
|
75
|
+
to: hash[:query_description][:to],
|
76
|
+
queries: [{
|
77
|
+
datasource: { type: type, uid: uid },
|
78
|
+
datasourceId: id,
|
79
|
+
exemplar: false,
|
80
|
+
expr: hash[:query],
|
81
|
+
format: 'time_series',
|
82
|
+
interval: '',
|
83
|
+
# intervalFactor: ### 2,
|
84
|
+
# intervalMs: ### 15000,
|
85
|
+
# legendFormat: '', ### {{job}}
|
86
|
+
# maxDataPoints: 999,
|
87
|
+
metric: '',
|
88
|
+
queryType: 'timeSeriesQuery',
|
89
|
+
refId: 'A',
|
90
|
+
# requestId: '14A',
|
91
|
+
# utcOffsetSec: 7200,
|
92
|
+
step: hash[:interval]
|
93
|
+
}],
|
94
|
+
range: {
|
95
|
+
#from: ### "2022-07-31T16:19:26.198Z",
|
96
|
+
#to: ### "2022-07-31T16:19:26.198Z",
|
97
|
+
raw: { from: hash[:query_description][:variables]['from'].raw_value, to: hash[:query_description][:variables]['to'].raw_value }
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
webrequest.options.merge!({ request: Net::HTTP::Post, body: params.to_json })
|
102
|
+
|
103
|
+
webrequest
|
104
|
+
end
|
57
105
|
|
58
106
|
def preformat_response(response_body)
|
59
107
|
# TODO: show raw response body to debug case https://github.com/divinity666/ruby-grafana-reporter/issues/24
|
60
108
|
begin
|
61
109
|
return preformat_dataframe_response(response_body)
|
62
110
|
rescue
|
63
|
-
# TODO: show an info, that the response
|
111
|
+
# TODO: show an info, that the response is not a dataframe
|
64
112
|
end
|
65
113
|
|
66
114
|
json = JSON.parse(response_body)
|
File without changes
|
data/lib/grafana/variable.rb
CHANGED
@@ -240,7 +240,7 @@ module Grafana
|
|
240
240
|
if !@config['current'].nil?
|
241
241
|
self.raw_value = @config['current']['value']
|
242
242
|
else
|
243
|
-
raise GrafanaError.new("Grafana variable with type '#{@config['type']}' and name '#{@config['name']}'
|
243
|
+
raise GrafanaError.new("Grafana variable with type '#{@config['type']}' and name '#{@config['name']}' cannot be handled properly by the reporter. Check your results and raise a ticket on github.")
|
244
244
|
end
|
245
245
|
end
|
246
246
|
end
|
@@ -13,7 +13,6 @@ module GrafanaReporter
|
|
13
13
|
attr_reader :variables, :result, :panel, :dashboard
|
14
14
|
|
15
15
|
def timeout
|
16
|
-
# TODO: PRIO check where value priorities should be evaluated
|
17
16
|
return @variables['timeout'].raw_value if @variables['timeout']
|
18
17
|
return @variables['grafana_default_timeout'].raw_value if @variables['grafana_default_timeout']
|
19
18
|
|
@@ -81,7 +80,11 @@ module GrafanaReporter
|
|
81
80
|
|
82
81
|
begin
|
83
82
|
@result = @datasource.request(from: from, to: to, raw_query: raw_query, variables: @variables,
|
84
|
-
prepared_request: @grafana.prepare_request, timeout: timeout
|
83
|
+
prepared_request: @grafana.prepare_request, timeout: timeout,
|
84
|
+
grafana_version: @grafana.version)
|
85
|
+
if @variables['verbose_log']
|
86
|
+
@logger.debug("Raw result: #{@result}") if @variables['verbose_log'].raw_value.downcase == "true"
|
87
|
+
end
|
85
88
|
rescue ::Grafana::GrafanaError
|
86
89
|
# grafana errors will be directly passed through
|
87
90
|
raise
|
@@ -95,6 +98,9 @@ module GrafanaReporter
|
|
95
98
|
raise DatasourceRequestInvalidReturnValueError.new(@datasource, @result) unless datasource_response_valid?
|
96
99
|
|
97
100
|
post_process
|
101
|
+
if @variables['verbose_log']
|
102
|
+
@logger.debug("Formatted result: #{@result}") if @variables['verbose_log'].raw_value.downcase == "true"
|
103
|
+
end
|
98
104
|
@result
|
99
105
|
end
|
100
106
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -201,7 +201,7 @@ end}
|
|
201
201
|
two digit decimals of a float. Several column formats are separated by `,`, i.e. `%.2f,%.3f` would
|
202
202
|
apply `%.2f` to the first column and `%.3f` to the second column. All other columns would not be
|
203
203
|
formatted. You may also format time in milliseconds to a time format by specifying e.g. `date:iso`.
|
204
|
-
Commas in format strings are supported, but have to be escaped by
|
204
|
+
Commas in format strings are supported, but have to be escaped by using `_,`.
|
205
205
|
Execution of related functions is applied in the following order `format`,
|
206
206
|
`replace_values`, `filter_columns`, `transpose`.
|
207
207
|
see: 'https://ruby-doc.org/core/Kernel.html#method-i-sprintf'
|
@@ -217,11 +217,16 @@ end}
|
|
217
217
|
`replace_values`, `filter_columns`, `transpose`.
|
218
218
|
see: https://ruby-doc.org/core/Regexp.html#class-Regexp-label-Character+Classes
|
219
219
|
|
220
|
+
include_headline:
|
221
|
+
call: include_headline="true"
|
222
|
+
description: >-
|
223
|
+
Adds the headline of the columns as first row of the resulting table.
|
224
|
+
|
220
225
|
filter_columns:
|
221
226
|
call: filter_columns="<column_name_1>,<column_name_2>,..."
|
222
227
|
description: >-
|
223
228
|
Removes specified columns from result. Commas in format strings are supported, but have to be
|
224
|
-
escaped by
|
229
|
+
escaped by using `_,`. Execution of related functions is applied in the following order
|
225
230
|
`format`, `replace_values`, `filter_columns`, `transpose`.
|
226
231
|
|
227
232
|
transpose:
|
@@ -271,6 +276,12 @@ end}
|
|
271
276
|
description: >-
|
272
277
|
Optional parameter for Prometheus `instant` queries. Ignored for other datasources than Prometheus.
|
273
278
|
|
279
|
+
verbose_log:
|
280
|
+
call: verbose_log="true"
|
281
|
+
description: >-
|
282
|
+
Setting this option will show additional information about the returned query results in the log as
|
283
|
+
DEBUG messages.
|
284
|
+
|
274
285
|
# ----------------------------------
|
275
286
|
# FUNCTION DOCUMENTATION STARTS HERE
|
276
287
|
# ----------------------------------
|
@@ -313,6 +324,7 @@ end}
|
|
313
324
|
filter_columns:
|
314
325
|
format:
|
315
326
|
from:
|
327
|
+
include_headline:
|
316
328
|
instance:
|
317
329
|
replace_values:
|
318
330
|
row_divider:
|
@@ -348,6 +360,7 @@ end}
|
|
348
360
|
filter_columns:
|
349
361
|
format:
|
350
362
|
from:
|
363
|
+
include_headline:
|
351
364
|
instance:
|
352
365
|
replace_values:
|
353
366
|
row_divider:
|
@@ -409,6 +422,7 @@ end}
|
|
409
422
|
filter_columns:
|
410
423
|
format:
|
411
424
|
from:
|
425
|
+
include_headline:
|
412
426
|
instance:
|
413
427
|
replace_values:
|
414
428
|
row_divider:
|
@@ -420,6 +434,7 @@ end}
|
|
420
434
|
to_timezone:
|
421
435
|
instant:
|
422
436
|
interval:
|
437
|
+
verbose_log:
|
423
438
|
|
424
439
|
grafana_panel_query_value:
|
425
440
|
call: 'grafana_panel_query_value:<panel_id>[query="<query_letter>",options]'
|
@@ -444,6 +459,7 @@ end}
|
|
444
459
|
to_timezone:
|
445
460
|
instant:
|
446
461
|
interval:
|
462
|
+
verbose_log:
|
447
463
|
|
448
464
|
grafana_sql_table:
|
449
465
|
call: 'include::grafana_sql_table:<datasource_id>[sql="<sql_query>",options]'
|
@@ -456,6 +472,7 @@ end}
|
|
456
472
|
filter_columns:
|
457
473
|
format:
|
458
474
|
from:
|
475
|
+
include_headline:
|
459
476
|
instance:
|
460
477
|
replace_values:
|
461
478
|
row_divider:
|
@@ -467,6 +484,7 @@ end}
|
|
467
484
|
to_timezone:
|
468
485
|
instant:
|
469
486
|
interval:
|
487
|
+
verbose_log:
|
470
488
|
|
471
489
|
grafana_sql_value:
|
472
490
|
call: 'grafana_sql_value:<datasource_id>[sql="<sql_query>",options]'
|
@@ -490,6 +508,30 @@ end}
|
|
490
508
|
to_timezone:
|
491
509
|
instant:
|
492
510
|
interval:
|
511
|
+
verbose_log:
|
512
|
+
|
513
|
+
grafana_value_as_variable:
|
514
|
+
call: 'include::grafana_value_as_variable[call="<grafana_reporter_call>",variable_name="<your_variable_name>",options]'
|
515
|
+
description: >-
|
516
|
+
Executes the given +<grafana_reporter_call>+ and stored the resulting value
|
517
|
+
in the given +<your_variable_name>+, so that it can be used in asciidoctor
|
518
|
+
at any position with +{<your_variable_name>}+.
|
519
|
+
|
520
|
+
A sample call could look like this: +include:grafana_value_as_variable[call="grafana_sql_value:1",variable_name="my_variable",sql="SELECT 'looks good'",<any_other_option>]+
|
521
|
+
|
522
|
+
If the function succeeds, it will add this to the asciidoctor file:
|
523
|
+
|
524
|
+
+:my_variable: looks good+
|
525
|
+
|
526
|
+
Please note, that you may add any other option to the call. These will
|
527
|
+
simply be passed 1:1 to the +<grafana_reporter_call>+.
|
528
|
+
options:
|
529
|
+
call:
|
530
|
+
call: call="<grafana_reporter_call>"
|
531
|
+
description: Call to grafana reporter function, for which the result shall be stored as variable. Please note that only functions without +include::+ are supported here.
|
532
|
+
variable_name:
|
533
|
+
call: variable_name="<your_variable_name>"
|
534
|
+
description: Name of the variable, which will get the value assigned.
|
493
535
|
YAML_HELP
|
494
536
|
end
|
495
537
|
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -40,7 +40,7 @@ module GrafanaReporter
|
|
40
40
|
k =~ /^(?:timeout|from|to)$/ ||
|
41
41
|
k =~ /filter_columns|format|replace_values_.*|transpose|from_timezone|
|
42
42
|
to_timezone|result_type|query|table_formatter|include_headline|
|
43
|
-
column_divider|row_divider|instant|interval/x
|
43
|
+
column_divider|row_divider|instant|interval|verbose_log/x
|
44
44
|
end)
|
45
45
|
|
46
46
|
result
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-grafana-reporter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Kohlmeyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: asciidoctor
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '2.3'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '2.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rubyzip
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,6 +86,20 @@ dependencies:
|
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '0.16'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: coveralls
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0.8'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0.8'
|
89
103
|
- !ruby/object:Gem::Dependency
|
90
104
|
name: webmock
|
91
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -167,7 +181,6 @@ files:
|
|
167
181
|
- "./lib/grafana_reporter/query_value_query.rb"
|
168
182
|
- "./lib/grafana_reporter/report_webhook.rb"
|
169
183
|
- "./lib/grafana_reporter/reporter_environment_datasource.rb"
|
170
|
-
- "./lib/ruby_grafana_extension.rb"
|
171
184
|
- "./lib/ruby_grafana_reporter.rb"
|
172
185
|
- LICENSE
|
173
186
|
- README.md
|
@@ -186,14 +199,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
199
|
requirements:
|
187
200
|
- - ">="
|
188
201
|
- !ruby/object:Gem::Version
|
189
|
-
version: '2.
|
202
|
+
version: '2.7'
|
190
203
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
204
|
requirements:
|
192
205
|
- - ">="
|
193
206
|
- !ruby/object:Gem::Version
|
194
207
|
version: '0'
|
195
208
|
requirements: []
|
196
|
-
rubygems_version: 3.2
|
209
|
+
rubygems_version: 3.1.2
|
197
210
|
signing_key:
|
198
211
|
specification_version: 4
|
199
212
|
summary: Reporter Service for Grafana
|