fluentd 0.14.1 → 0.14.2

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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +110 -1
  3. data/Rakefile +5 -1
  4. data/appveyor.yml +7 -1
  5. data/example/in_forward.conf +4 -0
  6. data/lib/fluent/compat/exec_util.rb +129 -0
  7. data/lib/fluent/compat/file_util.rb +54 -0
  8. data/lib/fluent/compat/filter.rb +21 -3
  9. data/lib/fluent/compat/formatter.rb +4 -2
  10. data/lib/fluent/compat/formatter_utils.rb +85 -0
  11. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +60 -0
  12. data/lib/fluent/compat/input.rb +1 -3
  13. data/lib/fluent/compat/output.rb +95 -39
  14. data/lib/fluent/compat/parser.rb +17 -0
  15. data/lib/fluent/compat/parser_utils.rb +40 -0
  16. data/lib/fluent/compat/socket_util.rb +165 -0
  17. data/lib/fluent/compat/string_util.rb +34 -0
  18. data/lib/fluent/{test/driver/owner.rb → compat/structured_format_mixin.rb} +5 -11
  19. data/lib/fluent/config/element.rb +2 -2
  20. data/lib/fluent/configurable.rb +2 -1
  21. data/lib/fluent/event.rb +61 -7
  22. data/lib/fluent/event_router.rb +1 -1
  23. data/lib/fluent/plugin.rb +7 -7
  24. data/lib/fluent/plugin/buf_file.rb +5 -2
  25. data/lib/fluent/plugin/buffer.rb +194 -64
  26. data/lib/fluent/plugin/buffer/chunk.rb +28 -3
  27. data/lib/fluent/plugin/buffer/file_chunk.rb +5 -21
  28. data/lib/fluent/plugin/buffer/memory_chunk.rb +1 -11
  29. data/lib/fluent/plugin/exec_util.rb +2 -112
  30. data/lib/fluent/plugin/file_util.rb +3 -38
  31. data/lib/fluent/plugin/file_wrapper.rb +1 -1
  32. data/lib/fluent/plugin/filter_grep.rb +3 -7
  33. data/lib/fluent/plugin/filter_record_transformer.rb +5 -5
  34. data/lib/fluent/plugin/filter_stdout.rb +18 -11
  35. data/lib/fluent/plugin/formatter.rb +0 -48
  36. data/lib/fluent/plugin/formatter_csv.rb +7 -8
  37. data/lib/fluent/plugin/formatter_hash.rb +1 -4
  38. data/lib/fluent/plugin/formatter_json.rb +1 -4
  39. data/lib/fluent/plugin/formatter_ltsv.rb +5 -6
  40. data/lib/fluent/plugin/formatter_msgpack.rb +1 -4
  41. data/lib/fluent/plugin/formatter_out_file.rb +36 -3
  42. data/lib/fluent/plugin/formatter_stdout.rb +36 -1
  43. data/lib/fluent/plugin/in_dummy.rb +9 -2
  44. data/lib/fluent/plugin/in_exec.rb +20 -57
  45. data/lib/fluent/plugin/in_forward.rb +4 -3
  46. data/lib/fluent/plugin/in_object_space.rb +8 -44
  47. data/lib/fluent/plugin/in_syslog.rb +13 -24
  48. data/lib/fluent/plugin/in_tail.rb +3 -0
  49. data/lib/fluent/plugin/out_buffered_stdout.rb +14 -4
  50. data/lib/fluent/plugin/out_exec.rb +7 -5
  51. data/lib/fluent/plugin/out_exec_filter.rb +10 -10
  52. data/lib/fluent/plugin/out_file.rb +1 -3
  53. data/lib/fluent/plugin/out_forward.rb +38 -57
  54. data/lib/fluent/plugin/out_stdout.rb +14 -5
  55. data/lib/fluent/plugin/out_stream.rb +3 -0
  56. data/lib/fluent/plugin/output.rb +31 -14
  57. data/lib/fluent/plugin/parser.rb +0 -69
  58. data/lib/fluent/plugin/parser_apache.rb +10 -6
  59. data/lib/fluent/plugin/parser_apache_error.rb +8 -3
  60. data/lib/fluent/plugin/parser_csv.rb +3 -1
  61. data/lib/fluent/plugin/parser_json.rb +1 -1
  62. data/lib/fluent/plugin/parser_multiline.rb +5 -3
  63. data/lib/fluent/plugin/parser_nginx.rb +10 -6
  64. data/lib/fluent/plugin/parser_regexp.rb +73 -0
  65. data/lib/fluent/plugin/socket_util.rb +2 -148
  66. data/lib/fluent/plugin/storage_local.rb +1 -1
  67. data/lib/fluent/plugin/string_util.rb +3 -18
  68. data/lib/fluent/plugin_helper.rb +1 -0
  69. data/lib/fluent/plugin_helper/compat_parameters.rb +166 -41
  70. data/lib/fluent/plugin_helper/formatter.rb +30 -19
  71. data/lib/fluent/plugin_helper/inject.rb +25 -12
  72. data/lib/fluent/plugin_helper/parser.rb +22 -13
  73. data/lib/fluent/plugin_helper/storage.rb +22 -13
  74. data/lib/fluent/registry.rb +19 -6
  75. data/lib/fluent/supervisor.rb +27 -1
  76. data/lib/fluent/test/driver/base.rb +16 -92
  77. data/lib/fluent/test/driver/base_owned.rb +17 -53
  78. data/lib/fluent/test/driver/base_owner.rb +125 -0
  79. data/lib/fluent/test/driver/filter.rb +24 -2
  80. data/lib/fluent/test/driver/input.rb +2 -2
  81. data/lib/fluent/test/driver/multi_output.rb +2 -2
  82. data/lib/fluent/test/driver/output.rb +3 -5
  83. data/lib/fluent/test/helpers.rb +25 -0
  84. data/lib/fluent/test/input_test.rb +4 -4
  85. data/lib/fluent/test/output_test.rb +3 -3
  86. data/lib/fluent/version.rb +1 -1
  87. data/test/config/test_element.rb +135 -6
  88. data/test/plugin/test_buf_file.rb +71 -3
  89. data/test/plugin/test_buffer.rb +305 -86
  90. data/test/plugin/test_buffer_chunk.rb +60 -2
  91. data/test/plugin/test_buffer_file_chunk.rb +4 -3
  92. data/test/plugin/test_filter_grep.rb +25 -21
  93. data/test/plugin/test_filter_record_transformer.rb +75 -67
  94. data/test/plugin/test_filter_stdout.rb +171 -74
  95. data/test/plugin/test_formatter_csv.rb +94 -0
  96. data/test/plugin/test_formatter_json.rb +30 -0
  97. data/test/plugin/test_formatter_ltsv.rb +52 -0
  98. data/test/plugin/test_formatter_msgpack.rb +28 -0
  99. data/test/plugin/test_formatter_out_file.rb +95 -0
  100. data/test/plugin/test_formatter_single_value.rb +38 -0
  101. data/test/plugin/test_in_dummy.rb +95 -0
  102. data/test/plugin/test_in_exec.rb +27 -31
  103. data/test/plugin/test_in_forward.rb +24 -0
  104. data/test/plugin/test_in_gc_stat.rb +5 -5
  105. data/test/plugin/test_in_object_space.rb +4 -4
  106. data/test/plugin/test_in_syslog.rb +60 -35
  107. data/test/plugin/test_out_buffered_stdout.rb +17 -3
  108. data/test/plugin/test_out_forward.rb +93 -5
  109. data/test/plugin/test_out_stdout.rb +14 -3
  110. data/test/plugin/test_output_as_buffered_retries.rb +20 -0
  111. data/test/plugin/test_output_as_buffered_secondary.rb +16 -0
  112. data/test/plugin/test_output_as_standard.rb +22 -22
  113. data/test/plugin/test_parser_apache.rb +13 -9
  114. data/test/plugin/test_parser_apache_error.rb +11 -6
  115. data/test/plugin/test_parser_csv.rb +35 -25
  116. data/test/plugin/test_parser_nginx.rb +11 -5
  117. data/test/plugin/test_parser_regexp.rb +235 -68
  118. data/test/plugin/test_parser_tsv.rb +54 -58
  119. data/test/plugin_helper/test_compat_parameters.rb +111 -46
  120. data/test/plugin_helper/test_formatter.rb +40 -0
  121. data/test/plugin_helper/test_inject.rb +101 -2
  122. data/test/plugin_helper/test_parser.rb +40 -0
  123. data/test/plugin_helper/test_storage.rb +43 -0
  124. data/test/test_event.rb +93 -0
  125. data/test/test_event_router.rb +13 -4
  126. data/test/test_event_time.rb +0 -3
  127. data/test/test_formatter.rb +7 -164
  128. data/test/test_plugin_classes.rb +28 -1
  129. metadata +24 -3
