ruby-grafana-reporter 0.4.4 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![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
|
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
|
-
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=35LH6JNLPHPHQ)
|
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 | 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
|
+
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](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
|