norikra 1.2.1-java → 1.2.2-java

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: 0e2938132b83a49bb328d060517750c5cb9d7e95
4
- data.tar.gz: 7c20e09c3795b209946f08f49d3503ccccec3caf
3
+ metadata.gz: fa80888abaaa606aa96b77fc3e1e33b17f8bb098
4
+ data.tar.gz: 423c64919bae627115f30f37ac6750883a534676
5
5
  SHA512:
6
- metadata.gz: 6a684145944825a6c813ec8695a7eee3ee6411de5654af120a90a77f7ee4e0fae9becef6535c476b5afce4754a7923d9188a8feca8c912a0c55393f2a6989794
7
- data.tar.gz: 4140d210916fcb7d575527fce40f9b3bda7d7fc3a9f7af9d88b2ef1c6202678d63b28c7369f71d227df399548d708486ccfea5aca2b7bfeb6f771acfda88e773
6
+ metadata.gz: 9981e141e6d99f2cb9a793629a6f0c46db0df1f8df8661e2fe6bfbddabc49fb946da8b839cd0b91352c09a6816340a1078b99d7211f29f90e706517b3576c31e
7
+ data.tar.gz: 755251d7e8fe172c7f72e8cebb37ecfdea10937de3f39c36300651647efa8ad1423ff7fd953e65392e794a0daf227e1312a8f3cbcb50cfb4d96b37e8db155191
data/Changes.md CHANGED
@@ -3,6 +3,8 @@
3
3
  Changes of norikra.
4
4
 
5
5
  ## v1
6
+ * v1.2.2
7
+ * Change API for custom listner plugins (incompatible with 1.2.0, 1.2.1)
6
8
  * v1.2.1
7
9
  * Fix bug to fail to rewrite nullable fields
8
10
  * v1.2.0
@@ -45,7 +45,7 @@ module Norikra
45
45
 
46
46
  @waiting_queries = []
47
47
 
48
- @listeners = []
48
+ @listeners = {} # Listener.label => Listener
49
49
  @running_listeners = {} # query_name => listener
50
50
  end
51
51
 
@@ -373,7 +373,7 @@ module Norikra
373
373
 
374
374
  def register_query(query)
375
375
 
376
- if lo_target_name = Norikra::Listener::Loopback.check(query.group)
376
+ if lo_target_name = Norikra::Listener::Loopback.target(query.group)
377
377
  raise "Invalid loopback target name should be checked before. THIS IS BUG." unless Norikra::Target.valid?(lo_target_name)
378
378
 
379
379
  target = Norikra::Target.new(lo_target_name)
@@ -388,7 +388,7 @@ module Norikra
388
388
  if reason = query.invalid?
389
389
  raise Norikra::ClientError, "invalid query '#{query.name}': #{reason}"
390
390
  end
391
- if lo_target_name = Norikra::Listener::Loopback.check(query.group)
391
+ if lo_target_name = Norikra::Listener::Loopback.target(query.group)
392
392
  raise Norikra::ClientError, "loopback target '#{lo_target_name}'" unless Norikra::Target.valid?(lo_target_name)
393
393
  end
394
394
 
@@ -520,8 +520,29 @@ module Norikra
520
520
  end
521
521
  end
522
522
 
523
- def load_listener(listener_klass)
524
- @listeners.push(listener_klass)
523
+ def load_listener(klass)
524
+ @listeners[klass.label] = klass
525
+ klass
526
+ end
527
+
528
+ def create_listener(query)
529
+ opts = Norikra::Listener.parse(query.group)
530
+ klass = if opts && @listeners.has_key?(opts[:name])
531
+ @listeners[opts[:name]]
532
+ else
533
+ Norikra::Listener::MemoryPool
534
+ end
535
+ argument = opts ? opts[:argument] : nil
536
+ trace("selecting listeners"){ { group: query.group, listener: klass, argument: argument } }
537
+
538
+ inst = klass.new(argument, query.name, query.group)
539
+ inst.events_statistics = @statistics[:events]
540
+
541
+ inst.engine = self if inst.respond_to?(:engine=)
542
+ inst.output_pool = @output_pool if inst.respond_to?(:output_pool=)
543
+
544
+ inst.start
545
+ inst
525
546
  end