@@ -20,9 +20,10 @@ module Fluent
20
20
  class Registry
21
21
  DEFAULT_PLUGIN_PATH = File.expand_path('../plugin', __FILE__)
22
22
 
23
- def initialize(kind, search_prefix)
23
+ def initialize(kind, search_prefix, dir_search_prefix: nil)
24
24
  @kind = kind
25
25
  @search_prefix = search_prefix
26
+ @dir_search_prefix = dir_search_prefix
26
27
  @map = {}
27
28
  @paths = [DEFAULT_PLUGIN_PATH]
28
29
  end
@@ -54,11 +55,10 @@ module Fluent
54
55
  end
55
56
 
56
57
  def search(type)
57
- path = "#{@search_prefix}#{type}"
58
-
59
- # prefer LOAD_PATH than gems
60
- [@paths, $LOAD_PATH].each do |paths|
61
- files = paths.map { |lp|
58
+ # search from additional plugin directories
59
+ if @dir_search_prefix
60
+ path = "#{@dir_search_prefix}#{type}"
61
+ files = @paths.map { |lp|
62
62
  lpath = File.expand_path(File.join(lp, "#{path}.rb"))
63
63
  File.exist?(lpath) ? lpath : nil
64
64
  }.compact
@@ -69,6 +69,19 @@ module Fluent
69
69
  end
70
70
  end
71
71
 
72
+ path = "#{@search_prefix}#{type}"
73
+
74
+ # prefer LOAD_PATH than gems
75
+ files = $LOAD_PATH.map { |lp|
76
+ lpath = File.expand_path(File.join(lp, "#{path}.rb"))
77
+ File.exist?(lpath) ? lpath : nil
78
+ }.compact
79
+ unless files.empty?
80
+ # prefer newer version
81
+ require files.sort.last
82
+ return
83
+ end
84
+
72
85
  specs = Gem::Specification.find_all { |spec|
73
86
  spec.contains_requirable_file? path
74
87
  }
@@ -75,6 +75,16 @@ module Fluent
75
75
  Process.kill :TERM, $$
76
76
  nil
77
77
  }
