sqreen 1.20.4-java → 1.21.0.beta3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -24
  3. data/lib/sqreen/condition_evaluator.rb +6 -5
  4. data/lib/sqreen/conditionable.rb +24 -6
  5. data/lib/sqreen/deliveries/batch.rb +8 -1
  6. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  7. data/lib/sqreen/ecosystem/exception_reporting.rb +26 -0
  8. data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
  9. data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
  10. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  11. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  12. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  13. data/lib/sqreen/ecosystem/module_api/message_producer.rb +51 -0
  14. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
  15. data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
  16. data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
  17. data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
  18. data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
  19. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  20. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  21. data/lib/sqreen/ecosystem/module_registry.rb +44 -0
  22. data/lib/sqreen/ecosystem/redis/redis_connection.rb +43 -0
  23. data/lib/sqreen/ecosystem/tracing/modules/client.rb +31 -0
  24. data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
  25. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  26. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  27. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  28. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  29. data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
  30. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  31. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  32. data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
  33. data/lib/sqreen/ecosystem.rb +96 -0
  34. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +99 -0
  35. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +42 -0
  36. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
  37. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  38. data/lib/sqreen/ecosystem_integration.rb +87 -0
  39. data/lib/sqreen/frameworks/generic.rb +15 -1
  40. data/lib/sqreen/graft/call.rb +30 -1
  41. data/lib/sqreen/graft/hook.rb +88 -78
  42. data/lib/sqreen/graft/hook_point.rb +17 -10
  43. data/lib/sqreen/legacy/old_event_submission_strategy.rb +7 -1
  44. data/lib/sqreen/metrics/req_detailed.rb +41 -0
  45. data/lib/sqreen/metrics.rb +1 -0
  46. data/lib/sqreen/remote_command.rb +3 -0
  47. data/lib/sqreen/rules/rule_cb.rb +2 -2
  48. data/lib/sqreen/runner.rb +44 -15
  49. data/lib/sqreen/session.rb +2 -0
  50. data/lib/sqreen/signals/conversions.rb +6 -1
  51. data/lib/sqreen/version.rb +1 -1
  52. data/lib/sqreen/weave/budget.rb +3 -14
  53. data/lib/sqreen/weave/legacy/instrumentation.rb +145 -94
  54. metadata +41 -5
