lookout-query_reviewer 0.1.5

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 (32) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +136 -0
  3. data/Rakefile +24 -0
  4. data/lib/query_reviewer.rb +66 -0
  5. data/lib/query_reviewer/array_extensions.rb +29 -0
  6. data/lib/query_reviewer/controller_extensions.rb +67 -0
  7. data/lib/query_reviewer/mysql_adapter_extensions.rb +92 -0
  8. data/lib/query_reviewer/mysql_analyzer.rb +62 -0
  9. data/lib/query_reviewer/query_warning.rb +17 -0
  10. data/lib/query_reviewer/rails.rb +37 -0
  11. data/lib/query_reviewer/sql_query.rb +131 -0
  12. data/lib/query_reviewer/sql_query_collection.rb +103 -0
  13. data/lib/query_reviewer/sql_sub_query.rb +45 -0
  14. data/lib/query_reviewer/tasks.rb +8 -0
  15. data/lib/query_reviewer/views/_box.html.erb +11 -0
  16. data/lib/query_reviewer/views/_box_ajax.js +34 -0
  17. data/lib/query_reviewer/views/_box_body.html.erb +73 -0
  18. data/lib/query_reviewer/views/_box_disabled.html.erb +2 -0
  19. data/lib/query_reviewer/views/_box_header.html.erb +1 -0
  20. data/lib/query_reviewer/views/_box_includes.html.erb +234 -0
  21. data/lib/query_reviewer/views/_explain.html.erb +30 -0
  22. data/lib/query_reviewer/views/_js_includes.html.erb +68 -0
  23. data/lib/query_reviewer/views/_js_includes_new.html.erb +68 -0
  24. data/lib/query_reviewer/views/_profile.html.erb +26 -0
  25. data/lib/query_reviewer/views/_query_sql.html.erb +8 -0
  26. data/lib/query_reviewer/views/_query_trace.html.erb +31 -0
  27. data/lib/query_reviewer/views/_query_with_warning.html.erb +54 -0
  28. data/lib/query_reviewer/views/_spectrum.html.erb +10 -0
  29. data/lib/query_reviewer/views/_warning_no_query.html.erb +8 -0
  30. data/lib/query_reviewer/views/query_review_box_helper.rb +99 -0
  31. data/query_reviewer_defaults.yml +39 -0
  32. metadata +77 -0