78
+ @rpc_server.mount_proc('/api/processes.flushBuffersAndKillWorkers') { |req, res|
79
+ $log.debug "fluentd RPC got /api/processes.flushBuffersAndKillWorkers request"
80
+ if Fluent.windows?
81
+ $log.warn "operation 'flushBuffersAndKillWorkers' is not supported on Windows now."
82
+ else
83
+ Process.kill :USR1, $$
84
+ Process.kill :TERM, $$
85
+ end
86
+ nil
87
+ }
78
88
  @rpc_server.mount_proc('/api/plugins.flushBuffers') { |req, res|
79
89
  $log.debug "fluentd RPC got /api/plugins.flushBuffers request"
80
90
  unless Fluent.windows?
@@ -114,12 +124,21 @@ module Fluent
114
124
  end
115
125
 
116
126
  def install_supervisor_signal_handlers
127
+ trap :HUP do
128
+ $log.debug "fluentd supervisor process get SIGHUP"
129
+ supervisor_sighup_handler
130
+ end unless Fluent.windows?
131
+
117
132
  trap :USR1 do
118
133
  $log.debug "fluentd supervisor process get SIGUSR1"
119
134
  supervisor_sigusr1_handler
120
135
  end unless Fluent.windows?
121
136
  end
122
137
 
138
+ def supervisor_sighup_handler
139
+ kill_worker
140
+ end
141
+
123
142
  def supervisor_sigusr1_handler
124
143
  if log = config[:logger_initializer]
125
144
  log.reopen!
@@ -136,7 +155,7 @@ module Fluent
136
155
  if Fluent.windows?
137
156
  Process.kill :KILL, pid
138
157
  else
139
- Process.kill :INT, pid
158
+ Process.kill :TERM, pid
140
159
  end
141
160
  end
142
161
  end
@@ -487,6 +506,13 @@ module Fluent
487
506
  # worker process SHOULD NOT do anything with SIGINT, SHOULD just ignore.
488
507
  trap :INT do
489
508
  $log.debug "fluentd main process get SIGINT"
509
+
510
+ # When Fluentd is launched without supervisor, worker should handle ctrl-c by itself
511
+ if @standalone_worker
512
+ @finished = true
513
+ $log.debug "getting start to shutdown main process"
514
+ Fluent::Engine.stop
515
+ end
490
516
  end
491
517
 
492
518
  trap :TERM do
@@ -14,9 +14,9 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ require 'fluent/config'
17
18
  require 'fluent/config/element'
18
19
  require 'fluent/log'
19
- require 'fluent/test/driver/test_event_router'
20
20
 
21
21
  require 'timeout'
22
22
 
@@ -24,6 +24,8 @@ module Fluent
24
24
  module Test
25
25
  module Driver
26
26
  class Base
27
+ attr_reader :instance, :logs
28
+
27
29
  def initialize(klass, opts: {}, &block)
28
30
  if klass.is_a?(Class)
29
31
  if block
@@ -36,47 +38,16 @@ module Fluent
36
38
  else
37
39
  @instance = klass
38
40
  end
39
- if opts
40
- @instance.system_config_override(opts)
41
- end
42
- @instance.log = TestLogger.new
43
- @logs = @instance.log.out.logs
41
+
42
+ @logs = []
44
43
 
45
44
  @run_post_conditions = []
46
45
  @run_breaking_conditions = []
47
-
48
46
  @broken = false
49
-
50
- @event_streams = nil
51
- @error_events = nil
52
47
  end
53
48
 
54
- attr_reader :instance, :logs
55
-
56
49
  def configure(conf, syntax: :v1)
57
- if conf.is_a?(Fluent::Config::Element)
58
- @config = conf
59
- else
60
- @config = Config.parse(conf, "(test)", "(test_dir)", syntax: syntax)
61
- end
62
-
63
- if @instance.respond_to?(:router=)
64
- @event_streams = []
65
- @error_events = []
66
-
67
- driver = self
68
- mojule = Module.new do
69
- define_method(:event_emitter_router) do |label_name|
70
- TestEventRouter.new(driver)
71
- end
72
- end
73
- @instance.singleton_class.module_eval do
74
- prepend mojule
75
- end
76
- end
77
-
78
- @instance.configure(@config)
79
- self
50
+ raise NotImplementedError
80
51
  end
81
52
 
82
53
  def end_if(&block)
@@ -93,50 +64,7 @@ module Fluent
93
64
  @broken
94
65
  end
95
66
 
96
- Emit = Struct.new(:tag, :es)
97
- ErrorEvent = Struct.new(:tag, :time, :record, :error)
98
-
99
- # via TestEventRouter
100
- def emit_event_stream(tag, es)
101
- @event_streams << Emit.new(tag, es)
102
- end
103
-
104
- def emit_error_event(tag, time, record, error)
105
- @error_events << ErrorEvent.new(tag, time, record, error)
106
- end
107
-
108
- def events(tag: nil)
109
- return [] if @event_streams.nil?
110
- selected = @event_streams.select{|e| tag.nil? ? true : e.tag == tag }
111
- if block_given?
112
- selected.each do |e|
113
- e.es.each do |time, record|
114
- yield e.tag, time, record
115
- end
116
- end
117
- else
118
- list = []
119
- selected.each do |e|
120
- e.es.each do |time, record|
121
- list << [e.tag, time, record]
122
- end
123
- end
124
- list
125
- end
126
- end
127
-
128
- def error_events(tag: nil)
129
- selected = @error_events.select{|e| tag.nil? ? true : e.tag == tag }
130
- if block_given?
131
- selected.each do |e|
132
- yield e.tag, e.time, e.record, e.error
133
- end
134
- else
135
- selected.map{|e| [e.tag, e.time, e.record, e.error] }
136
- end
137
- end
138
-
139
- def run(expect_emits: nil, expect_records: nil, timeout: nil, start: true, shutdown: true, &block)
67
+ def run(timeout: nil, start: true, shutdown: true, &block)
140
68
  instance_start if start
141
69
 
142
70
  if @instance.respond_to?(:thread_wait_until_start)
@@ -147,7 +75,7 @@ module Fluent
147
75
  end
148
76
 
149
77
  begin
150
- run_actual(expect_emits: expect_emits, expect_records: expect_records, timeout: timeout, &block)
78
+ run_actual(timeout: timeout, &block)
151
79
  ensure
152
80
  instance_shutdown if shutdown
153
81
  end
@@ -183,7 +111,7 @@ module Fluent
183
111
  @instance.terminate unless @instance.terminated?
184
112
  end
185
113
 
186
- def run_actual(expect_emits: nil, expect_records: nil, timeout: nil, &block)
114
+ def run_actual(timeout: nil, &block)
187
115
  if @instance.respond_to?(:_threads)
188
116
  until @instance._threads.values.all?(&:alive?)
189
117
  sleep 0.01
@@ -196,12 +124,6 @@ module Fluent
196
124
  end
197
125
  end
198
126
 
199
- if expect_emits
200
- @run_post_conditions << ->(){ @emit_streams.size >= expect_emits }
201
- end
202
- if expect_records
203
- @run_post_conditions << ->(){ @emit_streams.reduce(0){|a, e| a + e.es.size } >= expected_records }
204
- end
205
127
  if timeout
206
128
  stop_at = Time.now + timeout
207
129
  @run_breaking_conditions << ->(){ Time.now >= stop_at }
@@ -211,20 +133,22 @@ module Fluent
211
133
  raise ArgumentError, "no stop conditions nor block specified"
212
134
  end
213
135
 
214
- if !block_given?
215
- block = ->(){ sleep(0.1) until stop? }
216
- end
136
+ proc = if block_given?
137
+ ->(){ block.call; sleep(0.1) until stop? }
138
+ else
139
+ ->(){ sleep(0.1) until stop? }
140
+ end
217
141
 
218
142
  if timeout
219
143
  begin
220
144
  Timeout.timeout(timeout * 1.1) do |sec|
221
- block.call
145
+ proc.call
222
146
  end
223
147
  rescue Timeout::Error
224
148
  @broken = true
225
149
  end
226
150
  else
227
- block.call
151
+ proc.call
228
152
  end
229
153
  end
230
154
 
@@ -14,28 +14,29 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- require 'fluent/config'
18
- require 'fluent/config/element'
17
+ require 'fluent/test/driver/base'
18
+
19
+ require 'fluent/plugin/base'
20
+ require 'fluent/plugin_id'
19
21
  require 'fluent/log'
20
- require 'fluent/test/driver/owner'
22
+ require 'fluent/plugin_helper'
21
23
 
22
24
  module Fluent
23
25
  module Test
24
26
  module Driver
25
- class BaseOwned
27
+ class OwnerDummy < Fluent::Plugin::Base
28
+ include PluginId
29
+ include PluginLoggerMixin
30
+ include PluginHelper::Mixin
31
+ end
32
+
33
+ class BaseOwned < Base
34
+ attr_accessor :section_name
35
+
26
36
  def initialize(klass, opts: {}, &block)
27
- if klass.is_a?(Class)
28
- if block
29
- # Create new class for test w/ overwritten methods
30
- # klass.dup is worse because its ancestors does NOT include original class name
31
- klass = Class.new(klass)
32
- klass.module_eval(&block)
33
- end
34
- @instance = klass.new
35
- else
36
- @instance = klass
37
- end
38
- owner = Fluent::Test::Driver::Owner.new
37
+ super
38
+
39
+ owner = OwnerDummy.new
39
40
  if opts
40
41
  owner.system_config_override(opts)
41
42
  end
@@ -52,8 +53,6 @@ module Fluent
52
53
  @section_name = ''
53
54
  end
54
55
 
55
- attr_reader :instance, :logs
56
-
57
56
  def configure(conf, syntax: :v1)
58
57
  if conf.is_a?(Fluent::Config::Element)
59
58
  @config = conf
@@ -65,41 +64,6 @@ module Fluent
65
64
  @instance.configure(@config)
66
65
  self
67
66
  end
68
-
69
- def run(start: true, shutdown: true, &block)
70
- instance_start if start
71
-
72
- begin
73
- yield
74
- ensure
75
- instance_shutdown if shutdown
76
- end
77
- end
78
-
79
- def instance_start
80
- unless @instance.started?
81
- @instance.start
82
- instance_hook_after_started
83
- end
84
- end
85
-
86
- def instance_hook_after_started
87
- # insert hooks for tests available after instance.start
88
- end
89
-
90
- def instance_shutdown
91
- @instance.stop unless @instance.stopped?
92
- @instance.before_shutdown unless @instance.before_shutdown?
93
- @instance.shutdown unless @instance.shutdown?
94
-
95
- if @instance.respond_to?(:event_loop_wait_until_stop)
96
- @instance.event_loop_wait_until_stop
97
- end
98
-
99
- @instance.after_shutdown unless @instance.after_shutdown?
100
- @instance.close unless @instance.closed?
101
- @instance.terminate unless @instance.terminated?
102
- end
103
67
  end
104
68
  end
105
69
  end
@@ -0,0 +1,125 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'fluent/test/driver/base'
18
+ require 'fluent/test/driver/test_event_router'
19
+
20
+ module Fluent
21
+ module Test
22
+ module Driver
23
+ class BaseOwner < Base
24
+ def initialize(klass, opts: {}, &block)
25
+ super
26
+
27
+ if opts
28
+ @instance.system_config_override(opts)
29
+ end
30
+ @instance.log = TestLogger.new
31
+ @logs = @instance.log.out.logs
32
+
33
+ @event_streams = nil
34
+ @error_events = nil
35
+ end
36
+
37
+ def configure(conf, syntax: :v1)
38
+ if conf.is_a?(Fluent::Config::Element)
39
+ @config = conf
40
+ else
41
+ @config = Config.parse(conf, "(test)", "(test_dir)", syntax: syntax)
42
+ end
43
+
44
+ if @instance.respond_to?(:router=)
45
+ @event_streams = []
46
+ @error_events = []
47
+
48
+ driver = self
49
+ mojule = Module.new do
50
+ define_method(:event_emitter_router) do |label_name|
51
+ TestEventRouter.new(driver)
52
+ end
53
+ end
54
+ @instance.singleton_class.prepend mojule
55
+ end
56
+
57
+ @instance.configure(@config)
58
+ self
59
+ end
60
+
61
+ Emit = Struct.new(:tag, :es)
62
+ ErrorEvent = Struct.new(:tag, :time, :record, :error)
63
+
64
+ # via TestEventRouter
65
+ def emit_event_stream(tag, es)
66
+ @event_streams << Emit.new(tag, es)
67
+ end
68
+
69
+ def emit_error_event(tag, time, record, error)
70
+ @error_events << ErrorEvent.new(tag, time, record, error)
71
+ end
72
+
73
+ def events(tag: nil)
74
+ return [] if @event_streams.nil?
75
+ selected = @event_streams.select{|e| tag.nil? ? true : e.tag == tag }
76
+ if block_given?
77
+ selected.each do |e|
78
+ e.es.each do |time, record|
79
+ yield e.tag, time, record
80
+ end
81
+ end
82
+ else
83
+ list = []
84
+ selected.each do |e|
85
+ e.es.each do |time, record|
86
+ list << [e.tag, time, record]
87
+ end
88
+ end
89
+ list
90
+ end
91
+ end
92
+
93
+ def emit_count
94
+ @event_streams.size
95
+ end
96
+
97
+ def record_count
98
+ @event_streams.reduce(0) {|a, e| a + e.es.size }
99
+ end
100
+
101
+ def error_events(tag: nil)
102
+ selected = @error_events.select{|e| tag.nil? ? true : e.tag == tag }
103
+ if block_given?
104
+ selected.each do |e|
105
+ yield e.tag, e.time, e.record, e.error
106
+ end
107
+ else
108
+ selected.map{|e| [e.tag, e.time, e.record, e.error] }
109
+ end
110
+ end
111
+
112
+ def run(expect_emits: nil, expect_records: nil, timeout: nil, start: true, shutdown: true, &block)
113
+ if expect_emits
114
+ @run_post_conditions << ->(){ emit_count >= expect_emits }
115
+ end
116
+ if expect_records
117
+ @run_post_conditions << ->(){ record_count >= expect_records }
118
+ end
119
+
120
+ super(timeout: timeout, start: start, shutdown: shutdown, &block)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end