@@ -0,0 +1,64 @@
1
+ require 'sqreen/ecosystem/loggable'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ # The transaction storage is a mechanism for the modules to share
6
+ # request-scoped data with each other or to keep request-scoped objects
7
+ # themselves, although, for this last use case to be effective,
8
+ # propagation of request start/end events to the modules will likely be
9
+ # needed. A more generic notification of data availability to modules
10
+ # also may be needed in the future.
11
+ #
12
+ # This is not be used to share data with the Ecosystem client
13
+ #
14
+ # This class is not thread safe because it can call the lazy getter
15
+ # twice if [] is called concurrently.
16
+ class TransactionStorage
17
+ include Loggable
18
+
19
+ class << self
20
+ # @return [Sqreen::Ecosystem::TransactionStorage]
21
+ def create_thread_local
22
+ Thread.current[:tx_storage] = new
23
+ end
24
+
25
+ # @return [Sqreen::Ecosystem::TransactionStorage]
26
+ def fetch_thread_local
27
+ Thread.current[:tx_storage]
28
+ end
29
+
30
+ def destroy_thread_local
31
+ Thread.current[:tx_storage] = nil
32
+ end
33
+ end
34
+
35
+ def initialize
36
+ @values = {}
37
+ @values_lazy = {}
38
+ end
39
+
40
+ def []=(key, value)
41
+ @values[key] = value
42
+ end
43
+
44
+ def set_lazy(key, &block)
45
+ @values_lazy[key] = block
46
+ end
47
+
48
+ def [](key)
49
+ v = @values[key]
50
+ return v unless v.nil?
51
+
52
+ v = @values_lazy[key]
53
+ return nil if v.nil?
54
+
55
+ begin
56
+ @values[key] = v.call
57
+ rescue StandardError => e
58
+ logger.warn { "Error resolving key #{e} with lazy value: #{e.message}" }
59
+ raise
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,13 @@
1
+ module Sqreen
2
+ module Ecosystem
3
+ module Util
4
+ module CallWritersFromInit
5
+ def initialize(values = {})
6
+ values.each do |attr, val|
7
+ public_send("#{attr}=", val)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,96 @@
1
+ require 'securerandom'
2
+ require 'sqreen/ecosystem/module_registry'
3
+ require 'sqreen/ecosystem/tracing/sampling_configuration'
4
+ require 'sqreen/ecosystem/transaction_storage'
5
+ require 'sqreen/ecosystem/tracing_broker'
6
+ require 'sqreen/ecosystem/tracing_id_setup'
7
+ require 'sqreen/ecosystem/module_api/message_producer'
8
+ require 'sqreen/ecosystem/module_api/tracing_id_generation'
9
+ require 'sqreen/ecosystem/module_api/tracing'
10
+
11
+ module Sqreen
12
+ # The API for the ecosystem client (together with the dispatch table)
13
+ module Ecosystem
14
+ class << self
15
+ def init(opts = {})
16
+ @registry = ModuleRegistry.new
17
+ register_modules(opts[:modules])
18
+ @registry.init_all
19
+
20
+ # setup tracing generation
21
+ tracing_id_mods = @registry.module_subset(ModuleApi::TracingIdGeneration)
22
+ @tracing_id_setup = TracingIdSetup.new(tracing_id_mods)
23
+ @tracing_id_setup.setup_modules
24
+
25
+ # configure tracing broker with the consumers (tracing modules)
26
+ tracing_modules = @registry.module_subset(ModuleApi::Tracing)
27
+ @tracing_broker = TracingBroker.new(tracing_modules)
28
+
29
+ # inject tracing broker in message producers
30
+ @registry.each_module(ModuleApi::MessageProducer) do |mod|
31
+ mod.tracing_broker = @tracing_broker
32
+ end
33
+ rescue ::Exception # rubocop:disable Lint/RescueException
34
+ # TODO: modules must be disabled at this point
35
+ raise
36
+ end
37
+
38
+ def reset
39
+ instance_variables.each do |ia|
40
+ instance_variable_set(ia, nil)
41
+ end
42
+ end
43
+
44
+ # To be called by the Ecosystem client when a new transaction
45
+ # (generally: request) is started
46
+ # In the future, it's intended that request end/start detection be handled
47
+ # by the Ecosystem itself, so control will flow in the other direction,
48
+ # from the ecosystem to its client
49
+ def start_transaction
50
+ TransactionStorage.create_thread_local
51
+ end
52
+
53
+ def end_transaction
54
+ TransactionStorage.destroy_thread_local
55
+ end
56
+
57
+ # @param [String] tracing_id_prefix
58
+ # @param [Array<Hash{String=>Object}>] sampling_config
59
+ def configure_sampling(tracing_id_prefix, sampling_config)
60
+ @tracing_id_setup.tracing_id_prefix = tracing_id_prefix
61
+ built_samp_cfg = Tracing::SamplingConfiguration.new(sampling_config)
62
+ @tracing_broker.sampling_configuration = built_samp_cfg
63
+ end
64
+
65
+ private
66
+
67
+ def register_modules(modules)
68
+ return register_all_modules unless modules
69
+
70
+ modules.each { |mod| register mod }
71
+ end
72
+
73
+ def register_all_modules
74
+ # replace with something more magical?
75
+ require_relative 'ecosystem/http/rack_request'
76
+ register Http::RackRequest.new
77
+
78
+ require_relative 'ecosystem/http/net_http'
79
+ register Http::NetHttp.new
80
+
81
+ require_relative 'ecosystem/redis/redis_connection'
82
+ register Redis::RedisConnection.new
83
+
84
+ require_relative 'ecosystem/tracing/modules/client'
85
+ register Tracing::Modules::Client.new
86
+
87
+ require_relative 'ecosystem/tracing/modules/server'
88
+ register Tracing::Modules::Server.new
89
+ end
90
+
91
+ def register(mod)
92
+ @registry.register mod
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,99 @@
1
+ require 'sqreen/metrics_store'
2
+ require 'sqreen/log/loggable'
3
+ require 'sqreen/events/remote_exception'
4
+ require 'sqreen/mono_time'
5
+ require 'sqreen/graft/call'
6
+
7
+ module Sqreen
8
+ class EcosystemIntegration
9
+ module AroundCallbacks
10
+ class << self
11
+ include Log::Loggable::ClassMethods
12
+
13
+ # @return [Proc]
14
+ attr_accessor :create_metric
15
+
16
+ # for instrumentation hooks
17
+ # instrumentation hooks already handle budgets/metrics
18
+ def wrap_instrumentation_hook(module_name, action, callable)
19
+ # make tag similar to that of rules
20
+ # TODO: move to structured tags to avoid silly string manips
21
+ action_for_metric = if %w[pre post failing finally].include?(action)
22
+ action
23
+ else
24
+ 'pre'
25
+ end
26
+ metric_name = "sq.ecosystem_#{module_name}.#{action_for_metric}"
27
+ create_metric.call(metric_name)
28
+
29
+ Proc.new do |*args|
30
+ begin
31
+ callable.call(*args)
32
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
33
+ # 2) rescue exceptions
34
+ logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
35
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
36
+ Sqreen::RemoteException.record(e)
37
+ end # end begin
38
+ end # end proc
39
+ end
40
+
41
+ # XXX: not used yet
42
+ def wrap_generic_callback(module_name, action, callable)
43
+ timer_name = "ecosystem:#{module_name}@#{action}"
44
+ perf_notif_name = "ecosystem_#{module_name}"
45
+
46
+ # XXX: register metric
47
+
48
+ Proc.new do |*args|
49
+ begin
50
+ req_storage = Thread.current[:sqreen_http_request]
51
+
52
+ timer = Graft::Timer.new(timer_name) do |t|
53
+ # this is an epilogue to measure()
54
+ req_storage && req_storage[:timed_hooks] << t
55
+ end
56
+
57
+ req_timer = nil
58
+ timer.measure do
59
+ # not in a request, no budget; call cb
60
+ next callable.call(*args) unless req_storage
61
+
62
+ # 1) budget enforcement
63
+ # skip callback if budget already expended
64
+ next if req_storage[:time_budget_expended]
65
+
66
+ budget = req_storage[:time_budget]
67
+ if budget
68
+ req_timer = req_storage[:timer]
69
+ remaining = budget - req_timer.elapsed
70
+ unless remaining > 0
71
+ req_storage[:time_budget_expended] = true
72
+ next # skip callback
73
+ end
74
+ end
75
+
76
+ callable.call(*args)
77
+ end
78
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
79
+ # 2) rescue exceptions
80
+ logger.warn { "Error in #{module_name}:#{action}: #{e.message}" }
81
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
82
+ Sqreen::RemoteException.record(e)
83
+ ensure
84
+ # 3) contribute to performance metrics
85
+ if timer
86
+ req_timer.include_measurements(timer) if req_timer
87
+
88
+ # XXX: PerformanceNotifications is used no more
89
+ Sqreen::PerformanceNotifications.notify(
90
+ perf_notif_name, action, *timer.start_and_end
91
+ )
92
+ end
93
+ end # end begin
94
+ end # end proc
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,42 @@
1
+ require 'sqreen/graft/hook'
2
+ require 'sqreen/ecosystem_integration/around_callbacks'
3
+
4
+ module Sqreen
5
+ class EcosystemIntegration
6
+ module InstrumentationService
7
+ class << self
8
+ # @param [String] module_name
9
+ # @param [String] method in form A::B#c or A::B.c
10
+ # @param [Hash{Symbol=>Proc}] spec
11
+ def instrument(module_name, method, spec)
12
+ hook = Sqreen::Graft::Hook[method].add do
13
+ if spec[:before]
14
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'pre', spec[:before])
15
+ tag = "weave,rule=ecosystem_#{module_name}"
16
+ before(tag, flow: true, &cb)
17
+ end
18
+
19
+ if spec[:after]
20
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'post', spec[:after])
21
+ tag = "weave,rule=ecosystem_#{module_name}"
22
+ after(tag, flow: true, &cb)
23
+ end
24
+
25
+ if spec[:raised]
26
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'failing', spec[:raised])
27
+ tag = "weave,rule=ecosystem_#{module_name}"
28
+ raised(tag, flow: true, &cb)
29
+ end
30
+
31
+ if spec[:ensured]
32
+ cb = AroundCallbacks.wrap_instrumentation_hook(module_name, 'finally', spec[:ensured])
33
+ tag = "weave,rule=ecosystem_#{module_name}"
34
+ ensured(tag, flow: true, &cb)
35
+ end
36
+ end
37
+ hook.install
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,58 @@
1
+ require 'sqreen/events/remote_exception'
2
+ require 'sqreen/log/loggable'
3
+
4
+ module Sqreen
5
+ class EcosystemIntegration
6
+ # This class gets notified of request start/end and
7
+ # 1) distributes such events to listeners (typically ecosystem modules;
8
+ # the method add_start_observer is exposed to ecosystem modules through
9
+ # +Sqreen::Ecosystem::ModuleApi::EventListener+ and the dispatch table).
10
+ # 2) keeps track of whether a request is active on this thread. This is
11
+ # so that users of this class can have this information without needing
12
+ # to subscribe to request start/events and keeping thread local state
13
+ # themselves.
14
+ # XXX: Since the Ecosystem is also notified of request start/end, it could
15
+ # notify its modules of request start without going through the dispatch
16
+ # table and call add_start_observer. We need to think if we want to keep
17
+ # the transaction / request distinction or if they should just be
18
+ # assumed to be the same, though.
19
+ class RequestLifecycleTracking
20
+ include Sqreen::Log::Loggable
21
+
22
+ def initialize
23
+ @start_observers = []
24
+ @tl_key = "#{object_id}_req_in_flight"
25
+ end
26
+
27
+ # API for classes needing to know the request state
28
+
29
+ # @param cb A callback taking a Rack::Request
30
+ def add_start_observer(cb)
31
+ @start_observers << cb
32
+ end
33
+
34
+ def in_request?
35
+ Thread.current[@tl_key] ? true : false
36
+ end
37
+
38
+ # API for classes notifying this one of request events
39
+
40
+ def notify_request_start(rack_req)
41
+ Thread.current[@tl_key] = true
42
+ return if @start_observers.empty?
43
+ @start_observers.each do |cb|
44
+ begin
45
+ cb.call(rack_req)
46
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
47
+ logger.warn { "Error calling #{cb} on request start: #{e.message}" }
48
+ Sqreen::RemoteException.record(e)
49
+ end
50
+ end
51
+ end
52
+
53
+ def notify_request_end
54
+ Thread.current[@tl_key] = false
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ require 'sqreen/log/loggable'
2
+
3
+ module Sqreen
4
+ class EcosystemIntegration
5
+ class SignalConsumption
6
+ include Sqreen::Log::Loggable
7
+
8
+ PAYLOAD_CREATOR_SECTIONS = %w[request response params headers].freeze
9
+
10
+ # @param [Sqreen::Frameworks::GenericFramework] framework
11
+ # @param [Sqreen::EcosystemIntegration::RequestLifecycleTracking]
12
+ # @param [Sqreen::CappedQueue]
13
+ def initialize(framework, req_lifecycle, queue)
14
+ @framework = framework
15
+ @req_lifecycle = req_lifecycle
16
+ @queue = queue
17
+ end
18
+
19
+ def consume_signal(signal)
20
+ # transitional
21
+ unless Sqreen.features.fetch('use_signals', DEFAULT_USE_SIGNALS)
22
+ logger.debug { "Discarding signal #{signal} (signals disabled)" }
23
+ return
24
+ end
25
+
26
+ if @req_lifecycle.in_request?
27
+ # add it to the request record
28
+ @framework.observe(:signals, signal, PAYLOAD_CREATOR_SECTIONS, true)
29
+ else
30
+ @queue.push signal
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,87 @@
1
+ require 'sqreen/log/loggable'
2
+ require 'sqreen/metrics_store'
3
+ require 'sqreen/ecosystem'
4
+ require 'sqreen/ecosystem/dispatch_table'
5
+ require 'sqreen/ecosystem_integration/around_callbacks'
6
+ require 'sqreen/ecosystem_integration/instrumentation_service'
7
+ require 'sqreen/ecosystem_integration/request_lifecycle_tracking'
8
+ require 'sqreen/ecosystem_integration/signal_consumption'
9
+
10
+ module Sqreen
11
+ # This class is the interface through which the agent interacts
12
+ # with the ecosystem.
13
+ #
14
+ # Other classes in the EcosystemIntegration module implement the
15
+ # functionality that the ecosystem requires in order to deliver
16
+ # data to the agent and to be informed by the agent of certain
17
+ # key events (see Sqreen::Ecosystem::DispatchTable).
18
+ class EcosystemIntegration
19
+ include Sqreen::Log::Loggable
20
+
21
+ # @param [Sqreen::Framework] framework
22
+ # @param [Proc] create_binning_metric
23
+ def initialize(framework, queue, create_binning_metric)
24
+ @framework = framework
25
+ @queue = queue
26
+ # XXX: created metrics are insulated from feature upgrades
27
+ @create_binning_metric = create_binning_metric
28
+ @request_lifecycle = RequestLifecycleTracking.new
29
+ @online = false
30
+ end
31
+
32
+ def init
33
+ raise 'already initialized' if @online
34
+
35
+ setup_dispatch_table
36
+ AroundCallbacks.create_metric = @create_binning_metric
37
+ Ecosystem.init
38
+ logger.info 'Ecosystem successfully initialized'
39
+ @online = true
40
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
41
+ logger.warn { "Error initializing Ecosystem: #{e.message}" }
42
+ logger.debug { e.backtrace.map { |x| " #{x}" }.join("\n") }
43
+ Sqreen::RemoteException.record(e)
44
+ end
45
+
46
+ def disable
47
+ raise NotImplementedYet
48
+ end
49
+
50
+ def request_start(rack_request)
51
+ return unless @online
52
+
53
+ Ecosystem.start_transaction
54
+ @request_lifecycle.notify_request_start(rack_request)
55
+ end
56
+
57
+ def request_end
58
+ return unless @online
59
+
60
+ Ecosystem.end_transaction
61
+ end
62
+
63
+ def handle_tracing_command(trace_id_prefix, scopes_config)
64
+ return unless @online
65
+
66
+ Ecosystem.configure_sampling(trace_id_prefix, scopes_config)
67
+ end
68
+
69
+ private
70
+
71
+ def setup_dispatch_table
72
+ Ecosystem::DispatchTable.consume_signal =
73
+ create_signal_consumption.method(:consume_signal)
74
+
75
+ Ecosystem::DispatchTable.add_request_start_listener =
76
+ @request_lifecycle.method(:add_start_observer)
77
+
78
+ Ecosystem::DispatchTable.fetch_logger = lambda { logger }
79
+
80
+ Ecosystem::DispatchTable.instrument = InstrumentationService.method(:instrument)
81
+ end
82
+
83
+ def create_signal_consumption
84
+ SignalConsumption.new(@framework, @request_lifecycle, @queue)
85
+ end
86
+ end
87
+ end
@@ -22,8 +22,17 @@ module Sqreen
22
22
  include RequestRecorder
