ruby-grafana-reporter 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +336 -337
- data/lib/VERSION.rb +2 -2
- data/lib/grafana/errors.rb +0 -8
- data/lib/grafana/grafana.rb +1 -1
- data/lib/grafana/influxdb_datasource.rb +89 -3
- data/lib/grafana/prometheus_datasource.rb +11 -2
- data/lib/grafana/variable.rb +4 -2
- data/lib/grafana_reporter/abstract_query.rb +5 -5
- data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +2 -2
- data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +2 -2
- data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +1 -1
- data/lib/grafana_reporter/panel_image_query.rb +1 -0
- data/lib/ruby_grafana_reporter.rb +11 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd6bdf1a9856b3c0a663060c4b2ecfc3a094ae230635807398a611d38d74ac90
|
4
|
+
data.tar.gz: 95ac2750e962a67b0d3b71184b5409b88441840f4b08f31284b33f20b0d862a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53698b98b33afef1706243cf5c322bd1a9968ec723e18d32ec5d550769117fa9c64c43c9d174752630ac7f8e8015d1394d4f2060fbe6be1b2ce73d7ad8390801
|
7
|
+
data.tar.gz: 5f39c12e056e689ff442f00c175219e6a560e9e38362a5aec9f67dc738b172ebf0b9d288958e312020f8304603e54ff1c4e21ca44d77182342a3df9fe03825ba
|
data/README.md
CHANGED
@@ -1,337 +1,336 @@
|
|
1
|
-
[](https://github.com/divinity666/ruby-grafana-reporter/blob/master/LICENSE)
|
2
|
-
[](https://travis-ci.com/github/divinity666/ruby-grafana-reporter?branch=master)
|
3
|
-
[](https://coveralls.io/github/divinity666/ruby-grafana-reporter?branch=master)
|
4
|
-
[](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
|
34
|
-
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 | not (yet) 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
|
-
* Solve code TODOs
|
314
|
-
* Become [rubocop](https://rubocop.org/) ready
|
315
|
-
|
316
|
-
## Contributing
|
317
|
-
|
318
|
-
If you'd like to contribute, please fork the repository and use a feature
|
319
|
-
branch. Pull requests are warmly welcome.
|
320
|
-
|
321
|
-
## Licensing
|
322
|
-
|
323
|
-
The code in this project is licensed under MIT license.
|
324
|
-
|
325
|
-
## Acknowledgements
|
326
|
-
* [asciidoctor](https://github.com/asciidoctor/asciidoctor)
|
327
|
-
* [asciidoctor-pdf](https://github.com/asciidoctor/asciidoctor-pdf)
|
328
|
-
* [grafana](https://github.com/grafana/grafana)
|
329
|
-
|
330
|
-
Inspired by [Izak Marai's grafana reporter](https://github.com/IzakMarais/reporter)
|
331
|
-
|
332
|
-
## Donations
|
333
|
-
|
334
|
-
If this project
|
335
|
-
|
336
|
-
|
337
|
-
[](https://www.paypal.com/donate?hosted_button_id=35LH6JNLPHPHQ)
|
1
|
+
[](https://github.com/divinity666/ruby-grafana-reporter/blob/master/LICENSE)
|
2
|
+
[](https://travis-ci.com/github/divinity666/ruby-grafana-reporter?branch=master)
|
3
|
+
[](https://coveralls.io/github/divinity666/ruby-grafana-reporter?branch=master)
|
4
|
+
[](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 | not (yet) 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
|
+
* Solve code TODOs
|
314
|
+
* Become [rubocop](https://rubocop.org/) ready
|
315
|
+
|
316
|
+
## Contributing
|
317
|
+
|
318
|
+
If you'd like to contribute, please fork the repository and use a feature
|
319
|
+
branch. Pull requests are warmly welcome.
|
320
|
+
|
321
|
+
## Licensing
|
322
|
+
|
323
|
+
The code in this project is licensed under MIT license.
|
324
|
+
|
325
|
+
## Acknowledgements
|
326
|
+
* [asciidoctor](https://github.com/asciidoctor/asciidoctor)
|
327
|
+
* [asciidoctor-pdf](https://github.com/asciidoctor/asciidoctor-pdf)
|
328
|
+
* [grafana](https://github.com/grafana/grafana)
|
329
|
+
|
330
|
+
Inspired by [Izak Marai's grafana reporter](https://github.com/IzakMarais/reporter)
|
331
|
+
|
332
|
+
## Donations
|
333
|
+
|
334
|
+
If you like this project and you would like to support my work, feel free to donate. :)
|
335
|
+
|
336
|
+
[](https://www.paypal.com/donate?hosted_button_id=35LH6JNLPHPHQ)
|
data/lib/VERSION.rb
CHANGED
data/lib/grafana/errors.rb
CHANGED
@@ -70,12 +70,4 @@ module Grafana
|
|
70
70
|
super("The datasource query provided, does not look like a grafana datasource target (received: #{query}).")
|
71
71
|
end
|
72
72
|
end
|
73
|
-
|
74
|
-
# Raised if a datasource implementation cannot handle a query, which is composed
|
75
|
-
# in the grafana visual editor.
|
76
|
-
class ComposedQueryNotSupportedError < GrafanaError
|
77
|
-
def initialize(class_obj)
|
78
|
-
super("Composed queries are not yet supported for datasource '#{class_obj}'.")
|
79
|
-
end
|
80
|
-
end
|
81
73
|
end
|
data/lib/grafana/grafana.rb
CHANGED
@@ -50,7 +50,7 @@ module Grafana
|
|
50
50
|
# @return [Datasource] Datasource for the specified datasource name
|
51
51
|
def datasource_by_name(datasource_name)
|
52
52
|
datasource_name = 'default' if datasource_name.to_s.empty?
|
53
|
-
# TODO: add support for grafana builtin datasource types
|
53
|
+
# TODO: PRIO add support for grafana builtin datasource types
|
54
54
|
return UnsupportedDatasource.new(nil) if datasource_name.to_s =~ /-- (?:Mixed|Dashboard|Grafana) --/
|
55
55
|
raise DatasourceDoesNotExistError.new('name', datasource_name) unless @datasources[datasource_name]
|
56
56
|
|
@@ -15,7 +15,21 @@ module Grafana
|
|
15
15
|
def request(query_description)
|
16
16
|
raise MissingSqlQueryError if query_description[:raw_query].nil?
|
17
17
|
|
18
|
-
|
18
|
+
# replace variables
|
19
|
+
query = replace_variables(query_description[:raw_query], query_description[:variables])
|
20
|
+
|
21
|
+
# Unfortunately the grafana internal variables are not replaced in the grafana backend, but in the
|
22
|
+
# frontend, i.e. we have to replace them here manually
|
23
|
+
# replace $timeFilter variable
|
24
|
+
query = query.gsub(/\$timeFilter(?=\W|$)/, "time >= #{query_description[:from]}ms and time <= #{query_description[:to]}ms")
|
25
|
+
|
26
|
+
# replace grafana variables $__interval and $__interval_ms in query
|
27
|
+
# TODO: influx datasource currently uses a fixed values of 1000 width for interval variables specified in a query - it should be possible to calculate this according to grafana
|
28
|
+
# TODO: check where calculation and replacement of interval variable should take place
|
29
|
+
query = query.gsub(/\$(?:__)?interval(?=\W|$)/, "#{((query_description[:to].to_i - query_description[:from].to_i) / 1000 / 1000).to_i}s")
|
30
|
+
query = query.gsub(/\$(?:__)?interval_ms(?=\W|$)/, "#{((query_description[:to].to_i - query_description[:from].to_i) / 1000).to_i}")
|
31
|
+
|
32
|
+
url = "/api/datasources/proxy/#{id}/query?db=#{@model['database']}&q=#{ERB::Util.url_encode(query)}&epoch=ms"
|
19
33
|
|
20
34
|
webrequest = query_description[:prepared_request]
|
21
35
|
webrequest.relative_url = url
|
@@ -29,8 +43,8 @@ module Grafana
|
|
29
43
|
def raw_query_from_panel_model(panel_query_target)
|
30
44
|
return panel_query_target['query'] if panel_query_target['rawQuery']
|
31
45
|
|
32
|
-
#
|
33
|
-
|
46
|
+
# build composed queries
|
47
|
+
build_select(panel_query_target['select']) + build_from(panel_query_target) + build_where(panel_query_target['tags']) + build_group_by(panel_query_target['groupBy'])
|
34
48
|
end
|
35
49
|
|
36
50
|
# @see AbstractDatasource#default_variable_format
|
@@ -40,10 +54,82 @@ module Grafana
|
|
40
54
|
|
41
55
|
private
|
42
56
|
|
57
|
+
def build_group_by(stmt)
|
58
|
+
groups = []
|
59
|
+
fill = ""
|
60
|
+
|
61
|
+
stmt.each do |group|
|
62
|
+
case group['type']
|
63
|
+
when 'tag'
|
64
|
+
groups << "\"#{group['params'].first}\""
|
65
|
+
|
66
|
+
when 'fill'
|
67
|
+
fill = " fill(#{group['params'].first})"
|
68
|
+
|
69
|
+
else
|
70
|
+
groups << "#{group['type']}(#{group['params'].join(', ')})"
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
" GROUP BY #{groups.join(', ')}#{fill}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_where(stmt)
|
79
|
+
custom_where = []
|
80
|
+
|
81
|
+
stmt.each do |where|
|
82
|
+
value = where['operator'] =~ /^[=!]~$/ ? where['value'] : "'#{where['value']}'"
|
83
|
+
custom_where << "\"#{where['key']}\" #{where['operator']} #{value}"
|
84
|
+
end
|
85
|
+
|
86
|
+
" WHERE #{"(#{custom_where.join(' AND ')}) AND " unless custom_where.empty?}$timeFilter"
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_from(stmt)
|
90
|
+
" FROM \"#{"stmt['policy']." unless stmt['policy'] == 'default'}#{stmt['measurement']}\""
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_select(stmt)
|
94
|
+
res = "SELECT"
|
95
|
+
parts = []
|
96
|
+
|
97
|
+
stmt.each do |value|
|
98
|
+
part = ""
|
99
|
+
|
100
|
+
value.each do |item|
|
101
|
+
case item['type']
|
102
|
+
when 'field'
|
103
|
+
# frame field parameter as string
|
104
|
+
part = "\"#{item['params'].first}\""
|
105
|
+
|
106
|
+
when 'alias'
|
107
|
+
# append AS with parameter as string
|
108
|
+
part = "#{part} AS \"#{item['params'].first}\""
|
109
|
+
|
110
|
+
|
111
|
+
when 'math'
|
112
|
+
# append parameter as raw value for calculation
|
113
|
+
part = "#{part} #{item['params'].first}"
|
114
|
+
|
115
|
+
|
116
|
+
else
|
117
|
+
# frame current part by brackets and call by item function including parameters
|
118
|
+
part = "#{item['type']}(#{part}#{", #{item['params'].join(', ')}" unless item['params'].empty?})"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
parts << part
|
123
|
+
end
|
124
|
+
|
125
|
+
"#{res} #{parts.join(', ')}"
|
126
|
+
end
|
127
|
+
|
43
128
|
# @see AbstractDatasource#preformat_response
|
44
129
|
def preformat_response(response_body)
|
45
130
|
# TODO: how to handle multiple query results?
|
46
131
|
json = JSON.parse(response_body)['results'].first['series']
|
132
|
+
return {} if json.nil?
|
47
133
|
|
48
134
|
header = ['time']
|
49
135
|
content = {}
|
@@ -14,7 +14,11 @@ module Grafana
|
|
14
14
|
def request(query_description)
|
15
15
|
raise MissingSqlQueryError if query_description[:raw_query].nil?
|
16
16
|
|
17
|
-
|
17
|
+
# TODO: properly allow endpoint to be set - also check raw_query method
|
18
|
+
end_point = @endpoint ? @endpoint : "query_range"
|
19
|
+
|
20
|
+
# TODO: set query option 'step' on request
|
21
|
+
url = "/api/datasources/proxy/#{id}/api/v1/#{end_point}?"\
|
18
22
|
"start=#{query_description[:from]}&end=#{query_description[:to]}"\
|
19
23
|
"&query=#{replace_variables(query_description[:raw_query], query_description[:variables])}"
|
20
24
|
|
@@ -28,6 +32,7 @@ module Grafana
|
|
28
32
|
|
29
33
|
# @see AbstractDatasource#raw_query_from_panel_model
|
30
34
|
def raw_query_from_panel_model(panel_query_target)
|
35
|
+
@endpoint = panel_query_target['format'] == 'time_series' && (panel_query_target['instant'] == false || !panel_query_target['instant']) ? 'query_range' : 'query'
|
31
36
|
panel_query_target['expr']
|
32
37
|
end
|
33
38
|
|
@@ -47,7 +52,11 @@ module Grafana
|
|
47
52
|
|
48
53
|
# keep sorting, if json has only one target item, otherwise merge results and return
|
49
54
|
# as a time sorted array
|
50
|
-
|
55
|
+
# TODO properly set headlines
|
56
|
+
if json.length == 1
|
57
|
+
return { header: headers << json.first['metric'].to_s, content: [[json.first['value'][1], json.first['value'][0]]] } if json.first.has_key?('value') # this happens for the special case of calls to '/query' endpoint
|
58
|
+
return { header: headers << json.first['metric']['mode'], content: json.first['values'] }
|
59
|
+
end
|
51
60
|
|
52
61
|
# TODO: show warning if results may be sorted different
|
53
62
|
json.each_index do |i|
|
data/lib/grafana/variable.rb
CHANGED
@@ -33,6 +33,7 @@ module Grafana
|
|
33
33
|
if config_or_value.is_a? Hash
|
34
34
|
@config = config_or_value
|
35
35
|
@name = @config['name']
|
36
|
+
# TODO: if a variable uses type 'query' which is never updated, the selected values are stored in 'options'
|
36
37
|
unless @config['current'].nil?
|
37
38
|
@raw_value = @config['current']['value']
|
38
39
|
@text = @config['current']['text']
|
@@ -62,7 +63,8 @@ module Grafana
|
|
62
63
|
def value_formatted(format = '')
|
63
64
|
value = @raw_value
|
64
65
|
|
65
|
-
#
|
66
|
+
# if 'All' is selected for this template variable, capture all values properly
|
67
|
+
# (from grafana config or query) and format the results afterwards accordingly
|
66
68
|
if value == '$__all'
|
67
69
|
if !@config['options'].empty?
|
68
70
|
# this query contains predefined values, so capture them and format the values accordingly
|
@@ -147,7 +149,7 @@ module Grafana
|
|
147
149
|
value.gsub(%r{[" |=/\\]}, '\\\\\0')
|
148
150
|
|
149
151
|
when /^date(?::(?<format>.*))?$/
|
150
|
-
# TODO: grafana does not seem to allow
|
152
|
+
# TODO: grafana does not seem to allow multiselection of variables with date format - raise an error if this happens anyway
|
151
153
|
get_date_formatted(value, Regexp.last_match(1))
|
152
154
|
|
153
155
|
when ''
|
@@ -13,7 +13,7 @@ module GrafanaReporter
|
|
13
13
|
attr_reader :variables, :result, :panel, :dashboard
|
14
14
|
|
15
15
|
def timeout
|
16
|
-
# TODO: check where value priorities should be evaluated
|
16
|
+
# TODO: PRIO check where value priorities should be evaluated
|
17
17
|
return @variables['timeout'].raw_value if @variables['timeout']
|
18
18
|
return @variables['grafana_default_timeout'].raw_value if @variables['grafana_default_timeout']
|
19
19
|
|
@@ -307,13 +307,13 @@ module GrafanaReporter
|
|
307
307
|
|
308
308
|
if opts[:table_formatter].raw_value == 'adoc_deprecated'
|
309
309
|
@grafana.logger.warn("You are using deprecated 'table_formatter' named 'adoc_deprecated', which will be "\
|
310
|
-
"removed in a future version. Start using '
|
311
|
-
"of AbstractTableFormatStrategy.")
|
310
|
+
"removed in a future version. Start using 'adoc_plain' or register your own "\
|
311
|
+
"implementation of AbstractTableFormatStrategy.")
|
312
312
|
return result[:content].map do |row|
|
313
313
|
opts[:row_divider].raw_value + row.map do |item|
|
314
314
|
item.to_s.gsub('|', '\\|')
|
315
315
|
end.join(opts[:column_divider].raw_value)
|
316
|
-
end
|
316
|
+
end.join("\n")
|
317
317
|
end
|
318
318
|
|
319
319
|
AbstractTableFormatStrategy.get(opts[:table_formatter].raw_value).format(result, opts[:include_headline].raw_value.downcase == 'true')
|
@@ -349,7 +349,7 @@ module GrafanaReporter
|
|
349
349
|
raise TimeRangeUnknownError, orig_date unless date_spec
|
350
350
|
|
351
351
|
date = DateTime.parse(report_time.raw_value)
|
352
|
-
# TODO: allow from_translated or similar in ADOC template
|
352
|
+
# TODO: PRIO allow from_translated or similar in ADOC template
|
353
353
|
date = date.new_offset(timezone.raw_value) if timezone
|
354
354
|
|
355
355
|
until date_spec.empty?
|
@@ -71,7 +71,7 @@ module GrafanaReporter
|
|
71
71
|
query.raw_query = defaults.merge(selected_attrs.each_with_object({}) { |(k, v), h| h[k] = v })
|
72
72
|
|
73
73
|
begin
|
74
|
-
reader.unshift_lines query.execute
|
74
|
+
reader.unshift_lines query.execute.split("\n")
|
75
75
|
rescue GrafanaReporterError => e
|
76
76
|
@report.logger.error(e.message)
|
77
77
|
reader.unshift_line "|#{e.message}"
|
@@ -70,7 +70,7 @@ module GrafanaReporter
|
|
70
70
|
query.raw_query = defaults.merge(selected_attrs.each_with_object({}) { |(k, v), h| h[k] = v })
|
71
71
|
|
72
72
|
begin
|
73
|
-
reader.unshift_lines query.execute
|
73
|
+
reader.unshift_lines query.execute.split("\n")
|
74
74
|
rescue GrafanaReporterError => e
|
75
75
|
@report.logger.error(e.message)
|
76
76
|
reader.unshift_line "|#{e.message}"
|
@@ -51,7 +51,7 @@ module GrafanaReporter
|
|
51
51
|
|
52
52
|
image = query.execute
|
53
53
|
image_path = @report.save_image_file(image)
|
54
|
-
rescue GrafanaError => e
|
54
|
+
rescue Grafana::GrafanaError => e
|
55
55
|
@report.logger.error(e.message)
|
56
56
|
return create_paragraph(parent, e.message, attrs)
|
57
57
|
rescue GrafanaReporterError => e
|
@@ -53,7 +53,7 @@ module GrafanaReporter
|
|
53
53
|
|
54
54
|
image = query.execute
|
55
55
|
image_path = @report.save_image_file(image)
|
56
|
-
rescue GrafanaError => e
|
56
|
+
rescue Grafana::GrafanaError => e
|
57
57
|
@report.logger.error(e.message)
|
58
58
|
return create_inline(parent, :quoted, e.message)
|
59
59
|
rescue GrafanaReporterError => e
|
@@ -43,7 +43,7 @@ module GrafanaReporter
|
|
43
43
|
query.raw_query = { property_name: attrs[:field] }
|
44
44
|
|
45
45
|
description = query.execute
|
46
|
-
rescue GrafanaError => e
|
46
|
+
rescue Grafana::GrafanaError => e
|
47
47
|
@report.logger.error(e.message)
|
48
48
|
return create_inline(parent, :quoted, e.message)
|
49
49
|
rescue GrafanaReporterError => e
|
@@ -62,8 +62,8 @@ module GrafanaReporter
|
|
62
62
|
vars = { 'table_formatter' => 'adoc_plain' }.merge(build_attribute_hash(doc.attributes, attrs))
|
63
63
|
query = QueryValueQuery.new(panel, variables: vars)
|
64
64
|
|
65
|
-
reader.unshift_lines query.execute
|
66
|
-
rescue GrafanaError => e
|
65
|
+
reader.unshift_lines query.execute.split("\n")
|
66
|
+
rescue Grafana::GrafanaError => e
|
67
67
|
@report.logger.error(e.message)
|
68
68
|
reader.unshift_line "|#{e.message}"
|
69
69
|
rescue GrafanaReporterError => e
|
@@ -59,7 +59,7 @@ module GrafanaReporter
|
|
59
59
|
query = QueryValueQuery.new(panel, variables: build_attribute_hash(parent.document.attributes, attrs))
|
60
60
|
|
61
61
|
create_inline(parent, :quoted, query.execute)
|
62
|
-
rescue GrafanaError => e
|
62
|
+
rescue Grafana::GrafanaError => e
|
63
63
|
@report.logger.error(e.message)
|
64
64
|
create_inline(parent, :quoted, e.message)
|
65
65
|
rescue GrafanaReporterError => e
|
@@ -56,8 +56,8 @@ module GrafanaReporter
|
|
56
56
|
query.datasource = @report.grafana(instance).datasource_by_id(target.split(':')[1].to_i)
|
57
57
|
query.raw_query = attrs['sql']
|
58
58
|
|
59
|
-
reader.unshift_lines query.execute
|
60
|
-
rescue GrafanaError => e
|
59
|
+
reader.unshift_lines query.execute.split("\n")
|
60
|
+
rescue Grafana::GrafanaError => e
|
61
61
|
@report.logger.error(e.message)
|
62
62
|
reader.unshift_line "|#{e.message}"
|
63
63
|
rescue GrafanaReporterError => e
|
@@ -55,7 +55,7 @@ module GrafanaReporter
|
|
55
55
|
query.raw_query = attrs['sql']
|
56
56
|
|
57
57
|
create_inline(parent, :quoted, query.execute)
|
58
|
-
rescue GrafanaError => e
|
58
|
+
rescue Grafana::GrafanaError => e
|
59
59
|
@report.logger.error(e.message)
|
60
60
|
create_inline(parent, :quoted, e.message)
|
61
61
|
rescue GrafanaReporterError => e
|
@@ -5,6 +5,7 @@ module GrafanaReporter
|
|
5
5
|
class PanelImageQuery < AbstractQuery
|
6
6
|
# Sets the proper render variables.
|
7
7
|
def pre_process
|
8
|
+
# TODO: properly show error, if a (maybe a repeated template) panel can not be rendered
|
8
9
|
# TODO: ensure that in case of timezones are specified, that they are also forwarded to the image renderer
|
9
10
|
# rename "render-" variables
|
10
11
|
@variables = @variables.each_with_object({}) { |(k, v), h| h[k.gsub(/^render-/, '')] = v }
|
@@ -18,6 +18,17 @@ require 'asciidoctor-pdf'
|
|
18
18
|
require 'zip'
|
19
19
|
require_relative 'VERSION'
|
20
20
|
|
21
|
+
# TODO: add test to see if datasource default formats are applied
|
22
|
+
# TODO: add test for render-height and render-width, as they are not forwarded
|
23
|
+
# TODO: show convert-backend, template language and all variables in webservice overview
|
24
|
+
# TODO: add automated test against grafana playground before building a new release
|
25
|
+
# TODO: allow registration of files to be defined in config file
|
26
|
+
# TODO: PRIO: check in cloud - variables fetched from queries are replaced with SQL query instead of the resolved values, which can lead to issues, e.g. when using that in a function as SUBSTRING
|
27
|
+
# TODO: PRIO: allow multiple report classes in configuration file including possibility to decide for the individual class for each rendering call
|
28
|
+
# TODO: add configuration example to README
|
29
|
+
# TODO: find a way, how to automatically update test grafana responses with _real_ grafana responses
|
30
|
+
# TODO: append necessary variables on demo report creation for plain SQL queries, as they are lacking the grafana reference
|
31
|
+
|
21
32
|
folders = [
|
22
33
|
%w[grafana],
|
23
34
|
%w[grafana_reporter logger],
|
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.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Kohlmeyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: asciidoctor
|