sqlogger 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/lib/sqlogger/base.rb +31 -23
- data/lib/sqlogger/elite.rb +27 -0
- data/lib/sqlogger/elite/echo.rb +30 -20
- data/lib/sqlogger/elite/elasticsearch.rb +86 -71
- data/lib/sqlogger/monkey/include_active_record_log_subscriber.rb +52 -0
- data/lib/sqlogger/railtie.rb +3 -3
- data/lib/sqlogger/version.rb +1 -1
- data/lib/tasks/sqlogger_tasks.rake +1 -1
- metadata +8 -8
- data/lib/sqlogger/monkey/active_record/replace_log_subscriber.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbb935a232904d14b9ed45f474a9e577d289820b
|
4
|
+
data.tar.gz: 6724d5159ea9394746412ed20674aed43c590b8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0581dfeec3e08900b091b76cfc385edd5080e77d2b5b07fdf28120b41c60f6031c2dd78e8c1701c9df28ae04e0856ec93a3896c3183cdc078837032c9b1cfd8
|
7
|
+
data.tar.gz: fae857d8456b34d5d9dd69cee742118c19358cfc1ee8a2b56812a4c155cd5ce13f90172ae84fe0af011f6af30497de57f51cacab6b66ab538b27c0cec59543da
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
[](MIT-LICENSE)
|
2
|
+
[](https://badge.fury.io/rb/sqlogger)
|
2
3
|
[](https://travis-ci.org/metalels/sqlogger)
|
3
4
|
[](https://codeclimate.com/github/metalels/sqlogger)
|
4
5
|
[](https://codeclimate.com/github/metalels/sqlogger/coverage)
|
@@ -10,7 +11,8 @@ Collect 'ActiveRecord sql query' to monitoring system(s).
|
|
10
11
|
<img src="sqlogger_elasticsearch.png" width="580" alt="send sql-log to elasticsearch">
|
11
12
|
|
12
13
|
## Dependency
|
13
|
-
Currently
|
14
|
+
Currently supports Rails **3.1.0** and **upper**.
|
15
|
+
Tested minor version between **3.1.0** and **5.1.0beta1**
|
14
16
|
|
15
17
|
## Installation
|
16
18
|
Add this line to your application's Gemfile:
|
data/lib/sqlogger/base.rb
CHANGED
@@ -1,32 +1,40 @@
|
|
1
1
|
module Sqlogger
|
2
2
|
module Base
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
post_targets = Rails.application.config.sqlogger.post_targets
|
13
|
-
|
14
|
-
return if ignore_payloads.include? opts[:name]
|
15
|
-
return if ignore_commands.include? sql_command
|
16
|
-
if post_targets.include?("elasticsearch") || post_targets.include?(:elasticsearch)
|
17
|
-
Thread.start do
|
18
|
-
Sqlogger::Elite::Elasticsearch.post opts
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def logger opts={}
|
6
|
+
return unless opts[:sql]
|
7
|
+
opts = format_options opts
|
8
|
+
|
9
|
+
if config.ignore_payload_names.include?(opts[:name]) ||
|
10
|
+
config.ignore_sql_commands.include?(sql_command_of opts[:sql])
|
11
|
+
return
|
19
12
|
end
|
13
|
+
|
14
|
+
Sqlogger::Elite.post_with opts
|
20
15
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
16
|
+
|
17
|
+
def config
|
18
|
+
Rails.application.config.sqlogger
|
25
19
|
end
|
26
|
-
end
|
27
20
|
|
28
|
-
|
29
|
-
|
21
|
+
private
|
22
|
+
|
23
|
+
def sql_command_of sql=""
|
24
|
+
sql.split.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def format_sql sql=""
|
28
|
+
sql.gsub(/(\s+|\n)/, " ")
|
29
|
+
end
|
30
|
+
|
31
|
+
def format_options opts={}
|
32
|
+
opts[:name] ||= ""
|
33
|
+
opts[:sql] = format_sql opts[:sql]
|
34
|
+
opts[:binds] = format_sql opts[:binds].strip
|
35
|
+
opts
|
36
|
+
end
|
30
37
|
end
|
38
|
+
|
31
39
|
end
|
32
40
|
end
|
data/lib/sqlogger/elite.rb
CHANGED
@@ -1,2 +1,29 @@
|
|
1
1
|
require "sqlogger/elite/elasticsearch"
|
2
2
|
require "sqlogger/elite/echo"
|
3
|
+
|
4
|
+
module Sqlogger
|
5
|
+
module Elite
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def post_with opts
|
9
|
+
Thread.start do
|
10
|
+
unique_post_target.each do |post_target|
|
11
|
+
case post_target
|
12
|
+
when "echo"
|
13
|
+
Sqlogger::Elite::Echo::post opts
|
14
|
+
when "elasticsearch"
|
15
|
+
Sqlogger::Elite::Elasticsearch.post opts
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def unique_post_target
|
24
|
+
Sqlogger::Base.config.post_targets.map(&:to_s).uniq
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/sqlogger/elite/echo.rb
CHANGED
@@ -1,31 +1,41 @@
|
|
1
1
|
module Sqlogger
|
2
2
|
module Elite
|
3
|
-
|
3
|
+
class Echo
|
4
4
|
require "open3"
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
class << self
|
7
|
+
def post opts={}
|
8
|
+
return unless opts[:sql]
|
9
|
+
echo_file = Sqlogger::Base.config.echo.file
|
10
|
+
debug = Sqlogger::Base.config.echo.debug
|
11
|
+
information = information_string_of opts
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
Rails.logger.error "Echo fail."
|
13
|
+
echo_result = Open3.capture3 "echo \"#{information}\" >> #{echo_file}"
|
14
|
+
if debug
|
15
|
+
if echo_result.last.exitstatus == 0
|
16
|
+
Rails.logger.info "Echo ok."
|
17
|
+
else
|
18
|
+
Rails.logger.error "Echo fail."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
rescue => ex
|
22
|
+
if debug
|
23
|
+
Rails.logger.error ex.message
|
23
24
|
end
|
24
25
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def information_string_of opts={}
|
30
|
+
info = opts.map do |k,v|
|
31
|
+
"#{k.to_s.upcase}: #{v}#{"ms" if k == :dulation}"
|
32
|
+
end
|
33
|
+
info.unshift "Time: #{Time.now}"
|
34
|
+
info.unshift "PID: #{Process.pid()}"
|
35
|
+
info.push "" << "-" * 10
|
36
|
+
info.join("\n").tr '"', '\''
|
28
37
|
end
|
38
|
+
|
29
39
|
end
|
30
40
|
end
|
31
41
|
end
|
@@ -3,87 +3,102 @@ module Sqlogger
|
|
3
3
|
module Elasticsearch
|
4
4
|
require 'net/http'
|
5
5
|
require 'uri'
|
6
|
+
class << self
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
}
|
12
|
-
end
|
8
|
+
def post opts={}
|
9
|
+
return unless opts[:sql]
|
10
|
+
post_data = generate_postdata_with opts
|
11
|
+
post_keys = generate_postkeys
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
post_data.reject! do |k, _|
|
14
|
+
!post_keys.include? k
|
15
|
+
end
|
16
|
+
random_hash = Digest::MD5.hexdigest "#{post_data["key"]}#{Time.now.to_i}#{rand(999)}"
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
https_post "#{url}#{random_hash}", post_data
|
19
|
+
rescue => ex
|
20
|
+
if config.debug
|
21
|
+
Rails.logger.error ex.message
|
22
|
+
end
|
23
|
+
end
|
21
24
|
|
22
|
-
|
23
|
-
index_name = Rails.application.config.sqlogger.elasticsearch.index_name
|
24
|
-
"#{base_url}#{"/" unless base_url.end_with?('/')}#{index_name}/#{check_name}/"
|
25
|
-
end
|
25
|
+
private
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
sql_action = opts[:sql].split.first
|
30
|
-
hostname = `hostname`.strip
|
31
|
-
hostname = "CANTGET" if hostname.empty?
|
32
|
-
key = "#{hostname}.queries.#{sql_action}"
|
33
|
-
post_data = self.post_data_base
|
34
|
-
send_keys = Rails.application.config.sqlogger.elasticsearch.post_keys
|
35
|
-
dulation = opts[:dulation] || 0.00
|
36
|
-
|
37
|
-
send_keys.push "check_name"
|
38
|
-
send_keys.push "key"
|
39
|
-
send_keys.push "@timestamp"
|
40
|
-
crit_dul = Rails.application.config.sqlogger.elasticsearch.critical_dulation
|
41
|
-
warn_dul = Rails.application.config.sqlogger.elasticsearch.warning_dulation
|
42
|
-
http_ssl_none_verify = Rails.application.config.sqlogger.elasticsearch.ssl_verify_none
|
43
|
-
post_timeout = Rails.application.config.sqlogger.elasticsearch.post_timeout
|
44
|
-
http_open_timeout = Rails.application.config.sqlogger.elasticsearch.open_timeout
|
45
|
-
http_read_timeout = Rails.application.config.sqlogger.elasticsearch.read_timeout
|
46
|
-
debug = Rails.application.config.sqlogger.elasticsearch.debug
|
47
|
-
|
48
|
-
post_data['server'] = hostname
|
49
|
-
post_data['check_name'] = check_name
|
50
|
-
post_data['status'] = dulation > crit_dul ? 2 : dulation > warn_dul ? 1 : 0
|
51
|
-
post_data['key'] = key
|
52
|
-
post_data['sql'] = opts[:sql]
|
53
|
-
post_data['binds'] = opts[:binds]
|
54
|
-
post_data['dulation'] = dulation
|
55
|
-
post_data['payload'] = opts[:name]
|
56
|
-
|
57
|
-
post_data.reject! do |k, _|
|
58
|
-
!send_keys.include? k
|
27
|
+
def config
|
28
|
+
Sqlogger::Base.config.elasticsearch
|
59
29
|
end
|
60
30
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
31
|
+
def https_post full_url, post_data
|
32
|
+
Timeout.timeout(config.post_timeout) do
|
33
|
+
uri = URI.parse full_url
|
34
|
+
http = Net::HTTP.new uri.host, uri.port
|
35
|
+
http.use_ssl = true if base_url =~ /\Ahttps/
|
36
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.ssl_verify_none
|
37
|
+
http.open_timeout = config.open_timeout
|
38
|
+
http.read_timeout = config.read_timeout
|
39
|
+
|
40
|
+
request = Net::HTTP::Post.new(
|
41
|
+
uri.path,
|
42
|
+
"content-type" => "application/json; charset=utf-8"
|
43
|
+
)
|
44
|
+
request.body = JSON.dump post_data
|
45
|
+
response = http.request request
|
46
|
+
if config.debug
|
47
|
+
Rails.logger.info "Elasticsearch posted #{response.code}."
|
48
|
+
end
|
49
|
+
if response.code.to_i/10 != 20
|
50
|
+
Rails.logger.error "Elasticsearch posting with failure #{response.code}."
|
51
|
+
end
|
81
52
|
end
|
82
53
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
54
|
+
|
55
|
+
def generate_postkeys
|
56
|
+
config.post_keys + %w(check_name key @timestamp)
|
57
|
+
end
|
58
|
+
|
59
|
+
def generate_postdata_with opts
|
60
|
+
hostname = predictive_hostname
|
61
|
+
dulation = opts[:dulation] || 0.00
|
62
|
+
key = "#{hostname}.queries.#{opts[:sql].split.first}"
|
63
|
+
|
64
|
+
{
|
65
|
+
"@timestamp" => DateTime.now.to_s,
|
66
|
+
"pid" => Process.pid(),
|
67
|
+
"status" => status_determined_from(dulation),
|
68
|
+
"server" => hostname,
|
69
|
+
"check_name" => check_name,
|
70
|
+
"key" => key,
|
71
|
+
"sql" => opts[:sql],
|
72
|
+
"binds" => opts[:binds],
|
73
|
+
"dulation" => dulation,
|
74
|
+
"payload" => opts[:name]
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def predictive_hostname
|
79
|
+
hostname = `hostname`.strip
|
80
|
+
hostname.empty? ? "CANTGET" : hostname
|
81
|
+
end
|
82
|
+
|
83
|
+
def status_determined_from dulation
|
84
|
+
crit_dul = config.critical_dulation
|
85
|
+
warn_dul = config.warning_dulation
|
86
|
+
dulation > crit_dul ? 2 : dulation > warn_dul ? 1 : 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_name
|
90
|
+
config.check_name
|
91
|
+
end
|
92
|
+
|
93
|
+
def base_url
|
94
|
+
config.url
|
86
95
|
end
|
96
|
+
|
97
|
+
def url
|
98
|
+
index_name = config.index_name
|
99
|
+
"#{base_url}#{"/" unless base_url.end_with? '/'}#{index_name}/#{check_name}/"
|
100
|
+
end
|
101
|
+
|
87
102
|
end
|
88
103
|
end
|
89
104
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Sqlogger
|
2
|
+
module Monkey
|
3
|
+
module IncludeActiveRecordLogSubscriber
|
4
|
+
def self.included base
|
5
|
+
base.class_eval do
|
6
|
+
alias_method :sql_without_sqlogger, :sql
|
7
|
+
alias_method :sql, :sql_with_sqlogger
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def sql_with_sqlogger event
|
12
|
+
sql_without_sqlogger event
|
13
|
+
|
14
|
+
payload = event.payload
|
15
|
+
binds = ""
|
16
|
+
unless (payload[:binds] || []).empty?
|
17
|
+
casted_params = sqlogger_type_casted_binds payload[:binds], payload[:type_casted_binds]
|
18
|
+
binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
|
19
|
+
sqlogger_render_bind attr, value
|
20
|
+
}.inspect
|
21
|
+
end
|
22
|
+
Sqlogger::Base::logger(
|
23
|
+
sql: payload[:sql],
|
24
|
+
binds: binds,
|
25
|
+
dulation: event.duration.round(1),
|
26
|
+
name: "#{"CACHE " if payload[:cached]}#{payload[:name]}"
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# for support under rails 5.1.0
|
33
|
+
def sqlogger_type_casted_binds binds, casted_binds
|
34
|
+
casted_binds || binds.map do |attr|
|
35
|
+
ActiveRecord::Base.connection.type_cast attr.value_for_database
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# for support under rails 5.1.0
|
40
|
+
def sqlogger_render_bind attr, type_casted_value
|
41
|
+
value = if attr.type.binary? && attr.value
|
42
|
+
"<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
43
|
+
else
|
44
|
+
type_casted_value
|
45
|
+
end
|
46
|
+
|
47
|
+
[attr.name, value]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/sqlogger/railtie.rb
CHANGED
@@ -4,8 +4,8 @@ module Sqlogger
|
|
4
4
|
class Railtie < Rails::Railtie
|
5
5
|
initializer "sqlogger_railtie.configure_rails_initialization" do
|
6
6
|
ActiveSupport.on_load :active_record do
|
7
|
-
require 'sqlogger/monkey/
|
8
|
-
ActiveRecord::LogSubscriber.send :include, Sqlogger::Monkey::
|
7
|
+
require 'sqlogger/monkey/include_active_record_log_subscriber'
|
8
|
+
ActiveRecord::LogSubscriber.send :include, Sqlogger::Monkey::IncludeActiveRecordLogSubscriber
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -15,7 +15,7 @@ module Sqlogger
|
|
15
15
|
|
16
16
|
#define default values
|
17
17
|
config.sqlogger.ignore_payload_names = %w(SCHEMA EXPLAIN)
|
18
|
-
config.sqlogger.ignore_sql_commands =
|
18
|
+
config.sqlogger.ignore_sql_commands = %w(begin rollback SAVEPOINT RELEASE)
|
19
19
|
config.sqlogger.post_targets = []
|
20
20
|
|
21
21
|
config.sqlogger.elasticsearch.check_name = "metrics-query"
|
data/lib/sqlogger/version.rb
CHANGED
@@ -16,7 +16,7 @@ namespace :sqlogger do
|
|
16
16
|
|
17
17
|
## ignore_sql_commands
|
18
18
|
## set to not logging selected payloads.
|
19
|
-
#Rails.application.config.sqlogger.ignore_sql_commands = %w()
|
19
|
+
#Rails.application.config.sqlogger.ignore_sql_commands = %w(begin rollback SAVEPOINT RELEASE)
|
20
20
|
|
21
21
|
## post_targets: set to logging target. e.g. %w(elasticsearch echo)
|
22
22
|
## (current only support echo and elasticsearch)
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqlogger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- metalels
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: sqlite3
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,11 +81,11 @@ files:
|
|
81
81
|
- lib/sqlogger/elite.rb
|
82
82
|
- lib/sqlogger/elite/echo.rb
|
83
83
|
- lib/sqlogger/elite/elasticsearch.rb
|
84
|
-
- lib/sqlogger/monkey/
|
84
|
+
- lib/sqlogger/monkey/include_active_record_log_subscriber.rb
|
85
85
|
- lib/sqlogger/railtie.rb
|
86
86
|
- lib/sqlogger/version.rb
|
87
87
|
- lib/tasks/sqlogger_tasks.rake
|
88
|
-
homepage: https://github.com/metalels/
|
88
|
+
homepage: https://github.com/metalels/sqlogger
|
89
89
|
licenses:
|
90
90
|
- MIT
|
91
91
|
metadata: {}
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Sqlogger
|
2
|
-
module Monkey
|
3
|
-
module ActiveRecord
|
4
|
-
module ReplaceLogSubscriber
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval do
|
7
|
-
alias_method :sql_without_sqlogger, :sql
|
8
|
-
alias_method :sql, :sql_with_sqlogger
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def sql_with_sqlogger(event)
|
13
|
-
sql_without_sqlogger(event);
|
14
|
-
|
15
|
-
payload = event.payload
|
16
|
-
binds = nil
|
17
|
-
unless (payload[:binds] || []).empty? && payload[:type_casted_binds].present?
|
18
|
-
casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
|
19
|
-
binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
|
20
|
-
render_bind(attr, value)
|
21
|
-
}.inspect
|
22
|
-
end
|
23
|
-
Sqlogger::Base::logger(
|
24
|
-
sql: payload[:sql],
|
25
|
-
binds: binds,
|
26
|
-
dulation: event.duration.round(1),
|
27
|
-
name: "#{"CACHE " if payload[:cached]}#{payload[:name]}"
|
28
|
-
)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|