groonga-query-log 1.2.9 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. data/doc/text/news.md +28 -0
  3. data/lib/groonga-query-log/command/analyzer/reporter/console.rb +1 -1
  4. data/lib/groonga-query-log/command/check-crash.rb +128 -36
  5. data/lib/groonga-query-log/command/extract.rb +12 -2
  6. data/lib/groonga-query-log/command/replay.rb +6 -0
  7. data/lib/groonga-query-log/parser.rb +7 -7
  8. data/lib/groonga-query-log/replayer.rb +3 -0
  9. data/lib/groonga-query-log/statistic.rb +6 -2
  10. data/lib/groonga-query-log/version.rb +2 -2
  11. data/test/command/test-extract.rb +30 -27
  12. data/test/fixtures/multi.expected +7 -7
  13. data/test/fixtures/n_entries.expected +3 -3
  14. data/test/fixtures/no-report-summary.expected +3 -3
  15. data/test/fixtures/order/-elapsed.expected +5 -5
  16. data/test/fixtures/order/-start-time.expected +5 -5
  17. data/test/fixtures/order/elapsed.expected +5 -5
  18. data/test/fixtures/order/start-time.expected +5 -5
  19. data/test/fixtures/query.log +1 -1
  20. data/test/fixtures/reporter/console.expected +5 -5
  21. data/test/fixtures/reporter/html.expected +7 -7
  22. data/test/fixtures/reporter/json-stream.expected +1 -1
  23. data/test/fixtures/reporter/json.expected +1 -1
  24. data/test/fixtures/run-regression-test/db.new/db +0 -0
  25. data/test/fixtures/run-regression-test/db.new/db.0000000 +0 -0
  26. data/test/fixtures/run-regression-test/db.new/db.0000100 +0 -0
  27. data/test/fixtures/run-regression-test/db.new/db.0000101 +0 -0
  28. data/test/fixtures/run-regression-test/db.new/db.0000102 +0 -0
  29. data/test/fixtures/run-regression-test/db.new/db.0000103 +0 -0
  30. data/test/fixtures/run-regression-test/db.new/db.0000103.c +0 -0
  31. data/test/fixtures/run-regression-test/db.new/db.001 +0 -0
  32. data/test/fixtures/run-regression-test/db.new/db.conf +0 -0
  33. data/test/fixtures/run-regression-test/db.new/groonga.log +165 -0
  34. data/test/fixtures/run-regression-test/db.old/db +0 -0
  35. data/test/fixtures/run-regression-test/db.old/db.0000000 +0 -0
  36. data/test/fixtures/run-regression-test/db.old/db.0000100 +0 -0
  37. data/test/fixtures/run-regression-test/db.old/db.0000101 +0 -0
  38. data/test/fixtures/run-regression-test/db.old/db.0000102 +0 -0
  39. data/test/fixtures/run-regression-test/db.old/db.0000103 +0 -0
  40. data/test/fixtures/run-regression-test/db.old/db.0000103.c +0 -0
  41. data/test/fixtures/run-regression-test/db.old/db.001 +0 -0
  42. data/test/fixtures/run-regression-test/db.old/db.conf +0 -0
  43. data/test/fixtures/run-regression-test/db.old/groonga.log +79 -0
  44. data/test/fixtures/run-regression-test/results/query.log +0 -0
  45. data/test/fixtures/target-commands.expected +4 -4
  46. data/test/fixtures/target-tables.expected +3 -3
  47. data/test/run-test.rb +2 -0
  48. data/test/test-replayer.rb +2 -3
  49. metadata +78 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 213e0534e4b93c131e48f76cfe2c6f8c7fea4a44
4
- data.tar.gz: 326a76731faf0f948a8362800748503036dca728
2
+ SHA256:
3
+ metadata.gz: 2bb623843ad5665d8efc011ac13bc1e3a2b5b57deeef9c2af2f8de81e4a5a06c
4
+ data.tar.gz: 2cd63a89d51337d68d449f77ea0dbcf8698fa395c88a37707ce0b95a65c25bab
5
5
  SHA512:
