sqreen 1.20.1 → 1.21.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sqreen/attack_detected.html +1 -2
  3. data/lib/sqreen/deliveries/batch.rb +8 -1
  4. data/lib/sqreen/ecosystem.rb +80 -0
  5. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  6. data/lib/sqreen/ecosystem/http/net_http.rb +51 -0
  7. data/lib/sqreen/ecosystem/http/rack_request.rb +38 -0
  8. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  9. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  10. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  11. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  12. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +26 -0
  13. data/lib/sqreen/ecosystem/module_api/tracing_push_down.rb +34 -0
  14. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  15. data/lib/sqreen/ecosystem/module_registry.rb +39 -0
  16. data/lib/sqreen/ecosystem/redis/redis_connection.rb +35 -0
  17. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  18. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  19. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  20. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  21. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  22. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  23. data/lib/sqreen/ecosystem_integration.rb +70 -0
  24. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +89 -0
  25. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +38 -0
  26. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +56 -0
  27. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  28. data/lib/sqreen/frameworks/generic.rb +15 -1
  29. data/lib/sqreen/graft/call.rb +9 -0
  30. data/lib/sqreen/legacy/old_event_submission_strategy.rb +7 -1
  31. data/lib/sqreen/remote_command.rb +3 -0
  32. data/lib/sqreen/runner.rb +19 -5
  33. data/lib/sqreen/session.rb +2 -0
  34. data/lib/sqreen/signals/conversions.rb +6 -1
  35. data/lib/sqreen/version.rb +1 -1
  36. metadata +32 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7be645cedf514d1af5754fc7244738e17f817aad09761d2da7d7fa032ac55801
4
- data.tar.gz: f81295c9ac98714284d6368bc5e01c1a857317c8dad98d53366b24e53cc6cc47
3
+ metadata.gz: 49a6c95ab34d19ae0f769e475ae67c2c3e4d19b17582ee09d8a6d231ac3d9150
4
+ data.tar.gz: ad1ea7a80f3582ba90ffc6aa1cef7040459c56f65ea4712a023923b8bc7801e3
5
5
  SHA512:
