fluentd 0.14.7-x86-mingw32 → 0.14.10-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +2 -0
  4. data/CONTRIBUTING.md +6 -1
  5. data/ChangeLog +95 -0
  6. data/Rakefile +21 -0
  7. data/appveyor.yml +1 -0
  8. data/code-of-conduct.md +3 -0
  9. data/example/out_exec_filter.conf +42 -0
  10. data/fluentd.gemspec +1 -1
  11. data/lib/fluent/agent.rb +2 -2
  12. data/lib/fluent/command/binlog_reader.rb +1 -1
  13. data/lib/fluent/command/cat.rb +15 -4
  14. data/lib/fluent/compat/output.rb +14 -9
  15. data/lib/fluent/compat/parser.rb +141 -11
  16. data/lib/fluent/config/configure_proxy.rb +2 -11
  17. data/lib/fluent/config/section.rb +8 -1
  18. data/lib/fluent/configurable.rb +1 -3
  19. data/lib/fluent/env.rb +1 -1
  20. data/lib/fluent/log.rb +1 -1
  21. data/lib/fluent/plugin/base.rb +17 -0
  22. data/lib/fluent/plugin/filter_parser.rb +108 -0
  23. data/lib/fluent/plugin/filter_record_transformer.rb +14 -35
  24. data/lib/fluent/plugin/filter_stdout.rb +1 -1
  25. data/lib/fluent/plugin/formatter.rb +5 -0
  26. data/lib/fluent/plugin/formatter_msgpack.rb +4 -0
  27. data/lib/fluent/plugin/formatter_stdout.rb +3 -2
  28. data/lib/fluent/plugin/formatter_tsv.rb +34 -0
  29. data/lib/fluent/plugin/in_exec.rb +48 -93
  30. data/lib/fluent/plugin/in_forward.rb +66 -265
  31. data/lib/fluent/plugin/in_http.rb +68 -65
  32. data/lib/fluent/plugin/in_monitor_agent.rb +8 -4
  33. data/lib/fluent/plugin/in_syslog.rb +42 -58
  34. data/lib/fluent/plugin/in_tail.rb +29 -14
  35. data/lib/fluent/plugin/in_tcp.rb +54 -14
  36. data/lib/fluent/plugin/in_udp.rb +49 -13
  37. data/lib/fluent/plugin/multi_output.rb +1 -3
  38. data/lib/fluent/plugin/out_exec.rb +58 -71
  39. data/lib/fluent/plugin/out_exec_filter.rb +199 -279
  40. data/lib/fluent/plugin/out_file.rb +172 -81
  41. data/lib/fluent/plugin/out_forward.rb +229 -206
  42. data/lib/fluent/plugin/out_stdout.rb +6 -21
  43. data/lib/fluent/plugin/output.rb +90 -59
  44. data/lib/fluent/plugin/parser.rb +121 -61
  45. data/lib/fluent/plugin/parser_csv.rb +9 -3
  46. data/lib/fluent/plugin/parser_json.rb +37 -35
  47. data/lib/fluent/plugin/parser_ltsv.rb +11 -19
  48. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  49. data/lib/fluent/plugin/parser_regexp.rb +15 -42
  50. data/lib/fluent/plugin/parser_tsv.rb +8 -3
  51. data/lib/fluent/plugin_helper.rb +10 -1
  52. data/lib/fluent/plugin_helper/child_process.rb +139 -73
  53. data/lib/fluent/plugin_helper/compat_parameters.rb +93 -4
  54. data/lib/fluent/plugin_helper/event_emitter.rb +14 -1
  55. data/lib/fluent/plugin_helper/event_loop.rb +24 -6
  56. data/lib/fluent/plugin_helper/extract.rb +16 -4
  57. data/lib/fluent/plugin_helper/formatter.rb +9 -11
  58. data/lib/fluent/plugin_helper/inject.rb +16 -1
  59. data/lib/fluent/plugin_helper/parser.rb +3 -3
  60. data/lib/fluent/plugin_helper/server.rb +494 -0
  61. data/lib/fluent/plugin_helper/socket.rb +101 -0
  62. data/lib/fluent/plugin_helper/socket_option.rb +84 -0
  63. data/lib/fluent/plugin_helper/timer.rb +1 -0
  64. data/lib/fluent/root_agent.rb +1 -1
  65. data/lib/fluent/test/driver/base.rb +95 -49
  66. data/lib/fluent/test/driver/base_owner.rb +18 -8
  67. data/lib/fluent/test/driver/multi_output.rb +2 -1
  68. data/lib/fluent/test/driver/output.rb +29 -6
  69. data/lib/fluent/test/helpers.rb +3 -1
  70. data/lib/fluent/test/log.rb +4 -0
  71. data/lib/fluent/test/startup_shutdown.rb +13 -0
  72. data/lib/fluent/time.rb +14 -8
  73. data/lib/fluent/version.rb +1 -1
  74. data/lib/fluent/winsvc.rb +1 -1
  75. data/test/command/test_binlog_reader.rb +5 -1
  76. data/test/compat/test_parser.rb +10 -0
  77. data/test/config/test_configurable.rb +193 -0
  78. data/test/config/test_configure_proxy.rb +0 -43
  79. data/test/helper.rb +36 -1
  80. data/test/plugin/test_base.rb +16 -0
  81. data/test/plugin/test_filter_parser.rb +665 -0
  82. data/test/plugin/test_filter_record_transformer.rb +36 -100
  83. data/test/plugin/test_filter_stdout.rb +18 -27
  84. data/test/plugin/test_in_dummy.rb +1 -1
  85. data/test/plugin/test_in_exec.rb +206 -94
  86. data/test/plugin/test_in_forward.rb +268 -347
  87. data/test/plugin/test_in_http.rb +310 -186
  88. data/test/plugin/test_in_monitor_agent.rb +65 -35
  89. data/test/plugin/test_in_syslog.rb +39 -3
  90. data/test/plugin/test_in_tcp.rb +78 -62
  91. data/test/plugin/test_in_udp.rb +101 -80
  92. data/test/plugin/test_out_exec.rb +223 -68
  93. data/test/plugin/test_out_exec_filter.rb +520 -169
  94. data/test/plugin/test_out_file.rb +637 -177
  95. data/test/plugin/test_out_forward.rb +242 -234
  96. data/test/plugin/test_out_null.rb +1 -1
  97. data/test/plugin/test_out_secondary_file.rb +4 -2
  98. data/test/plugin/test_out_stdout.rb +14 -35
  99. data/test/plugin/test_output_as_buffered.rb +60 -2
  100. data/test/plugin/test_parser.rb +359 -0
  101. data/test/plugin/test_parser_csv.rb +1 -2
  102. data/test/plugin/test_parser_json.rb +3 -4
  103. data/test/plugin/test_parser_labeled_tsv.rb +1 -2
  104. data/test/plugin/test_parser_none.rb +1 -2
  105. data/test/plugin/test_parser_regexp.rb +8 -4
  106. data/test/plugin/test_parser_tsv.rb +4 -3
  107. data/test/plugin_helper/test_child_process.rb +184 -0
  108. data/test/plugin_helper/test_compat_parameters.rb +88 -1
  109. data/test/plugin_helper/test_extract.rb +0 -1
  110. data/test/plugin_helper/test_formatter.rb +5 -2
  111. data/test/plugin_helper/test_inject.rb +21 -0
  112. data/test/plugin_helper/test_parser.rb +6 -5
  113. data/test/plugin_helper/test_server.rb +905 -0
  114. data/test/test_event_time.rb +3 -1
  115. data/test/test_output.rb +53 -2
  116. data/test/test_plugin_classes.rb +20 -0
  117. data/test/test_root_agent.rb +139 -0
  118. data/test/test_test_drivers.rb +135 -0
  119. metadata +28 -8
  120. data/test/plugin/test_parser_base.rb +0 -32
