-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
197 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,62 @@ | ||
class NoBrainer::Profiler::Logger | ||
def on_query(env) | ||
not_indexed = env[:criteria] && env[:criteria].where_present? && | ||
!env[:criteria].where_indexed? && | ||
!env[:criteria].model.try(:perf_warnings_disabled) | ||
|
||
level = env[:exception] ? Logger::ERROR : | ||
not_indexed ? Logger::INFO : Logger::DEBUG | ||
return if NoBrainer.logger.level > level | ||
|
||
msg_duration = (env[:duration] * 1000.0).round(1).to_s | ||
msg_duration = " " * [0, 6 - msg_duration.size].max + msg_duration | ||
msg_duration = "[#{msg_duration}ms] " | ||
|
||
env[:query_type] = NoBrainer::RQL.type_of(env[:query]) | ||
|
||
msg_db = "[#{env[:options][:db]}] " if env[:options][:db] | ||
msg_query = env[:query].inspect.gsub(/\n/, '').gsub(/ +/, ' ') | ||
|
||
msg_exception = "#{env[:exception].class} #{env[:exception].message.split("\n").first}" if env[:exception] | ||
msg_exception ||= "perf: filtering without using an index" if not_indexed | ||
|
||
msg_last = nil | ||
|
||
if NoBrainer::Config.colorize_logger | ||
query_color = case env[:query_type] | ||
when :write then "\e[1;31m" # red | ||
when :read then "\e[1;32m" # green | ||
when :management then "\e[1;33m" # yellow | ||
end | ||
msg_duration = [query_color, msg_duration].join | ||
msg_db = ["\e[0;34m", msg_db, query_color].join if msg_db | ||
if msg_exception | ||
exception_color = "\e[0;31m" if level == Logger::ERROR | ||
msg_exception = ["\e[0;39m", " -- ", exception_color, msg_exception].compact.join | ||
# frozen_string_literal: true | ||
|
||
module NoBrainer | ||
module Profiler | ||
class Logger | ||
def on_query(env) | ||
level = ::Logger::ERROR if env[:exception] | ||
level ||= not_indexed(env) ? ::Logger::INFO : ::Logger::DEBUG | ||
return if NoBrainer.logger.level > level | ||
|
||
NoBrainer.logger.add(level, build_message(env)) | ||
end | ||
msg_last = "\e[0m" | ||
end | ||
|
||
msg = [msg_duration, msg_db, msg_query, msg_exception, msg_last].join | ||
NoBrainer.logger.add(level, msg) | ||
end | ||
private | ||
|
||
def build_message(env) | ||
msg_duration = (env[:duration] * 1000.0).round(1).to_s | ||
msg_duration = (' ' * [0, 6 - msg_duration.size].max) + msg_duration | ||
msg_duration = "[#{msg_duration}ms] " | ||
|
||
env[:query_type] = NoBrainer::RQL.type_of(env[:query]) | ||
|
||
msg_db = "[#{env[:options][:db]}] " if env[:options][:db] | ||
msg_query = env[:query].inspect.gsub(/\n/, '').gsub(/ +/, ' ') | ||
|
||
msg_exception = "#{env[:exception].class} #{env[:exception].message.split("\n").first}" if env[:exception] | ||
msg_exception ||= 'perf: filtering without using an index' if not_indexed(env) | ||
|
||
msg_last = nil | ||
|
||
NoBrainer::Profiler.register(self.new) | ||
if NoBrainer::Config.colorize_logger | ||
msg_duration = [query_color(env[:query_type]), msg_duration].join | ||
msg_db = ["\e[0;34m", msg_db, query_color(env[:query_type])].join if msg_db | ||
if msg_exception | ||
exception_color = "\e[0;31m" if level == Logger::ERROR | ||
msg_exception = ["\e[0;39m", ' -- ', exception_color, msg_exception].compact.join | ||
end | ||
msg_last = "\e[0m" | ||
end | ||
|
||
[msg_duration, msg_db, msg_query, msg_exception, msg_last].join | ||
end | ||
|
||
def not_indexed(env) | ||
env[:criteria] && | ||
env[:criteria].where_present? && | ||
!env[:criteria].where_indexed? && | ||
!env[:criteria].model.try(:perf_warnings_disabled) | ||
end | ||
|
||
def query_color(query_type) | ||
{ | ||
write: "\e[1;31m", # red | ||
read: "\e[1;32m", # green | ||
management: "\e[1;33m" # yellow | ||
}[query_type] | ||
end | ||
|
||
NoBrainer::Profiler.register(new) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# frozen_string_literal: true | ||
|
||
module NoBrainer | ||
module Profiler | ||
class SlowQueries < Logger | ||
def on_query(env) | ||
return unless NoBrainer::Config.log_slow_queries | ||
|
||
query_duration = (env[:duration] * 1000.0).round(1) | ||
|
||
return unless query_duration > NoBrainer::Config.long_query_time | ||
|
||
File.write( | ||
NoBrainer::Config.slow_query_log_file, | ||
build_message(env), | ||
mode: 'a' | ||
) | ||
end | ||
|
||
NoBrainer::Profiler.register(new) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'spec_helper' | ||
|
||
describe 'NoBrainer slow queries feature' do # rubocop:disable RSpec/DescribeClass | ||
before do | ||
NoBrainer.configure do |config| | ||
config.long_query_time = 0.4 # 700ms | ||
config.slow_query_log_file = File.join('/tmp', 'rdb_slow.log') | ||
end | ||
|
||
load_simple_document | ||
end | ||
|
||
def run_long_query | ||
SimpleDocument.first | ||
|
||
sleep NoBrainer::Config.long_query_time + 0.1 # + 100ms | ||
end | ||
|
||
context 'when config.log_slow_queries is not `true`' do | ||
before { NoBrainer::Config.log_slow_queries = false } | ||
|
||
context "when the slow query log file doesn't exist" do | ||
before do | ||
FileUtils.rm_f(NoBrainer::Config.slow_query_log_file) && run_long_query | ||
end | ||
|
||
it "doesn't not create the log file" do | ||
expect(File.exist?(NoBrainer::Config.slow_query_log_file)).to be_falsy # rubocop:disable RSpec/PredicateMatcher | ||
end | ||
end | ||
|
||
context 'when the slow query log file exist' do | ||
before do | ||
File.write(NoBrainer::Config.slow_query_log_file, "test\n") | ||
run_long_query | ||
end | ||
|
||
it "doesn't write to the log file" do | ||
expect(File.read(NoBrainer::Config.slow_query_log_file)).to eql("test\n") | ||
end | ||
end | ||
end | ||
|
||
context 'when config.log_slow_queries is `true`' do | ||
before do | ||
NoBrainer::Config.log_slow_queries = true | ||
run_long_query | ||
end | ||
|
||
it 'does create the log file' do | ||
expect(File.exist?(NoBrainer::Config.slow_query_log_file)).to be_truthy # rubocop:disable RSpec/PredicateMatcher | ||
end | ||
|
||
it 'does write to the log file' do | ||
expect(File.read(NoBrainer::Config.slow_query_log_file)).to match( | ||
/\[\s+[\d.]+ms\] r\.table\("simple_documents"\)\.order_by\(\{"index" => r.asc\(:_id_\)\}\).limit\(1\)/ | ||
) | ||
end | ||
end | ||
end |