6
- metadata.gz: 55fd3f7d8ed2fa51f94ed333e8afeaaef19ad5230b2a4ab8c0ce9622cde117891e3c26bda0f78a0560dcb0382a3b09c843c09cc474cb62faf3b3607a59d51b44
7
- data.tar.gz: eff099b4e055fc9e6e332a4d8bd8ae1987596ddab11fc842e83d965603147b68270395102cc9860cc09fb13e6004ebaf2a2bf8a949552bb9712e16552ff7bc5f
6
+ metadata.gz: a965a269d6b3f4562e55af72df38b0aaf1354fcdb27c068f9ca6c867997a130811c5386e919abb9410d3ed9895329f3a10551c5ebe01bb90845c3c9eb2cc9a73
7
+ data.tar.gz: dc717453f8f8f7f122eb6202c4825e543102dfd544d1f06a2c722d078e81aefa9a3949f120788f2f91bc469a98637c864a344d5acb348ecb87812509658ca4a9
@@ -1,5 +1,33 @@
1
1
  # News
2
2
 
3
+ ## 1.3.0: 2018-06-11
4
+
5
+ ### Improvements
6
+
7
+ * Added support for filter context.
8
+
9
+ * Added `xterm-256color` as a colorable terminal.
10
+
11
+ * `groonga-query-log-check-crash`:
12
+
13
+ * Added support for multiple process logs.
14
+
15
+ * Added more crash detection patterns.
16
+
17
+ * Added support for showing running queries on crash.
18
+
19
+ * Added support for showing important messages.
20
+
21
+ * `groonga-query-log-extract`:
22
+
23
+ * Added `--no-include-arguments` option.
24
+
25
+ * Improved pipe support.
26
+
27
+ * `groonga-query-log-replay`:
28
+
29
+ * Added `--read-timeout` option.
30
+
3
31
  ## 1.2.9: 2018-02-04
4
32
 
5
33
  ### Improvements
@@ -245,7 +245,7 @@ module GroongaQueryLog
245
245
  def guess_color_availability(output)
246
246
  return false unless output.tty?
247
247
  case ENV["TERM"]
248
- when /term(?:-color)?\z/, "screen"
248
+ when /term(?:-(?:256)?color)?\z/, "screen"
249
249
  true
250
250
  else
251
251
  return true if ENV["EMACS"] == "t"
@@ -70,31 +70,82 @@ module GroongaQueryLog
70
70
  checker.check
71
71
  end
72
72
 
73
+ class GroongaProcess
74
+ attr_reader :pid
75
+ attr_reader :start_time
76
+ attr_reader :log_path
77
+ attr_accessor :last_time
78
+ attr_accessor :n_leaks
79
+ attr_writer :crashed
80
+ attr_reader :important_entries
81
+ def initialize(pid, start_time, log_path)
82
+ @pid = pid
83
+ @start_time = start_time
84
+ @last_time = @start_time
85
+ @log_path = log_path
86
+ @n_leaks = 0
87
+ @crashed = false
88
+ @important_entries = []
89
+ end
90
+
91
+ def crashed?
92
+ @crashed
93
+ end
94
+ end
95
+
73
96
  class Checker
74
97
  def initialize(log_paths)
75
- @general_log_parser = GroongaLog::Parser.new
76
- @query_log_parser = Parser.new
77
98
  split_log_paths(log_paths)
78
-
79
- @running = nil
80
- @crash_sessions = []
81
- @session_start = nil
82
99
  end
83
100
 
84
101
  def check
85
- @general_log_parser.parse_paths(@general_log_paths) do |entry|
86
- check_general_log_entry(@general_log_parser.current_path,
87
- entry)
88
- end
89
- @crash_sessions.each do |start, last|
102
+ processes = ProcessEnumerator.new(@general_log_paths)
103
+ processes.each do |process|
104
+ if process.crashed?
105
+ p [:crashed,
106
+ process.start_time.iso8601,
107
+ process.last_time.iso8601,
108
+ process.pid,
109
+ process.log_path]
110
+ end
111
+
112
+ unless process.important_entries.empty?
113
+ puts("Important entries:")
114
+ process.important_entries.each_with_index do |entry, i|
115
+ puts("#{entry.timestamp.iso8601}: " +
116
+ "#{entry.log_level}: " +
117
+ "#{entry.message}")
118
+ end
119
+ end
120
+
121
+ unless process.n_leaks.zero?
122
+ p [:leak,
123
+ process.n_leaks,
124
+ process.last_time.iso8601,
125
+ process.log_path]
126
+ end
127
+
128
+ next unless process.crashed?
129
+
130
+ start = process.start_time
131
+ last = process.last_time
90
132
  @flushed = nil
