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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7fada0c387b433c3fb508705aeb367ce751e94a1
4
- data.tar.gz: cbe9067586ae812c40021f17d9b3e19aa6ac6bcf
3
+ metadata.gz: bbb935a232904d14b9ed45f474a9e577d289820b
4
+ data.tar.gz: 6724d5159ea9394746412ed20674aed43c590b8d
5
5
  SHA512:
6
- metadata.gz: 0fca7bbbcfb231c69d9b982518b482be14a15efef924ca92ad423eda6ace636bb9d19d52dc54712d5a7efd1f2061df6e9209ca20adc3d75e9549e417c7c799d0
7
- data.tar.gz: d72f581068251375945aa2f3fafdd0aef0ad7bb265a0f1c15100898fc42864602430911c8b0cf69551a63386a2b68ac8db29df3c6a50f32290cdccaf1d90b831
6
+ metadata.gz: b0581dfeec3e08900b091b76cfc385edd5080e77d2b5b07fdf28120b41c60f6031c2dd78e8c1701c9df28ae04e0856ec93a3896c3183cdc078837032c9b1cfd8
7
+ data.tar.gz: fae857d8456b34d5d9dd69cee742118c19358cfc1ee8a2b56812a4c155cd5ce13f90172ae84fe0af011f6af30497de57f51cacab6b66ab538b27c0cec59543da
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](MIT-LICENSE)
2
+ [![Gem Version](https://badge.fury.io/rb/sqlogger.svg)](https://badge.fury.io/rb/sqlogger)
2
3
  [![Build Status](https://travis-ci.org/metalels/sqlogger.svg?branch=master)](https://travis-ci.org/metalels/sqlogger)
3
4
  [![Code Climate](https://codeclimate.com/github/metalels/sqlogger/badges/gpa.svg)](https://codeclimate.com/github/metalels/sqlogger)
4
5
  [![Test Coverage](https://codeclimate.com/github/metalels/sqlogger/badges/coverage.svg)](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 only supports Rails 5.1.0beta1 or more.
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:
@@ -1,32 +1,40 @@
1
1
  module Sqlogger
2
2
  module Base
3
- def self.logger(opts={})
4
- return unless opts[:sql]
5
- opts[:name] ||= ""
6
- opts[:sql] = format_sql opts[:sql]
7
- opts[:binds] = format_sql opts[:binds].strip
8
- sql_command = opts[:sql].split.first
9
-
10
- ignore_payloads = Rails.application.config.sqlogger.ignore_payload_names
11
- ignore_commands = Rails.application.config.sqlogger.ignore_sql_commands
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
- if post_targets.include?("echo") || post_targets.include?(:echo)
22
- Thread.start do
23
- Sqlogger::Elite::Echo::post opts
24
- end
16
+
17
+ def config
18
+ Rails.application.config.sqlogger
25
19
  end
26
- end
27
20
 
28
- def self.format_sql(sql="")
29
- sql.gsub(/(\s+|\n)/, " ")
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
@@ -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
@@ -1,31 +1,41 @@
1
1
  module Sqlogger
2
2
  module Elite
3
- module Echo
3
+ class Echo
4
4
  require "open3"
5
5
 
6
- def self.post(opts={})
7
- return unless opts[:sql]
8
- echo_file = Rails.application.config.sqlogger.echo.file
9
- debug = Rails.application.config.sqlogger.echo.debug
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
- info = opts.map do |k,v|
12
- "#{k.to_s.upcase}: #{v}#{"ms" if k == :dulation}"
13
- end
14
- info.unshift "Time: #{Time.now}"
15
- info.unshift "PID: #{Process.pid()}"
16
- info.push "" << "-" * 10
17
- echo_result = Open3.capture3 "echo \"#{info.join("\n")}\" >> #{echo_file}"
18
- if debug
19
- if echo_result.last.exitstatus == 0
20
- Rails.logger.info "Echo ok."
21
- else
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
- rescue => ex
26
- if debug
27
- Rails.logger.error ex.message
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
- def self.post_data_base
8
- {
9
- "@timestamp" => DateTime.now.to_s,
10
- "pid" => Process.pid()
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
- def self.check_name
15
- Rails.application.config.sqlogger.elasticsearch.check_name
16
- end
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
- def self.base_url
19
- Rails.application.config.sqlogger.elasticsearch.url
20
- end
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
- def self.url
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
- def self.post(opts={})
28
- return unless opts[:sql]
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
- Timeout.timeout(post_timeout) do
62
- random_hash = Digest::MD5.hexdigest("#{key}#{Time.now.to_i}#{rand(999)}")
63
- uri = URI.parse "#{url}#{random_hash}"
64
- http = Net::HTTP.new uri.host, uri.port
65
- http.use_ssl = true if base_url =~ /\Ahttps/
66
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http_ssl_none_verify
67
- http.open_timeout = http_open_timeout
68
- http.read_timeout = http_read_timeout
69
-
70
- request = Net::HTTP::Post.new(
71
- uri.path,
72
- "content-type" => "application/json; charset=utf-8"
73
- )
74
- request.body = JSON.dump post_data
75
- response = http.request request
76
- if debug
77
- Rails.logger.info "Elasticsearch posted #{response.code}."
78
- end
79
- if response.code.to_i/10 != 20
80
- Rails.logger.error "Elasticsearch posting with failure #{response.code}."
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
- rescue => ex
84
- if debug
85
- Rails.logger.error ex.message
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
@@ -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/active_record/replace_log_subscriber'
8
- ActiveRecord::LogSubscriber.send :include, Sqlogger::Monkey::ActiveRecord::ReplaceLogSubscriber
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"
@@ -1,3 +1,3 @@
1
1
  module Sqlogger
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -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.1.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-07 00:00:00.000000000 Z
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: 5.1.0.beta.pre.1
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: 5.1.0.beta.pre.1
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/active_record/replace_log_subscriber.rb
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/rails-sqlogger
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