@@ -0,0 +1,101 @@
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 'socket'
18
+ require 'ipaddr'
19
+
20
+ require_relative 'socket_option'
21
+
22
+ module Fluent
23
+ module PluginHelper
24
+ module Socket
25
+ # stop : [-]
26
+ # shutdown : [-]
27
+ # close : [-]
28
+ # terminate: [-]
29
+
30
+ include Fluent::PluginHelper::SocketOption
31
+
32
+ attr_reader :_sockets # for tests
33
+
34
+ # TODO: implement connection pool for specified host
35
+
36
+ def socket_create(proto, host, port, **kwargs, &block)
37
+ case proto
38
+ when :tcp
39
+ socket_create_tcp(host, port, **kwargs, &block)
40
+ when :udp
41
+ socket_create_udp(host, port, **kwargs, &block)
42
+ when :tls
43
+ socket_create_tls(host, port, **kwargs, &block)
44
+ when :unix
45
+ raise "not implemented yet"
46
+ else
47
+ raise ArgumentError, "invalid protocol: #{proto}"
48
+ end
49
+ end
50
+
51
+ def socket_create_tcp(host, port, resolve_name: false, **kwargs, &block)
52
+ sock = TCPSocket.new(host, port)
53
+ socket_option_set(sock, resolve_name: resolve_name, **kwargs)
54
+ if block
55
+ begin
56
+ block.call(sock)
57
+ ensure
58
+ sock.close_write rescue nil
59
+ sock.close rescue nil
60
+ end
61
+ else
62
+ sock
63
+ end
64
+ end
65
+
66
+ def socket_create_udp(host, port, resolve_name: false, connect: false, **kwargs, &block)
67
+ family = IPAddr.new(IPSocket.getaddress(host)).ipv4? ? ::Socket::AF_INET : ::Socket::AF_INET6
68
+ sock = UDPSocket.new(family)
69
+ socket_option_set(sock, resolve_name: resolve_name, **kwargs)
70
+ sock.connect(host, port) if connect
71
+ if block
72
+ begin
73
+ block.call(sock)
74
+ ensure
75
+ sock.close rescue nil
76
+ end
77
+ else
78
+ sock
79
+ end
80
+ end
81
+
82
+ def socket_create_tls(host, port, resolve_name: false, certopts: {}, &block)
83
+ raise "not implemented yet"
84
+ end
85
+
86
+ # socket_create_socks ?
87
+
88
+ def initialize
89
+ super
90
+ # @_sockets = [] # for keepalived sockets / connection pool
91
+ end
92
+
93
+ # def close
94
+ # @_sockets.each do |sock|
95
+ # sock.close
96
+ # end
97
+ # super
98
+ # end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,84 @@
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 'socket'
18
+ require 'fcntl'
19
+
20
+ # this module is only for Socket/Server plugin helpers
21
+ module Fluent
22
+ module PluginHelper
23
+ module SocketOption
24
+ FORMAT_STRUCT_LINGER = 'I!I!' # { int l_onoff; int l_linger; }
25
+ FORMAT_STRUCT_TIMEVAL = 'L!L!' # { time_t tv_sec; suseconds_t tv_usec; }
26
+
27
+ def socket_option_validate!(protocol, resolve_name: nil, linger_timeout: nil, recv_timeout: nil, send_timeout: nil, certopts: nil)
28
+ unless resolve_name.nil?
29
+ if protocol != :tcp && protocol != :udp && protocol != :tls
30
+ raise ArgumentError, "BUG: resolve_name in available for tcp/udp/tls"
31
+ end
32
+ end
33
+ if linger_timeout
34
+ if protocol != :tcp && protocol != :tls
35
+ raise ArgumentError, "BUG: linger_timeout is available for tcp/tls"
36
+ end
37
+ end
38
+ if certopts
39
+ if protocol != :tls
40
+ raise ArgumentError, "BUG: certopts is available only for tls"
41
+ end
42
+ else
43
+ if protocol == :tls
44
+ raise ArgumentError, "BUG: certopts (certificate options) not specified for TLS"
45
+ socket_option_certopts_validate!(certopts)
46
+ end
47
+ end
48
+ end
49
+
50
+ def socket_option_certopts_validate!(certopts)
51
+ raise "not implemented yet"
52
+ end
53
+
54
+ def socket_option_set(sock, resolve_name: nil, nonblock: false, linger_timeout: nil, recv_timeout: nil, send_timeout: nil, certopts: nil)
55
+ unless resolve_name.nil?
56
+ sock.do_not_reverse_lookup = !resolve_name
57
+ end
58
+ if nonblock
59
+ sock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
60
+ end
61
+ if linger_timeout
62
+ optval = [1, linger_timeout.to_i].pack(FORMAT_STRUCT_LINGER)
63
+ socket_option_set_one(sock, :SO_LINGER, optval)
64
+ end
65
+ if recv_timeout
66
+ optval = [recv_timeout.to_i, 0].pack(FORMAT_STRUCT_TIMEVAL)
67
+ socket_option_set_one(sock, :SO_RCVTIMEO, optval)
68
+ end
69
+ if send_timeout
70
+ optval = [send_timeout.to_i, 0].pack(FORMAT_STRUCT_TIMEVAL)
71
+ socket_option_set_one(sock, :SO_SNDTIMEO, optval)
72
+ end
73
+ # TODO: certopts for TLS
74
+ sock
75
+ end
76
+
77
+ def socket_option_set_one(sock, option, value)
78
+ sock.setsockopt(::Socket::SOL_SOCKET, option, value)
79
+ rescue => e
80
+ log.warn "failed to set socket option", sock: sock.class, option: option, value: value, error: e
81
+ end
82
+ end
83
+ end
84
+ end
@@ -36,6 +36,7 @@ module Fluent
36
36
  timer = TimerWatcher.new(title, interval, repeat, log, checker, &block)