526
547
 
527
548
  # this method should be protected with @mutex lock
@@ -537,19 +558,7 @@ module Norikra
537
558
  statement_model = administrator.compileEPL(query.expression)
538
559
  Norikra::Query.rewrite_query(statement_model, event_type_name_map)
539
560
 
540
- listener = nil
541
- @listeners.each do |klass|
542
- trace("checking listeners"){ {target: klass, result: klass.check(query.group)} }
543
- if klass.check(query.group)
544
- listener = klass.new(query.name, query.group, @statistics[:events])
545
- break
546
- end
547
- end
548
- raise "BUG: no listener is selected" unless listener
549
- listener.engine = self if listener.respond_to?(:engine=)
550
- listener.output_pool = @output_pool if listener.respond_to?(:output_pool=)
551
- listener.start
552
- @running_listeners[query.name] = listener
561
+ @running_listeners[query.name] = listener = create_listener(query)
553
562
 
554
563
  epl = administrator.create(statement_model)
555
564
  epl.java_send :addListener, [com.espertech.esper.client.UpdateListener.java_class], listener
@@ -16,6 +16,14 @@ require 'json'
16
16
 
17
17
  module Norikra
18
18
  module Listener
19
+ def self.parse(group_name)
20
+ if group_name && group_name =~ /^([_A-Z]+)\((.*)\)$/
21
+ {name: $1, argument: $2}
22
+ else
23
+ nil
24
+ end
25
+ end
26
+
19
27
  def self.listup
20
28
  return unless defined? Gem
21
29
 
@@ -41,7 +49,7 @@ module Norikra
41
49
  end
42
50
 
43
51
  known_consts = [:Base, :MemoryPool, :Loopback, :Stdout]
44
- listeners = [Norikra::Listener::Stdout, Norikra::Listener::Loopback]
52
+ listeners = []
45
53
  self.constants.each do |c|
46
54
  next if known_consts.include?(c)
47
55
 
@@ -50,7 +58,9 @@ module Norikra
50
58
  listeners.push(klass)
51
59
  end
52
60
  end
53
- listeners.push(Norikra::Listener::MemoryPool)
61
+ [Norikra::Listener::Stdout, Norikra::Listener::Loopback].each do |listener|
62
+ listeners.push(listener)
63
+ end
54
64
  listeners
55
65
  end
56
66
 
@@ -59,33 +69,37 @@ module Norikra
59
69
 
60
70
  DEFAULT_ASYNC_INTERVAL = 0.1
61
71
 
62
- def self.check(group_name)
72
+ attr_writer :events_statistics
73
+ # attr_writer :engine
74
+ # attr_writer :output_pool
75
+
76
+ def self.label
63
77
  raise NotImplementedError
64
78
  end
65
79
 
66
- def initialize(query_name, query_group, events_statistics)
80
+ def initialize(argument, query_name, query_group)
81
+ @argument = argument
67
82
  @query_name = query_name
68
83
  @query_group = query_group
69
- @events_statistics = events_statistics
70
84
 
71
85
  @async_interval = DEFAULT_ASYNC_INTERVAL
72
86
 
87
+ @mode = if self.respond_to?(:process_sync)
88
+ :sync
89
+ elsif self.respond_to?(:process_async)
90
+ :async
91
+ else
92
+ raise "BUG: Invalid custom listener '#{self.class.to_s}'"
93
+ end
94
+
73
95
  @thread = nil
74
96
  @events = []
75
97
  @mutex = Mutex.new
76
98
  @running = true
77
99
  end
78
100
 
79
- # def engine=(engine)
80
- # @engine = engine
81
- # end
82
-
83
- # def output_pool=(output_pool)
84
- # @output_pool = output_pool
85
- # end
86
-
87
101
  def start
88
- if self.respond_to?(:process_async)
102
+ if @mode == :async
89
103
  trace "starting thread to process events in background", query: @query_name
90
104
  @thread = Thread.new(&method(:background))
91
105
  end
@@ -117,9 +131,6 @@ module Norikra
117
131
  end
