module LogsVisualizer
Constants
- VERSION
Public Class Methods
produce_graph(string_input, graph_name = nil, options = {})
click to toggle source
# File lib/logs_visualizer.rb, line 4 def self.produce_graph(string_input, graph_name = nil, options = {}) if string_input.present? g = GraphViz::new( :G, :type => :digraph ) g[:rankdir] ='LR' data = populate_data string_input, options populate_graph g, data, graph_name end end
Protected Class Methods
populate_data(logs_text, options = {})
click to toggle source
# File lib/logs_visualizer.rb, line 18 def self.populate_data(logs_text, options = {}) data = { :nodes => [] } logs_text.split('Started ').each do |action| if action.present? && action != "" action_parsed = /(?<action>.*) for/.match(action)[:action] rendered_partials = [] service_requests = [] service_times = [] compiled_assets = [] sql_insertions = [] sql_selections = [] sql_updates = [] serializers = [] unless /.*\"\/assets.*/.match(action_parsed) action.split("\r\n").each do |log_line| partial_in_line = /Rendered (?<partial>(\S)*).*\((?<time>(\S)*)ms/.match(log_line) service_request_in_line = /\[httplog\] Sending: (?<service>.*)/.match(log_line) service_time_in_line = /\[httplog\] Benchmark: (?<time>\S*)/.match(log_line) compiled_asset_in_line = /Compiled (?<asset>(\S)*).*\((?<time>\S*)ms\)/.match(log_line) sql_insertion_in_line = /SQL.*\((?<time>\S*)ms\).*INSERT INTO (?<table>(\S)*)/.match(log_line) sql_select_in_line = /\((?<time>\S*)ms\).*SELECT .* FROM (?<table>(\S)*)/.match(log_line) sql_update_in_line = /\((?<time>\S*)ms\).*UPDATE (?<table>(\S)*)/.match(log_line) serializer_in_line = /\[active_model_serializers\] Rendered (?<serializer>\S*) with .* \((?<time>\S*)ms\)/.match(log_line) if partial_in_line.present? && (options[:rendered_partials] == true || options[:all] == true) rendered_partials << { :partial => partial_in_line[:partial], :time => partial_in_line[:time].to_f } end if service_time_in_line.present? && (options[:service_requests] == true || options[:all] == true) service_times << service_time_in_line[:time].to_f end if service_request_in_line.present? && (options[:service_requests] == true || options[:all] == true) service_requests << service_request_in_line[:service] end if compiled_asset_in_line.present? && (options[:compiled_assets] == true || options[:all] == true) compiled_assets << { :asset => compiled_asset_in_line[:asset], :time => compiled_asset_in_line[:time].to_f } end if sql_insertion_in_line.present? && (options[:sql_visualization] == true || options[:all] == true) sql_insertions << { :table => sql_insertion_in_line[:table], :time => sql_insertion_in_line[:time].to_f } end if sql_select_in_line.present? && (options[:sql_visualization] == true || options[:all] == true) sql_selections << { :table => sql_select_in_line[:table], :time => sql_select_in_line[:time].to_f } end if sql_update_in_line.present? && (options[:sql_visualization] == true || options[:all] == true) sql_updates << { :table => sql_update_in_line[:table], :time => sql_update_in_line[:time].to_f } end if serializer_in_line.present? && (options[:serializers] == true || options[:all] == true) serializers << { :serializer => serializer_in_line[:serializer], :time => serializer_in_line[:time].to_f } end end controller_processing_request = /Processing by (?<controller>.*) as/.match(action)[:controller] redirect_to_url = /Redirected to (?<redirect_url>(\S)*)/.match(action) if data[:nodes].size > 0 && (data[:nodes].map { |node| node[:label] }.include? action_parsed) data[:nodes].map { |node| node[:size] += 1 if node[:label] == action_parsed } else services = [] service_requests.each_with_index do |service, index| services << { :service => service, :time => service_times[index] } end data[:nodes] << { :size => 1, :label => action_parsed, :controller => controller_processing_request, :redirect => (URI(redirect_to_url[:redirect_url]) if redirect_to_url.present? ), :rendered_partials => rendered_partials.group_by { |x| x[:partial] }, :service_requests => services, :compiled_assets => compiled_assets.group_by { |x| x[:asset] }, :sql_insertions => sql_insertions.group_by { |x| x[:table] }, :sql_selections => sql_selections.group_by { |x| x[:table] }, :sql_updates => sql_updates.group_by { |x| x[:table] }, :serializers => serializers.group_by { |x| x[:serializer] }, } end end end end data end
populate_graph(graph, data, graph_name = nil)
click to toggle source
# File lib/logs_visualizer.rb, line 110 def self.populate_graph(graph, data, graph_name = nil) if data[:nodes].present? data[:nodes].each do |node| node[:graph_node] = graph.add_nodes(node[:label], :label => "<<b>#{node[:label].gsub('&', '%26')}</b><br/><i>#{node[:controller]}</i>>") maximum_rendered_time = node[:rendered_partials].map { |partial, partials_array| partials_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.max maximum_sql_insertion_time = node[:sql_insertions].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.max maximum_asset_compilation_time = node[:compiled_assets].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.max maximum_sql_selection_time = node[:sql_selections].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.max maximum_sql_update_time = node[:sql_updates].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.max maximum_service_time = node[:service_requests].group_by { |x| x[:service] }.map { |service, services_array| (services_array.inject(0){|sum,x| sum + x[:time] } * 1000).round(2) }.max maximum_serializer_time = node[:serializers].map { |array_name, names_array| (names_array.inject(0){|sum,x| sum + x[:time] }).round(2) }.max minimum_rendered_time = node[:rendered_partials].map { |partial, partials_array| partials_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.min minimum_sql_insertion_time = node[:sql_insertions].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.min minimum_asset_compilation_time = node[:compiled_assets].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.min minimum_sql_selection_time = node[:sql_selections].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.min minimum_sql_update_time = node[:sql_updates].map { |array_name, names_array| names_array.inject(0){|sum,x| sum + x[:time] }.round(2) }.min minimum_service_time = node[:service_requests].group_by { |x| x[:service] }.map { |service, services_array| (services_array.inject(0){|sum,x| sum + x[:time] } * 1000).round(2) }.min minimum_serializer_time = node[:serializers].map { |service, services_array| (services_array.inject(0){|sum,x| sum + x[:time] }).round(2) }.min node[:rendered_partials].each do |partial, partials_array| partial_node = graph.add_nodes(partial, :shape => :component) total_time = partials_array.inject(0){|sum,x| sum + x[:time] }.round(2) if maximum_rendered_time == total_time graph.add_edges( node[:graph_node], partial_node, :label => "<<i>Renders<br/>(#{partials_array.size} times)<br/>in <b>#{partials_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'red', :fontcolor => 'red') elsif minimum_rendered_time == total_time graph.add_edges( node[:graph_node], partial_node, :label => "<<i>Renders<br/>(#{partials_array.size} times)<br/>in <b>#{partials_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'darkgreen', :fontcolor => 'darkgreen') else graph.add_edges( node[:graph_node], partial_node, :label => "<<i>Renders<br/>(#{partials_array.size} times)<br/>in #{partials_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</i>>") end end if node[:redirect].present? graph.add_edges( node[:graph_node], "GET \"#{node[:redirect].path}\"", :label => "<<i>Redirects to</i>>") end node[:compiled_assets].each do |asset, assets_array| asset_node = graph.add_nodes(asset, :shape => :folder) total_time = assets_array.inject(0){|sum,x| sum + x[:time] }.round(2) if maximum_asset_compilation_time == total_time graph.add_edges( node[:graph_node], asset_node, :label => "<<i>Compiles asset<br/>(#{assets_array.size} times)<br/>in <b>#{assets_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'red', :fontcolor => 'red') elsif minimum_asset_compilation_time == total_time graph.add_edges( node[:graph_node], asset_node, :label => "<<i>Compiles asset<br/>(#{assets_array.size} times)<br/>in <b>#{assets_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'darkgreen', :fontcolor => 'darkgreen') else graph.add_edges( node[:graph_node], asset_node, :label => "<<i>Compiles asset<br/>(#{assets_array.size} times)<br/>in #{assets_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</i>>") end end node[:serializers].each do |name, serializers_array| graph_node = graph.add_nodes(name, :shape => :msquare) total_time = serializers_array.inject(0){|sum,x| sum + x[:time] }.round(2) if maximum_serializer_time == total_time graph.add_edges( node[:graph_node], graph_node, :label => "<<i>Serializes through<br/>(#{serializers_array.size} times)<br/>in <b>#{serializers_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'red', :fontcolor => 'red') elsif minimum_serializer_time == total_time graph.add_edges( node[:graph_node], graph_node, :label => "<<i>Serializes through<br/>(#{serializers_array.size} times)<br/>in <b>#{serializers_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'darkgreen', :fontcolor => 'darkgreen') else graph.add_edges( node[:graph_node], graph_node, :label => "<<i>Serializes through<br/>(#{serializers_array.size} times)<br/>in #{serializers_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</i>>") end end node[:sql_insertions].each do |array_name, names_array| array_node = graph.add_nodes(array_name, :shape => :box3d) total_time = names_array.inject(0){|sum,x| sum + x[:time] }.round(2) if maximum_sql_insertion_time == total_time graph.add_edges( node[:graph_node], array_node, :label => "<<i>Inserts into<br/>(#{names_array.size} times)<br/>in <b>#{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'red', :fontcolor => 'red') elsif minimum_sql_insertion_time == total_time graph.add_edges( node[:graph_node], array_node, :label => "<<i>Inserts into<br/>(#{names_array.size} times)<br/>in <b>#{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'darkgreen', :fontcolor => 'darkgreen') else graph.add_edges( node[:graph_node], array_node, :label => "<<i>Inserts into<br/>(#{names_array.size} times)<br/>in #{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</i>>") end end node[:sql_selections].each do |array_name, names_array| array_node = graph.add_nodes(array_name, :shape => :box3d) total_time = names_array.inject(0){|sum,x| sum + x[:time] }.round(2) if maximum_sql_selection_time == total_time graph.add_edges( node[:graph_node], array_node, :label => "<<i>Selects from<br/>(#{names_array.size} times)<br/>in <b>#{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'red', :fontcolor => 'red') elsif minimum_sql_selection_time == total_time graph.add_edges( node[:graph_node], array_node, :label => "<<i>Selects from<br/>(#{names_array.size} times)<br/>in <b>#{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'darkgreen', :fontcolor => 'darkgreen') else graph.add_edges( node[:graph_node], array_node, :label => "<<i>Selects from<br/>(#{names_array.size} times)<br/>in #{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</i>>") end end node[:sql_updates].each do |array_name, names_array| array_node = graph.add_nodes(array_name, :shape => :box3d) total_time = names_array.inject(0){|sum,x| sum + x[:time] }.round(2) if maximum_sql_update_time == total_time graph.add_edges( node[:graph_node], array_node, :label => "<<i>Updates<br/>(#{names_array.size} times)<br/>in <b>#{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'red', :fontcolor => 'red') elsif minimum_sql_update_time == total_time graph.add_edges( node[:graph_node], array_node, :label => "<<i>Updates<br/>(#{names_array.size} times)<br/>in <b>#{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</b></i>>", :color => 'darkgreen', :fontcolor => 'darkgreen') else graph.add_edges( node[:graph_node], array_node, :label => "<<i>Updates<br/>(#{names_array.size} times)<br/>in #{names_array.inject(0){|sum,x| sum + x[:time] }.round(2)}ms</i>>") end end node[:service_requests]. map { |service| {:service => URI(service[:service].split(' ')[1]).host, :time => service[:time]} }. group_by { |x| x[:service] }. each do |service, services_array| service_node = graph.add_nodes(service, :shape => :note) graph.add_edges( node[:graph_node], service_node, :label => "<<i>Requests<br/>(#{services_array.size} times)<br/>in #{(services_array.inject(0){|sum,x| sum + x[:time] } * 1000).round(2)}ms</i>>") end node[:service_requests].group_by { |x| x[:service] }.each do |service, services_array| service_split = service.split(' ') full_service_node = graph.add_nodes("#{service_split[0]} #{[URI(service.split(' ')[1]).path, URI(service.split(' ')[1]).query.presence].reject { |x| x.blank? }.join('?')}", :shape => :note) total_time = (services_array.inject(0){|sum,x| sum + x[:time] } * 1000).round(2) if total_time == maximum_service_time graph.add_edges( URI(service.split(' ')[1]).host, full_service_node, :label => "<<i>Includes requests to<br/>(#{services_array.size} times)<br/>in <b>#{(services_array.inject(0){|sum,x| sum + x[:time] } * 1000).round(2)}ms</b></i>>", :color => 'red', :fontcolor => 'red') elsif total_time == minimum_service_time graph.add_edges( URI(service.split(' ')[1]).host, full_service_node, :label => "<<i>Includes requests to<br/>(#{services_array.size} times)<br/>in <b>#{(services_array.inject(0){|sum,x| sum + x[:time] } * 1000).round(2)}ms</b></i>>", :color => 'darkgreen', :fontcolor => 'darkgreen') else graph.add_edges( URI(service.split(' ')[1]).host, full_service_node, :label => "<<i>Includes requests to<br/>(#{services_array.size} times)<br/>in #{(services_array.inject(0){|sum,x| sum + x[:time] } * 1000).round(2)}ms</i>>") end end end directory_name = 'app/assets/images/graphs' FileUtils.mkdir_p('app') unless File.directory?('app') FileUtils.mkdir_p('app/assets') unless File.directory?('app/assets') FileUtils.mkdir_p('app/assets/images') unless File.directory?('app/assets/images') FileUtils.mkdir_p(directory_name) unless File.directory?(directory_name) graph.output(:png => "#{directory_name}/#{graph_name.present? ? graph_name : "graph_#{DateTime.now.strftime('%H%M%S%L')}" }.png" ) end end