37
37
  @_timers << title
38
38
  event_loop_attach(timer)
39
+ timer
39
40
  end
40
41
 
41
42
  def timer_running?
@@ -233,7 +233,7 @@ module Fluent
233
233
  # <source> emits events to the top-level event router (RootAgent#event_router).
234
234
  # Input#configure overwrites event_router to a label's event_router if it has `@label` parameter.
235
235
  # See also 'fluentd/plugin/input.rb'
236
- input.router = @event_router
236
+ input.context_router = @event_router
237
237
  input.configure(conf)
238
238
  @inputs << input
239
239
 
@@ -18,32 +18,38 @@ require 'fluent/config'
18
18
  require 'fluent/config/element'
19
19
  require 'fluent/log'
20
20
 
21
+ require 'serverengine/socket_manager'
22
+ require 'fileutils'
21
23
  require 'timeout'
22
24
 
23
25
  module Fluent
24
26
  module Test
25
27
  module Driver
28
+ class TestTimedOut < RuntimeError; end
29
+
26
30
  class Base
27
31
  attr_reader :instance, :logs
28
32
 
33
+ DEFAULT_TIMEOUT = 300
34
+
29
35
  def initialize(klass, opts: {}, &block)
30
36
  if klass.is_a?(Class)
37
+ @instance = klass.new
31
38
  if block