118
132
  end
119
133
 
120
- # def process_async
121
- # end
122
-
123
134
  def shutdown
124
135
  trace "stopping listener", query: @query_name
125
136
  @running = false
@@ -149,74 +160,88 @@ module Norikra
149
160
 
150
161
  def update(new_events, old_events)
151
162
  t = Time.now.to_i
152
- events = new_events.map{|e| [t, type_convert(e)]}
153
- trace("updated event"){ { query: @query_name, group: @query_group, event: events } }
154
- push(events)
155
- @events_statistics[:output] += events.size
163
+ if @mode == :sync
164
+ news = new_events.map{|e| type_convert(e) }
165
+ olds = if old_events.respond_to?(:map)
166
+ old_events.map{|e| type_convert(e) }
167
+ else
168
+ type_convert(old_events)
169
+ end
170
+ trace("produced events"){ { listener: self.class, query: @query_name, group: @query_group, news: news, olds: olds } }
171
+ process_sync(news, olds)
172
+ @events_statistics[:output] += news.size
173
+ else # async
174
+ events = new_events.map{|e| [t, type_convert(e)]}
175
+ trace("pushed events"){ { listener: self.class, query: @query_name, group: @query_group, event: events } }
176
+ push(events)
177
+ @events_statistics[:output] += events.size
178
+ end
156
179
  end
157
180
  end
158
181
 
159
182
  class MemoryPool < Base
160
- def self.check(group_name)
161
- true
162
- end
183
+ attr_writer :output_pool
163
184
 
164
- def output_pool=(output_pool)
165
- @output_pool = output_pool
185
+ def self.label
186
+ nil # Memory pool listener is built-in and implicit listener
166
187
  end
167
188
 
168
- def update(new_events, old_events)
189
+ def process_sync(news, olds)
169
190
  t = Time.now.to_i
170
- events = new_events.map{|e| [t, type_convert(e)]}
171
- trace("updated event"){ { query: @query_name, group: @query_group, event: events } }
191
+ events = news.map{|e| [t, e]}
172
192
  @output_pool.push(@query_name, @query_group, events)
173
- @events_statistics[:output] += events.size
174
193
  end
175
194
  end
176
195
 
177
196
  class Loopback < Base
178
- def self.check(group_name)
179
- group_name && group_name =~ /^LOOPBACK\((.+)\)$/ && $1
197
+ attr_writer :engine
198
+
199
+ def self.label
200
+ "LOOPBACK"
180
201
  end
181
202
 
182
- def initialize(query_name, query_group, events_statistics)
183
- super
184
- @loopback_target = Loopback.check(query_group)
185
- raise "BUG: query group is not 'LOOPBACK(...)'" unless @loopback_target
203
+ def self.target(query_group)
204
+ if query_group
205
+ opts = Norikra::Listener.parse(query_group)
206
+ if opts && opts[:name] == label()
207
+ opts[:argument]
208
+ else
209
+ nil
210
+ end
211
+ else
212
+ nil
213
+ end
186
214
  end
187
215
 
188
- def engine=(engine)
189
- @engine = engine
216
+ def initialize(argument, query_name, query_group)
217
+ super
218
+ if @argument.nil? || @argument.empty?
219
+ raise Norikra::ClientError, "LOOPBACK target name not specified"
220
+ end
190
221
  end
191
222
 
192
- def update(new_events, old_events)
193
- event_list = new_events.map{|e| type_convert(e) }
194
- trace("loopback event"){ { query: @query_name, group: @query_group, event: event_list } }
195
- @events_statistics[:output] += event_list.size
196
- #
223
+ def process_sync(news, olds)
197
224
  # We does NOT convert 'container.$0' into container['field'].
198
225
  # Use escaped names like 'container__0'. That is NOT so confused.
199
- @engine.send(@loopback_target, event_list)
226
+ trace("loopback event"){ { query: @query_name, group: @query_group, event: news } }
227
+ @engine.send(@argument, news)
200
228
  end
201
229
  end
202
230
 
203
231
  class Stdout < Base
204
- def self.check(group_name)
205
- group_name && group_name == "STDOUT()"
232
+ attr_accessor :stdout
233
+
234
+ def self.label
235
+ "STDOUT"
206
236
  end
