sqreen 1.19.1-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 (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +34 -0
  3. data/lib/sqreen/actions/block_user.rb +1 -1
  4. data/lib/sqreen/actions/redirect_ip.rb +1 -1
  5. data/lib/sqreen/actions/redirect_user.rb +1 -1
  6. data/lib/sqreen/agent_message.rb +20 -0
  7. data/lib/sqreen/aggregated_metric.rb +25 -0
  8. data/lib/sqreen/attack_detected.html +1 -2
  9. data/lib/sqreen/ca.crt +24 -0
  10. data/lib/sqreen/condition_evaluator.rb +9 -2
  11. data/lib/sqreen/conditionable.rb +24 -6
  12. data/lib/sqreen/configuration.rb +11 -5
  13. data/lib/sqreen/deferred_logger.rb +50 -14
  14. data/lib/sqreen/deliveries/batch.rb +12 -2
  15. data/lib/sqreen/deliveries/simple.rb +4 -0
  16. data/lib/sqreen/deprecation.rb +38 -0
  17. data/lib/sqreen/ecosystem.rb +96 -0
  18. data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
  19. data/lib/sqreen/ecosystem/exception_reporting.rb +26 -0
  20. data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
  21. data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
  22. data/lib/sqreen/ecosystem/loggable.rb +13 -0
  23. data/lib/sqreen/ecosystem/module_api.rb +30 -0
  24. data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
  25. data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
  26. data/lib/sqreen/ecosystem/module_api/message_producer.rb +51 -0
  27. data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
  28. data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
  29. data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
  30. data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
  31. data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
  32. data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
  33. data/lib/sqreen/ecosystem/module_registry.rb +44 -0
  34. data/lib/sqreen/ecosystem/redis/redis_connection.rb +43 -0
  35. data/lib/sqreen/ecosystem/tracing/modules/client.rb +31 -0
  36. data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
  37. data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
  38. data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
  39. data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
  40. data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
  41. data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
  42. data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
  43. data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
  44. data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
  45. data/lib/sqreen/ecosystem_integration.rb +87 -0
  46. data/lib/sqreen/ecosystem_integration/around_callbacks.rb +99 -0
  47. data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +42 -0
  48. data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
  49. data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
  50. data/lib/sqreen/endpoint_testing.rb +184 -0
  51. data/lib/sqreen/event.rb +7 -5
  52. data/lib/sqreen/events/attack.rb +23 -18
  53. data/lib/sqreen/events/remote_exception.rb +0 -22
  54. data/lib/sqreen/events/request_record.rb +15 -71
  55. data/lib/sqreen/frameworks/generic.rb +24 -1
  56. data/lib/sqreen/frameworks/rails.rb +0 -7
  57. data/lib/sqreen/frameworks/request_recorder.rb +15 -2
  58. data/lib/sqreen/graft/call.rb +106 -19
  59. data/lib/sqreen/graft/callback.rb +1 -1
  60. data/lib/sqreen/graft/hook.rb +212 -100
  61. data/lib/sqreen/graft/hook_point.rb +18 -11
  62. data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
  63. data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
  64. data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
  65. data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
  66. data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
  67. data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +57 -0
  68. data/lib/sqreen/legacy/instrumentation.rb +22 -10
  69. data/lib/sqreen/legacy/old_event_submission_strategy.rb +228 -0
  70. data/lib/sqreen/legacy/waf_redactions.rb +49 -0
  71. data/lib/sqreen/log.rb +3 -2
  72. data/lib/sqreen/log/loggable.rb +2 -1
  73. data/lib/sqreen/logger.rb +24 -0
  74. data/lib/sqreen/metrics.rb +1 -0
  75. data/lib/sqreen/metrics/base.rb +3 -0
  76. data/lib/sqreen/metrics/req_detailed.rb +41 -0
  77. data/lib/sqreen/metrics_store.rb +33 -12
  78. data/lib/sqreen/null_logger.rb +22 -0
  79. data/lib/sqreen/performance_notifications/binned_metrics.rb +8 -2
  80. data/lib/sqreen/remote_command.rb +4 -0
  81. data/lib/sqreen/rules.rb +12 -6
  82. data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
  83. data/lib/sqreen/rules/custom_error_cb.rb +3 -3
  84. data/lib/sqreen/rules/not_found_cb.rb +2 -0
  85. data/lib/sqreen/rules/rule_cb.rb +6 -2
  86. data/lib/sqreen/rules/waf_cb.rb +16 -13
  87. data/lib/sqreen/runner.rb +138 -16
  88. data/lib/sqreen/sensitive_data_redactor.rb +19 -31
  89. data/lib/sqreen/session.rb +53 -43
  90. data/lib/sqreen/signals/conversions.rb +288 -0
  91. data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
  92. data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
  93. data/lib/sqreen/version.rb +1 -1
  94. data/lib/sqreen/weave/budget.rb +35 -0
  95. data/lib/sqreen/weave/legacy/instrumentation.rb +277 -135
  96. data/lib/sqreen/worker.rb +6 -2
  97. metadata +86 -10
  98. data/lib/sqreen/backport.rb +0 -9
  99. data/lib/sqreen/backport/clock_gettime.rb +0 -74
  100. data/lib/sqreen/backport/original_name.rb +0 -88
  101. data/lib/sqreen/encoding_sanitizer.rb +0 -27
@@ -0,0 +1,228 @@
1
+ # typed: ignore
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ require 'sqreen/aggregated_metric'
7
+ require 'sqreen/log/loggable'
8
+ require 'sqreen/legacy/waf_redactions'
9
+ require 'sqreen/kit/string_sanitizer'
10
+
11
+ module Sqreen
12
+ module Legacy
13
+ # see also Sqreen::Signals::SignalsSubmissionStrategy
14
+ # usage in Sqreen:Session
15
+ class OldEventSubmissionStrategy
16
+ include Sqreen::Log::Loggable
17
+
18
+ RETRY_MANY = 301
19
+
20
+ def initialize(post_proc)
21
+ @post_proc = post_proc
22
+ end
23
+
24
+ def post_metrics(metrics)
25
+ return if metrics.nil? || metrics.empty?
26
+ payload = { metrics: metrics.map { |m| EventToHash.convert_agg_metric(m) } }
27
+ post('metrics', payload, {}, RETRY_MANY)
28
+ end
29
+
30
+ # @param attack [Sqreen::Attack]
31
+ def post_attack(attack)
32
+ post('attack', EventToHash.convert_attack(attack), {}, RETRY_MANY)
33
+ end
34
+
35
+ # @param [Sqreen::RequestRecord] request_record
36
+ def post_request_record(request_record)
37
+ rr_hash = EventToHash.convert_request_record(request_record)
38
+ post('request_record', rr_hash, {}, RETRY_MANY)
39
+ end
40
+
41
+ # Post an exception to Sqreen for analysis
42
+ # @param exception [RemoteException] Exception and context to be sent over
43
+ def post_sqreen_exception(exception)
44
+ data = EventToHash.convert_exception(exception)
45
+ post('sqreen_exception', data, {}, 5)
46
+ rescue StandardError => e
47
+ logger.warn(format('Could not post exception (network down? %s) %s',
48
+ e.inspect,
49
+ exception.inspect))
50
+ nil
51
+ end
52
+
53
+ def post_batch(events)
54
+ batch = events.map do |event|
55
+ h = case event
56
+ when AggregatedMetric
57
+ logger.warn "Aggregated metric event in non-signal mode. Signals disabled at runtime?"
58
+ next
59
+ when Sqreen::Kit::Signals::Signal
60
+ logger.warn "Signal event in non-signal mode"
61
+ next
62
+ when Sqreen::Kit::Signals::Trace
63
+ logger.warn "Trace event in non-signal mode"
64
+ next
65
+ when Attack # in practice only found inside req rec
66
+ EventToHash.convert_attack event
67
+ when RemoteException
68
+ EventToHash.convert_exception event
69
+ when RequestRecord
70
+ EventToHash.convert_request_record event
71
+ else
72
+ logger.warn "Unexpected event type: #{event}"
73
+ next
74
+ end
75
+ h['event_type'] = event_kind(event)
76
+ h
77
+ end
78
+ Sqreen.log.debug do
79
+ tally = Hash[events.group_by(&:class).map { |k, v| [k, v.count] }]
80
+ "Doing batch with the following tally of event types: #{tally}"
81
+ end
82
+ post('batch', { batch: batch.compact }, {}, RETRY_MANY)
83
+ end
84
+
85
+ private
86
+
87
+ # see +Sqreen::Session.post+
88
+ def post(*args)
89
+ @post_proc[*args]
90
+ end
91
+
92
+ def event_kind(event)
93
+ case event
94
+ when Sqreen::RemoteException then 'sqreen_exception'
95
+ when Sqreen::Attack then 'attack'
96
+ when Sqreen::RequestRecord then 'request_record'
97
+ end
98
+ end
99
+ end
100
+
101
+ module EventToHash
102
+ class << self
103
+ # @param attack [Sqreen::Attack]
104
+ def convert_attack(attack)
105
+ payload = attack.payload
106
+ res = {}
107
+ rule_p = payload['rule']
108
+ request_p = payload['request']
109
+ res[:rule_name] = rule_p['name'] if rule_p && rule_p['name']
110
+ res[:rulespack_id] = rule_p['rulespack_id'] if rule_p && rule_p['rulespack_id']
111
+ res[:test] = rule_p['test'] if rule_p && rule_p['test']
112
+ res[:infos] = payload['infos'] if payload['infos']
113
+ res[:time] = attack.time
114
+ res[:client_ip] = request_p[:addr] if request_p && request_p[:addr]
115
+ res[:request] = request_p if request_p
116
+ res[:params] = payload['params'] if payload['params']
117
+ res[:context] = payload['context'] if payload['context']
118
+ res[:headers] = payload['headers'] if payload['headers']
119
+ res
120
+ end
121
+
122
+ # @param [Sqreen::RequestRecord] rr
123
+ def convert_request_record(rr)
124
+ res = { :version => '20171208' }
125
+ payload = rr.payload
126
+
127
+ if payload[:observed]
128
+ res[:observed] = payload[:observed].dup
129
+ rulespack = nil
130
+ if rr.observed[:attacks]
131
+ res[:observed][:attacks] = rr.observed[:attacks].map do |att|
132
+ natt = att.dup
133
+ [:attack_type, :block].each { |k| natt.delete(k) } # signals stuff
134
+ rulespack = natt.delete(:rulespack_id) || rulespack
135
+ natt
136
+ end
137
+ end
138
+ if rr.observed[:sqreen_exceptions]
139
+ res[:observed][:sqreen_exceptions] = rr.observed[:sqreen_exceptions].map do |exc|
140
+ nex = exc.dup
141
+ excp = nex.delete(:exception)
142
+ if excp
143
+ nex[:message] = excp.message
144
+ nex[:klass] = excp.class.name
145
+ end
146
+ rulespack = nex.delete(:rulespack_id) || rulespack
147
+ nex
148
+ end
149
+ end
150
+ res[:rulespack_id] = rulespack unless rulespack.nil?
151
+ if rr.observed[:observations]
152
+ res[:observed][:observations] = rr.observed[:observations].map do |cat, key, value, time|
153
+ { :category => cat, :key => key, :value => value, :time => time }
154
+ end
155
+ end
156
+ if rr.observed[:sdk] # rubocop:disable Style/IfUnlessModifier
157
+ res[:observed][:sdk] = rr.processed_sdk_calls
158
+ end
159
+ end
160
+ res[:local] = payload['local'] if payload['local']
161
+ if payload['request']
162
+ res[:request] = payload['request'].dup
163
+ res[:client_ip] = res[:request].delete(:client_ip) if res[:request][:client_ip]
164
+ else
165
+ res[:request] = {}
166
+ end
167
+ if payload['response']
168
+ res[:response] = payload['response'].dup
169
+ else
170
+ res[:response] = {}
171
+ end
172
+
173
+ res[:request][:parameters] = payload['params'] if payload['params']
174
+ res[:request][:headers] = payload['headers'] if payload['headers']
175
+
176
+ res = Sqreen::Kit::StringSanitizer.sanitize(res)
177
+
178
+ if rr.redactor
179
+ res[:request], redacted = rr.redactor.redact(res[:request])
180
+ redacted = redacted.uniq
181
+ if redacted.any? && res[:observed] && res[:observed][:attacks]
182
+ res[:observed][:attacks] = WafRedactions.redact_attacks!(res[:observed][:attacks], redacted)
183
+ end
184
+ if redacted.any? && res[:observed] && res[:observed][:sqreen_exceptions]
185
+ res[:observed][:sqreen_exceptions] = WafRedactions.redact_exceptions!(res[:observed][:sqreen_exceptions], redacted)
186
+ end
187
+ end
188
+
189
+ res
190
+ end
191
+
192
+ # @param exception_evt [Sqreen::RemoteException]
193
+ def convert_exception(exception_evt)
194
+ payload = exception_evt.payload
195
+ exception = payload['exception']
196
+ ev = {
197
+ :klass => exception.class.name,
198
+ :message => exception.message,
199
+ :params => payload['request_params'],
200
+ :time => payload['time'],
201
+ :infos => {
202
+ :client_ip => payload['client_ip'],
203
+ },
204
+ :request => payload['request_infos'],
205
+ :headers => payload['headers'],
206
+ :rule_name => payload['rule_name'],
207
+ :rulespack_id => payload['rulespack_id'],
208
+ }
209
+
210
+ ev[:infos].merge!(payload['infos']) if payload['infos']
211
+ return ev unless exception.backtrace
212
+ ev[:context] = { :backtrace => exception.backtrace.map(&:to_s) }
213
+ ev
214
+ end
215
+
216
+ # @param [Sqreen::AggregatedMetric] agg_metric
217
+ def convert_agg_metric(agg_metric)
218
+ {
219
+ name: agg_metric.name,
220
+ observation: agg_metric.data,
221
+ start: agg_metric.start,
222
+ finish: agg_metric.finish,
223
+ }
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,49 @@
1
+ # typed: ignore
2
+
3
+ # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
+ # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
+
6
+ module Sqreen
7
+ module Legacy
8
+ module WafRedactions
9
+ class << self
10
+ def redact_attacks!(attacks, values)
11
+ return attacks if values.empty?
12
+
13
+ values = values.map { |v| v.downcase if v.is_a?(String) }
14
+
15
+ attacks.each do |e|
16
+ next(e) unless e[:infos]
17
+ next(e) unless e[:infos][:waf_data]
18
+
19
+ parsed = JSON.parse(e[:infos][:waf_data])
20
+ redacted = parsed.each do |w|
21
+ next unless (filters = w['filter'])
22
+
23
+ filters.each do |f|
24
+ next unless (v = f['resolved_value'])
25
+ next unless values.include?(v.downcase)
26
+
27
+ f['match_status'] = SensitiveDataRedactor::MASK
28
+ f['resolved_value'] = SensitiveDataRedactor::MASK
29
+ end
30
+ end
31
+ e[:infos][:waf_data] = JSON.dump(redacted)
32
+ end
33
+ end
34
+
35
+ # see https://github.com/sqreen/TechDoc/blob/master/content/specs/spec000022-waf-data-sanitization.md#changes-to-the-agents
36
+ def redact_exceptions!(exceptions, values)
37
+ return exceptions if values.empty?
38
+
39
+ exceptions.each do |e|
40
+ next(e) unless e[:infos]
41
+ next(e) unless e[:infos][:waf]
42
+
43
+ e[:infos][:waf].delete(:args)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -14,16 +14,17 @@ require 'sqreen/deferred_logger'
14
14
 
15
15
  module Sqreen
16
16
  def self.log_init
17
+ deferred_logger = @logger
17
18
  @logger = Sqreen::Logger.new(
18
19
  Sqreen.config_get(:log_level).to_s.upcase,
19
20
  Sqreen.config_get(:log_location)
20
21
  )
21
- Sqreen::DeferredLogger.instance.flush_to(@logger.instance_eval { @logger })
22
+ deferred_logger.flush_to(@logger.instance_eval { @logger })
22
23
  rescue => e
23
24
  warn "Sqreen logger exception: #{e}"
24
25
  end
25
26
 
26
27
  def self::log
27
- @logger || Sqreen::DeferredLogger.instance
28
+ @logger ||= Sqreen::DeferredLogger.new
28
29
  end
29
30
  end
@@ -4,6 +4,7 @@
4
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
5
 
6
6
  require 'logger'
7
+ require 'sqreen/log'
7
8
 
8
9
  module Sqreen; end
9
10
  module Sqreen::Log; end
@@ -23,6 +24,6 @@ module Sqreen::Log::Loggable
23
24
  end
24
25
 
25
26
  def logger
26
- @logger || self.class.logger
27
+ @logger || singleton_class.logger
27
28
  end
28
29
  end
@@ -28,6 +28,26 @@ module Sqreen
28
28
  create_error_logger
29
29
  end
30
30
 
31
+ def debug?
32
+ @logger.debug?
33
+ end
34
+
35
+ def info?
36
+ @logger.info?
37
+ end
38
+
39
+ def warn?
40
+ @logger.warn?
41
+ end
42
+
43
+ def error?
44
+ @logger.error?
45
+ end
46
+
47
+ def fatal?
48
+ @logger.fatal?
49
+ end
50
+
31
51
  def debug(msg = nil, &block)
32
52
  @logger.debug(msg, &block)
33
53
  end
@@ -45,6 +65,10 @@ module Sqreen
45
65
  @logger.error(msg, &block)
46
66
  end
47
67
 
68
+ def unknown(msg = nil, &block)
69
+ @logger.unknown(msg, &block)
70
+ end
71
+
48
72
  def add(severity, msg = nil, &block)
49
73
  send(SEVERITY_TO_METHOD[severity], msg, &block)
50
74
  end
@@ -7,3 +7,4 @@ require 'sqreen/metrics/collect'
7
7
  require 'sqreen/metrics/average'
8
8
  require 'sqreen/metrics/sum'
9
9
  require 'sqreen/metrics/binning'
10
+ require 'sqreen/metrics/req_detailed'
@@ -12,6 +12,9 @@ module Sqreen
12
12
  FINISH_KEY = 'finish'.freeze
13
13
  # Base interface for a metric
14
14
  class Base
15
+ attr_accessor :name, :period # for signals serialization
16
+ attr_accessor :rule # optional
17
+
15
18
  def initialize(_opts={})
16
19
  @sample = nil
17
20
  end
@@ -0,0 +1,41 @@
1
+ require 'base64'
2
+ require 'sqreen/mono_time'
3
+ require 'sqreen/metrics/base'
4
+ begin
5
+ require 'sq_detailed_metrics'
6
+ rescue LoadError => _e # rubocop:disable Lint/HandleExceptions
7
+ end
8
+
9
+ module Sqreen
10
+ module Metric
11
+ class ReqDetailed < Base
12
+ attr_reader :num_requests
13
+
14
+ def initialize(opts = {})
15
+ raise 'SqDetailedMetrics unavailable' unless defined?(SqDetailedMetrics)
16
+ super(opts)
17
+ @coll = SqDetailedMetrics::RequestCollection.new
18
+ @start_time = Sqreen.time
19
+ @num_requests = 0
20
+ end
21
+
22
+ # @param [SqDetailedMetrics::Request] value
23
+ def update(_key, value)
24
+ @coll << value
25
+ @num_requests += 1
26
+ end
27
+
28
+ def next_sample(time)
29
+ data = @coll.serialize
30
+ @num_requests = 0
31
+ return nil unless data
32
+
33
+ {
34
+ OBSERVATION_KEY => { 'v1' => Base64.strict_encode64(data) },
35
+ START_KEY => @start_time,
36
+ FINISH_KEY => (@start_time = time),
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -3,6 +3,7 @@
3
3
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
4
4
  # Please refer to our terms for more information: https://www.sqreen.com/terms.html
5
5
 
6
+ require 'sqreen/aggregated_metric'
6
7
  require 'sqreen/metrics'
7
8
  require 'sqreen/mono_time'
8
9
  require 'sqreen/metrics_store/unknown_metric'
@@ -26,12 +27,16 @@ module Sqreen
26
27
  def initialize
27
28
  @store = []
28
29
  @metrics = {} # name => (metric, period, start)
30
+ @mutex = Mutex.new
29
31
  end
30
32
 
31
33
  # Definition contains a name,period and aggregate at least
32
34
  # @param definition [Hash] a metric definition
35
+ # @param rule [RuleCB] the rule associated with this metric, if any
33
36
  # @param mklass [Object] Override metric object (used in testing)
34
- def create_metric(definition, mklass = nil)
37
+ def create_metric(definition, rule = nil, mklass = nil)
38
+ @mutex.lock
39
+
35
40
  name = definition[NAME_KEY]
36
41
  kind = definition[KIND_KEY]
37
42
  klass = valid_metric(kind, name)
@@ -43,30 +48,41 @@ module Sqreen
43
48
  definition[PERIOD_KEY],
44
49
  nil # Start
45
50
  ]
51
+ metric.name = name
52
+ metric.rule = rule
53
+ metric.period = definition[PERIOD_KEY]
46
54
  metric
55
+ ensure
56
+ @mutex.unlock
47
57
  end
48
58
 
49
59
  def metric?(name)
50
60
  @metrics.key?(name)
51
61
  end
52
62
 
53
- # @params at [Time] when is the store emptied
63
+ # @param at [Time] when is the store emptied
54
64
  def update(name, at, key, value)
65
+ @mutex.lock
55
66
  metric, period, start = @metrics[name]
56
67
  raise UnregisteredMetric, "Unknown metric #{name}" unless metric
57
68
  next_sample(name, at) if start.nil? || (start + period) < at
58
69
  metric.update(key, value)
70
+ ensure
71
+ @mutex.unlock
59
72
  end
60
73
 
61
74
  # Drains every metrics and returns the store content
62
- # @params at [Time] when is the store emptied
75
+ # @param at [Time] when is the store emptied
63
76
  def publish(flush = true, at = Sqreen.time)
77
+ @mutex.lock
64
78
  @metrics.each do |name, (_, period, start)|
65
79
  next_sample(name, at) if flush || !start.nil? && (start + period) < at
66
80
  end
67
81
  out = @store
68
82
  @store = []
69
83
  out
84
+ ensure
85
+ @mutex.unlock
70
86
  end
71
87
 
72
88
  protected
@@ -75,15 +91,20 @@ module Sqreen
75
91
  metric = @metrics[name][0]
76
92
  r = metric.next_sample(at)
77
93
  @metrics[name][2] = at # new start
78
- if r
79
- r[NAME_KEY] = name
80
- obs = r[Metric::OBSERVATION_KEY]
81
- start_of_mono = Time.now.utc - Sqreen.time
82
- r[Metric::START_KEY] = start_of_mono + r[Metric::START_KEY]
83
- r[Metric::FINISH_KEY] = start_of_mono + r[Metric::FINISH_KEY]
84
- @store << r if obs && (!obs.respond_to?(:empty?) || !obs.empty?)
85
- end
86
- r
94
+ return unless r
95
+
96
+ r[NAME_KEY] = name
97
+ obs = r[Metric::OBSERVATION_KEY]
98
+ return unless obs && (!obs.respond_to?(:empty?) || !obs.empty?)
99
+ start_of_mono = Time.now.utc - Sqreen.time
100
+
101
+ agg = AggregatedMetric.new
102
+ agg.metric = metric
103
+ agg.rule = agg.metric.rule
104
+ agg.start = start_of_mono + r[Metric::START_KEY]
105
+ agg.finish = start_of_mono + r[Metric::FINISH_KEY]
106
+ agg.data = obs
107
+ @store << agg
87
108
  end
88
109
 
89
110
  def valid_metric(kind, name)