32
- # Create new class for test w/ overwritten methods
33
- # klass.dup is worse because its ancestors does NOT include original class name
34
- klass_name = klass.name
35
- klass = Class.new(klass)
36
- klass.define_singleton_method("name") { klass_name }
37
- klass.module_eval(&block)
39
+ @instance.singleton_class.module_eval(&block)
40
+ @instance.send(:initialize)
38
41
  end
39
- @instance = klass.new
40
42
  else
41
43
  @instance = klass
42
44
  end
43
45
  @instance.under_plugin_development = true
44
46
 
47
+ @socket_manager_server = nil
48
+
45
49
  @logs = []
46
50
 
51
+ @test_clock_id = Process::CLOCK_MONOTONIC_RAW rescue Process::CLOCK_MONOTONIC
52
+
47
53
  @run_post_conditions = []
48
54
  @run_breaking_conditions = []
49
55
  @broken = false
@@ -70,97 +76,137 @@ module Fluent
70
76
  def run(timeout: nil, start: true, shutdown: true, &block)
71
77
  instance_start if start
72
78
 
73
- if @instance.respond_to?(:thread_wait_until_start)
74
- @instance.thread_wait_until_start
75
- end
76
- if @instance.respond_to?(:event_loop_wait_until_start)
77
- @instance.event_loop_wait_until_start
79
+ timeout ||= DEFAULT_TIMEOUT
80
+ stop_at = Process.clock_gettime(@test_clock_id) + timeout
81
+ @run_breaking_conditions << ->(){ Process.clock_gettime(@test_clock_id) >= stop_at }
82
+
83
+ if !block_given? && @run_post_conditions.empty? && @run_breaking_conditions.empty?
84
+ raise ArgumentError, "no stop conditions nor block specified"
78
85
  end