207
237
 
208
- def initialize(query_name, query_group, events_statistics)
238
+ def initialize(argument, query_name, query_group)
209
239
  super
210
- raise "BUG: query group is not 'STDOUT()'" unless Stdout.check(query_group)
211
240
  @stdout = STDOUT
212
241
  end
213
242
 
214
- def update(new_events, old_events)
215
- event_list = new_events.map{|e| type_convert(e) }
216
- trace("stdout event"){ { query: @query_name, event: event_list } }
217
- @events_statistics[:output] += event_list.size
218
-
219
- event_list.each do |e|
243
+ def process_sync(news, olds)
244
+ news.each do |e|
220
245
  @stdout.puts @query_name + "\t" + JSON.dump(e)
221
246
  end
222
247
  end
@@ -0,0 +1,35 @@
1
+ module Norikra
2
+ module ListenerSpecHelper
3
+
4
+ ### TODO: more util methods?
5
+ # utilities w/ #start, #shutdown ?
6
+ # utilities to produce dummy output events ?
7
+
8
+ class DummyEngine
9
+ attr_reader :events
10
+
11
+ def initialize
12
+ @events = {}
13
+ end
14
+
15
+ def send(target, events)
16
+ @events[target] ||= []
17
+ @events[target].push(*events)
18
+ end
19
+ end
20
+
21
+ class DummyOutputPool
22
+ attr_reader :pool
23
+
24
+ def initialize
25
+ @pool = {}
26
+ end
27
+
28
+ def push(query_name, query_group, events)
29
+ @pool[query_group] ||= {}
30
+ @pool[query_group][query_name] ||= []
31
+ @pool[query_group][query_name].push(*events)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Norikra
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
  end
@@ -1,45 +1,26 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require_relative './spec_helper'
3
-
4
3
  require 'json'
5
4
  require 'norikra/listener'
5
+ require 'norikra/listener_spec_helper'
6
6
 
7
- class DummyEngine
8
- attr_reader :events
9
-
10
- def initialize
11
- @events = {}
12
- end
13
-
14
- def send(target, events)
15
- @events[target] ||= []
16
- @events[target].push(*events)
7
+ class DummySyncListener < Norikra::Listener::Base
8
+ def process_sync(news, olds)
9
+ # drop
17
10
  end
18
11
  end
19
12
 
20
- class DummyOutputPool
21
- attr_reader :pool
22
-
23
- def initialize
24
- @pool = {}
25
- end
26
-
27
- def push(query_name, query_group, events)
28
- @pool[query_group] ||= {}
29
- @pool[query_group][query_name] ||= []
30
- @pool[query_group][query_name].push(*events)
13
+ class DummyAsyncListener < Norikra::Listener::Base
14
+ def process_async(news, olds)
15
+ # drop
31
16
  end
32
17
  end
33
18
 
34
19
  describe Norikra::Listener::Base do
35
- it 'should be initialized' do
36
- statistics = {output: 0}
37
- expect { Norikra::Listener::Base.new('name', 'group', statistics) }.not_to raise_error
38
- end
39
-
40
20
  describe '#type_convert' do
41
21
  statistics = {output: 0}
42
- listener = Norikra::Listener::Base.new('name', 'group', statistics)
22
+ listener = DummySyncListener.new('', 'name', 'group')
23
+ listener.events_statistics = statistics
43
24
 
44
25
  it 'returns value itself for number, boolean and nil' do
45
26
  val = 10001
@@ -115,103 +96,77 @@ describe Norikra::Listener::Base do
115
96
  end
116
97
  end
117
98
 
118
- ### TODO: add specs of #start, #process_async, #push and default #update
119
- ### TODO: add norikra/listener_spec_helper.rb
99
+ ### TODO: add specs of .parse, #start, #shutdown, #push and #update
120
100
  end
121
101
 
122
102
  describe Norikra::Listener::MemoryPool do
