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,44 @@
1
+ require 'sqreen/ecosystem/loggable'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ class ModuleRegistry
6
+ include Sqreen::Ecosystem::Loggable
7
+
8
+ def initialize
9
+ @mods = []
10
+ end
11
+
12
+ def register(mod)
13
+ @mods << mod
14
+ end
15
+
16
+ def init_all
17
+ logger.info { "Initializing #{@mods.size} ecosystem modules" }
18
+ each_module do |mod|
19
+ next unless mod.respond_to? :setup
20
+ logger.debug { "Initializing module with type #{mod.class}" }
21
+ mod.setup
22
+ end
23
+ end
24
+
25
+ def destroy_all
26
+ # not implemented
27
+ end
28
+
29
+ # @param [Class] type
30
+ def each_module(type = nil, &block)
31
+ selected_mods = type ? (@mods.select { |mod| mod.is_a?(type) }) : @mods
32
+ if block_given?
33
+ selected_mods.each(&block)
34
+ else
35
+ selected_mods.each
36
+ end
37
+ end
38
+
39
+ def module_subset(type)
40
+ each_module(type).to_a
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/instrumentation'
3
+ require 'sqreen/ecosystem/module_api/message_producer'
4
+ require 'sqreen/ecosystem/module_api/tracing_id_generation'
5
+ require 'sqreen/ecosystem/module_api/tracing/client_data'
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Redis
10
+ class RedisConnection
11
+ class RedisConnectionData
12
+ include ModuleApi::Tracing::ClientData
13
+
14
+ attr_accessor :port
15
+ end
16
+
17
+ include ModuleApi::Instrumentation
18
+ include ModuleApi::MessageProducer
19
+ include ModuleApi::TracingIdGeneration
20
+
21
+ def setup
22
+ advice = wrap_for_interest(ModuleApi::Tracing::ClientData, &method(:before_advice))
23
+ instrument 'Redis::Connection::TCPSocket#connect',
24
+ before: advice
25
+ end
26
+
27
+ private
28
+
29
+ def before_advice
30
+ host = call.args[0]
31
+ port = call.args[1]
32
+
33
+ RedisConnectionData.new(
34
+ transport: 'redis',
35
+ host: host,
36
+ port: port,
37
+ tracing_identifier: create_tracing_id
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ require 'sqreen/ecosystem/tracing/signals/tracing_client'
2
+ require 'sqreen/ecosystem/module_api/tracing'
3
+ require 'sqreen/ecosystem/module_api/tracing/client_data'
4
+
5
+ module Sqreen
6
+ module Ecosystem
7
+ module Tracing
8
+ module Modules
9
+ class Client
10
+ include ModuleApi::Tracing
11
+
12
+ consumes ModuleApi::Tracing::ClientData
13
+ fixed_scope 'client'
14
+
15
+ # @param [Sqreen::Ecosystem::ModuleApi::Tracing::ClientData] data
16
+ def receive(data)
17
+ signal = Tracing::Signals::TracingClient.new
18
+ signal.payload = Tracing::Signals::TracingClient::Payload.new(
19
+ transport: data.transport,
20
+ host: data.host,
21
+ ip: data.ip,
22
+ tracing_identifier: data.tracing_identifier
23
+ )
24
+
25
+ submit_signal signal
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ require 'sqreen/ecosystem/tracing/signals/tracing_server'
2
+ require 'sqreen/ecosystem/module_api/tracing'
3
+ require 'sqreen/ecosystem/module_api/tracing/server_data'
4
+
5
+ module Sqreen
6
+ module Ecosystem
7
+ module Tracing
8
+ module Modules
9
+ class Server
10
+ include ModuleApi::Tracing
11
+
12
+ consumes ModuleApi::Tracing::ServerData
13
+ fixed_scope 'server'
14
+
15
+ # @param [Sqreen::Ecosystem::ModuleApi::Tracing::ServerData] data
16
+ def receive(data)
17
+ signal = Tracing::Signals::TracingServer.new
18
+ signal.payload = Tracing::Signals::TracingServer::Payload.new(
19
+ transport: data.transport,
20
+ client_ip: data.client_ip,
21
+ tracing_identifier: data.tracing_identifier
22
+ )
23
+
24
+ submit_signal signal
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,160 @@
1
+ require 'thread' # for Mutex
2
+ require 'singleton'
3
+ require 'sqreen/ecosystem/loggable'
4
+
5
+ # see https://github.com/sqreen/TechDoc/blob/master/content/specs/spec000024-sampling.md
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Tracing
10
+ class Sampler
11
+ # @param [Array<Hash{String=>Object}>] definition
12
+ def initialize(definition)
13
+ @lines = definition.map { |h| Line.new(h) }
14
+ end
15
+
16
+ def should_sample?
17
+ line = @lines.find(&:triggers?)
18
+ line ? line.saved_definition : false
19
+ end
20
+
21
+ class Line
22
+ include Loggable
23
+
24
+ attr_reader :saved_definition
25
+
26
+ # @param [Hash{String=>Object}] definition
27
+ def initialize(definition)
28
+ @saved_definition = definition
29
+ @primitives = []
30
+
31
+ unknown = definition.keys - PRIMITIVES_MAP.keys
32
+ unless unknown.empty?
33
+ logger.warn "Unknown primitives: #{unknown}"
34
+ @primitives << AlwaysFalsePrimitive.instance
35
+ return
36
+ end
37
+
38
+ PRIMITIVES_MAP.each do |key, prim_class|
39
+ next unless definition[key]
40
+ @primitives << prim_class.new(definition[key])
41
+ end
42
+ # if @primitives is empty the line will always
43
+ # return true: [].all?(&:triggers?) is true
44
+ end
45
+
46
+ def triggers?
47
+ @primitives.all?(&:triggers?)
48
+ end
49
+ end
50
+
51
+ class AlwaysFalsePrimitive
52
+ include Singleton
53
+
54
+ def triggers?
55
+ false
56
+ end
57
+ end
58
+
59
+ class CallsPrimitive
60
+ def initialize(calls_period)
61
+ @calls_period = calls_period
62
+ @count = 0
63
+ @mutex = Mutex.new
64
+ end
65
+
66
+ def triggers?
67
+ prev_count = nil
68
+ @mutex.synchronize do
69
+ prev_count = @count
70
+ @count += 1
71
+ end
72
+
73
+ (prev_count % @calls_period).zero?
74
+ end
75
+ end
76
+
77
+ class RandomPrimitive
78
+ def initialize(probability)
79
+ @probability = probability
80
+ end
81
+
82
+ def triggers?
83
+ @probability >= rand
84
+ end
85
+ end
86
+
87
+ class MaxDurationMinutesPrimitive
88
+ def initialize(time_in_minutes)
89
+ @deadline = Sqreen.time + time_in_minutes * 60
90
+ @passed = false # no locking needed
91
+ end
92
+
93
+ def triggers?
94
+ return false if @passed
95
+ if Sqreen.time > @deadline
96
+ @passed = true
97
+ return false
98
+ end
99
+ true
100
+ end
101
+ end
102
+
103
+ class TargetPerMinutePrimitive
104
+ def initialize(max_calls)
105
+ @max_calls = max_calls
106
+ @minute_last_call = cur_minute
107
+ @calls_accumulated = 0
108
+ @mutex = Mutex.new
109
+ end
110
+
111
+ def triggers?
112
+ this_minute = cur_minute
113
+ calls_cur_minute = @mutex.synchronize do
114
+ if @minute_last_call == this_minute
115
+ @calls_accumulated += 1
116
+ else
117
+ @minute_last_call = this_minute
118
+ @calls_accumulated = 1
119
+ end
120
+ end
121
+
122
+ calls_cur_minute <= @max_calls
123
+ end
124
+
125
+ private
126
+
127
+ def cur_minute
128
+ (Sqreen.time / 60).floor
129
+ end
130
+ end
131
+
132
+ class MaxCallsPrimitive
133
+ def initialize(max_calls)
134
+ @max_calls = max_calls
135
+ @disabled = false # to avoid lock
136
+ @mutex = Mutex.new
137
+ @num_calls = 0
138
+ end
139
+
140
+ def triggers?
141
+ return false if @disabled
142
+ num_calls = @mutex.synchronize do
143
+ @num_calls += 1
144
+ end
145
+
146
+ num_calls <= @max_calls
147
+ end
148
+ end
149
+
150
+ PRIMITIVES_MAP = {
151
+ "calls" => CallsPrimitive,
152
+ "random" => RandomPrimitive,
153
+ "max_duration_minutes" => MaxDurationMinutesPrimitive,
154
+ "target_per_minute" => TargetPerMinutePrimitive,
155
+ "max_calls" => MaxCallsPrimitive,
156
+ }.freeze
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,150 @@
1
+ require 'thread'
2
+ require 'sqreen/ecosystem/loggable'
3
+ require 'sqreen/ecosystem/tracing/sampler'
4
+
5
+ module Sqreen
6
+ module Ecosystem
7
+ module Tracing
8
+ # tracing sampling configuration, as specified by the 2nd argument of the
9
+ # tracing_enable command.
10
+ # See https://github.com/sqreen/TechDoc/blob/master/content/specs/spec000025-enabling-tracing.md
11
+ class SamplingConfiguration
12
+ include Loggable
13
+
14
+ DEFAULT_SCOPE = '*'.freeze
15
+
16
+ def initialize(sampling_config)
17
+ @sampling_config = sampling_config
18
+ @samplers = {}
19
+ @samplers_virtual = build_virtual_holders(sampling_config)
20
+ end
21
+
22
+ # @param [String] scope either the scope or the part behind @ in a virtual scope
23
+ # @param [String] qualifier the part after @ in a virtual scope or nil
24
+ def should_sample?(scope, qualifier = nil)
25
+ if qualifier
26
+ fetch_sampler_virtual(scope, qualifier).should_sample?
27
+ else
28
+ fetch_sampler(scope).should_sample?
29
+ end
30
+ end
31
+
32
+ def forget_virtual_scope(scope, qualifier)
33
+ holder = @samplers_virtual[scope]
34
+ return unless holder.delete!(qualifier)
35
+ end
36
+
37
+ private
38
+
39
+ def fetch_sampler_virtual(scope, qualifier)
40
+ holder = @samplers_virtual[scope]
41
+
42
+ # no virtual scope configured, fallback to plain scope
43
+ return fetch_sampler(scope) unless holder
44
+
45
+ holder[qualifier]
46
+ end
47
+
48
+ def fetch_sampler(scope)
49
+ sampler = @samplers[scope]
50
+ return sampler if sampler
51
+
52
+ cfg = @sampling_config[scope] || @sampling_config[DEFAULT_SCOPE]
53
+ if cfg
54
+ @samplers[scope] = SamplingConfiguration.build_sampler(scope, cfg)
55
+ else
56
+ logger.info { "Disabling scope #{scope} due to its not being configured" }
57
+ @samplers[scope] = DisabledScopeSampler
58
+ end
59
+ end
60
+
61
+ def build_virtual_holders(sampling_config)
62
+ Hash[
63
+ sampling_config
64
+ .select { |scope, _cfg| scope.end_with?('@*') }
65
+ .map do |scope, cfg|
66
+ parent = scope[0...-2] # remove trailing @*
67
+ [parent, VirtualScopesHolder.new(parent, cfg)]
68
+ end
69
+ ]
70
+ end
71
+
72
+ class << self
73
+ include Loggable
74
+
75
+ def build_sampler(scope, cfg)
76
+ do_build_sampler(cfg['enabled'], cfg['sampling'] || [{}])
77
+ rescue StandardError => e
78
+ logger.warn "Invalid sampling configuration for #{scope}: #{e.inspect}"
79
+ logger.debug { e.backtrace }
80
+ DisabledScopeSampler
81
+ end
82
+
83
+ private
84
+
85
+ def do_build_sampler(enabled, sampling)
86
+ if enabled
87
+ Sampler.new(sampling)
88
+ else
89
+ logger.debug { "Disabling scope #{scope}" }
90
+ DisabledScopeSampler
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ class VirtualScopesHolder
97
+ include Loggable
98
+
99
+ MAX_VIRTUAL_SCOPES = 120
100
+ DISCARD_SIZE = 20
101
+
102
+ def initialize(parent, cfg)
103
+ @parent = parent
104
+ @cfg = cfg
105
+ @virtual_scopes = {}
106
+ @mutex = Mutex.new
107
+ end
108
+
109
+ def [](qualifier)
110
+ return false unless @cfg['enabled']
111
+
112
+ @mutex.synchronize do
113
+ sampler = @virtual_scopes[qualifier]
114
+ return sampler if sampler
115
+
116
+ @virtual_scopes[qualifier] = create_virtual(qualifier)
117
+ end
118
+ end
119
+
120
+ def delete!(qualifier)
121
+ @virtual_scopes.delete(qualifier)
122
+ end
123
+
124
+ private
125
+
126
+ def create_virtual(qualifier)
127
+ discard if @virtual_scopes.size >= MAX_VIRTUAL_SCOPES
128
+
129
+ @virtual_scopes[qualifier] =
130
+ SamplingConfiguration.build_sampler("#{@parent}@#{qualifier}", @cfg)
131
+ end
132
+
133
+ def discard
134
+ logger.info { "Discarding excess virtual scopes for scope '#{@parent}'" }
135
+ discard_keys = @virtual_scopes.keys.sample(DISCARD_SIZE)
136
+ @virtual_scopes.delete_if { |k, _v| discard_keys.include? k }
137
+ end
138
+ end
139
+
140
+ # fake sampler that always returns false
141
+ class DisabledScopeSampler
142
+ class << self
143
+ def should_sample?
144
+ false
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,53 @@
1
+ require 'sqreen/kit/configuration'
2
+ require 'sqreen/kit/signals/point'
3
+ require 'sqreen/kit/signals/dto_helper'
4
+
5
+ # reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/tracing/client-2020-04-21/schema.cue
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Tracing
10
+ module Signals
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class Sqreen::Ecosystem::Tracing::Signals::TracingClient < Sqreen::Kit::Signals::Point
17
+ readonly_attrs :payload_schema, :source, :signal_name
18
+
19
+ def initialize(values = {})
20
+ self.payload_schema = Payload::SCHEMA_VERSION
21
+ self.source = Sqreen::Kit::Configuration.default_source
22
+ self.signal_name = 'tracing.client'
23
+ self.time = values[:time] || Time.now
24
+ super
25
+ end
26
+
27
+ def payload=(payload)
28
+ unless payload.is_a?(Payload)
29
+ raise ArgumentError, "Payload should be a #{Payload}"
30
+ end
31
+ super
32
+ end
33
+
34
+ class Payload
35
+ include Sqreen::Kit::Signals::DtoHelper
36
+
37
+ add_mandatory_attrs :transport, :host
38
+
39
+ SCHEMA_VERSION = 'tracing/client-2020-04-21'.freeze
40
+
41
+ # @return [Symbol]
42
+ attr_accessor :transport
43
+
44
+ # @return [String]
45
+ attr_accessor :host
46
+
47
+ # @return [String]
48
+ attr_accessor :ip
49
+
50
+ # @return [String]
51
+ attr_accessor :tracing_identifier
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ require 'sqreen/kit/configuration'
2
+ require 'sqreen/kit/signals/point'
3
+ require 'sqreen/kit/signals/dto_helper'
4
+
5
+ # reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/tracing/server-2020-04-21/schema.cue
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Tracing
10
+ module Signals
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class Sqreen::Ecosystem::Tracing::Signals::TracingServer < Sqreen::Kit::Signals::Point
17
+ readonly_attrs :payload_schema, :source, :signal_name
18
+
19
+ def initialize(values = {})
20
+ self.payload_schema = Payload::SCHEMA_VERSION
21
+ self.source = Sqreen::Kit::Configuration.default_source
22
+ self.signal_name = 'tracing.server'
23
+ self.time = values[:time] || Time.now
24
+ super
25
+ end
26
+
27
+ def payload=(payload)
28
+ unless payload.is_a?(Payload)
29
+ raise ArgumentError, "Payload should be a #{Payload}"
30
+ end
31
+ super
32
+ end
33
+
34
+ class Payload
35
+ include Sqreen::Kit::Signals::DtoHelper
36
+
37
+ add_mandatory_attrs :transport, :client_ip
38
+
39
+ SCHEMA_VERSION = 'tracing/server-2020-04-21'.freeze
40
+
41
+ # @return [Symbol]
42
+ attr_accessor :transport
43
+
44
+ # @return [String]
45
+ attr_accessor :client_ip
46
+
47
+ # @return [Array<String>]
48
+ attr_accessor :previous_hops
49
+
50
+ # @return [String]
51
+ attr_accessor :tracing_identifier
52
+ end
53
+ end
@@ -0,0 +1,101 @@
1
+ require 'sqreen/ecosystem/loggable'
2
+ require 'sqreen/ecosystem/exception_reporting'
3
+
4
+ module Sqreen
5
+ module Ecosystem
6
+ class TracingBroker
7
+ include Loggable
8
+ include ExceptionReporting
9
+
10
+ # Stores a lookup resolution so that lookup (incl. sampling) is done
11
+ # only once, not in the beginning of the producer code (when it asks
12
+ # whether it should proceed) and again once it delivers the data
13
+ ObserverLookup = Struct.new(:modules)
14
+
15
+ # @return [Sqreen::Ecosystem::Tracing::SamplingConfiguration]
16
+ attr_writer :sampling_configuration
17
+
18
+ # @param [Array<Sqreen::Ecosystem::ModuleApi::Tracing>] tracing_modules
19
+ def initialize(tracing_modules)
20
+ @sampling_configuration = nil
21
+ @type_to_subscribers = {}
22
+ tracing_modules.each do |mod|
23
+ consumed_type = mod.consumed_type
24
+ @type_to_subscribers[consumed_type] ||= []
25
+ @type_to_subscribers[consumed_type] << mod
26
+ end
27
+ end
28
+
29
+ # @param [Object] data
30
+ # @param [Sqreen::Ecosystem::TracingBroker::ObserverLookup] prev_lookup
31
+ def publish(data, prev_lookup)
32
+ prev_lookup.modules.each do |mod|
33
+ mod_process_data mod, data
34
+ end
35
+ end
36
+
37
+ # @param [Module] data_type
38
+ # @param [Hash] _hints reserved for future use, e.g. virtual scopes
39
+ # @return [Sqreen::Ecosystem::TracingBroker::ObserverLookup]
40
+ def interested_consumers(data_type, _hints = {})
41
+ unless @sampling_configuration
42
+ logger.debug do
43
+ "Declaring no one is interested in #{data_type} " \
44
+ "because tracing hasn't been enabled yet"
45
+ end
46
+ return false
47
+ end
48
+
49
+ # if we have several modules with the same scope, we
50
+ # should ask whether we should sample only once
51
+ scope_to_should_sample = Hash.new do |hash, scope|
52
+ result = @sampling_configuration.should_sample?(scope)
53
+ if result
54
+ logger.debug { "Will sample scope #{scope}. Sampling line: #{result}" }
55
+ else
56
+ logger.debug { "Will NOT sample scope #{scope}" }
57
+ end
58
+
59
+ hash[scope] = result
60
+ end
61
+
62
+ res = subscribers(data_type).select do |mod|
63
+ scope_to_should_sample[mod.scope]
64
+ end
65
+
66
+ res.empty? ? false : ObserverLookup.new(res)
67
+ end
68
+
69
+ private
70
+
71
+ # @param [Sqreen::Ecosystem::ModuleApi::Tracing] mod
72
+ def mod_process_data(mod, data)
73
+ mod.receive(data)
74
+ rescue ::Exception => e # rubocop:disable Lint/RescueException
75
+ report_exception("Error invoking tracing module #{mod}", e)
76
+ end
77
+
78
+ # @param [Module] data_type
79
+ # @return Array<Sqreen::Ecosystem::ModuleApi::Tracing>
80
+ def subscribers(data_type)
81
+ subscribers = @type_to_subscribers[data_type]
82
+
83
+ # None of the modules subscribes to data_type directly,
84
+ # but maybe they subscribe to one of the ancestors
85
+ # Cache this lookup
86
+ unless subscribers
87
+ subscribers = parents(data_type).inject([]) do |accum, type|
88
+ accum + (@type_to_subscribers[type] || [])
89
+ end
90
+ @type_to_subscribers[data_type] = subscribers
91
+ end
92
+
93
+ subscribers
94
+ end
95
+
96
+ def parents(type)
97
+ type.ancestors - Object.ancestors - [type]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,34 @@
1
+ require 'sqreen/ecosystem/module_registry'
2
+ require 'sqreen/ecosystem/transaction_storage'
3
+ require 'sqreen/ecosystem/module_api/signal_producer'
4
+
5
+ module Sqreen
6
+ module Ecosystem
7
+ class TracingIdSetup
8
+ # @param [Array<Sqreen::Ecosystem::ModuleApi::SignalProducer>] signal_producer_modules
9
+ def initialize(signal_producer_modules)
10
+ @modules = signal_producer_modules
11
+ @tracing_id_prefix = nil
12
+ end
13
+
14
+ def setup_modules
15
+ inject_out_of_tx_tracing_id_gen
16
+ end
17
+
18
+ attr_writer :tracing_id_prefix
19
+
20
+ private
21
+
22
+ def inject_out_of_tx_tracing_id_gen
23
+ @modules.each do |mod|
24
+ mod.tracing_id_producer = method(:generate_tracing_id)
25
+ end
26
+ end
27
+
28
+ def generate_tracing_id
29
+ return nil unless @tracing_id_prefix
30
+ "#{@tracing_id_prefix}.#{SecureRandom.uuid}"
31
+ end
32
+ end
33
+ end
34
+ end