79
86
 
87
+ sleep_with_watching_threads = ->(){
88
+ if @instance.respond_to?(:_threads)
89
+ @instance._threads.values.each{|t| t.join(0) }
90
+ end
91
+ sleep 0.1
92
+ }
93
+
80
94
  begin
81
- run_actual(timeout: timeout, &block)
95
+ retval = run_actual(timeout: timeout, &block)
96
+ sleep_with_watching_threads.call until stop?
97
+ retval
82
98
  ensure
83
99
  instance_shutdown if shutdown
84
100
  end
85
101
  end
86
102
 
87
103
  def instance_start
104
+ if @instance.respond_to?(:server_wait_until_start)
105
+ @socket_manager_path = ServerEngine::SocketManager::Server.generate_path
106
+ if @socket_manager_path.is_a?(String) && File.exist?(@socket_manager_path)
107
+ FileUtils.rm_f @socket_manager_path
108
+ end
109
+ @socket_manager_server = ServerEngine::SocketManager::Server.open(@socket_manager_path)
110
+ ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = @socket_manager_path.to_s
111
+ end
112
+
88
113
  unless @instance.started?
89
114
  @instance.start
90
- instance_hook_after_started
91
115
  end
92
116
  unless @instance.after_started?
93
117
  @instance.after_start
94
118
  end
119
+
120
+ if @instance.respond_to?(:thread_wait_until_start)
121
+ @instance.thread_wait_until_start
122
+ end
123
+
124
+ if @instance.respond_to?(:event_loop_wait_until_start)
125
+ @instance.event_loop_wait_until_start
126
+ end
127
+
128
+ instance_hook_after_started
95
129
  end
96
130
 
97
131
  def instance_hook_after_started
98
132
  # insert hooks for tests available after instance.start
99
133
  end
100
134
 
135
+ def instance_hook_before_stopped
136
+ # same with above
137
+ end
138
+
101
139
  def instance_shutdown
102
- @instance.stop unless @instance.stopped?
103
- @instance.before_shutdown unless @instance.before_shutdown?
104
- @instance.shutdown unless @instance.shutdown?
140
+ instance_hook_before_stopped
141
+
142
+ unless @instance.stopped?
143
+ @instance.stop rescue nil
144
+ end
145
+ unless @instance.before_shutdown?
146
+ @instance.before_shutdown rescue nil
147
+ end
148
+ unless @instance.shutdown?
149
+ @instance.shutdown rescue nil
150
+ end
105
151
 
106
152
  if @instance.respond_to?(:event_loop_wait_until_stop)
107
153
  @instance.event_loop_wait_until_stop
108
154
  end
109
155
 
110
- @instance.after_shutdown unless @instance.after_shutdown?
111
- @instance.close unless @instance.closed?
156
+ unless @instance.after_shutdown?
157
+ @instance.after_shutdown rescue nil
158
+ end
159
+ unless @instance.closed?
160
+ @instance.close rescue nil
161
+ end
112
162
 
113
163
  if @instance.respond_to?(:thread_wait_until_stop)
114
164
  @instance.thread_wait_until_stop
115
165
  end
116
166
 
117
- @instance.terminate unless @instance.terminated?
118
- end
167
+ if @instance.respond_to?(:server_wait_until_stop)
168
+ @instance.server_wait_until_stop
169
+ end
119
170
 