123
- describe '.check' do
124
- it 'always returns true' do
125
- expect(Norikra::Listener::MemoryPool.check(nil)).to be_truthy
126
- expect(Norikra::Listener::MemoryPool.check('')).to be_truthy
127
- expect(Norikra::Listener::MemoryPool.check('FOO')).to be_truthy
128
- expect(Norikra::Listener::MemoryPool.check('FOO()')).to be_truthy
103
+ describe '.label' do
104
+ it 'returns nil' do
105
+ expect(Norikra::Listener::MemoryPool.label).to be_nil
129
106
  end
130
107
  end
131
108
 
132
- describe '#update' do
133
- statistics = {output: 0}
134
- listener = Norikra::Listener::MemoryPool.new('name', 'group', statistics)
135
- listener.output_pool = dummy_pool = DummyOutputPool.new
109
+ describe '#process_sync' do
110
+ listener = Norikra::Listener::MemoryPool.new(nil, 'name', 'group')
111
+ listener.output_pool = dummy_pool = Norikra::ListenerSpecHelper::DummyOutputPool.new
136
112
 
137
113
  it 'pushs events into pool, with current time' do
138
- listener.update([{"n1" => 100, "s" => "string one"}, {"n1" => 101, "s" => "string two"}], [])
139
- expect(statistics[:output]).to eql(2)
114
+ listener.process_sync([{"n1" => 100, "s" => "string one"}, {"n1" => 101, "s" => "string two"}], [])
140
115
  expect(dummy_pool.pool['group']['name'].size).to eql(2)
141
116
  expect(dummy_pool.pool['group']['name'][0][0]).to be_a(Fixnum)
142
117
  expect(dummy_pool.pool['group']['name'][0][1]).to eql({"n1" => 100, "s" => "string one"})
143
118
  expect(dummy_pool.pool['group']['name'][1][1]).to eql({"n1" => 101, "s" => "string two"})
144
119
 
145
- listener.update([{"n1" => 102, "s" => "string three"}], [])
146
- expect(statistics[:output]).to eql(3)
120
+ listener.process_sync([{"n1" => 102, "s" => "string three"}], [])
147
121
  expect(dummy_pool.pool['group']['name'].size).to eql(3)
148
122
  end
149
123
  end
150
124
  end
151
125
 
152
126
  describe Norikra::Listener::Loopback do
153
- describe '.check' do
154
- it 'returns nil for nil group' do
155
- expect(Norikra::Listener::Loopback.check(nil)).to be_nil
156
- end
157
-
158
- it 'returns nil for string group name without prefix' do
159
- expect(Norikra::Listener::Loopback.check('a')).to be_nil
160
- expect(Norikra::Listener::Loopback.check('group1')).to be_nil
161
- expect(Norikra::Listener::Loopback.check('LOOPBACK')).to be_nil
162
- end
163
-
164
- it 'returns specified string as loopback target by parentheses' do
165
- expect(Norikra::Listener::Loopback.check('LOOPBACK()')).to be_nil
166
- expect(Norikra::Listener::Loopback.check('LOOPBACK(a)')).to eql('a')
167
- expect(Norikra::Listener::Loopback.check('LOOPBACK(loopback_target)')).to eql('loopback_target')
168
- expect(Norikra::Listener::Loopback.check('LOOPBACK(target name)')).to eql('target name') # should be invalid on 'open'
127
+ describe '.label' do
128
+ it 'returns "LOOPBACK"' do
129
+ expect(Norikra::Listener::Loopback.label).to eql("LOOPBACK")
169
130
  end
170
131
  end
171
132
 
172
133
  it 'should be initialized' do
173
134
  statistics = {output: 0}
174
- expect { Norikra::Listener::Loopback.new('name', 'LOOPBACK(target1)', statistics) }.not_to raise_error
135
+ inst = Norikra::Listener::Loopback.new('target1', 'name', 'LOOPBACK(target1)')
136
+ inst.events_statistics = statistics
175
137
  end
176
138
 
177
- describe '#update' do
178
- statistics = {output: 0}
179
- listener = Norikra::Listener::Loopback.new('name', 'LOOPBACK(target1)', statistics)
180
- listener.engine = dummy_engine = DummyEngine.new
139
+ describe '#process_sync' do
140
+ listener = Norikra::Listener::Loopback.new('target1', 'name', 'LOOPBACK(target1)')
141
+ listener.engine = dummy_engine = Norikra::ListenerSpecHelper::DummyEngine.new
181
142
 
