query_tracer 0.0.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.
- data/.gitignore +2 -0
- data/Gemfile +1 -0
- data/README.markdown +54 -0
- data/Rakefile +31 -0
- data/lib/query_tracer/configuration.rb +32 -0
- data/lib/query_tracer/db/mysql.rb +11 -0
- data/lib/query_tracer/db/sqlite.rb +11 -0
- data/lib/query_tracer/db.rb +7 -0
- data/lib/query_tracer/logger.rb +32 -0
- data/lib/query_tracer/tracer/revision.rb +36 -0
- data/lib/query_tracer/tracer.rb +50 -0
- data/lib/query_tracer.rb +4 -0
- data/query_tracer.gemspec +30 -0
- data/test/test_helper.rb +0 -0
- metadata +85 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gemspec
|
data/README.markdown
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# QueryTracer
|
2
|
+
|
3
|
+
* https://github.com/daemon/query_tracer
|
4
|
+
|
5
|
+
## DESCRIPTION
|
6
|
+
|
7
|
+
QueryTracer is designed to log where from queries to DB were made.
|
8
|
+
|
9
|
+
## Origins
|
10
|
+
|
11
|
+
Original code was built for Scribd by Dmitry Shteflyuk.
|
12
|
+
|
13
|
+
## TODO
|
14
|
+
|
15
|
+
Rewrite SQL queries adding small trace as a comment. It will show where long running queries came from.
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Add following code to config/initializers/query_tracer.rb:
|
20
|
+
|
21
|
+
QueryTracer::Configuration.set do |tracer|
|
22
|
+
tracer.enabled = true
|
23
|
+
tracer.colorize = true
|
24
|
+
tracer.show_revision = true
|
25
|
+
tracer.multiline = true
|
26
|
+
tracer.skip_queries = [%r{FROM sqlite_master}]
|
27
|
+
end
|
28
|
+
|
29
|
+
QueryTracer::Logger.attach_to :active_record
|
30
|
+
|
31
|
+
`skip_queries` will merge one/many regexps into default expressions list (QueryTracer::Tracer::EXCLUDE_SQL) for current DB.
|
32
|
+
|
33
|
+
## Example
|
34
|
+
|
35
|
+
Let's say we have model
|
36
|
+
|
37
|
+
class User < ActiveRecord::Base
|
38
|
+
scope :active, where(:active => true)
|
39
|
+
|
40
|
+
def self.traceme
|
41
|
+
active.to_a
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Invoking `User.traceme` will produce in logs:
|
46
|
+
|
47
|
+
SQL (0.9ms) SELECT name
|
48
|
+
FROM sqlite_master
|
49
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
50
|
+
|
51
|
+
User Load (25.8ms) SELECT "users".* FROM "users" WHERE "users"."active" = 't'
|
52
|
+
^^^^ Called from: Rev[d11816c90c2a38dcd866b115ed2ffa28d7d84e2c]
|
53
|
+
-> /Users/dm/Projects/tmp/loggertest/app/models/user.rb:5:in `traceme'
|
54
|
+
-> (irb):1:in `irb_binding'
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
begin
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup
|
4
|
+
rescue LoadError
|
5
|
+
$stderr.puts "You need to have Bundler installed to be able build this gem."
|
6
|
+
end
|
7
|
+
|
8
|
+
gemspec = eval(File.read(Dir["*.gemspec"].first))
|
9
|
+
|
10
|
+
|
11
|
+
desc "Validate the gemspec"
|
12
|
+
task :gemspec do
|
13
|
+
gemspec.validate
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Build gem locally"
|
17
|
+
task :build => :gemspec do
|
18
|
+
system "gem build #{gemspec.name}.gemspec"
|
19
|
+
FileUtils.mkdir_p "pkg"
|
20
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", "pkg"
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Install gem locally"
|
24
|
+
task :install => :build do
|
25
|
+
system "gem install pkg/#{gemspec.name}-#{gemspec.version}"
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Clean automatically generated files"
|
29
|
+
task :clean do
|
30
|
+
FileUtils.rm_rf "pkg"
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module QueryTracer
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
attr_accessor :show_revision, :enabled, :colorize
|
7
|
+
attr_accessor :multiline, :log_level
|
8
|
+
|
9
|
+
def set
|
10
|
+
db_adapter = ActiveRecord::Base.connection.adapter_name.capitalize
|
11
|
+
begin
|
12
|
+
QueryTracer::Tracer::EXCLUDE_SQL << QueryTracer::Db.const_get(db_adapter)::SKIP_QUERIES
|
13
|
+
QueryTracer::Tracer::EXCLUDE_SQL.flatten!
|
14
|
+
rescue
|
15
|
+
end
|
16
|
+
|
17
|
+
yield self
|
18
|
+
end
|
19
|
+
|
20
|
+
def log_level
|
21
|
+
@log_level || :debug
|
22
|
+
end
|
23
|
+
|
24
|
+
def skip_queries=(val)
|
25
|
+
QueryTracer::Tracer::EXCLUDE_SQL << val
|
26
|
+
QueryTracer::Tracer::EXCLUDE_SQL.flatten!
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_record/log_subscriber'
|
2
|
+
|
3
|
+
require 'query_tracer/configuration'
|
4
|
+
require 'query_tracer/tracer'
|
5
|
+
|
6
|
+
module QueryTracer
|
7
|
+
|
8
|
+
class Logger < ActiveSupport::LogSubscriber
|
9
|
+
# event.payload[:name]
|
10
|
+
# event.duration
|
11
|
+
# event.payload[:sql]
|
12
|
+
def sql(event)
|
13
|
+
return unless Configuration.enabled
|
14
|
+
|
15
|
+
sql = event.payload[:sql]
|
16
|
+
# Skip noisy queries
|
17
|
+
trace = Tracer.build_trace(sql)
|
18
|
+
return if trace.blank?
|
19
|
+
# We're done
|
20
|
+
if Configuration.colorize
|
21
|
+
message = "\e[34m\e[43m^^^^ Called from:\e[0m "
|
22
|
+
indent = "\e[34m\e[43m->\e[0m "
|
23
|
+
else
|
24
|
+
message = '^^^^ Called from: '
|
25
|
+
indent = " "
|
26
|
+
end
|
27
|
+
logger.send QueryTracer::Configuration.log_level.to_sym, message + trace.join("\n#{indent}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module QueryTracer
|
2
|
+
module Tracer
|
3
|
+
|
4
|
+
module Revision
|
5
|
+
# Detect the current code revision and memoize it for the future.
|
6
|
+
def self.current
|
7
|
+
return nil unless QueryTracer::Configuration.show_revision
|
8
|
+
|
9
|
+
begin
|
10
|
+
# Do we have the code revision memoized?
|
11
|
+
unless defined?(@@current_code_revision)
|
12
|
+
@@current_code_revision = if File.exists?("#{Rails.root}/REVISION")
|
13
|
+
# Capistrano-deployed application, we know where to get current revision
|
14
|
+
File.read("#{Rails.root}/REVISION").chomp.strip
|
15
|
+
else
|
16
|
+
# Try to use git
|
17
|
+
rev = `git rev-parse HEAD 2>/dev/null`.chomp.strip
|
18
|
+
rev.empty? ? nil : rev
|
19
|
+
end
|
20
|
+
end
|
21
|
+
rescue
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
humanized_revision
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.humanized_revision
|
28
|
+
if @@current_code_revision
|
29
|
+
"Rev[#{@@current_code_revision}]"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'query_tracer/configuration'
|
2
|
+
require 'query_tracer/tracer/revision'
|
3
|
+
|
4
|
+
module QueryTracer
|
5
|
+
module Tracer
|
6
|
+
extend self
|
7
|
+
|
8
|
+
INCLUDE_CODEPOINTS = [
|
9
|
+
%r{^#{Rails.root}/(app/presenters/.*)},
|
10
|
+
%r{^#{Rails.root}/(app/views/.*)},
|
11
|
+
%r{^#{Rails.root}/(app/controllers/.*)},
|
12
|
+
%r{^#{Rails.root}/(app/models/.*)},
|
13
|
+
%r{^#{Rails.root}/(lib/.*)},
|
14
|
+
%r{^#{Rails.root}/(spec/.*)},
|
15
|
+
%r{^#{Rails.root}/(app/.*)},
|
16
|
+
%r{^#{Rails.root}/(vendor/(?:gems|plugins)/.*)},
|
17
|
+
%r{^#{Rails.root}/(.*)},
|
18
|
+
%r{in `(irb)_binding'}
|
19
|
+
]
|
20
|
+
# A regular expression used to skip certain code points (gems that do nothing
|
21
|
+
# but add noice to the result).
|
22
|
+
EXCLUDE_CODEPOINT = %r{^(#{Rails.root}/(?:vendor/(?:rails|gems/(?:composite_primary_keys|db-charmer)|plugins/(?:paginating_find|acts_as_sluggable))|config/initializers/mysql_adapter_extensions\.rb|tmp/gems|lib/query_tracer))|\.rvm/|/gems/}
|
23
|
+
# A regular expression to exclude certain SQL queries from processing (who cares
|
24
|
+
# where SHOW TABLES was issues from).
|
25
|
+
EXCLUDE_SQL = []
|
26
|
+
|
27
|
+
def build_trace(sql)
|
28
|
+
unless skip_query?(sql)
|
29
|
+
# Skip noisy codepoints
|
30
|
+
lines = caller.inject([]) do |filtered, line|
|
31
|
+
unless line =~ EXCLUDE_CODEPOINT
|
32
|
+
filtered << line unless INCLUDE_CODEPOINTS.select{ |expr| line =~ expr }.blank?
|
33
|
+
end
|
34
|
+
filtered
|
35
|
+
end
|
36
|
+
|
37
|
+
unless lines.blank?
|
38
|
+
lines = lines.first unless QueryTracer::Configuration.multiline
|
39
|
+
[QueryTracer::Tracer::Revision.current, lines].flatten
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def skip_query?(sql)
|
46
|
+
!EXCLUDE_SQL.select { |expr| sql =~ expr }.blank?
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/query_tracer.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "query_tracer"
|
5
|
+
s.version = "0.0.2"
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Dmitry Shaposhnik", "Dmytro Shteflyuk"]
|
8
|
+
s.email = ["dmitry@shaposhnik.name", "kpumuk@kpumuk.info"]
|
9
|
+
s.homepage = "http://github.com/daemon/query_tracer"
|
10
|
+
s.summary = "Query tracer and logger for Rails3"
|
11
|
+
s.description = ""
|
12
|
+
|
13
|
+
s.required_rubygems_version = ">= 1.3.6"
|
14
|
+
|
15
|
+
# If you have runtime dependencies, add them here
|
16
|
+
# s.add_runtime_dependency "other", "~> 1.2"
|
17
|
+
|
18
|
+
# If you have development dependencies, add them here
|
19
|
+
# s.add_development_dependency "another", "= 0.9"
|
20
|
+
|
21
|
+
# The list of files to be contained in the gem
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
# s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
24
|
+
# s.extensions = `git ls-files ext/extconf.rb`.split("\n")
|
25
|
+
|
26
|
+
s.require_path = 'lib'
|
27
|
+
|
28
|
+
# For C extensions
|
29
|
+
# s.extensions = "ext/extconf.rb"
|
30
|
+
end
|
data/test/test_helper.rb
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: query_tracer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Dmitry Shaposhnik
|
14
|
+
- Dmytro Shteflyuk
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-04-18 00:00:00 +03:00
|
20
|
+
default_executable:
|
21
|
+
dependencies: []
|
22
|
+
|
23
|
+
description: ""
|
24
|
+
email:
|
25
|
+
- dmitry@shaposhnik.name
|
26
|
+
- kpumuk@kpumuk.info
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- README.markdown
|
37
|
+
- Rakefile
|
38
|
+
- lib/query_tracer.rb
|
39
|
+
- lib/query_tracer/configuration.rb
|
40
|
+
- lib/query_tracer/db.rb
|
41
|
+
- lib/query_tracer/db/mysql.rb
|
42
|
+
- lib/query_tracer/db/sqlite.rb
|
43
|
+
- lib/query_tracer/logger.rb
|
44
|
+
- lib/query_tracer/tracer.rb
|
45
|
+
- lib/query_tracer/tracer/revision.rb
|
46
|
+
- query_tracer.gemspec
|
47
|
+
- test/test_helper.rb
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://github.com/daemon/query_tracer
|
50
|
+
licenses: []
|
51
|
+
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 23
|
72
|
+
segments:
|
73
|
+
- 1
|
74
|
+
- 3
|
75
|
+
- 6
|
76
|
+
version: 1.3.6
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.5.3
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: Query tracer and logger for Rails3
|
84
|
+
test_files: []
|
85
|
+
|