120
- def run_actual(timeout: nil, &block)
121
- if @instance.respond_to?(:_threads)
122
- until @instance._threads.values.all?(&:alive?)
123
- sleep 0.01
124
- end
171
+ unless @instance.terminated?
172
+ @instance.terminate rescue nil
125
173
  end
126
174
 
127
- if @instance.respond_to?(:event_loop_running?)
128
- until @instance.event_loop_running?
129
- sleep 0.01
175
+ if @socket_manager_server
176
+ @socket_manager_server.close
177
+ if @socket_manager_server.is_a?(String) && File.exist?(@socket_manager_path)
178
+ FileUtils.rm_f @socket_manager_path
130
179
  end
131
180
  end
181
+ end
132
182
 
133
- if timeout
134
- stop_at = Time.now + timeout
135
- @run_breaking_conditions << ->(){ Time.now >= stop_at }
183
+ def run_actual(timeout: DEFAULT_TIMEOUT, &block)
184
+ if @instance.respond_to?(:_threads)
185
+ sleep 0.1 until @instance._threads.values.all?(&:alive?)
136
186
  end
137
187
 
138
- if !block_given? && @run_post_conditions.empty? && @run_breaking_conditions.empty?
139
- raise ArgumentError, "no stop conditions nor block specified"
188
+ if @instance.respond_to?(:event_loop_running?)
189
+ sleep 0.1 until @instance.event_loop_running?
140
190
  end
141
191
 
142
- proc = if block_given?
143
- ->(){ block.call; sleep(0.1) until stop? }
144
- else
145
- ->(){ sleep(0.1) until stop? }
146
- end
192
+ if @instance.respond_to?(:_child_process_processes)
193
+ sleep 0.1 until @instance._child_process_processes.values.all?{|pinfo| pinfo.alive }
194
+ end
147
195
 
148
- if timeout
149
- begin
150
- Timeout.timeout(timeout * 1.1) do |sec|
151
- proc.call
152
- end
153
- rescue Timeout::Error
154
- @broken = true
196
+ return_value = nil
197
+ begin
198
+ Timeout.timeout(timeout * 1.1) do |sec|
199
+ return_value = block.call if block_given?
155
200
  end
156
- else
157
- proc.call
201
+ rescue Timeout::Error
202
+ raise TestTimedOut, "Test case timed out with hard limit."
158
203
  end
204
+ return_value
159
205
  end
160
206
 
161
207
  def stop?
162
208
  # Should stop running if post conditions are not registered.
163
- return true unless @run_post_conditions
209
+ return true unless @run_post_conditions || @run_post_conditions.empty?
164
210
 
165
211
  # Should stop running if all of the post conditions are true.
166
212
  return true if @run_post_conditions.all? {|proc| proc.call }
@@ -71,25 +71,35 @@ module Fluent
71
71
  end
72
72
 
73
73
  def events(tag: nil)
74
- return [] if @event_streams.nil?
75
- selected = @event_streams.select{|e| tag.nil? ? true : e.tag == tag }
76
74
  if block_given?
77
- selected.each do |e|
78
- e.es.each do |time, record|
79
- yield e.tag, time, record
75
+ event_streams(tag: tag) do |t, es|
76
+ es.each do |time, record|
77
+ yield t, time, record
80
78
  end
81
79
  end
82
80
  else
83
81
  list = []
84
- selected.each do |e|
85
- e.es.each do |time, record|
86
- list << [e.tag, time, record]
82
+ event_streams(tag: tag) do |t, es|
83
+ es.each do |time, record|
84
+ list << [t, time, record]
87
85
  end
88
86
  end
89
87
  list
90
88
  end
91
89
  end
92
90
 
91
+ def event_streams(tag: nil)
92
+ return [] if @event_streams.nil?
93
+ selected = @event_streams.select{|e| tag.nil? ? true : e.tag == tag }
94
+ if block_given?
95
+ selected.each do |e|
96
+ yield e.tag, e.es
97
+ end
98
+ else
99
+ selected.map{|e| [e.tag, e.es] }
100
+ end
101
+ end
102
+
93
103
  def emit_count
94
104
  @event_streams.size
95
105
  end