@@ -0,0 +1,54 @@
1
+ <li id="query_<%= query_with_warning.id %>">
2
+ <div>
3
+ <%= render :partial => "/spectrum", :locals => {:severity => query_with_warning.max_severity} %>
4
+ <% if QueryReviewer::CONFIGURATION["production_data"] %>
5
+ <div style="float: left; padding-right: 5px;">
6
+ <%= duration_with_color(query_with_warning) %>s
7
+ </div>
8
+ <% end %>
9
+ <p>
10
+ <% if query_with_warning.count > 1 %>
11
+ <b title="<%= query_with_warning.count %> queries were executed with the same stack trace and similar SQL structure">
12
+ <%= query_with_warning.count %> identical queries
13
+ </b>
14
+ <% end %>
15
+ <i>Table <%= (query_with_warning.warnings.detect {|w| !w.table.blank? } || query_with_warning.warnings.last).table %>:</i>
16
+ <% query_with_warning.warnings.sort{|a,b| a.severity <=> b.severity}.reverse.each_with_index do |warn, index| %>
17
+ <span style="color: <%= severity_color warn.severity%>;" title="<%= warn.desc%>"><%= warn.problem %></span><%= ", " if index < query_with_warning.warnings.length - 1 %>
18
+ <% end %>
19
+ <a href="#" onclick="query_review_toggle('warning_<%= query_with_warning.id %>_desc')" title="show/hide warning message">MSG</a>
20
+ <a href="#" onclick="query_review_toggle('warning_<%= query_with_warning.id %>_sql')" title="show/hide sql">SQL</a>
21
+ <% if query_with_warning.select? %>
22
+ <a href="#" onclick="query_review_toggle('warning_<%= query_with_warning.id %>_explain')" title="show/hide explain output">EXPLN</a>
23
+ <% end %>
24
+ <% if QueryReviewer::CONFIGURATION["profiling"] && query_with_warning.profile %>
25
+ <a href="#" onclick="query_review_toggle('warning_<%= query_with_warning.id %>_profile')" title="show/hide profile output">PROF</a>
26
+ <% end %>
27
+ <a href="#" onclick="query_review_toggle('warning_<%= query_with_warning.id %>_trace')" title="show/hide stack trace">TRACE</a>
28
+ <% if ignore_hash?(query_with_warning.to_hash) %>
29
+ <a href="#" onclick="remove_ignore_hash('<%= query_with_warning.to_hash %>'); query_review_hide('query_<%= query_with_warning.id %>')" title="stop ignore this query from now on">UNIGNR</a>
30
+ <% else %>
31
+ <a href="#" onclick="add_ignore_hash('<%= query_with_warning.to_hash %>'); query_review_hide('query_<%= query_with_warning.id %>')" title="ignoring this query from now on">IGNR</a>
32
+ <% end %>
33
+ </p>
34
+ </div>
35
+ <p style="display: none" id="warning_<%= query_with_warning.id %>_desc" class="indent">
36
+ <% query_with_warning.warnings.each do |warn| %>
37
+ <span style="color: <%= severity_color warn.severity%>"><%= warn.desc %></span><br/>
38
+ <% end %>
39
+ </p>
40
+ <p style="display: none" id="warning_<%= query_with_warning.id %>_sql" class="indent small tbpadded">
41
+ <%= render :partial => "/query_sql", :locals => {:query_sql => query_with_warning} %>
42
+ </p>
43
+ <div style="display: none" id="warning_<%= query_with_warning.id %>_explain" class="indent small tbpadded">
44
+ <%= render :partial => "/explain", :locals => {:query => query_with_warning} %>
45
+ </div>
46
+ <% if QueryReviewer::CONFIGURATION["profiling"] && query_with_warning.profile %>
47
+ <div style="display: none" id="warning_<%= query_with_warning.id %>_profile" class="indent small tbpadded">
48
+ <%= render :partial => "/profile", :locals => {:query => query_with_warning} %>
49
+ </div>
50
+ <% end %>
51
+ <div style="display: none" id="warning_<%= query_with_warning.id %>_trace" class="indent small">
52
+ <%= render :partial => "/query_trace", :locals => {:query_id => query_with_warning.id, :full_trace => query_with_warning.full_trace,:query_trace => query_with_warning.relevant_trace} %>
53
+ </div>
54
+ </li>
@@ -0,0 +1,10 @@
1
+ <div class="spectrum_container" title="Severity: <%= severity%> out of 10">
2
+ <% 0.upto(15) do |i| %>
3
+ <div class="spectrum_elem" style="background-color: <%= "##{i.to_s(16)}F0"%>"></div>
4
+ <% end %>
5
+ <% 0.upto(15) do |i| %>
6
+ <div class="spectrum_elem" style="background-color: <%= "#F#{((15-i)).to_s(16)}0"%>"></div>
7
+ <% end %>
8
+ <div class="spectrum_elem" style="background-color: #FF0000"></div>
9
+ <div class="spectrum_pointer" style="left: <%= severity * 3 + 2 %>px;"></div>
10
+ </div>
@@ -0,0 +1,8 @@
1
+ <li id="warning_<%= warning_no_query.id %>">
2
+ <div>
3
+ <%= render :partial => "/spectrum", :locals => {:severity => warning_no_query.severity} %>
4
+ <p>
5
+ <span style="color: <%= severity_color warning_no_query.severity%>" title="<%= warning_no_query.desc %>"><%= warning_no_query.problem %></span>
6
+ </p>
7
+ </div>
8
+ </li>
@@ -0,0 +1,99 @@
1
+ module QueryReviewer
2
+ module Views
3
+ module QueryReviewBoxHelper
4
+ def parent_div_class
5
+ "sql_#{parent_div_status.downcase}"
6
+ end
7
+
8
+ def parent_div_status
9
+ if !enabled_by_cookie
10
+ "DISABLED"
11
+ elsif overall_max_severity < (QueryReviewer::CONFIGURATION["warn_severity"] || 4)
12
+ "OK"
13
+ elsif overall_max_severity < (QueryReviewer::CONFIGURATION["critical_severity"] || 7)
14
+ # uh oh
15
+ "WARNING"
16
+ else
17
+ # oh @#&!
18
+ "CRITICAL"
19
+ end
20
+ end
21
+
22
+ def syntax_highlighted_sql(sql)
23
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
24
+ if QueryReviewer::CONFIGURATION["uv"]
25
+ uv_out = Uv.parse(sql, "xhtml", "sql_rails", false, "blackboard")
26
+ uv_out.gsub("<pre class=\"blackboard\">", "<code class=\"sql\">").gsub("</pre>", "</code>")
27
+ else
28
+ sql.gsub(/</, "&lt;").gsub(/>/, "&gt;")
29
+ end
30
+ end
31
+
32
+ def overall_max_severity
33
+ max = 0
34
+ max = queries_with_warnings_sorted_nonignored[0].max_severity unless queries_with_warnings_sorted_nonignored.empty?
35
+ max = warnings_no_query_sorted.first.severity unless warnings_no_query_sorted.empty? || warnings_no_query_sorted.first.severity < max
36
+ max
37
+ end
38
+
39
+ def severity_color(severity)
40
+ red = (severity * 16.0 / 10).to_i
41
+ green = ((10-severity) * 16.0 / 10).to_i
42
+ red = 8 if red > 8
43
+ red = 0 if red < 0
44
+ green = 8 if green > 8
45
+ green = 0 if green < 0
46
+ "##{red.to_s(16)}#{green.to_s(16)}0"
47
+ end
48
+
49
+ def ignore_hash?(h)
50
+ (controller.send(:cookies)["query_review_ignore_list"] || "").split(",").include?(h.to_s)
51
+ end
52
+
53
+ def queries_with_warnings
54
+ @queries.queries.select{|q| q.has_warnings?}
55
+ end
56
+
57
+ def queries_with_warnings_sorted
58
+ queries_with_warnings.sort{|a,b| (b.max_severity * 1000 + (b.duration || 0)) <=> (a.max_severity * 1000 + (a.duration || 0))}
59
+ end
60
+
61
+ def queries_with_warnings_sorted_nonignored
62
+ queries_with_warnings_sorted.select{|q| q.max_severity >= ::QueryReviewer::CONFIGURATION["warn_severity"] && !ignore_hash?(q.to_hash)}
63
+ end
64
+
65
+ def queries_with_warnings_sorted_ignored
66
+ queries_with_warnings_sorted.reject{|q| q.max_severity >= ::QueryReviewer::CONFIGURATION["warn_severity"] && !ignore_hash?(q.to_hash)}
67
+ end
68
+
69
+ def warnings_no_query_sorted
70
+ @queries.collection_warnings.sort{|a,b| a.severity <=> b.severity}.reverse
71
+ end
72
+
73
+ def warnings_no_query_sorted_ignored
74
+ warnings_no_query_sorted.select{|q| q.severity < ::QueryReviewer::CONFIGURATION["warn_severity"]}
75
+ end
76
+
77
+ def warnings_no_query_sorted_nonignored
78
+ warnings_no_query_sorted.select{|q| q.severity >= ::QueryReviewer::CONFIGURATION["warn_severity"]}
79
+ end
80
+
81
+ def enabled_by_cookie
82
+ controller.send(:cookies)["query_review_enabled"]
83
+ end
84
+
85
+ def duration_with_color(query)
86
+ title = query.duration_stats
87
+ duration = query.duration
88
+ span_html = if duration > QueryReviewer::CONFIGURATION["critical_duration_threshold"]
89
+ "<span style=\"color: #{severity_color(9)}\" title=\"#{title}\">#{"%.3f" % duration}</span>"
90
+ elsif duration > QueryReviewer::CONFIGURATION["warn_duration_threshold"]
91
+ "<span style=\"color: #{severity_color(QueryReviewer::CONFIGURATION["critical_severity"])}\" title=\"#{title}\">#{"%.3f" % duration}</span>"
92
+ else
93
+ "<span title=\"#{title}\">#{"%.3f" % duration}</span>"
94
+ end
95
+ span_html.respond_to?(:html_safe) ? span_html.html_safe : span_html
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,39 @@
1
+ all:
2
+ inject_view: true
3
+ stack_trace_lines: 3
4
+ trace_includes_vendor: false
5
+ trace_includes_lib: true
6
+ profiling: enabled
7
+ production_data: true
8
+ max_safe_key_length: 100
9
+ disable_sql_cache: true
10
+
11
+ warn_severity: 3
12
+ critical_severity: 7
13
+
14
+ warn_select_count: 20
15
+ critical_select_count: 50
16
+
17
+ warn_update_count: 5
18
+ critical_update_count: 10
19
+
20
+ warn_insert_count: 5
21
+ critical_insert_count: 10
22
+
23
+ warn_delete_count: 5
24
+ critical_delete_count: 10
25
+
26
+ warn_duration_threshold: 0.2
27
+ critical_duration_threshold: 1.0
28
+
29
+ warn_affected_rows: 10
30
+ critical_affected_rows: 100
31
+
32
+ development:
33
+ enabled: true
34
+
35
+ production:
36
+ enabled: false
37
+
38
+ test:
39
+ enabled: false
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lookout-query_reviewer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.5
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - dsboulder, nesquena
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-17 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Runs explain before each select query and displays results in an overlayed
15
+ div
16
+ email: nesquena@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - MIT-LICENSE
22
+ - Rakefile
23
+ - README.md
24
+ - query_reviewer_defaults.yml
25
+ - lib/query_reviewer.rb
26
+ - lib/query_reviewer/rails.rb
27
+ - lib/query_reviewer/mysql_analyzer.rb
28
+ - lib/query_reviewer/sql_query.rb
29
+ - lib/query_reviewer/controller_extensions.rb
30
+ - lib/query_reviewer/query_warning.rb
31
+ - lib/query_reviewer/sql_query_collection.rb
32
+ - lib/query_reviewer/views/_box_header.html.erb
33
+ - lib/query_reviewer/views/_js_includes_new.html.erb
34
+ - lib/query_reviewer/views/_js_includes.html.erb
35
+ - lib/query_reviewer/views/_box_ajax.js
36
+ - lib/query_reviewer/views/_query_trace.html.erb
37
+ - lib/query_reviewer/views/query_review_box_helper.rb
38
+ - lib/query_reviewer/views/_profile.html.erb
39
+ - lib/query_reviewer/views/_query_sql.html.erb
40
+ - lib/query_reviewer/views/_box.html.erb
41
+ - lib/query_reviewer/views/_warning_no_query.html.erb
42
+ - lib/query_reviewer/views/_box_body.html.erb
43
+ - lib/query_reviewer/views/_query_with_warning.html.erb
44
+ - lib/query_reviewer/views/_spectrum.html.erb
45
+ - lib/query_reviewer/views/_box_includes.html.erb
46
+ - lib/query_reviewer/views/_box_disabled.html.erb
47
+ - lib/query_reviewer/views/_explain.html.erb
48
+ - lib/query_reviewer/tasks.rb
49
+ - lib/query_reviewer/mysql_adapter_extensions.rb
50
+ - lib/query_reviewer/sql_sub_query.rb
51
+ - lib/query_reviewer/array_extensions.rb
52
+ homepage: https://github.com/lookout/query_reviewer
53
+ licenses: []
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 1.8.10
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Runs explain before each select query and displays results in an overlayed
76
+ div
77
+ test_files: []