23
23
  attr_accessor :sqreen_configuration
24
24
 
25
+ attr_writer :req_start_cb, :req_end_cb
26
+
25
27
  def initialize
26
28
  clean_request_record
29
+
30
+ # for notifying the ecosystem of request boundaries
31
+ # XXX: this should be refactored. It shouldn't be
32
+ # the framework doing these notifications to the ecosystem
33
+ # Probably the rule callback should do it itself
34
+ @req_start_cb = Proc.new {}
35
+ @req_end_cb = Proc.new {}
27
36
  end
28
37
 
29
38
  # What kind of database is this
@@ -260,8 +269,12 @@ module Sqreen
260
269
  # Nota: cleanup should be performed at end of request (see clean_request)
261
270
  def store_request(object)
262
271
  return unless ensure_rack_loaded
272
+
273
+ rack_req = Rack::Request.new(object)
274
+ @req_start_cb.call(rack_req)
275
+
263
276
  self.remaining_perf_budget = Sqreen.performance_budget
264
- SharedStorage.set(:request, Rack::Request.new(object))
277
+ SharedStorage.set(:request, rack_req)
265
278
  SharedStorage.set(:xss_params, nil)
266
279
  SharedStorage.set(:whitelisted, nil)
267
280
  SharedStorage.set(:request_overtime, nil)
