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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/VERSION.rb +2 -2
  4. data/lib/grafana/abstract_datasource.rb +8 -0
  5. data/lib/grafana/dashboard.rb +6 -1
  6. data/lib/grafana/errors.rb +9 -1
  7. data/lib/grafana/grafana.rb +35 -0
  8. data/lib/grafana/grafana_environment_datasource.rb +56 -0
  9. data/lib/grafana/graphite_datasource.rb +3 -0
  10. data/lib/grafana/image_rendering_datasource.rb +5 -1
  11. data/lib/grafana/influxdb_datasource.rb +11 -4
  12. data/lib/grafana/panel.rb +5 -1
  13. data/lib/grafana/prometheus_datasource.rb +71 -11
  14. data/lib/grafana/sql_datasource.rb +10 -4
  15. data/lib/grafana/variable.rb +46 -23
  16. data/lib/grafana/webrequest.rb +1 -0
  17. data/lib/grafana_reporter/abstract_query.rb +31 -24
  18. data/lib/grafana_reporter/abstract_report.rb +2 -0
  19. data/lib/grafana_reporter/abstract_table_format_strategy.rb +44 -4
  20. data/lib/grafana_reporter/alerts_table_query.rb +2 -1
  21. data/lib/grafana_reporter/annotations_table_query.rb +2 -1
  22. data/lib/grafana_reporter/application/webservice.rb +8 -4
  23. data/lib/grafana_reporter/asciidoctor/adoc_plain_table_format_strategy.rb +11 -9
  24. data/lib/grafana_reporter/asciidoctor/help.rb +53 -14
  25. data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +2 -4
  26. data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +2 -4
  27. data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +1 -1
  28. data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +37 -6
  29. data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +11 -2
  30. data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +0 -5
  31. data/lib/grafana_reporter/configuration.rb +53 -22
  32. data/lib/grafana_reporter/console_configuration_wizard.rb +3 -1
  33. data/lib/grafana_reporter/csv_table_format_strategy.rb +11 -9
  34. data/lib/grafana_reporter/demo_report_wizard.rb +3 -6
  35. data/lib/grafana_reporter/errors.rb +2 -2
  36. data/lib/grafana_reporter/panel_image_query.rb +0 -1
  37. data/lib/grafana_reporter/query_value_query.rb +7 -1
  38. data/lib/grafana_reporter/reporter_environment_datasource.rb +24 -0
  39. data/lib/ruby_grafana_reporter.rb +7 -7
  40. 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: grafana_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(',').each do |filter_column|
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(',').each_index do |i|
177
- format = formats.text.split(',')[i]
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
- row[i] = format % row[i] if row[i]
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
- @grafana.logger.error(e.message)
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
- @grafana.logger.error(e.message)
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
- @grafana.logger.error(e.message)
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(' | ') }.merge(opts.delete_if {|_k, v| v.nil? })
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
- @grafana.logger.warn("You are using deprecated 'table_formatter' named 'adoc_deprecated', which will be "\
310
- "removed in a future version. Start using 'adoc_plain' or register your own "\
311
- "implementation of AbstractTableFormatStrategy.")
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
- @grafana.logger.warn("#translate_date has been called without 'report_time' - using current time as fallback.") unless report_time
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
- # TODO: compare how empty valid responses look like in grafana
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
- # @abstract
27
- # @param column [Array] datasource table result
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
- raise NotImplementedError
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
- accept_requests_loop
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('.') %>.</p>
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#format
14
- def format(result, include_headline)
15
- headline = '| ' + result[:header].map { |item| item.to_s.gsub(' | ', '\\|') }.join(' | ')
16
-
17
- content = result[:content].map do |row|
18
- '| ' + row.map { |item| item.to_s.gsub(' | ', '\\|') }.join(' | ')
19
- end.join("\n")
20
-
21
- "#{"#{headline}\n" if include_headline}#{content}"
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 item['standard_options'].key?(opt_key)
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 two digit decimals of a
200
- float. Several columns are separated by `,`. Execution is applied in the following order `format`,
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 is applied in the following order `format`,
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. Execution is applied in the following order `format`, `replace_values`,
218
- `filter_columns`, `transpose`.
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 is applied in the
224
- following order `format`, `replace_values`, `filter_columns`, `transpose`.
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`. . Defaults to `| ` for being interpreted as a asciidoctor row. DEPRECATED: switch to
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: Shows all available variables in the rendering context which can be used in the asciidoctor template.
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 an asciidoctor link
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 an asciidoctor link
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, _attrs)
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
- vars = ['== Accessible Variables',
31
- '|===']
60
+ result += ['|===', '',
61
+ '== Accessible Variables',
62
+ '|===']
32
63
  doc.attributes.sort.each do |k, v|
33
- vars << "| `+{#{k}}+` | #{v}"
64
+ result << "| `+{#{k}}+` | #{v}"
34
65
  end
35
- vars << '|==='
66
+ result << '|==='
36
67
 
37
- reader.unshift_lines vars
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: #{attrs['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 = attrs['sql']
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