6
- metadata.gz: 986748fc2c11ee0a6ea7a997d661246d2840e170fdcbe5d7c1221fb189e3b6a9281e4feb1025f58f1ac6712ff10f598caa7d81867a7a62898138e743eadc9262
7
- data.tar.gz: 39b246ce351e780f539a0d2fe3df9791996d8203056545ab5472f0deec2e1526351bfc9cbb4f8c4cd5c145969db0b177b1b559a7491e4a8a15de4e9dd3721665
6
+ metadata.gz: 0da6438f20a00a84914db2bb06c24feab53e164feae1cb7d2af0b53afe24c5d5ea54a0cb68a8872a49aadedae4450de57e63da6a4f4a2ec21ac85eaa84c13acf
7
+ data.tar.gz: 3e75918e810529674e10d693fd2a62c0eeeeecb18bac06a25fdd686d29c45d55333abb0feea790ec1d89e29ce71097bd1de16c2d31d98a6219e985d806793008
@@ -1,2 +1 @@
1
- <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Sqreen has detected an attack.</title> <style>html, body, div, span, h1, a{margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline}body{background: -webkit-radial-gradient(26% 19%, circle, #fff, #f4f7f9); background: radial-gradient(circle at 26% 19%, #fff, #f4f7f9); display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -ms-flex-align: center; align-items: center; -ms-flex-line-pack: center; align-content: center; width: 100%; min-height: 100vh; line-height: 1}svg, h1, p{display: block}svg{margin: 0 auto 4vh}h1{font-family: sans-serif; font-weight: 300; font-size: 34px; color: #384886; line-height: normal}p{font-size: 18px; line-height: normal; color: #b8bccc; font-family: sans-serif; font-weight: 300}a{color: #b8bccc}.flex{text-align: center}</style></head><body> <div class="flex"> <svg xmlns="http://www.w3.org/2000/svg" width="230" height="250" viewBox="0 0 230 250" enable-background="new 0 0 230 250"> <style>.st0{opacity: 0.4; filter: url(#a);}.st1{fill: #FFFFFF;}.st2{fill: #B0ACFF;}.st3{fill: #4842B7;}.st4{fill: #1E0936;}</style> <filter id="a" width="151.7%" height="146%" x="-25.8%" y="-16%" filterUnits="objectBoundingBox"> <feOffset dy="14" in="SourceAlpha" result="shadowOffsetOuter1"/> <feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="13"/> <feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/> </filter> <g class="st0"> <path id="b_2_" d="M202.6 34.9c-.2-1.2-.8-2.1-1.9-2.8-3.8-2-37.9-20.1-85.7-20.1-48.8 0-84.2 19.3-85.7 20.1-1 .6-1.6 1.6-1.8 2.7-14.8 123.2 84.7 176.3 85.7 176.8.6.3 1.2.4 1.8.4.6 0 1.2-.1 1.7-.4 1-.5 100.4-55 85.9-176.7z"/> </g> <path id="b_1_" d="M202.6 34.9c-.2-1.2-.8-2.1-1.9-2.8-3.8-2-37.9-20.1-85.7-20.1-48.8 0-84.2 19.3-85.7 20.1-1 .6-1.6 1.6-1.8 2.7-14.8 123.2 84.7 176.3 85.7 176.8.6.3 1.2.4 1.8.4.6 0 1.2-.1 1.7-.4 1-.5 100.4-55 85.9-176.7z" class="st1"/> <g id="nest-cmyk-indigo"> <ellipse id="sqreen" cx="115.5" cy="69.9" class="st2" rx="12.7" ry="12.7"/> <path id="app" d="M113.6 91.9V71.5L95.5 61.1v18l6.4-3.7c.5 1.1 1 2.2 1.7 3.2L97 82.3l16.6 9.6zm3.7 0l16.6-9.6-6.7-3.9c.7-1 1.3-2 1.7-3.2l6.4 3.7v-18l-18.1 10.5v20.5zM96.9 57.6l18.6 10.7L134 57.6 117.3 48v7.6c-.6-.1-1.2-.1-1.8-.1-.6 0-1.2 0-1.8.1V48l-16.8 9.6zm20.2-13.9l20.3 11.7c1 .6 1.6 1.7 1.6 2.8v23.5c0 1.2-.6 2.2-1.6 2.8l-20.3 11.7c-1 .6-2.3.6-3.3 0L93.5 84.5c-1-.6-1.6-1.7-1.6-2.8V58.2c0-1.2.6-2.2 1.6-2.8l20.3-11.7c1-.6 2.3-.6 3.3 0z" class="st3"/> </g> <path id="s" d="M74.6 113c-1.8-1-3.5-1.5-5.2-1.5-1.4 0-2.3.6-2.3 1.5 0 2.7 10.1.4 10.1 7.7 0 3.3-2.9 6-7.6 6-2.1 0-4.7-.5-6.4-1.4l-.1-.1c-.3-.2-.3-.5-.2-.8l1.2-2.7c.1-.3.5-.5.9-.3.1 0 .1.1.2.1 1.5.6 3.1 1 4.6 1 2.2 0 2.9-.6 2.9-1.7 0-3-10.1-.8-10.1-7.7 0-3.1 2.7-5.8 7-5.8 2.1 0 5 .7 6.9 1.8.1 0 .1.1.2.1.3.2.4.5.3.8l-1.2 2.7c-.1.3-.5.5-.9.3h-.3z" class="st4"/> <path id="q" d="M93.6 107.8h3.2c.4 0 .7.3.7.7v25.9c0 .4-.3.7-.7.7h-3.2c-.4 0-.7-.3-.7-.7v-9.1c-1.2.8-2.9 1.4-4.7 1.4-5.4 0-9.6-4.3-9.6-9.7 0-5.4 4.1-9.7 9.6-9.7 1.8 0 3.5.6 4.7 1.4v-.1c0-.5.3-.8.7-.8zm-.7 12.4v-6.5c-1.3-1.3-2.8-2.1-4.5-2.1-2.9 0-5.1 2.3-5.1 5.4s2.2 5.4 5.1 5.4c1.7-.1 3.2-.7 4.5-2.2z" class="st4"/> <path id="r" d="M112.5 107.8c-1-.4-2-.6-3-.6-1.8 0-3.5.6-4.9 1.4v-.2c0-.3-.2-.5-.5-.5h-3.4c-.3 0-.5.2-.5.5v17.8c0 .3.2.5.5.5h3.4c.3 0 .5-.2.5-.5v-12.6c1.1-1.2 2.8-1.9 4.6-1.9.4 0 .9 0 1.5.2.3.1.6-.1.7-.4l1.3-2.9c.1-.4 0-.7-.2-.8z" class="st4"/> <path id="e" d="M129 124.7c-1.7 1-4.2 2-6.7 2-6 0-10.3-4.4-10.3-9.9 0-5.3 3.7-9.6 9.4-9.6 5.2 0 8.4 4.4 8.4 9 0 .4 0 .9-.1 1.2 0 .3-.3.6-.7.6h-12.5c.5 2.8 2.8 4.5 5.8 4.5 1.7 0 3.4-.5 5.1-1.4.3-.2.6-.1.8.2l1.2 2.6c.1.2 0 .4-.2.6-.2.1-.2.2-.2.2zm-12.4-10h8.5c-.2-1.8-1.9-3.3-3.9-3.3-2.5-.1-4 1.4-4.6 3.3z" class="st4"/> <path id="e_1_" d="M148.7 124.7c-1.7 1-4.2 2-6.7 2-6 0-10.3-4.4-10.3-9.9 0-5.3 3.7-9.6 9.4-9.6 5.2 0 8.4 4.4 8.4 9 0 .4 0 .9-.1 1.2 0 .3-.3.6-.7.6h-12.5c.5 2.8 2.8 4.5 5.8 4.5 1.7 0 3.4-.5 5.1-1.4.3-.2.6-.1.8.2l1.2 2.6c.1.2 0 .4-.2.6-.2.1-.2.2-.2.2zm-12.4-10h8.5c-.2-1.8-1.9-3.3-3.9-3.3-2.5-.1-4 1.4-4.6 3.3z" class="st4"/> <path id="n" d="M151.5 108.5V126c0 .4.3.7.7.7h3.2c.4 0 .7-.3.7-.7v-12.5c1.1-1.2 2.8-1.9 4.6-1.9 2.9 0 4.5 1.6 4.5 4.7v9.7c0 .4.3.7.7.7h3.2c.4 0 .7-.3.7-.7v-10.2c0-5.2-2.9-8.5-8.8-8.5-1.8 0-3.5.6-4.9 1.4v-.1c0-.4-.3-.7-.7-.7h-3.2c-.4-.1-.7.2-.7.6z" class="st4"/> </svg> <h1>Uh Oh! Sqreen has detected an attack.</h1> <p>If you are the application owner, check the Sqreen <a href="https://my.sqreen.com/">dashboard</a> for more information.</p></div></body></html>
2
-
1
+ <!-- Sorry, you’ve been blocked --><!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>You've been blocked</title><style>a,body,div,h1,html,span{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{background:-webkit-radial-gradient(26% 19%,circle,#fff,#f4f7f9);background:radial-gradient(circle at 26% 19%,#fff,#f4f7f9);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;width:100%;min-height:100vh;line-height:1;flex-direction:column}h1,p,svg{display:block}svg{margin:0 auto 4vh}main{text-align:center;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;flex-direction:column}h1{font-family:sans-serif;font-weight:600;font-size:34px;color:#1e0936;line-height:1.2}p{font-size:18px;line-height:normal;color:#646464;font-family:sans-serif;font-weight:400}a{color:#4842b7}footer{width:100%;text-align:center}footer p{font-size:16px}</style></head><body><main><svg width="170px" height="193px" viewBox="0 0 170 193" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true"><g id="exports" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Artboard" transform="translate(-186.000000, -189.000000)"><g id="logo-cmyk-indigo" transform="translate(186.000000, 189.000000)"><g id="nest-cmyk-indigo"><ellipse id="sqreen" fill="#B0ACFF" cx="85" cy="96.5" rx="45.7692308" ry="45.7966102"></ellipse><path d="M78.4615385,175.749389 L78.4615385,102.2092 L13.1398162,64.4731256 L13.1398162,129.181112 L36.352167,115.771438 C37.9764468,119.873152 40.1038639,123.720553 42.6582364,127.237412 L18.5723996,141.151695 L78.4615385,175.749389 Z M91.5384615,175.749389 L151.4276,141.151695 L127.341764,127.237412 C129.896136,123.720553 132.023553,119.873152 133.647833,115.771438 L156.860184,129.181112 L156.860184,64.4731256 L91.5384615,102.2092 L91.5384615,175.749389 Z M18.0061522,52.1754237 L85,90.8774777 L151.993848,52.1754237 L91.5384615,17.2506105 L91.5384615,44.565949 C89.3964992,44.2986903 87.2143177,44.1610169 85,44.1610169 C82.7856823,44.1610169 80.6035008,44.2986903 78.4615385,44.565949 L78.4615385,17.2506105 L18.0061522,52.1754237 Z M90.8846156,1.76392358 L164.052491,44.0326866 C167.693904,46.1363149 169.937107,50.0239804 169.937107,54.231237 L169.937107,138.768763 C169.937107,142.97602 167.693904,146.863685 164.052491,148.967313 L90.8846156,191.236076 C87.2432028,193.339705 82.7567972,193.339705 79.1153844,191.236076 L5.94750871,148.967313 C2.30609589,146.863685 0.0628930904,142.97602 0.0628930904,138.768763 L0.0628930904,54.231237 C0.0628930904,50.0239804 2.30609589,46.1363149 5.94750871,44.0326866 L79.1153844,1.76392358 C82.7567972,-0.339704735 87.2432028,-0.339704735 90.8846156,1.76392358 Z" id="app" fill="#4842B7"></path></g></g></g></g></svg><h1>Sorry, you've been blocked</h1><p>Contact the website owner</p></main><footer><p>Security provided by <a href="https://www.sqreen.com/?utm_medium=block_page" target="_blank">Sqreen</a></p></footer></body></html>
@@ -13,6 +13,8 @@ require 'sqreen/events/attack'
13
13
  require 'sqreen/events/remote_exception'
14
14
  require 'sqreen/mono_time'
15
15
  require 'sqreen/deliveries/simple'
16
+ require 'sqreen/kit/signals/signal'
17
+ require 'sqreen/kit/signals/trace'
16
18
 
17
19
  module Sqreen
18
20
  module Deliveries
@@ -58,7 +60,7 @@ module Sqreen
58
60
  def post_batch_needed?(event)
59
61
  now = Sqreen.time
60
62
  # do not use any? {} due to side effects inside block
61
- event_keys(event).map do |key|
63
+ event_keys(event).uniq.map do |key|
62
64
  was = @first_seen[key]
63
65
  @first_seen[key] ||= now
64
66
  was.nil? || current_batch.size > max_batch || now > (was + max_staleness)
@@ -86,6 +88,7 @@ module Sqreen
86
88
  res += event.observed.fetch(:sdk, []).select { |e|
87
89
  e[0] == :track
88
90
  }.map { |e| "sdk-track".freeze }
91
+ res += event.observed.fetch(:signals, []).map { "signal".freeze }
89
92
  return res
90
93
  end
91
94
 
@@ -97,6 +100,10 @@ module Sqreen
97
100
  "rex-#{event.klass}"
98
101
  when Sqreen::AggregatedMetric
99
102
  "agg-metric"
103
+ when Sqreen::Kit::Signals::Signal
104
+ "signal"
105
+ when Sqreen::Kit::Signals::Trace
106
+ "signal"
100
107
  end
101
108
  end
102
109
  end
@@ -0,0 +1,80 @@
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_id_setup'
6
+ require 'sqreen/ecosystem/module_api/tracing_push_down'
7
+
8
+ module Sqreen
9
+ # The API for the ecosystem client (together with the dispatch table)
10
+ module Ecosystem
11
+ class << self
12
+ def init(opts = {})
13
+ @registry = ModuleRegistry.new
14
+ register_modules(opts[:modules])
15
+ @registry.init_all
16
+
17
+ @tracing_id_setup = TracingIdSetup.new(@registry)
18
+ @tracing_id_setup.setup_modules
19
+ end
20
+
21
+ def reset
22
+ instance_variables.each do |ia|
23
+ instance_variable_set(ia, nil)
24
+ end
25
+ end
26
+
27
+ # To be called by the Ecosystem client when a new transaction
28
+ # (generally: request) is started
29
+ # In the future, it's intended that request end/start detection be handled
30
+ # by the Ecosystem itself, so control will flow in the other direction,
31
+ # from the ecosystem to its client
32
+ def start_transaction
33
+ TransactionStorage.create_thread_local
34
+ end
35
+
36
+ def end_transaction
37
+ TransactionStorage.destroy_thread_local
38
+ end
39
+
40
+ # @param [String] tracing_id_prefix
41
+ # @param [Array<Hash{String=>Object}>] sampling_config
42
+ def configure_sampling(tracing_id_prefix, sampling_config)
43
+ @tracing_id_setup.tracing_id_prefix = tracing_id_prefix
44
+ built_samp_cfg = Tracing::SamplingConfiguration.new(sampling_config)
45
+ inject_sampling_config(built_samp_cfg)
46
+ end
47
+
48
+ private
49
+
50
+ def register_modules(modules)
51
+ return register_all_modules unless modules
52
+
53
+ modules.each { |mod| register mod }
54
+ end
55
+
56
+ def register_all_modules
57
+ # replace with something more magical?
58
+ require_relative 'ecosystem/http/rack_request'
59
+ register Http::RackRequest.new
60
+
61
+ require_relative 'ecosystem/http/net_http'
62
+ register Http::NetHttp.new
63
+
64
+ require_relative 'ecosystem/redis/redis_connection'
65
+ register Redis::RedisConnection.new
66
+ end
67
+
68
+ def register(mod)
69
+ @registry.register mod
70
+ end
71
+
72
+ # @param [Sqreen::Ecosystem::SamplingConfiguration] config
73
+ def inject_sampling_config(config)
74
+ @registry.each_module(Sqreen::Ecosystem::ModuleApi::TracingPushDown) do |mod|
75
+ mod.sampling_config = config
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ require 'logger'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ # Configured by the ecosystem client
6
+ module DispatchTable
7
+ class << self
8
+ # data consumption
9
+ # argument: +Sqreen::Kit::Signals::Signal+
10
+ # see +Sqreen::EcosystemIntegration::SignalConsumption#consume_signal+
11
+ attr_accessor :consume_signal
12
+
13
+ # argument: block taking a Rack::Request
14
+ # see +Sqreen::EcosystemIntegration::RequestLifecycleTracking#add_start_observer+
15
+ attr_accessor :add_request_start_listener
16
+
17
+ attr_accessor :fetch_logger
18
+
19
+ # argument: callback taking:
20
+ # * the method to instrument
21
+ # * A Hash{Symbol=>Proc} with the advice. The proc takes the
22
+ # arguments and the ball, so these details of the instrumentation
23
+ # implementation leak through the abstraction
24
+ # see +Sqreen::EcosystemIntegration::InstrumentationService+
25
+ attr_accessor :instrument
26
+
27
+ def reset
28
+ instance_variables.each do |ia|
29
+ instance_variable_set(ia, nil)
30
+ end
31
+
32
+ # set default logger
33
+ logger = ::Logger.new(STDERR)
34
+ logger.level = ::Logger::WARN
35
+ logger.progname = 'sqreen-ecosystem'
36
+ self.fetch_logger = proc { logger }
37
+ end
38
+ end
39
+
40
+ reset
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/instrumentation'
3
+ require 'sqreen/ecosystem/module_api/tracing_push_down'
4
+ require 'sqreen/ecosystem/module_api/signal_producer'
5
+ require 'sqreen/ecosystem/module_api/transaction_storage'
6
+ require 'sqreen/ecosystem/tracing/signals/tracing_client'
7
+
8
+ module Sqreen
9
+ module Ecosystem
10
+ module Http
11
+ class NetHttp
12
+ include ModuleApi::Instrumentation
13
+ include ModuleApi::SignalProducer
14
+ include ModuleApi::TracingPushDown
15
+
16
+ def setup
17
+ instrument 'Net::HTTP#request', before: method(:before_advice)
18
+ end
19
+
20
+ private
21
+
22
+ # instr. def request(req, body = nil, &block) # :yield: +response+
23
+ # req is of type +Net::HTTPGenericRequest+
24
+ def before_advice(call, _ball)
25
+ return unless should_sample?('client')
26
+
27
+ tracing_id = create_tracing_id
28
+
29
+ # build & submit signal
30
+ host = call.instance.address
31
+ port = call.instance.port
32
+
33
+ host += ':' + port.to_s if port != 80 && port != 443
34
+
35
+ signal = Tracing::Signals::TracingClient.new
36
+ signal.payload = Tracing::Signals::TracingClient::Payload.new(
37
+ transport: 'http',
38
+ host: host,
39
+ tracing_identifier: tracing_id
40
+ )
41
+
42
+ submit_signal signal
43
+
44
+ # add tracing header, if available
45
+ req = call.args[0]
46
+ req[ModuleApi::TRACE_ID_HEADER] = tracing_id
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+ require 'sqreen/ecosystem/module_api/event_listener'
3
+ require 'sqreen/ecosystem/module_api/signal_producer'
4
+ require 'sqreen/ecosystem/module_api/tracing_push_down'
5
+ require 'sqreen/ecosystem/tracing/signals/tracing_server'
6
+
7
+ module Sqreen
8
+ module Ecosystem
9
+ module Http
10
+ class RackRequest
11
+ include ModuleApi::EventListener
12
+ include ModuleApi::TracingPushDown
13
+ include ModuleApi::SignalProducer
14
+
15
+ def setup
16
+ on_request_start(&method(:handle_request))
17
+ end
18
+
19
+ private
20
+
21
+ def handle_request(rack_request)
22
+ return unless should_sample?('server')
23
+
24
+ trace_id = rack_request.env[ModuleApi::TRACE_ID_ENV_KEY]
25
+
26
+ signal = Tracing::Signals::TracingServer.new
27
+ signal.payload = Tracing::Signals::TracingServer::Payload.new(
28
+ transport: 'http',
29
+ client_ip: rack_request.ip,
30
+ tracing_identifier: trace_id
31
+ )
32
+
33
+ submit_signal signal
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ require 'sqreen/ecosystem/dispatch_table'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ module Loggable
6
+ private
7
+
8
+ def logger
9
+ DispatchTable.fetch_logger.call
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ require 'sqreen/ecosystem/loggable'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ # The API that the transport/tracing modules are written against
6
+ module ModuleApi
7
+ TRACE_ID_HEADER = 'X-Sqreen-Trace-Identifier'.freeze
8
+ TRACE_ID_ENV_KEY = 'HTTP_X_SQREEN_TRACE_IDENTIFIER'.freeze
9
+
10
+ Loggable = Sqreen::Ecosystem::Loggable
11
+
12
+ module ClassMethods
13
+ attr_writer :module_name
14
+
15
+ def module_name
16
+ if instance_variable_defined?(:@module_name)
17
+ @module_name
18
+ else
19
+ # to snake case
20
+ @module_name = to_s.sub(/.*::/, '').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.included(mod)
26
+ mod.extend(ClassMethods)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require 'sqreen/ecosystem/dispatch_table'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ module ModuleApi
6
+ module EventListener
7
+ private
8
+
9
+ # XXX: callbacks need to be wrapped in order ot handle
10
+ # perfcap, exceptions, and maybe other concerns applying
11
+ # across the board
12
+ def on_request_start(&cb)
13
+ DispatchTable.add_request_start_listener.call(cb)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ require 'sqreen/ecosystem/module_api'
2
+
3
+ module Sqreen
4
+ module Ecosystem
5
+ module ModuleApi
6
+ module Instrumentation
7
+ def self.included(mod)
8
+ mod.send :include, ModuleApi unless mod.ancestors.include?(ModuleApi)
9
+ end
10
+
11
+ private
12
+
13
+ # Just forwards the call to the instrumentation service
14
+ # @param [String] method
15
+ # @param [Hash{Symbol=>Proc}] advice keys are one of: :before, :after,
16
+ # :raised,
17
+ def instrument(method, advice)
18
+ DispatchTable.instrument.call(self.class.module_name, method, advice)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ require 'sqreen/ecosystem/dispatch_table'
2
+ require 'sqreen/ecosystem/module_api/transaction_storage'
3
+
4
+ module Sqreen
5
+ module Ecosystem
6
+ module ModuleApi
7
+ module SignalProducer
8
+ include TransactionStorage
9
+
10
+ # for injection
11
+ attr_writer :tracing_id_producer
12
+
13
+ private
14
+
15
+ def create_tracing_id
16
+ @tracing_id_producer.call
17
+ end
18
+
19
+ # @param [Sqreen::Kit::Signals::Signal] signal
20
+ def submit_signal(signal)
21
+ DispatchTable.consume_signal.call(signal)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ require 'sqreen/ecosystem/loggable'
2
+ require 'sqreen/ecosystem/tracing/sampling_configuration'
3
+
4
+ module Sqreen
5
+ module Ecosystem
6
+ module ModuleApi
7
+ module TracingPushDown
8
+ include Loggable
9
+
10
+ # method for ecosystem to inject the config
11
+ # @param [Sqreen::Ecosystem::SamplingConfiguration]
12
+ attr_writer :sampling_config
13
+
14
+ private
15
+
16
+ def should_sample?(scope)
17
+ unless @sampling_config
18
+ logger.debug { "Scope #{scope} is disabled because tracing hasn't been enabled yet" }
19
+ return
20
+ end
21
+
22
+ result = @sampling_config.should_sample?(scope)
23
+ if result
24
+ logger.debug { "Will sample scope #{scope}. Sampling line: #{result}" }
25
+ else
26
+ logger.debug { "Will NOT sample scope #{scope}" }
27
+ end
28
+
29
+ result
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,71 @@
1
+ require 'sqreen/ecosystem/transaction_storage'
2
+ require 'sqreen/ecosystem/loggable'
3
+
4
+ module Sqreen
5
+ module Ecosystem
6
+ module ModuleApi
7
+ module TransactionStorage
8
+ class TxLocalVariables
9
+ class << self
10
+ class << self
11
+ include Sqreen::Ecosystem::Loggable
12
+
13
+ private
14
+
15
+ def attr_reader(attr, _opts = {})
16
+ define_method attr do
17
+ tx_storage = Ecosystem::TransactionStorage.fetch_thread_local
18
+ return unless tx_storage
19
+ tx_storage[attr]
20
+ end
21
+ end
22
+
23
+ def attr_accessor(attr, opts = {})
24
+ # reader
25
+ attr_reader attr, opts
26
+
27
+ # writer (2 variants)
28
+ do_assign = proc do |value|
29
+ tx_storage = Ecosystem::TransactionStorage.fetch_thread_local
30
+ unless tx_storage
31
+ logger.debug do
32
+ "Assignment of tx local attribute #{attr} to #{value} has no effect"
33
+ end
34
+ return
35
+ end
36
+
37
+ tx_storage[attr] = value
38
+ end
39
+
40
+ if opts.fetch(:allow_overwrite, false)
41
+ define "#{attr}=", &do_assign
42
+ else
43
+ define_method "#{attr}=" do |value|
44
+ cur = public_send(attr)
45
+ unless cur.nil?
46
+ raise "Cannot override value of #{attr} from #{cur} with #{value}"
47
+ end
48
+
49
+ do_assign.call(value)
50
+ end
51
+ end
52
+ end
53
+ end # TxLocalVariables.singleton_class.singleton_class
54
+
55
+ # usage:
56
+ # attr_reader :xxx
57
+
58
+ # in the future, we'll possibly need to expose the full
59
+ # TransactionStorage to the modules, at least if we don't
60
+ # opt for a more structured fashion of data exchange between
61
+ # the modules.
62
+ end # TxLocalVariables.singleton_class
63
+ end # TxLocalVariables
64
+
65
+ def tx_local_vars
66
+ TxLocalVariables
67
+ end
68
+ end # TransactionStorage module
69
+ end
70
+ end
71
+ end