182
143
  it 'sends events into engine with target name' do
183
- listener.update([{"n1" => 100, "s" => "string one"}, {"n1" => 101, "s" => "string two"}], [])
184
- expect(statistics[:output]).to eql(2)
144
+ listener.process_sync([{"n1" => 100, "s" => "string one"}, {"n1" => 101, "s" => "string two"}], [])
185
145
  expect(dummy_engine.events['target1'].size).to eql(2)
186
146
  expect(dummy_engine.events['target1'][0]).to eql({"n1" => 100, "s" => "string one"})
187
147
  expect(dummy_engine.events['target1'][1]).to eql({"n1" => 101, "s" => "string two"})
188
148
 
189
- listener.update([{"n1" => 102, "s" => "string three"}], [])
190
- expect(statistics[:output]).to eql(3)
149
+ listener.process_sync([{"n1" => 102, "s" => "string three"}], [])
191
150
  expect(dummy_engine.events['target1'].size).to eql(3)
192
151
  end
193
152
  end
194
153
  end
195
154
 
196
155
  describe Norikra::Listener::Stdout do
197
- describe '.check' do
198
- it 'returns true if group name is "STDOUT()"' do
199
- expect(Norikra::Listener::Stdout.check(nil)).to be_falsy
200
- expect(Norikra::Listener::Stdout.check("")).to be_falsy
201
- expect(Norikra::Listener::Stdout.check("foo")).to be_falsy
202
- expect(Norikra::Listener::Stdout.check("STDOUT")).to be_falsy
203
- expect(Norikra::Listener::Stdout.check("STDOUT()")).to be_truthy
156
+ describe '.label' do
157
+ it 'returns "STDOUT"' do
158
+ expect(Norikra::Listener::Stdout.label).to eql("STDOUT")
204
159
  end
205
160
  end
206
161
 
207
162
  it 'should be initialized' do
208
163
  statistics = {output: 0}
209
- expect { Norikra::Listener::Stdout.new('name', 'STDOUT()', statistics) }.not_to raise_error
164
+ inst = Norikra::Listener::Stdout.new('', 'name', 'STDOUT()')
165
+ inst.events_statistics = statistics
210
166
  end
211
167
 
212
- describe '#update' do
213
- statistics = {output: 0}
214
- listener = Norikra::Listener::Stdout.new('name', 'STDOUT()', statistics)
168
+ describe '#process_sync' do
169
+ listener = Norikra::Listener::Stdout.new('', 'name', 'STDOUT()')
215
170
  dummyio = StringIO.new
216
171
  listener.instance_eval{ @stdout = dummyio }
217
172
 
@@ -219,12 +174,10 @@ describe Norikra::Listener::Stdout do
219
174
  dummyio.truncate(0)
220
175
 
221
176
  events1 = [{"n1" => 100, "s" => "string one"}, {"n1" => 101, "s" => "string two"}]
222
- listener.update(events1, [])
223
- expect(statistics[:output]).to eql(2)
177
+ listener.process_sync(events1, [])
224
178
 
225
179
  events2 = [{"n1" => 102, "s" => "string three"}]
226
- listener.update(events2, [])
227
- expect(statistics[:output]).to eql(3)
180
+ listener.process_sync(events2, [])
228
181
 
229
182
  results = []
230
183
  dummyio.string.split("\n").each do |line|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: norikra
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.2
5
5
  platform: java
6
6
  authors:
7
7
  - TAGOMORI Satoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-16 00:00:00.000000000 Z
11
+ date: 2015-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mizuno
@@ -218,6 +218,7 @@ files:
218
218
  - lib/norikra/field.rb
219
219
  - lib/norikra/fieldset.rb
220
220
  - lib/norikra/listener.rb
221
+ - lib/norikra/listener_spec_helper.rb
221
222
  - lib/norikra/logger.rb
222
223
  - lib/norikra/logger_mizuno_patch.rb
223
224
  - lib/norikra/output_pool.rb