@@ -290,6 +303,7 @@ module Sqreen
290
303
  SharedStorage.set(:xss_params, nil)
291
304
  SharedStorage.set(:whitelisted, nil)
292
305
  SharedStorage.set(:request_overtime, nil)
306
+ @req_end_cb.call
293
307
  end
294
308
 
295
309
  def remaining_perf_budget
@@ -27,6 +27,10 @@ module Sqreen
27
27
  def raise(value)
28
28
  Flow.raise(value)
29
29
  end
30
+
31
+ def noop
32
+ Flow.noop
33
+ end
30
34
  end
31
35
 
32
36
  class Flow
@@ -46,12 +50,17 @@ module Sqreen
46
50
  def raise(value)
47
51
  new(:raise, value)
48
52
  end
53
+
54
+ def noop
55
+ new(:noop, nil)
56
+ end
49
57
  end
50
58
 
51
59
  def initialize(action, value, brk = false)
52
60
  @action = action
53
61
  @value = value
54
62
  @break = brk
63
+ @passed_conditions = false
55
64
  end
56
65
 
57
66
  def return?
@@ -91,6 +100,16 @@ module Sqreen
91
100
  def break?
92
101
  @break ? true : false
93
102
  end
103
+
104
+ def passed_conditions!
105
+ @passed_conditions = true
106
+
107
+ self
108
+ end
109
+
110
+ def passed_conditions?
111
+ @passed_conditions
112
+ end
94
113
  end
95
114
 
96
115
  class TimerError < StandardError; end
@@ -101,6 +120,7 @@ module Sqreen
101
120
  end
102
121
 
103
122
  attr_reader :tag, :size
123
+ attr_accessor :conditions_passed
104
124
 
105
125
  def initialize(tag, &block)
106
126
  @tag = tag
@@ -195,8 +215,17 @@ module Sqreen
195
215
  @size.even?
196
216
  end
197
217
 
218
+ def include_measurements(another_timer)
219
+ @blips += another_timer.instance_variable_get(:@blips)
220
+ end
221
+
222
+ def start_and_end
223
+ raise 'Not exactly two measurements recorded' unless size == 2
224
+ @blips
225
+ end
226
+
198
227
  def to_s
199
- "#{@tag}: time=%.03fus" % (duration * 1_000_000)
228
+ "#{@tag}: time=%.03fus" % (@tally * 1_000_000)
200
229
  end
201
230
 
202
231
  protected