sqlogger 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7fada0c387b433c3fb508705aeb367ce751e94a1
4
+ data.tar.gz: cbe9067586ae812c40021f17d9b3e19aa6ac6bcf
5
+ SHA512:
6
+ metadata.gz: 0fca7bbbcfb231c69d9b982518b482be14a15efef924ca92ad423eda6ace636bb9d19d52dc54712d5a7efd1f2061df6e9209ca20adc3d75e9549e417c7c799d0
7
+ data.tar.gz: d72f581068251375945aa2f3fafdd0aef0ad7bb265a0f1c15100898fc42864602430911c8b0cf69551a63386a2b68ac8db29df3c6a50f32290cdccaf1d90b831
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 metalels
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](MIT-LICENSE)
2
+ [![Build Status](https://travis-ci.org/metalels/sqlogger.svg?branch=master)](https://travis-ci.org/metalels/sqlogger)
3
+ [![Code Climate](https://codeclimate.com/github/metalels/sqlogger/badges/gpa.svg)](https://codeclimate.com/github/metalels/sqlogger)
4
+ [![Test Coverage](https://codeclimate.com/github/metalels/sqlogger/badges/coverage.svg)](https://codeclimate.com/github/metalels/sqlogger/coverage)
5
+ [![Issue Count](https://codeclimate.com/github/metalels/sqlogger/badges/issue_count.svg)](https://codeclimate.com/github/metalels/sqlogger)
6
+
7
+ # Sqlogger
8
+ Collect 'ActiveRecord sql query' to monitoring system(s).
9
+
10
+ <img src="sqlogger_elasticsearch.png" width="580" alt="send sql-log to elasticsearch">
11
+
12
+ ## Dependency
13
+ Currently only supports Rails 5.1.0beta1 or more.
14
+
15
+ ## Installation
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'sqlogger', group: :development
20
+ ```
21
+
22
+ And then execute in Rails Project root directory:
23
+ ```bash
24
+ $ bundle
25
+ $ rake sqlogger:install
26
+ ```
27
+
28
+ ## Usage
29
+ edit sqlogger setting file in *config/initializers/sqlogger.rb*.
30
+
31
+ ## Contributing
32
+ git-flow.
33
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Sqlogger'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
18
+
19
+ require 'rake/testtask'
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.libs << 'test'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+
27
+
28
+ task default: :test
@@ -0,0 +1,32 @@
1
+ module Sqlogger
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
19
+ end
20
+ end
21
+ if post_targets.include?("echo") || post_targets.include?(:echo)
22
+ Thread.start do
23
+ Sqlogger::Elite::Echo::post opts
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.format_sql(sql="")
29
+ sql.gsub(/(\s+|\n)/, " ")
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ module Sqlogger
2
+ module Elite
3
+ module Echo
4
+ require "open3"
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
10
+
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."
23
+ end
24
+ end
25
+ rescue => ex
26
+ if debug
27
+ Rails.logger.error ex.message
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,90 @@
1
+ module Sqlogger
2
+ module Elite
3
+ module Elasticsearch
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ def self.post_data_base
8
+ {
9
+ "@timestamp" => DateTime.now.to_s,
10
+ "pid" => Process.pid()
11
+ }
12
+ end
13
+
14
+ def self.check_name
15
+ Rails.application.config.sqlogger.elasticsearch.check_name
16
+ end
17
+
18
+ def self.base_url
19
+ Rails.application.config.sqlogger.elasticsearch.url
20
+ end
21
+
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
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
59
+ end
60
+
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}."
81
+ end
82
+ end
83
+ rescue => ex
84
+ if debug
85
+ Rails.logger.error ex.message
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,2 @@
1
+ require "sqlogger/elite/elasticsearch"
2
+ require "sqlogger/elite/echo"
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,40 @@
1
+ require 'rails'
2
+
3
+ module Sqlogger
4
+ class Railtie < Rails::Railtie
5
+ initializer "sqlogger_railtie.configure_rails_initialization" do
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
9
+ end
10
+ end
11
+
12
+ config.sqlogger = ActiveSupport::OrderedOptions.new
13
+ config.sqlogger.elasticsearch = ActiveSupport::OrderedOptions.new
14
+ config.sqlogger.echo = ActiveSupport::OrderedOptions.new
15
+
16
+ #define default values
17
+ config.sqlogger.ignore_payload_names = %w(SCHEMA EXPLAIN)
18
+ config.sqlogger.ignore_sql_commands = []
19
+ config.sqlogger.post_targets = []
20
+
21
+ config.sqlogger.elasticsearch.check_name = "metrics-query"
22
+ config.sqlogger.elasticsearch.url = "http://localhost:9200/"
23
+ config.sqlogger.elasticsearch.index_name = "sqlogger-metrics"
24
+ config.sqlogger.elasticsearch.post_keys = %w(server status pid sql binds dulation payload)
25
+ config.sqlogger.elasticsearch.critical_dulation = 10.0
26
+ config.sqlogger.elasticsearch.warning_dulation = 5.0
27
+ config.sqlogger.elasticsearch.ssl_verify_none = false
28
+ config.sqlogger.elasticsearch.post_timeout = 1.0
29
+ config.sqlogger.elasticsearch.open_timeout = 1.0
30
+ config.sqlogger.elasticsearch.read_timeout = 1.0
31
+ config.sqlogger.elasticsearch.debug = false
32
+
33
+ config.sqlogger.echo.file = "log/sqlogger.log"
34
+ config.sqlogger.echo.debug = false
35
+
36
+ rake_tasks do
37
+ load "tasks/sqlogger_tasks.rake"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Sqlogger
2
+ VERSION = '0.1.0'
3
+ end
data/lib/sqlogger.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "sqlogger/version"
2
+ require "sqlogger/base"
3
+ require "sqlogger/elite"
4
+ require "sqlogger/railtie" if defined?(Rails)
@@ -0,0 +1,49 @@
1
+ # desc "Explaining what the task does"
2
+
3
+ task :sqlogger => "sqlogger:install"
4
+ namespace :sqlogger do
5
+ desc "install sqlogger to a rails project."
6
+ task :install do
7
+ conf_path = Rails.root.join("config").join("initializers").join("sqlogger.rb")
8
+ if File.exist? conf_path
9
+ puts "Sqlogger has already installed."
10
+ puts "For more details, see your config/initializers/sqlogger.rb file."
11
+ else
12
+ conf_default_body = <<-EOS
13
+ ## ignore_payload_names
14
+ ## set to not logging selected payloads.
15
+ #Rails.application.config.sqlogger.ignore_payload_names = %w(SCHEMA EXPLAIN)
16
+
17
+ ## ignore_sql_commands
18
+ ## set to not logging selected payloads.
19
+ #Rails.application.config.sqlogger.ignore_sql_commands = %w()
20
+
21
+ ## post_targets: set to logging target. e.g. %w(elasticsearch echo)
22
+ ## (current only support echo and elasticsearch)
23
+ #Rails.application.config.sqlogger.post_targets = %w()
24
+
25
+ ## elasticsearch options
26
+ ## only uses set to logging to elasticsearch on post_targets.
27
+ #Rails.application.config.sqlogger.elasticsearch.url = "http://localhost:9200/"
28
+ #Rails.application.config.sqlogger.elasticsearch.index_name = "sqlogger-metrics"
29
+ #Rails.application.config.sqlogger.elasticsearch.check_name = "metrics-query"
30
+ #Rails.application.config.sqlogger.elasticsearch.post_keys = %w(server status pid sql binds dulation payload)
31
+ #Rails.application.config.sqlogger.elasticsearch.critical_dulation = 10.0
32
+ #Rails.application.config.sqlogger.elasticsearch.warning_dulation = 5.0
33
+ #Rails.application.config.sqlogger.elasticsearch.ssl_verify_none = false
34
+ #Rails.application.config.sqlogger.elasticsearch.post_timeout = 1.0
35
+ #Rails.application.config.sqlogger.elasticsearch.open_timeout = 1.0
36
+ #Rails.application.config.sqlogger.elasticsearch.read_timeout = 1.0
37
+ #Rails.application.config.sqlogger.elasticsearch.debug = false
38
+
39
+ ## echo options (sysmem echo to file)
40
+ ## only uses set to logging to echo on post_targets.
41
+ #Rails.application.config.sqlogger.echo.file = "log/sqlogger.log"
42
+ #Rails.application.config.sqlogger.echo.debug = false
43
+ EOS
44
+ File.write(conf_path, conf_default_body)
45
+ puts "Sqlogger has been successfully installed."
46
+ puts "For more details, see your config/initializers/sqlogger.rb file."
47
+ end
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sqlogger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - metalels
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.0.beta.pre.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.1.0.beta.pre.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mocha
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Collect 'ActiveRecord sql query' to monitoring system(s).
70
+ email:
71
+ - metalels86@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - lib/sqlogger.rb
80
+ - lib/sqlogger/base.rb
81
+ - lib/sqlogger/elite.rb
82
+ - lib/sqlogger/elite/echo.rb
83
+ - lib/sqlogger/elite/elasticsearch.rb
84
+ - lib/sqlogger/monkey/active_record/replace_log_subscriber.rb
85
+ - lib/sqlogger/railtie.rb
86
+ - lib/sqlogger/version.rb
87
+ - lib/tasks/sqlogger_tasks.rake
88
+ homepage: https://github.com/metalels/rails-sqlogger
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.6.8
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Collect 'ActiveRecord sql query' to monitoring system(s).
112
+ test_files: []