91
133
  @unflushed_statistics = []
92
- @query_log_parser.parse_paths(@query_log_paths) do |statistic|
134
+ query_log_parser = Parser.new
135
+ query_log_parser.parse_paths(@query_log_paths) do |statistic|
93
136
  next if statistic.start_time < start
94
137
  break if statistic.start_time > last
95
- check_query_log_statistic(@query_log_parser.current_path,
138
+ check_query_log_statistic(query_log_parser.current_path,
96
139
  statistic)
97
140
  end
141
+ parsing_statistics = query_log_parser.parsing_statistics
142
+ unless parsing_statistics.empty?
143
+ puts("Running queries:")
144
+ parsing_statistics.each do |statistic|
145
+ puts("#{statistic.start_time.iso8601}:")
146
+ puts(statistic.command.to_command_format(pretty_print: true))
147
+ end
148
+ end
98
149
  unless @unflushed_statistics.empty?
99
150
  puts("Unflushed statistics in #{start.iso8601}/#{last.iso8601}")
100
151
  @unflushed_statistics.each do |statistic|
@@ -120,29 +171,6 @@ module GroongaQueryLog
120
171
  end
121
172
  end
122
173
 
123
- def check_general_log_entry(path, entry)
124
- # p [path, entry]
125
- case entry.log_level
126
- when :emergency, :alert, :critical, :error, :warning
127
- # p [entry.log_level, entry.message, entry.timestamp.iso8601]
128
- end
129
-
130
- case entry.message
131
- when /\Agrn_init:/
132
- if @running
133
- @crash_sessions << [@session_start, entry.timestamp]
134
- p [:crashed, entry.timestamp.iso8601, path]
135
- end
136
- @running = true
137
- @session_start = entry.timestamp
138
- when /\Agrn_fin \(\d+\)\z/
139
- n_leaks = $1.to_i
140
- @running = false
141
- @session_start = nil
142
- p [:leak, n_leask, entry.timestamp.iso8601] unless n_leaks.zero?
143
- end
144
- end
145
-
146
174
  def check_query_log_statistic(path, statistic)
147
175
  case statistic.command.command_name
148
176
  when "load"
@@ -164,6 +192,70 @@ module GroongaQueryLog
164
192
  end
165
193
  end
166
194
  end
195
+
196
+ class ProcessEnumerator
197
+ def initialize(general_log_paths)
198
+ @general_log_paths = general_log_paths
199
+ @running_processes = {}
200
+ end
201
+
202
+ def each(&block)
203
+ general_log_parser = GroongaLog::Parser.new
204
+ general_log_parser.parse_paths(@general_log_paths) do |entry|
205
+ check_general_log_entry(general_log_parser.current_path,
206
+ entry,
207
+ &block)
208
+ end
209
+ @running_processes.each_value do |process|
210
+ yield(process)
211
+ end
212
+ end
213
+
214
+ private
215
+ def check_general_log_entry(path, entry, &block)
216
+ # p [path, entry]
217
+ case entry.log_level
218
+ when :emergency, :alert, :critical, :error, :warning
219
+ # p [entry.log_level, entry.message, entry.timestamp.iso8601]
220
+ end
221
+
222
+ case entry.message
223
+ when /\Agrn_init:/
224
+ process = @running_processes[entry.pid]
225
+ if process
226
+ process.crashed = true
227
+ yield(process)
228
+ @running_processes.delete(entry.pid)
229
+ end
230
+ process = GroongaProcess.new(entry.pid, entry.timestamp, path)
231
+ @running_processes[entry.pid] = process
232
+ when /\Agrn_fin \(\d+\)\z/
233
+ n_leaks = $1.to_i
234
+ process = @running_processes[entry.pid]
235
+ process.n_leaks = n_leaks
236
+ yield(process)
237
+ @running_processes.delete(entry.pid)
238
+ else
239
+ @running_processes[entry.pid] ||=
240
+ GroongaProcess.new(entry.pid, Time.at(0), path)
241
+ process = @running_processes[entry.pid]
242
+ case entry.log_level
243
+ when :emergency, :alert, :critical, :error
244
+ process.important_entries << entry
245
+ end
246
+ process.last_time = entry.timestamp
247
+ case entry.message
248
+ when "-- CRASHED!!! --"
249
+ process.crashed = true
250
+ when "----------------"
251
+ if process.crashed?
252
+ yield(process)
253
+ @running_processes.delete(entry.pid)
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
167
259
  end
168
260
  end
169
261
  end
@@ -64,7 +64,7 @@ module GroongaQueryLog
64
64
  else
65
65
  extract(log_paths, $stdout)
66
66
  end
67
- rescue Interrupt
67
+ rescue Interrupt, Errno::EPIPE
68
68
  rescue Error
69
69
  $stderr.puts($!.message)
70
70
  return false
@@ -79,6 +79,7 @@ module GroongaQueryLog
79
79
  @options.unify_format = nil
80
80
  @options.commands = []
81
81
  @options.exclude_commands = []
82
+ @options.include_arguments = true
82
83
  @options.output_path = nil
83
84
  @option_parser = OptionParser.new do |parser|
84
85
  parser.version = VERSION
@@ -121,6 +122,12 @@ module GroongaQueryLog
121
122
  end
122
123
  end
123
124
 
125
+ parser.on("--[no-]include-arguments",
126
+ "Whether include command arguments",
127
+ "[#{@options.include_arguments}]") do |include_arguments|
128
+ @options.include_arguments = include_arguments
129
+ end
130
+
124
131
  parser.on("--output=PATH",
125
132
  "Output to PATH.",
126
133
  "[standard output]") do |path|
@@ -139,6 +146,9 @@ module GroongaQueryLog
139
146
  def extract_command(statistic, output)
140
147
  command = statistic.command
141
148
  return unless target?(command)
149
+ unless @options.include_arguments
150
+ command.arguments.clear
151
+ end
142
152
  command_text = nil
143
153
  case @options.unify_format
144
154
  when "uri"
@@ -146,7 +156,7 @@ module GroongaQueryLog
146
156
  when "command"
147
157
  command_text = command.to_command_format
148
158
  else
149
- command_text = statistic.raw_command
159
+ command_text = command.to_s
150
160
  end
151
161
  output.puts(command_text)
152
162
  end
@@ -67,6 +67,12 @@ module GroongaQueryLog
67
67
  @options.protocol = protocol
68
68
  end
69
69
 
70
+ parser.on("--read-timeout=TIMEOUT", Integer,
71
+ "Read timeout",
72
+ "[#{@options.read_timeout}]") do |read_timeout|
73
+ @options.read_timeout = read_timeout
74
+ end
75
+
70
76
  parser.on("--n-clients=N", Integer,
71
77
  "The max number of concurrency",
72
78
  "[#{@options.n_clients}]") do |n_clients|
@@ -66,13 +66,13 @@ module GroongaQueryLog
66
66
  match_data = PATTERN.match(line)
67
67
  next if match_data.nil?
68
68
 
69
- year = match_data[:year].to_i
70
- month = match_data[:month].to_i
71
- day = match_data[:day].to_i
72
- hour = match_data[:hour].to_i
73
- minute = match_data[:minute].to_i
74
- second = match_data[:second].to_i
75
- microsecond = match_data[:microsecond].to_i
69
+ year = Integer(match_data[:year], 10)
70
+ month = Integer(match_data[:month], 10)
71
+ day = Integer(match_data[:day], 10)
72
+ hour = Integer(match_data[:hour], 10)
73
+ minute = Integer(match_data[:minute], 10)
74
+ second = Integer(match_data[:second], 10)
75
+ microsecond = Integer(match_data[:microsecond], 10)
76
76
  context_id = match_data[:context_id]
77
77
  type = match_data[:type]
78
78
  rest = match_data.post_match.strip
@@ -142,6 +142,7 @@ module GroongaQueryLog
142
142
  attr_accessor :host
143
143
  attr_accessor :port
144
144
  attr_accessor :protocol
145
+ attr_accessor :read_timeout
145
146
  attr_accessor :n_clients
146
147
  attr_writer :request_queue_size
147
148
  attr_writer :disable_cache
@@ -152,6 +153,7 @@ module GroongaQueryLog
152
153
  @host = "127.0.0.1"
153
154
  @port = 10041
154
155
  @protocol = :http
156
+ @read_timeout = Groonga::Client::Default::READ_TIMEOUT
155
157
  @n_clients = 8
156
158
  @request_queue_size = nil
157
159
  @disable_cache = false
@@ -164,6 +166,7 @@ module GroongaQueryLog
164
166
  Groonga::Client.open(:host => @host,
165
167
  :port => @port,
166
168
  :protocol => @protocol,
169
+ :read_timeout => @read_timeout,
167
170
  &block)
168
171
  end
169
172
 
@@ -92,7 +92,7 @@ module GroongaQueryLog
92
92
  :relative_elapsed => relative_elapsed,
93
93
  :relative_elapsed_in_seconds => relative_elapsed_in_seconds,
94
94
  :name => operation[:name],
95
- :context => operation_context(operation[:name],
95
+ :context => operation_context(operation,
96
96
  operation_context_context),
97
97
  :n_records => operation[:n_records],
98
98
  :extra => operation[:extra],
@@ -152,9 +152,13 @@ module GroongaQueryLog
152
152
  nano_seconds / 1000.0 / 1000.0 / 1000.0
153
153
  end
154
154
 
155
- def operation_context(label, context)
155
+ def operation_context(operation, context)
156
156
  return nil if @select_command.nil?
157
157
 
158
+ extra = operation[:extra]
159
+ return extra if extra
160
+
161
+ label = operation[:name]
158
162
  case label
159
163
  when "filter"
160
164
  if @select_command.query and context[:query_used].nil?
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2012-2017 Kouhei Sutou <kou@clear-code.com>
1
+ # Copyright (C) 2012-2018 Kouhei Sutou <kou@clear-code.com>
2
2
  #
3
3
  # This library is free software; you can redistribute it and/or
4
4
  # modify it under the terms of the GNU Lesser General Public
@@ -15,5 +15,5 @@
15
15
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
  module GroongaQueryLog
18
- VERSION = "1.2.9"
18
+ VERSION = "1.3.0"
19
19
  end
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env ruby
2
- #
3
1
  # Copyright (C) 2012 Haruka Yoshihara <yoshihara@clear-code.com>
4
2
  # Copyright (C) 2015-2018 Kouhei Sutou <kou@clear-code.com>
5
3
  #
@@ -37,12 +35,12 @@ class ExtractCommandTest < Test::Unit::TestCase
37
35
  def test_multi
38
36
  other_query_log_path = File.join(@fixtures_path, "other-query.log")
39
37
  actual_commands = run_extractor(@query_log_path, other_query_log_path)
40
- expected_commands = <<-EOC
41
- load --table Video
42
- select --table Users --query follower:@groonga --output_columns _key,name
43
- table_create --name Comments --flags TABLE_HASH_KEY --key_type UInt32
44
- column_create --table Comments --name title --flags COLUMN_SCALAR --type ShortText
45
- EOC
38
+ expected_commands = <<-COMMAND
39
+ load --table "Video"
40
+ select --output_columns "_key,name" --query "follower:@groonga" --table "Users"
41
+ table_create --flags "TABLE_HASH_KEY" --key_type "UInt32" --name "Comments"
42
+ column_create --flags "COLUMN_SCALAR" --name "title" --table "Comments" --type "ShortText"
43
+ COMMAND
46
44
  assert_equal(expected_commands, actual_commands)
47
45
  end
48
46
 
@@ -57,44 +55,47 @@ EOC
57
55
  actual_commands = run_extractor(@query_log_path,
58
56
  "--unify-format", "command")
59
57
 
60
- expected_commands = <<-EOC
58
+ expected_commands = <<-COMMAND
61
59
  load --table "Video"
62
60
  select --output_columns "_key,name" --query "follower:@groonga" --table "Users"
63
- EOC
61
+ COMMAND
64
62
  assert_equal(expected_commands, actual_commands)
65
63
  end
66
64
 
67
65
  def test_uri
68
66
  actual_commands = run_extractor(@query_log_path,
69
67
  "--unify-format", "uri")
70
- expected_commands = <<-EOC
68
+ expected_commands = <<-COMMAND
71
69
  /d/load?table=Video
72
70
  /d/select?output_columns=_key%2Cname&query=follower%3A%40groonga&table=Users
73
- EOC
71
+ COMMAND
74
72
  assert_equal(expected_commands, actual_commands)
75
73
  end
76
74
 
77
75
  def test_not_unify
78
76
  actual_commands = run_extractor(@query_log_path)
79
- expected_commands = <<-EOC
80
- load --table Video
81
- select --table Users --query follower:@groonga --output_columns _key,name
82
- EOC
77
+ expected_commands = <<-COMMAND
78
+ load --table "Video"
79
+ select --output_columns "_key,name" --query "follower:@groonga" --table "Users"
80
+ COMMAND
83
81
  assert_equal(expected_commands, actual_commands)
84
82
  end
85
83
  end
86
84
 
87
85
  def test_command
88
86
  actual_command = run_extractor(@query_log_path, "--command", "load")
89
- expected_command = "load --table Video\n"
87
+ expected_command = <<-COMMAND
88
+ load --table "Video"
89
+ COMMAND
90
90
 
91
91
  assert_equal(expected_command, actual_command)
92
92
  end
93
93
 
94
94
  def test_exclude_command
95
95
  actual_command = run_extractor(@query_log_path, "--exclude-command", "load")
96
- expected_command = "select --table Users --query follower:@groonga" +
97
- " --output_columns _key,name\n"
96
+ expected_command = <<-COMMAND
97
+ select --output_columns "_key,name" --query "follower:@groonga" --table "Users"
98
+ COMMAND
98
99
 
99
100
  assert_equal(expected_command, actual_command)
100
101
  end
@@ -116,20 +117,21 @@ EOC
116
117
  class TestExtract < self
117
118
  def setup
118
119
  super
119
- @log = <<-EOL
120
+ @log = <<-LOG
120
121
  2012-12-12 17:39:17.628846|0x7fff786aa2b0|>select --table Users --query follower:@groonga --output_columns _key,name
121
122
  2012-12-12 17:39:17.629676|0x7fff786aa2b0|:000000000842953 filter(2)
122
123
  2012-12-12 17:39:17.629709|0x7fff786aa2b0|:000000000870900 select(2)
123
124
  2012-12-12 17:39:17.629901|0x7fff786aa2b0|:000000001066752 output(2)
124
125
  2012-12-12 17:39:17.630052|0x7fff786aa2b0|<000000001217140 rc=0
125
- EOL
126
+ LOG
126
127
  end
127
128
 
128
129
  def test_command_format
129
130
  @extract_command.options.unify_format = "command"
130
- expected_formatted_command = "select --output_columns \"_key,name\""+
131
- " --query \"follower:@groonga\"" +
132
- " --table \"Users\"\n"
131
+ expected_formatted_command = <<-COMMAND
132
+ select --output_columns "_key,name" --query "follower:@groonga" --table "Users"
133
+ COMMAND
134
+
133
135
  assert_equal(expected_formatted_command, extract)
134
136
  end
135
137
 
@@ -143,9 +145,10 @@ EOL
143
145
 
144
146
  def test_not_unify
145
147
  @extract_command.options.unify_format = nil
146
- expected_formatted_command = "select --table Users" +
147
- " --query follower:@groonga" +
148
- " --output_columns _key,name\n"
148
+ expected_formatted_command = <<-COMMAND
149
+ select --output_columns "_key,name" --query "follower:@groonga" --table "Users"
150
+ COMMAND
151
+
149
152
  assert_equal(expected_formatted_command, extract)
150
153
  end
151
154