pg-connection-general_log 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 867e6e436c5d596d967ce3752bb0d6628ce265ee
4
+ data.tar.gz: '092e57f1a54da5a7d549e46731e3a25950f7cbb6'
5
+ SHA512:
6
+ metadata.gz: bd28c680fc41cd5373a542b6f53824026c0faf0f6363c520e6b349a5bae2500dfa0737d51912d7b80886bc84fab5faf43675e1a53e9d4dfeada2ef50d4e4b111
7
+ data.tar.gz: daa5b3ccefa426f44ab11f42d7a2943aa1de20d50514a0151e4691525dd1a98550cc4e8dae2196955765a22d2c4f6ea8c6124f06856540a3dcafb45a3ebd4fb7
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.5
4
+ - 2.4.2
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pg-connection-general_log.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 abcang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,83 @@
1
+ PG:Connection::GeneralLog
2
+ ===
3
+
4
+ [![Build Status](https://travis-ci.org/abcang/pg-connection-general_log.svg?branch=v0.0.1)](https://travis-ci.org/abcang/pg-connection-general_log)
5
+
6
+ A monkey patch for pg.
7
+ Stock all general logs.
8
+
9
+ Inspired by [mysql2-client-general_log](https://github.com/ksss/mysql2-client-general_log)
10
+
11
+ ```ruby
12
+ #! /usr/bin/env ruby
13
+
14
+ require "pg/connection/general_log"
15
+
16
+ client = PG::Connection.new(config)
17
+ client.query("SELECT * FROM users LIMIT 1")
18
+
19
+ p client.general_log #=>
20
+ # [
21
+ # #<struct PG::Connection::GeneralLog::Log
22
+ # sql="SELECT * FROM users LIMIT 1",
23
+ # args=[],
24
+ # backtrace=["script.rb:6:in `<main>'"],
25
+ # time=0.0909838349907659>
26
+ # ]
27
+ ```
28
+
29
+ ## Examples
30
+
31
+ ### sinatra
32
+
33
+ test.rb:
34
+ ```ruby
35
+ require 'sinatra'
36
+ require 'pg'
37
+ require "pg/connection/general_log"
38
+
39
+ helpers do
40
+ def db
41
+ Thread.current[:db] ||= PG::Connection.new(config)
42
+ end
43
+ end
44
+
45
+ get '/' do
46
+ db.exec("SELECT * FROM users WHERE name = 'hoge'")
47
+ db.exec_params('SELECT * FROM users WHERE name = $1', ['hoge'])
48
+
49
+ db.prepare('select', 'SELECT * FROM users WHERE name = $1')
50
+ db.exec_prepared('select', ['bar'])
51
+ db.exec_prepared('select', ['foo'])
52
+ end
53
+
54
+ after do
55
+ db.general_log.writefile(path: '/tmp/sql.log', req: request, backtrace: true)
56
+ end
57
+ ```
58
+
59
+ /tmp/sql.log:
60
+ ```
61
+ REQUEST GET / 4
62
+ SQL (0000.89ms) SELECT * FROM users WHERE name = 'hoge' [] /path/to/test.rb:12:in `block in <main>'
63
+ SQL (0000.78ms) SELECT * FROM users WHERE name = $1 ["hoge"] /path/to/test.rb:13:in `block in <main>'
64
+ SQL (0000.66ms) SELECT * FROM users WHERE name = $1 ["barr"] /path/to/test.rb:16:in `block in <main>'
65
+ SQL (0000.65ms) SELECT * FROM users WHERE name = $1 ["foo"] /path/to/test.rb:17:in `block in <main>'
66
+ ```
67
+
68
+ ## Installation
69
+
70
+ Add this line to your application's Gemfile:
71
+
72
+ ```ruby
73
+ gem 'pg-connection-general_log'
74
+ ```
75
+
76
+ And then execute:
77
+
78
+ $ bundle
79
+
80
+
81
+ ## License
82
+
83
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :test do
4
+ sh "rgot -v #{Dir.glob('lib/**/*_test.rb').join(' ')}"
5
+ end
6
+
7
+ task default: [:test]
@@ -0,0 +1,48 @@
1
+ require 'sinatra'
2
+ require 'pg'
3
+ require 'pg/connection/general_log'
4
+
5
+ helpers do
6
+ def db
7
+ Thread.current[:db] ||= PG.connect(
8
+ host: '127.0.0.1',
9
+ user: 'postgres',
10
+ dbname: 'pg_connection_general_log_test'
11
+ )
12
+ end
13
+
14
+ def init
15
+ db.exec('DROP TABLE IF EXISTS users')
16
+ db.exec(<<-SQL)
17
+ CREATE TABLE users (
18
+ id SERIAL NOT NULL PRIMARY KEY,
19
+ name varchar(255) NOT NULL UNIQUE,
20
+ password varchar(255) NOT NULL
21
+ );
22
+ SQL
23
+ db.exec(<<-SQL)
24
+ INSERT INTO users (name, password)
25
+ VALUES ('hoge', 'cheap-pass'),
26
+ ('foo', 'fooo'),
27
+ ('bar', 'barr')
28
+ ;
29
+ SQL
30
+ end
31
+ end
32
+
33
+ get '/' do
34
+ db.exec("SELECT * FROM users WHERE name = 'hoge'")
35
+ db.exec_params('SELECT * FROM users WHERE name = $1', ['hoge'])
36
+
37
+ db.prepare('select', 'SELECT * FROM users WHERE name = $1')
38
+ db.exec_prepared('select', ['bar'])
39
+ db.exec_prepared('select', ['foo'])
40
+ end
41
+
42
+ get '/init' do
43
+ init
44
+ end
45
+
46
+ after do
47
+ db.general_log.writefile(req: request, backtrace: true)
48
+ end
@@ -0,0 +1,89 @@
1
+ require 'pg'
2
+ require 'benchmark'
3
+
4
+ module PG
5
+ class Connection
6
+ module GeneralLog
7
+ require 'pg/connection/general_log/version'
8
+
9
+ class Log < Struct.new(:sql, :args, :backtrace, :time)
10
+ def format(use_bt = false)
11
+ ret = [
12
+ 'SQL',
13
+ '(%07.2fms)' % (time * 1000),
14
+ sql.gsub(/[\r\n]/, ' ').gsub(/ +/, ' ').strip,
15
+ args.to_s
16
+ ]
17
+ ret << backtrace[0] if use_bt
18
+
19
+ ret.join("\t")
20
+ end
21
+ end
22
+
23
+ class Logger < Array
24
+ def writefile(path: '/tmp/sql.log', req: nil, backtrace: false)
25
+ File.open(path, 'a') do |file|
26
+ if req
27
+ file.puts "REQUEST\t#{req.request_method}\t#{req.path}\t#{self.length}"
28
+ end
29
+
30
+ file.puts self.map { |log| log.format(backtrace) }.join("\n")
31
+ file.puts ''
32
+ end
33
+ self.clear
34
+ end
35
+
36
+ def push(sql, args, backtrace, time)
37
+ super(Log.new(sql, args, backtrace, time))
38
+ end
39
+ end
40
+
41
+ attr_accessor :general_log
42
+
43
+ def initialize(opts = {})
44
+ @general_log = Logger.new
45
+ @stmt_map = {}
46
+ super
47
+ end
48
+
49
+ def exec(sql)
50
+ ret = nil
51
+ time = Benchmark.realtime do
52
+ ret = super
53
+ end
54
+ @general_log.push(sql, [], caller_locations, time)
55
+ ret
56
+ end
57
+
58
+ def exec_params(*args)
59
+ sql, params = args
60
+ ret = nil
61
+ time = Benchmark.realtime do
62
+ ret = super
63
+ end
64
+ @general_log.push(sql, params, caller_locations, time)
65
+ ret
66
+ end
67
+
68
+ def prepare(*args)
69
+ stmt_name, sql = args
70
+ @stmt_map[stmt_name] = sql
71
+ super
72
+ end
73
+
74
+ def exec_prepared(*args)
75
+ stmt_name, params = args
76
+ sql = @stmt_map[stmt_name]
77
+
78
+ ret = nil
79
+ time = Benchmark.realtime do
80
+ ret = super
81
+ end
82
+ @general_log.push(sql, params, caller_locations, time)
83
+ ret
84
+ end
85
+ end
86
+
87
+ prepend GeneralLog
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ module PG
2
+ module GeneralLog
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,144 @@
1
+ require 'pg/connection/general_log'
2
+
3
+ module PGConnectionGeneralLogTest
4
+ def test_main(m)
5
+ client = PG.connect(
6
+ host: '127.0.0.1',
7
+ user: 'postgres'
8
+ )
9
+ client.exec('DROP DATABASE IF EXISTS pg_connection_general_log_test')
10
+ client.exec('CREATE DATABASE pg_connection_general_log_test')
11
+
12
+ @client = PG.connect(
13
+ host: '127.0.0.1',
14
+ user: 'postgres',
15
+ dbname: 'pg_connection_general_log_test'
16
+ )
17
+ exit m.run
18
+
19
+ client.exec('DROP DATABASE IF EXISTS pg_connection_general_log_test')
20
+ end
21
+
22
+ def db_init
23
+ @client.exec('DROP TABLE IF EXISTS users')
24
+ @client.exec(<<~SQL)
25
+ CREATE TABLE users (
26
+ id SERIAL NOT NULL PRIMARY KEY,
27
+ name varchar(255) NOT NULL UNIQUE,
28
+ password varchar(255) NOT NULL
29
+ );
30
+ SQL
31
+ @client.exec(<<~SQL)
32
+ INSERT INTO users (name, password)
33
+ VALUES ('hoge', 'cheap-pass'),
34
+ ('foo', 'fooo'),
35
+ ('bar', 'barr')
36
+ ;
37
+ SQL
38
+ @client.general_log.clear
39
+ end
40
+
41
+ def test_init(t)
42
+ unless @client.general_log.is_a?(Array)
43
+ t.error("initial value expect Array class got #{@client.general_log.class}")
44
+ end
45
+ unless @client.general_log.empty?
46
+ t.error("initial value expect [] got #{@client.general_log}")
47
+ end
48
+ end
49
+
50
+ def test_values(t)
51
+ db_init
52
+ ret = @client.exec("SELECT * FROM users WHERE name = '#{'hoge'}'").first
53
+ @client.exec("SELECT * FROM users WHERE name = '#{'bar'}'")
54
+ @client.exec("SELECT * FROM users WHERE name = '#{'foo'}'")
55
+
56
+ if @client.general_log.length != 3
57
+ t.error("expect log length 3 got #{@client.general_log.length}")
58
+ end
59
+ if @client.general_log.any?{|log| !log.is_a?(PG::Connection::GeneralLog::Log)}
60
+ t.error("expect all collection item is instance of PG::Connection::GeneralLog::Log got #{@client.general_log.map(&:class).uniq}")
61
+ end
62
+ expect = { 'id' => '1', 'name' => 'hoge', 'password' => 'cheap-pass' }
63
+ if ret != expect
64
+ t.error("expect exec output not change from #{expect} got #{ret}")
65
+ end
66
+ unless @client.general_log.first.format =~ /^SQL\t\(\d+\.\d+ms\)\tSELECT \* FROM users WHERE name = 'hoge'\t\[\]$/
67
+ t.error("expect log format not correct got `#{@client.general_log.first.format}`")
68
+ end
69
+ unless @client.general_log.first.format(true) =~ /^SQL\t\(\d+\.\d+ms\)\tSELECT \* FROM users WHERE name = 'hoge'\t\[\].+in `test_values'$/
70
+ t.error("expect log format not correct got `#{@client.general_log.first.format(true)}`")
71
+ end
72
+ end
73
+
74
+ def test_params_values(t)
75
+ db_init
76
+ ret = @client.exec_params('SELECT * FROM users WHERE name = $1', ['hoge']).first
77
+ @client.exec_params('SELECT * FROM users WHERE name = $1', ['bar'])
78
+ @client.exec_params('SELECT * FROM users WHERE name = $1', ['foo'])
79
+
80
+ if @client.general_log.length != 3
81
+ t.error("expect log length 3 got #{@client.general_log.length}")
82
+ end
83
+ if @client.general_log.any?{|log| !log.is_a?(PG::Connection::GeneralLog::Log)}
84
+ t.error("expect all collection item is instance of PG::Connection::GeneralLog::Log got #{@client.general_log.map(&:class).uniq}")
85
+ end
86
+ expect = { 'id' => '1', 'name' => 'hoge', 'password' => 'cheap-pass' }
87
+ if ret != expect
88
+ t.error("expect exec output not change from #{expect} got #{ret}")
89
+ end
90
+ unless @client.general_log.first.format =~ /^SQL\t\(\d+\.\d+ms\)\tSELECT \* FROM users WHERE name = \$1\t\["hoge"\]$/
91
+ t.error("expect log format not correct got `#{@client.general_log.first.format}`")
92
+ end
93
+ unless @client.general_log.first.format(true) =~ /^SQL\t\(\d+\.\d+ms\)\tSELECT \* FROM users WHERE name = \$1\t\["hoge"\].+in `test_params_values'$/
94
+ t.error("expect log format not correct got `#{@client.general_log.first.format(true)}`")
95
+ end
96
+ end
97
+
98
+ def test_prepare_values(t)
99
+ db_init
100
+ @client.prepare('select', 'SELECT * FROM users WHERE name = $1')
101
+ ret = @client.exec_prepared('select', ['hoge']).first
102
+ @client.exec_prepared('select', ['bar'])
103
+ @client.exec_prepared('select', ['foo'])
104
+
105
+ if @client.general_log.length != 3
106
+ t.error("expect log length 3 got #{@client.general_log.length}")
107
+ end
108
+ if @client.general_log.any?{|log| !log.is_a?(PG::Connection::GeneralLog::Log)}
109
+ t.error("expect all collection item is instance of PG::Connection::GeneralLog::Log got #{@client.general_log.map(&:class).uniq}")
110
+ end
111
+ expect = { 'id' => '1', 'name' => 'hoge', 'password' => 'cheap-pass' }
112
+ if ret != expect
113
+ t.error("expect exec output not change from #{expect} got #{ret}")
114
+ end
115
+ unless @client.general_log.first.format =~ /^SQL\t\(\d+\.\d+ms\)\tSELECT \* FROM users WHERE name = \$1\t\["hoge"\]$/
116
+ t.error("expect log format not correct got `#{@client.general_log.first.format}`")
117
+ end
118
+ unless @client.general_log.first.format(true) =~ /^SQL\t\(\d+\.\d+ms\)\tSELECT \* FROM users WHERE name = \$1\t\["hoge"\].+in `test_prepare_values'$/
119
+ t.error("expect log format not correct got `#{@client.general_log.first.format(true)}`")
120
+ end
121
+ end
122
+
123
+ def test_log_class(t)
124
+ if PG::Connection::GeneralLog::Log.members != %i[sql args backtrace time]
125
+ t.error("expect PG::Connection::GeneralLog::Log.members is [:sql, :args, :backtrace, :time] got #{PG::Connection::GeneralLog::Log.members}")
126
+ end
127
+ end
128
+
129
+ def example_general_log
130
+ db_init
131
+ @client.exec("SELECT * FROM users WHERE name = '#{'hoge'}'")
132
+ @client.exec_params('SELECT * FROM users WHERE name = $1', ['hoge'])
133
+
134
+ @client.prepare('select2', 'SELECT * FROM users WHERE name = $1')
135
+ @client.exec_prepared('select2', ['bar'])
136
+ @client.exec_prepared('select2', ['foo'])
137
+ puts @client.general_log.map { |log| [log.sql, log.args.to_s, log.backtrace.find{|c| %r{/gems/} !~ c.to_s}.to_s.gsub(/.*?:/, '')].join(' ') }
138
+ # Output:
139
+ # SELECT * FROM users WHERE name = 'hoge' [] in `example_general_log'
140
+ # SELECT * FROM users WHERE name = $1 ["hoge"] in `example_general_log'
141
+ # SELECT * FROM users WHERE name = $1 ["bar"] in `example_general_log'
142
+ # SELECT * FROM users WHERE name = $1 ["foo"] in `example_general_log'
143
+ end
144
+ end
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ File.read('lib/pg/connection/general_log/version.rb') =~ /.*VERSION\s*=\s*['"](.*?)['"]\s.*/
4
+ version = Regexp.last_match(1)
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'pg-connection-general_log'
8
+ spec.version = version
9
+ spec.authors = ['abcang']
10
+ spec.email = ['abcang1015@gmail.com']
11
+
12
+ spec.summary = 'Simple stocker general log for pg gem.'
13
+ spec.description = 'Simple stocker general log for pg gem.'
14
+ spec.homepage = 'https://github.com/abcang/pg-connection-general_log'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_runtime_dependency 'pg'
21
+ spec.add_development_dependency 'bundler', '~> 1.15'
22
+ spec.add_development_dependency 'rake', '~> 12.2'
23
+ spec.add_development_dependency 'rgot'
24
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pg-connection-general_log
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - abcang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pg
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.15'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.15'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rgot
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: Simple stocker general log for pg gem.
70
+ email:
71
+ - abcang1015@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - example/test.rb
83
+ - lib/pg/connection/general_log.rb
84
+ - lib/pg/connection/general_log/version.rb
85
+ - lib/pg/connection/general_log_test.rb
86
+ - pg-connection-general_logs.gemspec
87
+ homepage: https://github.com/abcang/pg-connection-general_log
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.6.13
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Simple stocker general log for pg gem.
111
+ test_files: []