ruby-grafana-reporter 0.4.5 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/VERSION.rb +2 -2
- data/lib/grafana/abstract_datasource.rb +8 -0
- data/lib/grafana/dashboard.rb +6 -1
- data/lib/grafana/errors.rb +9 -1
- data/lib/grafana/grafana.rb +35 -0
- data/lib/grafana/grafana_environment_datasource.rb +56 -0
- data/lib/grafana/graphite_datasource.rb +3 -0
- data/lib/grafana/image_rendering_datasource.rb +5 -1
- data/lib/grafana/influxdb_datasource.rb +11 -4
- data/lib/grafana/panel.rb +5 -1
- data/lib/grafana/prometheus_datasource.rb +71 -11
- data/lib/grafana/sql_datasource.rb +10 -4
- data/lib/grafana/variable.rb +46 -23
- data/lib/grafana/webrequest.rb +1 -0
- data/lib/grafana_reporter/abstract_query.rb +31 -24
- data/lib/grafana_reporter/abstract_report.rb +2 -0
- data/lib/grafana_reporter/abstract_table_format_strategy.rb +44 -4
- data/lib/grafana_reporter/alerts_table_query.rb +2 -1
- data/lib/grafana_reporter/annotations_table_query.rb +2 -1
- data/lib/grafana_reporter/application/webservice.rb +8 -4
- data/lib/grafana_reporter/asciidoctor/adoc_plain_table_format_strategy.rb +11 -9
- data/lib/grafana_reporter/asciidoctor/help.rb +53 -14
- data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +2 -4
- data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +2 -4
- data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +1 -1
- data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +37 -6
- data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +11 -2
- data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +0 -5
- data/lib/grafana_reporter/configuration.rb +53 -22
- data/lib/grafana_reporter/console_configuration_wizard.rb +3 -1
- data/lib/grafana_reporter/csv_table_format_strategy.rb +11 -9
- data/lib/grafana_reporter/demo_report_wizard.rb +3 -6
- data/lib/grafana_reporter/errors.rb +2 -2
- data/lib/grafana_reporter/panel_image_query.rb +0 -1
- data/lib/grafana_reporter/query_value_query.rb +7 -1
- data/lib/grafana_reporter/reporter_environment_datasource.rb +24 -0
- data/lib/ruby_grafana_reporter.rb +7 -7
- metadata +8 -7
@@ -13,7 +13,6 @@ module GrafanaReporter
|
|
13
13
|
attr_reader :variables, :result, :panel, :dashboard
|
14
14
|
|
15
15
|
def timeout
|
16
|
-
# TODO: PRIO check where value priorities should be evaluated
|
17
16
|
return @variables['timeout'].raw_value if @variables['timeout']
|
18
17
|
return @variables['grafana_default_timeout'].raw_value if @variables['grafana_default_timeout']
|
19
18
|
|
@@ -44,6 +43,7 @@ module GrafanaReporter
|
|
44
43
|
else
|
45
44
|
raise GrafanaReporterError, "Internal error in AbstractQuery: given object is of type #{grafana_obj.class.name}, which is not supported"
|
46
45
|
end
|
46
|
+
@logger = @grafana ? @grafana.logger : ::Logger.new($stderr, level: :info)
|
47
47
|
@variables = {}
|
48
48
|
@variables['from'] = Grafana::Variable.new(nil)
|
49
49
|
@variables['to'] = Grafana::Variable.new(nil)
|
@@ -79,7 +79,7 @@ module GrafanaReporter
|
|
79
79
|
raise DatasourceNotSupportedError.new(@datasource, self) if @datasource.is_a?(Grafana::UnsupportedDatasource)
|
80
80
|
|
81
81
|
begin
|
82
|
-
@result = @datasource.request(from: from, to: to, raw_query: raw_query, variables:
|
82
|
+
@result = @datasource.request(from: from, to: to, raw_query: raw_query, variables: @variables,
|
83
83
|
prepared_request: @grafana.prepare_request, timeout: timeout)
|
84
84
|
rescue ::Grafana::GrafanaError
|
85
85
|
# grafana errors will be directly passed through
|
@@ -143,6 +143,8 @@ module GrafanaReporter
|
|
143
143
|
#
|
144
144
|
# Multiple columns may be filtered. Therefore the column titles have to be named in the
|
145
145
|
# {Grafana::Variable#raw_value} and have to be separated by +,+ (comma).
|
146
|
+
#
|
147
|
+
# Commas can be used in a format string, but need to be escaped by using +_,+.
|
146
148
|
# @param result [Hash] preformatted sql hash, (see {Grafana::AbstractDatasource#request})
|
147
149
|
# @param filter_columns_variable [Grafana::Variable] column names, which shall be removed in the query result
|
148
150
|
# @return [Hash] filtered query result
|
@@ -150,8 +152,8 @@ module GrafanaReporter
|
|
150
152
|
return result unless filter_columns_variable
|
151
153
|
|
152
154
|
filter_columns = filter_columns_variable.raw_value
|
153
|
-
filter_columns.split(
|
154
|
-
pos = result[:header].index(filter_column)
|
155
|
+
filter_columns.split(/(?<!_),/).each do |filter_column|
|
156
|
+
pos = result[:header].index(filter_column.gsub("_,", ","))
|
155
157
|
|
156
158
|
unless pos.nil?
|
157
159
|
result[:header].delete_at(pos)
|
@@ -167,23 +169,33 @@ module GrafanaReporter
|
|
167
169
|
# The formatting will be applied separately for every column. Therefore the column formats have to be named
|
168
170
|
# in the {Grafana::Variable#raw_value} and have to be separated by +,+ (comma). If no value is specified for
|
169
171
|
# a column, no change will happen.
|
172
|
+
#
|
173
|
+
# It is also possible to format milliseconds as dates by specifying date formats, e.g. +date:iso+. It is
|
174
|
+
# possible to use any date format according
|
175
|
+
# {https://grafana.com/docs/grafana/latest/variables/variable-types/global-variables/#from-and-to}
|
176
|
+
#
|
177
|
+
# Commas can be used in a format string, but need to be escaped by using +_,+.
|
170
178
|
# @param result [Hash] preformatted sql hash, (see {Grafana::AbstractDatasource#request})
|
171
179
|
# @param formats [Grafana::Variable] formats, which shall be applied to the columns in the query result
|
172
180
|
# @return [Hash] formatted query result
|
173
181
|
def format_columns(result, formats)
|
174
182
|
return result unless formats
|
175
183
|
|
176
|
-
formats.text.split(
|
177
|
-
format = formats.text.split(
|
184
|
+
formats.text.split(/(?<!_),/).each_index do |i|
|
185
|
+
format = formats.text.split(/(?<!_),/)[i].gsub("_,", ",")
|
178
186
|
next if format.empty?
|
179
187
|
|
180
188
|
result[:content].map do |row|
|
181
189
|
next unless row.length > i
|
182
190
|
|
183
191
|
begin
|
184
|
-
|
192
|
+
if format =~ /^date:/
|
193
|
+
row[i] = ::Grafana::Variable.format_as_date(row[i], format.sub(/^date:/, '')) if row[i]
|
194
|
+
else
|
195
|
+
row[i] = format % row[i] if row[i]
|
196
|
+
end
|
185
197
|
rescue StandardError => e
|
186
|
-
@
|
198
|
+
@logger.error(e.message)
|
187
199
|
row[i] = e.message
|
188
200
|
end
|
189
201
|
end
|
@@ -248,7 +260,7 @@ module GrafanaReporter
|
|
248
260
|
begin
|
249
261
|
row[i] = row[i].to_s.gsub(/#{k}/, v) if row[i].to_s =~ /#{k}/
|
250
262
|
rescue StandardError => e
|
251
|
-
@
|
263
|
+
@logger.error(e.message)
|
252
264
|
row[i] = e.message
|
253
265
|
end
|
254
266
|
|
@@ -273,7 +285,7 @@ module GrafanaReporter
|
|
273
285
|
end
|
274
286
|
end
|
275
287
|
rescue StandardError => e
|
276
|
-
@
|
288
|
+
@logger.error(e.message)
|
277
289
|
row[i] = e.message
|
278
290
|
end
|
279
291
|
end
|
@@ -298,17 +310,19 @@ module GrafanaReporter
|
|
298
310
|
# @option opts [Grafana::Variable] :column_divider requested row divider for the result table, only to be used with table_formatter `adoc_deprecated`
|
299
311
|
# @option opts [Grafana::Variable] :include_headline specifies if table should contain headline, defaults to false
|
300
312
|
# @option opts [Grafana::Variable] :table_formatter specifies which formatter shall be used, defaults to 'csv'
|
313
|
+
# @option opts [Grafana::Variable] :transposed specifies whether the result table is transposed
|
301
314
|
# @return [String] table in custom output format
|
302
315
|
def format_table_output(result, opts)
|
303
316
|
opts = { include_headline: Grafana::Variable.new('false'),
|
304
317
|
table_formatter: Grafana::Variable.new('csv'),
|
305
318
|
row_divider: Grafana::Variable.new('| '),
|
306
|
-
column_divider: Grafana::Variable.new(' | ')
|
319
|
+
column_divider: Grafana::Variable.new(' | '),
|
320
|
+
transpose: Grafana::Variable.new('false') }.merge(opts.delete_if {|_k, v| v.nil? })
|
307
321
|
|
308
322
|
if opts[:table_formatter].raw_value == 'adoc_deprecated'
|
309
|
-
@
|
310
|
-
|
311
|
-
|
323
|
+
@logger.warn("You are using deprecated 'table_formatter' named 'adoc_deprecated', which will be "\
|
324
|
+
"removed in a future version. Start using 'adoc_plain' or register your own "\
|
325
|
+
"implementation of AbstractTableFormatStrategy.")
|
312
326
|
return result[:content].map do |row|
|
313
327
|
opts[:row_divider].raw_value + row.map do |item|
|
314
328
|
item.to_s.gsub('|', '\\|')
|
@@ -316,7 +330,7 @@ module GrafanaReporter
|
|
316
330
|
end.join("\n")
|
317
331
|
end
|
318
332
|
|
319
|
-
AbstractTableFormatStrategy.get(opts[:table_formatter].raw_value).format(result, opts[:include_headline].raw_value.downcase == 'true')
|
333
|
+
AbstractTableFormatStrategy.get(opts[:table_formatter].raw_value).format(result, opts[:include_headline].raw_value.downcase == 'true', opts[:transpose].raw_value.downcase == 'true')
|
320
334
|
end
|
321
335
|
|
322
336
|
# Used to translate the relative date strings used by grafana, e.g. +now-5d/w+ to the
|
@@ -334,7 +348,7 @@ module GrafanaReporter
|
|
334
348
|
# @param timezone [Grafana::Variable] timezone to use, if not system timezone
|
335
349
|
# @return [String] translated date as timestamp string
|
336
350
|
def translate_date(orig_date, report_time, is_to_time, timezone = nil)
|
337
|
-
@
|
351
|
+
@logger.warn("#translate_date has been called without 'report_time' - using current time as fallback.") unless report_time
|
338
352
|
report_time ||= ::Grafana::Variable.new(Time.now.to_s)
|
339
353
|
orig_date = orig_date.raw_value if orig_date.is_a?(Grafana::Variable)
|
340
354
|
|
@@ -405,8 +419,7 @@ module GrafanaReporter
|
|
405
419
|
def datasource_response_valid?
|
406
420
|
return false if @result.nil?
|
407
421
|
return false unless @result.is_a?(Hash)
|
408
|
-
|
409
|
-
return true if @result.empty?
|
422
|
+
return false if @result.empty?
|
410
423
|
return false unless @result.key?(:header)
|
411
424
|
return false unless @result.key?(:content)
|
412
425
|
return false unless @result[:header].is_a?(Array)
|
@@ -415,12 +428,6 @@ module GrafanaReporter
|
|
415
428
|
true
|
416
429
|
end
|
417
430
|
|
418
|
-
# @return [Hash<String, Variable>] all grafana variables stored in this query, i.e. the variable name
|
419
|
-
# is prefixed with +var-+
|
420
|
-
def grafana_variables
|
421
|
-
@variables.select { |k, _v| k =~ /^var-.+/ }
|
422
|
-
end
|
423
|
-
|
424
431
|
def delta_date(date, delta_count, time_letter)
|
425
432
|
# substract specified time
|
426
433
|
case time_letter
|
@@ -143,6 +143,8 @@ module GrafanaReporter
|
|
143
143
|
notify(:on_before_create)
|
144
144
|
@start_time = Time.new
|
145
145
|
logger.info("Report started at #{@start_time}")
|
146
|
+
logger.info("You are running ruby-grafana-reporter version #{GRAFANA_REPORTER_VERSION.join('.')}.")
|
147
|
+
logger.info("A newer version is released. Check out https://github.com/divinity666/ruby-grafana-reporter/releases/latest") unless @config.latest_version_check_ok?
|
146
148
|
build
|
147
149
|
rescue MissingTemplateError => e
|
148
150
|
@logger.error(e.message)
|
@@ -23,12 +23,52 @@ module GrafanaReporter
|
|
23
23
|
raise NotImplementedError
|
24
24
|
end
|
25
25
|
|
26
|
-
#
|
27
|
-
#
|
26
|
+
# Used to format a given content array to the desired output format. The default
|
27
|
+
# implementation applies the {#format_rules} to create a custom string export. If
|
28
|
+
# this is not sufficient for a desired table format, you may simply overwrite this
|
29
|
+
# function to have full freedom about the desired output.
|
30
|
+
# @param content [Hash] datasource table result
|
28
31
|
# @param include_headline [Boolean] true, if headline should be included in result
|
32
|
+
# @param transposed [Boolean] true, if result array is in transposed format
|
29
33
|
# @return [String] formatted in table format
|
30
|
-
def format(content, include_headline)
|
31
|
-
|
34
|
+
def format(content, include_headline, transposed)
|
35
|
+
result = content[:content]
|
36
|
+
|
37
|
+
# add the headline at the correct position to the content array
|
38
|
+
if include_headline
|
39
|
+
if transposed
|
40
|
+
result.each_index do |i|
|
41
|
+
result[i] = [content[:header][i]] + result[i]
|
42
|
+
end
|
43
|
+
else
|
44
|
+
result = result.unshift(content[:header])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# translate the content to a table
|
49
|
+
result.map do |row|
|
50
|
+
format_rules[:row_start] + row.map do |item|
|
51
|
+
value = item.to_s
|
52
|
+
if format_rules[:replace_string_or_regex]
|
53
|
+
value = value.gsub(format_rules[:replace_string_or_regex], format_rules[:replacement])
|
54
|
+
end
|
55
|
+
|
56
|
+
format_rules[:cell_start] + value + format_rules[:cell_end]
|
57
|
+
end.join(format_rules[:between_cells])
|
58
|
+
end.join(format_rules[:row_end])
|
59
|
+
end
|
60
|
+
|
61
|
+
# Formatting rules, which are applied to build the table output format.
|
62
|
+
def format_rules
|
63
|
+
{
|
64
|
+
row_start: '',
|
65
|
+
row_end: '',
|
66
|
+
cell_start: '',
|
67
|
+
between_cells: '',
|
68
|
+
cell_end: '',
|
69
|
+
replace_string_or_regex: nil,
|
70
|
+
replacement: ''
|
71
|
+
}
|
32
72
|
end
|
33
73
|
end
|
34
74
|
end
|
@@ -37,7 +37,8 @@ module GrafanaReporter
|
|
37
37
|
row_divider: @variables['row_divider'],
|
38
38
|
column_divider: @variables['column_divider'],
|
39
39
|
table_formatter: @variables['table_formatter'],
|
40
|
-
include_headline: @variables['include_headline']
|
40
|
+
include_headline: @variables['include_headline'],
|
41
|
+
transpose: @variables['transpose'])
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
@@ -36,7 +36,8 @@ module GrafanaReporter
|
|
36
36
|
row_divider: @variables['row_divider'],
|
37
37
|
column_divider: @variables['column_divider'],
|
38
38
|
table_formatter: @variables['table_formatter'],
|
39
|
-
include_headline: @variables['include_headline']
|
39
|
+
include_headline: @variables['include_headline'],
|
40
|
+
transpose: @variables['transpose'])
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
@@ -26,7 +26,13 @@ module GrafanaReporter
|
|
26
26
|
@progress_reporter = Thread.new {}
|
27
27
|
|
28
28
|
@status = :running
|
29
|
-
|
29
|
+
begin
|
30
|
+
accept_requests_loop
|
31
|
+
rescue SystemExit, Interrupt
|
32
|
+
@logger.info("Server shutting down.")
|
33
|
+
stop!
|
34
|
+
retry
|
35
|
+
end
|
30
36
|
@status = :stopped
|
31
37
|
end
|
32
38
|
|
@@ -57,8 +63,6 @@ module GrafanaReporter
|
|
57
63
|
# step 1) accept incoming connection
|
58
64
|
socket = @server.accept
|
59
65
|
|
60
|
-
# TODO: shutdown properly on SIGINT/SIGHUB
|
61
|
-
|
62
66
|
# stop webservice properly, if shall be shutdown
|
63
67
|
if @status == :stopping
|
64
68
|
socket.close
|
@@ -252,7 +256,7 @@ module GrafanaReporter
|
|
252
256
|
<% end.join('') %>
|
253
257
|
<tbody>
|
254
258
|
</table>
|
255
|
-
<p style="font-size: small; color:grey">You are running ruby-grafana-reporter version <%= GRAFANA_REPORTER_VERSION.join('.')
|
259
|
+
<p style="font-size: small; color:grey">You are running ruby-grafana-reporter version <%= GRAFANA_REPORTER_VERSION.join('.') %>.<%= @config.latest_version_check_ok? ? '' : ' Check out the latest version <a href="https://github.com/divinity666/ruby-grafana-reporter/releases/latest">here</a>.' %></p>
|
256
260
|
</body>
|
257
261
|
</html>
|
258
262
|
HTML_TEMPLATE
|
@@ -10,15 +10,17 @@ module GrafanaReporter
|
|
10
10
|
'adoc_plain'
|
11
11
|
end
|
12
12
|
|
13
|
-
# @see AbstractTableFormatStrategy#
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
# @see AbstractTableFormatStrategy#format_rules
|
14
|
+
def format_rules
|
15
|
+
{
|
16
|
+
row_start: '| ',
|
17
|
+
row_end: "\n",
|
18
|
+
cell_start: '',
|
19
|
+
between_cells: ' | ',
|
20
|
+
cell_end: '',
|
21
|
+
replace_string_or_regex: '|',
|
22
|
+
replacement: '\\|'
|
23
|
+
}
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -22,12 +22,13 @@ module GrafanaReporter
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def github_options
|
25
|
-
{ headline_separator: '#', code_begin: '`', code_end: '`', table_begin: "\n", head_postfix_col: '| -- '
|
25
|
+
{ headline_separator: '#', code_begin: '`', code_end: '`', table_begin: "\n", head_postfix_col: '| -- ',
|
26
|
+
table_linebreak: "<br />"}
|
26
27
|
end
|
27
28
|
|
28
29
|
def asciidoctor_options
|
29
30
|
{ headline_separator: '=', code_begin: '`+', code_end: '+`', table_begin: "\n[%autowidth.stretch, "\
|
30
|
-
"options=\"header\"]\n|===\n", table_end: "\n|===" }
|
31
|
+
"options=\"header\"]\n|===\n", table_end: "\n|===", table_linebreak: "\n\n" }
|
31
32
|
end
|
32
33
|
|
33
34
|
def help_text(opts)
|
@@ -82,7 +83,7 @@ Usage: #{opts[:code_begin]}#{v[:call]}#{opts[:code_end]}
|
|
82
83
|
#{v[:description]}#{"\n\nSee also: #{v[:see]}" if v[:see]}#{unless v[:options].empty?
|
83
84
|
%(
|
84
85
|
#{opts[:table_begin]}| Option | Description#{"\n#{opts[:head_postfix_col] * 2}" if opts[:head_postfix_col]}
|
85
|
-
#{v[:options].sort.map { |_opt_k, opt_v| "| #{opts[:code_begin]}#{opt_v[:call]}#{opts[:code_end]} | #{opt_v[:description].gsub('|', '\|')}" }.join("\n") }#{opts[:table_end]})
|
86
|
+
#{v[:options].sort.map { |_opt_k, opt_v| "| #{opts[:code_begin]}#{opt_v[:call]}#{opts[:code_end]} | #{opt_v[:description].gsub('|', '\|')}#{"#{opts[:table_linebreak]}See also: #{opt_v[:see]}" if opt_v[:see]}" }.join("\n") }#{opts[:table_end]})
|
86
87
|
end}
|
87
88
|
)
|
88
89
|
end
|
@@ -109,12 +110,12 @@ end}
|
|
109
110
|
res_item[:description] = item['description']
|
110
111
|
res_item[:see] = item['see']
|
111
112
|
|
112
|
-
opts = ((item['options'] ? item['options'].keys : [])
|
113
|
+
opts = ((item['options'] ? item['options'].keys : []) +
|
113
114
|
(item['standard_options'] ? item['standard_options'].keys : [])).sort
|
114
115
|
opts.each do |opt_key|
|
115
116
|
res_item[:options][opt_key] = {}
|
116
117
|
|
117
|
-
if
|
118
|
+
if std_opts.key?(opt_key)
|
118
119
|
res_item[:options][opt_key][:call] = std_opts[opt_key]['call']
|
119
120
|
res_item[:options][opt_key][:description] = "#{std_opts[opt_key]['description']} "\
|
120
121
|
"#{item['standard_options'][opt_key]}".chop
|
@@ -196,8 +197,12 @@ end}
|
|
196
197
|
format:
|
197
198
|
call: format="<format_col1>,<format_col2>,..."
|
198
199
|
description: >-
|
199
|
-
Specify format in which the results shall be returned, e.g. `%.2f` for only
|
200
|
-
float. Several
|
200
|
+
Specify format in which the results in a specific column shall be returned, e.g. `%.2f` for only
|
201
|
+
two digit decimals of a float. Several column formats are separated by `,`, i.e. `%.2f,%.3f` would
|
202
|
+
apply `%.2f` to the first column and `%.3f` to the second column. All other columns would not be
|
203
|
+
formatted. You may also format time in milliseconds to a time format by specifying e.g. `date:iso`.
|
204
|
+
Commas in format strings are supported, but have to be escaped by useing `_,`.
|
205
|
+
Execution of related functions is applied in the following order `format`,
|
201
206
|
`replace_values`, `filter_columns`, `transpose`.
|
202
207
|
see: 'https://ruby-doc.org/core/Kernel.html#method-i-sprintf'
|
203
208
|
|
@@ -207,21 +212,24 @@ end}
|
|
207
212
|
Specify result values which shall be replaced, e.g. `2:OK` will replace query values `2` with value `OK`.
|
208
213
|
Replacing several values is possible by separating by `,`. Matches with regular expressions are also
|
209
214
|
supported, but must be full matches, i.e. have to start with `^` and end with `$`, e.g. `^[012]$:OK`.
|
210
|
-
Number replacements can also be performed, e.g. `<8.2` or `<>3`. Execution
|
215
|
+
Number replacements can also be performed, e.g. `<8.2` or `<>3`. Execution of related functions is
|
216
|
+
applied in the following order `format`,
|
211
217
|
`replace_values`, `filter_columns`, `transpose`.
|
212
218
|
see: https://ruby-doc.org/core/Regexp.html#class-Regexp-label-Character+Classes
|
213
219
|
|
214
220
|
filter_columns:
|
215
221
|
call: filter_columns="<column_name_1>,<column_name_2>,..."
|
216
222
|
description: >-
|
217
|
-
Removes specified columns from result.
|
218
|
-
`
|
223
|
+
Removes specified columns from result. Commas in format strings are supported, but have to be
|
224
|
+
escaped by useing `_,`. Execution of related functions is applied in the following order
|
225
|
+
`format`, `replace_values`, `filter_columns`, `transpose`.
|
219
226
|
|
220
227
|
transpose:
|
221
228
|
call: transpose="true"
|
222
229
|
description: >-
|
223
|
-
Transposes the query result, i.e. columns become rows and rows become columnns. Execution
|
224
|
-
following order `format`, `replace_values`, `filter_columns`,
|
230
|
+
Transposes the query result, i.e. columns become rows and rows become columnns. Execution of related
|
231
|
+
functions is applied in the following order `format`, `replace_values`, `filter_columns`,
|
232
|
+
`transpose`.
|
225
233
|
|
226
234
|
column_divider:
|
227
235
|
call: column_divider="<divider>"
|
@@ -234,7 +242,7 @@ end}
|
|
234
242
|
call: row_divider="<divider>"
|
235
243
|
description: >-
|
236
244
|
Replace the default row divider with another one, when used in conjunction with `table_formatter` set to
|
237
|
-
`adoc_deprecated`.
|
245
|
+
`adoc_deprecated`. Defaults to `| ` for being interpreted as a asciidoctor row. DEPRECATED: switch to
|
238
246
|
`table_formatter` named `adoc_plain`, or implement a custom table formatter.
|
239
247
|
|
240
248
|
table_formatter:
|
@@ -249,6 +257,20 @@ end}
|
|
249
257
|
Set a timeout for the current query. If not overridden with `grafana_default_timeout` in the report template,
|
250
258
|
this defaults to 60 seconds.
|
251
259
|
|
260
|
+
interval:
|
261
|
+
call: interval="<intervaL>"
|
262
|
+
description: >-
|
263
|
+
Used to set the interval size for timescale datasources, whereas the value is used without further
|
264
|
+
conversion directly in the datasource specific interval parameter.
|
265
|
+
Prometheus default: 15 (passed as `step` parameter)
|
266
|
+
Influx default: similar to grafana default, i.e. `(to_time - from_time) / 1000`
|
267
|
+
(replaces `interval_ms` and `interval` variables in query)
|
268
|
+
|
269
|
+
instant:
|
270
|
+
call: instant="true"
|
271
|
+
description: >-
|
272
|
+
Optional parameter for Prometheus `instant` queries. Ignored for other datasources than Prometheus.
|
273
|
+
|
252
274
|
# ----------------------------------
|
253
275
|
# FUNCTION DOCUMENTATION STARTS HERE
|
254
276
|
# ----------------------------------
|
@@ -258,8 +280,13 @@ end}
|
|
258
280
|
call: 'include::grafana_help[]'
|
259
281
|
|
260
282
|
grafana_environment:
|
261
|
-
description:
|
283
|
+
description: >-
|
284
|
+
Shows all available variables in the rendering context which can be used in the asciidoctor template.
|
285
|
+
If optional `instance` is specified, additional information about the configured grafana instance will be provided.
|
286
|
+
This is especially helpful for debugging.
|
262
287
|
call: 'include::grafana_environment[]'
|
288
|
+
standard_options:
|
289
|
+
instance:
|
263
290
|
|
264
291
|
grafana_alerts:
|
265
292
|
description: >-
|
@@ -391,6 +418,8 @@ end}
|
|
391
418
|
transpose:
|
392
419
|
from_timezone:
|
393
420
|
to_timezone:
|
421
|
+
instant:
|
422
|
+
interval:
|
394
423
|
|
395
424
|
grafana_panel_query_value:
|
396
425
|
call: 'grafana_panel_query_value:<panel_id>[query="<query_letter>",options]'
|
@@ -413,6 +442,8 @@ end}
|
|
413
442
|
to:
|
414
443
|
from_timezone:
|
415
444
|
to_timezone:
|
445
|
+
instant:
|
446
|
+
interval:
|
416
447
|
|
417
448
|
grafana_sql_table:
|
418
449
|
call: 'include::grafana_sql_table:<datasource_id>[sql="<sql_query>",options]'
|
@@ -434,12 +465,18 @@ end}
|
|
434
465
|
transpose:
|
435
466
|
from_timezone:
|
436
467
|
to_timezone:
|
468
|
+
instant:
|
469
|
+
interval:
|
437
470
|
|
438
471
|
grafana_sql_value:
|
439
472
|
call: 'grafana_sql_value:<datasource_id>[sql="<sql_query>",options]'
|
440
473
|
description: >-
|
441
474
|
Returns the value in the first column and the first row of the given query.
|
442
475
|
Grafana variables will be replaced in the SQL statement.
|
476
|
+
|
477
|
+
Please note that asciidoctor might fail, if you use square brackets in your
|
478
|
+
sql statement. To overcome this issue, you'll need to escape the closing
|
479
|
+
square brackets, i.e. +]+ needs to be replaced with +\\]+.
|
443
480
|
see: https://grafana.com/docs/grafana/latest/variables/syntax/
|
444
481
|
standard_options:
|
445
482
|
filter_columns:
|
@@ -451,6 +488,8 @@ end}
|
|
451
488
|
to:
|
452
489
|
from_timezone:
|
453
490
|
to_timezone:
|
491
|
+
instant:
|
492
|
+
interval:
|
454
493
|
YAML_HELP
|
455
494
|
end
|
456
495
|
end
|
@@ -7,8 +7,8 @@ module GrafanaReporter
|
|
7
7
|
# Implements the hook
|
8
8
|
# grafana_panel_image::<panel_id>[<options>]
|
9
9
|
#
|
10
|
-
# Stores the queried panel as a temporary image file and returns
|
11
|
-
# to be included in the report.
|
10
|
+
# Stores the queried panel as a temporary image file and returns a relative asciidoctor link
|
11
|
+
# to the storage location, which can then be included in the report.
|
12
12
|
#
|
13
13
|
# == Used document parameters
|
14
14
|
# +grafana_default_instance+ - name of grafana instance, 'default' if not specified
|
@@ -20,8 +20,6 @@ module GrafanaReporter
|
|
20
20
|
# +to+ - 'to' time for the sql query
|
21
21
|
#
|
22
22
|
# == Supported options
|
23
|
-
# +field+ - property to query for, e.g. +description+ or +title+ (*mandatory*)
|
24
|
-
#
|
25
23
|
# +instance+ - name of grafana instance, 'default' if not specified
|
26
24
|
#
|
27
25
|
# +dashboard+ - uid of grafana dashboard to use
|
@@ -7,8 +7,8 @@ module GrafanaReporter
|
|
7
7
|
# Implements the hook
|
8
8
|
# grafana_panel_image:<panel_id>[<options>]
|
9
9
|
#
|
10
|
-
# Stores the queried panel as a temporary image file and returns
|
11
|
-
# to be included in the report.
|
10
|
+
# Stores the queried panel as a temporary image file and returns a relative asciidoctor link
|
11
|
+
# to the storage location, which can then be included in the report.
|
12
12
|
#
|
13
13
|
# == Used document parameters
|
14
14
|
# +grafana_default_instance+ - name of grafana instance, 'default' if not specified
|
@@ -20,8 +20,6 @@ module GrafanaReporter
|
|
20
20
|
# +to+ - 'to' time for the sql query
|
21
21
|
#
|
22
22
|
# == Supported options
|
23
|
-
# +field+ - property to query for, e.g. +description+ or +title+ (*mandatory*)
|
24
|
-
#
|
25
23
|
# +instance+ - name of grafana instance, 'default' if not specified
|
26
24
|
#
|
27
25
|
# +dashboard+ - uid of grafana dashboard to use
|
@@ -40,7 +40,7 @@ module GrafanaReporter
|
|
40
40
|
k =~ /^(?:timeout|from|to)$/ ||
|
41
41
|
k =~ /filter_columns|format|replace_values_.*|transpose|from_timezone|
|
42
42
|
to_timezone|result_type|query|table_formatter|include_headline|
|
43
|
-
column_divider|row_divider/x
|
43
|
+
column_divider|row_divider|instant|interval/x
|
44
44
|
end)
|
45
45
|
|
46
46
|
result
|
@@ -13,6 +13,9 @@ module GrafanaReporter
|
|
13
13
|
#
|
14
14
|
# == Used document parameters
|
15
15
|
# All, to be listed as the available environment.
|
16
|
+
#
|
17
|
+
# == Supported options
|
18
|
+
# +instance+ - grafana instance name, if extended information about the grafana instance shall be printed
|
16
19
|
class ShowEnvironmentIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
17
20
|
include ProcessorMixin
|
18
21
|
|
@@ -22,19 +25,47 @@ module GrafanaReporter
|
|
22
25
|
end
|
23
26
|
|
24
27
|
# :nodoc:
|
25
|
-
def process(doc, reader, _target,
|
28
|
+
def process(doc, reader, _target, attrs)
|
26
29
|
# return if @report.cancel
|
27
30
|
@report.next_step
|
31
|
+
instance = attrs['instance'] || doc.attr('grafana_default_instance') || 'default'
|
32
|
+
attrs['result_type'] = 'sql_table'
|
28
33
|
@report.logger.debug('Processing ShowEnvironmentIncludeProcessor')
|
34
|
+
grafana = @report.grafana(instance)
|
35
|
+
|
36
|
+
vars = { 'table_formatter' => 'adoc_plain', 'include_headline' => 'true'}
|
37
|
+
vars = vars.merge(build_attribute_hash(doc.attributes, attrs))
|
38
|
+
|
39
|
+
# query reporter environment
|
40
|
+
result = ['== Reporter', '|===']
|
41
|
+
query = QueryValueQuery.new(grafana, variables: vars.merge({'transpose' => 'true'}))
|
42
|
+
query.datasource = ::GrafanaReporter::ReporterEnvironmentDatasource.new(nil)
|
43
|
+
result += query.execute.split("\n")
|
44
|
+
|
45
|
+
# query grafana environment
|
46
|
+
result += ['|===', '',
|
47
|
+
'== Grafana Instance', '|===']
|
48
|
+
query = QueryValueQuery.new(grafana, variables: vars.merge({'transpose' => 'true'}))
|
49
|
+
query.raw_query = {grafana: grafana, mode: 'general'}
|
50
|
+
query.datasource = ::Grafana::GrafanaEnvironmentDatasource.new(nil)
|
51
|
+
result += query.execute.split("\n")
|
52
|
+
|
53
|
+
result += ['|===', '',
|
54
|
+
'== Accessible Dashboards', '|===']
|
55
|
+
query = QueryValueQuery.new(grafana, variables: vars)
|
56
|
+
query.raw_query = {grafana: grafana, mode: 'dashboards'}
|
57
|
+
query.datasource = Grafana::GrafanaEnvironmentDatasource.new(nil)
|
58
|
+
result += query.execute.split("\n")
|
29
59
|
|
30
|
-
|
31
|
-
|
60
|
+
result += ['|===', '',
|
61
|
+
'== Accessible Variables',
|
62
|
+
'|===']
|
32
63
|
doc.attributes.sort.each do |k, v|
|
33
|
-
|
64
|
+
result << "| `+{#{k}}+` | #{v}"
|
34
65
|
end
|
35
|
-
|
66
|
+
result << '|==='
|
36
67
|
|
37
|
-
reader.unshift_lines
|
68
|
+
reader.unshift_lines result
|
38
69
|
end
|
39
70
|
|
40
71
|
# @see ProcessorMixin#build_demo_entry
|
@@ -44,15 +44,24 @@ module GrafanaReporter
|
|
44
44
|
@report.next_step
|
45
45
|
instance = attrs['instance'] || parent.document.attr('grafana_default_instance') || 'default'
|
46
46
|
attrs['result_type'] = 'sql_value'
|
47
|
+
sql = attrs['sql']
|
47
48
|
@report.logger.debug("Processing SqlValueInlineMacro (instance: #{instance}, datasource: #{target},"\
|
48
|
-
" sql: #{
|
49
|
+
" sql: #{sql})")
|
50
|
+
|
51
|
+
# translate sql statement to fix asciidoctor issue
|
52
|
+
# refer https://github.com/asciidoctor/asciidoctor/issues/4072#issuecomment-991305715
|
53
|
+
sql_translated = CGI::unescapeHTML(sql) if sql
|
54
|
+
if sql != sql_translated
|
55
|
+
@report.logger.debug("Translating SQL query to fix asciidoctor issue: #{sql_translated}")
|
56
|
+
sql = sql_translated
|
57
|
+
end
|
49
58
|
|
50
59
|
begin
|
51
60
|
# catch properly if datasource could not be identified
|
52
61
|
query = QueryValueQuery.new(@report.grafana(instance),
|
53
62
|
variables: build_attribute_hash(parent.document.attributes, attrs))
|
54
63
|
query.datasource = @report.grafana(instance).datasource_by_id(target)
|
55
|
-
query.raw_query =
|
64
|
+
query.raw_query = sql
|
56
65
|
|
57
66
|
create_inline(parent, :quoted, query.execute)
|
58
67
|
rescue Grafana::GrafanaError => e
|
@@ -58,11 +58,6 @@ module GrafanaReporter
|
|
58
58
|
return reader
|
59
59
|
end
|
60
60
|
|
61
|
-
# TODO: remove dirty hack to allow the document as parameter for other processors
|
62
|
-
def doc.document
|
63
|
-
self
|
64
|
-
end
|
65
|
-
|
66
61
|
# TODO: properly show error messages also in document
|
67
62
|
ext = doc.extensions.find_inline_macro_extension(call) if doc.extensions.inline_macros?
|
68
63
|
if !ext
|