ManageIQ/manageiq-gems-pending

View on GitHub
lib/gems/pending/util/miq_logger_processor.rb

Summary

Maintainability
A
4 hrs
Test Coverage
D
69%
class MiqLoggerProcessor
include Enumerable
 
attr_accessor :file_name
 
def initialize(file_name)
@file_name = file_name
end
 
def each
File.open(file_name, "r") do |f|
while (line = get_next_line(f))
yield line
end
end
ensure
cleanup_get_next_line
end
 
#
# Convert the provided data into a graph in png format. Valid options are:
#
# :graph_type:: :line or :stacked
# :outfile:: output file name (default is "graph.png")
#
# :title_font_size:: size of the title in pixels (default is 24)
# :legend_font_size:: size of the legend items in pixels (default is 12)
# :marker_font_size:: size of the x axis labels in pixels (default is 10)
#
# :x_axis_label:: x axis label (default is header of first column in data)
# :y_axis_label:: y axis label (default is nil)
# :title:: Title of the graph (default is "y_axis_label by x_axis_label"; if
# y_axis_label is nil, defaults to "series 1, series 2, ...")
#
# :y_axis_increment:: y axis increment
# :minimum_value:: y axis minimum value
# :maximum_value:: y axis maximum value
#
# :convert_data_method:: method to call on each data point if a conversion is
# needed (e.g. :to_i)
#
Cyclomatic complexity for to_png is too high. [17/11]
Method `to_png` has 31 lines of code (exceeds 25 allowed). Consider refactoring.
Method `to_png` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.
def self.to_png(data, options = {})
require 'gruff'
 
graph = case options[:graph_type]
when :line then Gruff::Line.new
when :stacked then Gruff::StackedBar.new
Duplicate branch body detected.
else Gruff::Line.new
end
 
graph.title_font_size = options[:title_font_size] || 24
graph.legend_font_size = options[:legend_font_size] || 12
graph.marker_font_size = options[:marker_font_size] || 10
 
header = data[0]
data = data.transpose
x_axis_values = data.shift
x_axis_values.shift
 
# TODO: Move this outside of the to_png method to the method that collects the data (like the csv_to_png method)
convert_data_method = options[:convert_data_method]
data.each do |datum|
label = datum.shift
datum.collect! { |d| d.send(convert_data_method) } if convert_data_method
graph.data(label, datum)
end
 
graph.x_axis_label = options[:x_axis_label] || header[0]
graph.y_axis_label = options[:y_axis_label]
graph.title = options[:title] || "#{graph.y_axis_label || header[1..-1].join(", ")} by #{graph.x_axis_label}"
 
graph.y_axis_increment = options[:y_axis_increment] if options[:y_axis_increment]
graph.minimum_value = options[:minimum_value] if options[:minimum_value]
graph.maximum_value = options[:maximum_value] if options[:maximum_value]
 
graph.labels = {
0 => x_axis_values[0],
x_axis_values.length - 1 => x_axis_values[-1]
}
graph.labels[x_axis_values.length / 2] = x_axis_values[x_axis_values.length / 2] if x_axis_values.length > 2
 
graph.write(options[:outfile] || 'graph.png')
end
 
def self.csv_to_png(filename, options = {})
to_png(read_csv(filename), options)
end
 
def self.read_csv(filename)
require 'csv'
CSV.read(filename)
end
 
private
 
Method `get_next_line` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.
def get_next_line(f)
return nil if @next_line == :eof
 
line = @next_line || ""
 
loop do
new_line = f.gets
if new_line.nil?
@next_line = :eof
return MiqLoggerLine.new(line)
elsif @next_line.nil?
@next_line = new_line
line << new_line
Use `match?` instead of `=~` when `MatchData` is not used.
elsif new_line[0, 6] =~ /^\[(?:----|\d{4})\]$/
@next_line = new_line
return MiqLoggerLine.new(line)
else
line << new_line
end
end
end
 
def cleanup_get_next_line
remove_instance_variable(:@next_line) if instance_variable_defined?(:@next_line)
end
end
 
class MiqLoggerLine < String
def parts
@parts ||= self.class.split_raw_line(self).freeze
end
alias_method :to_a, :parts
alias_method :split, :parts
 
PARTS = %w(time pid tid level progname q_task_id fq_method message).freeze
PARTS.each_with_index do |m, i|
define_method(m) { parts[i] }
end
 
def each
parts.each { |p| yield p }
end
 
Useless `private` access modifier.
private
 
Method `split_raw_line` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.
`private` (on line 139) does not make singleton methods private. Use `private_class_method` or `private` inside a `class << self` block instead.
def self.split_raw_line(line)
line = line.to_s
return if line.empty?
 
time = line[11, 26]
 
bracket_index = line.index(']', 39)
pidtid = line[39...bracket_index]
pid, tid = pidtid.split(':')
 
level_index = bracket_index + 2
level = line[level_index, 5].strip
 
progname_index = level_index + 9 # 5 for the level + 4 for the " -- "
message_index = line.index(": ", progname_index) + 2 # Find up to the ": " separator for the message
 
progname = line[progname_index...message_index].strip[0..-2]
progname = nil if progname.empty?
 
message = line[message_index..-1]
if message[0, 9] == "Q-task_id"
q_bracket_index = message.index(']', 11)
q_task_id = message[11...q_bracket_index]
message = message[q_bracket_index + 3..-1]
else
q_task_id = nil
end
fq_method = message[/^MIQ\(([\d\w\:\.\#\_\-]*)\)/, 1] if message && message.start_with?("MIQ(")
message.chomp!
 
return time, pid, tid, level, progname, q_task_id, fq_method, message
rescue
return nil, nil, nil, nil, nil, nil, line
end
end