ruby-grafana-reporter 0.4.5 → 0.5.2
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 +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
|