sqreen 1.19.3-java → 1.20.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/sqreen/aggregated_metric.rb +25 -0
- data/lib/sqreen/configuration.rb +7 -3
- data/lib/sqreen/deliveries/batch.rb +4 -1
- data/lib/sqreen/deliveries/simple.rb +4 -0
- data/lib/sqreen/event.rb +7 -5
- data/lib/sqreen/events/attack.rb +23 -18
- data/lib/sqreen/events/remote_exception.rb +0 -22
- data/lib/sqreen/events/request_record.rb +15 -70
- data/lib/sqreen/frameworks/request_recorder.rb +13 -2
- data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
- data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
- data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
- data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
- data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
- data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +57 -0
- data/lib/sqreen/legacy/old_event_submission_strategy.rb +221 -0
- data/lib/sqreen/legacy/waf_redactions.rb +49 -0
- data/lib/sqreen/metrics/base.rb +3 -0
- data/lib/sqreen/metrics_store.rb +22 -12
- data/lib/sqreen/performance_notifications/binned_metrics.rb +8 -2
- data/lib/sqreen/rules.rb +4 -2
- data/lib/sqreen/rules/rule_cb.rb +2 -0
- data/lib/sqreen/rules/waf_cb.rb +11 -8
- data/lib/sqreen/runner.rb +43 -5
- data/lib/sqreen/sensitive_data_redactor.rb +19 -31
- data/lib/sqreen/session.rb +39 -37
- data/lib/sqreen/signals/conversions.rb +283 -0
- data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
- data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
- data/lib/sqreen/version.rb +1 -1
- metadata +42 -5
- data/lib/sqreen/backport.rb +0 -9
- data/lib/sqreen/backport/clock_gettime.rb +0 -74
- data/lib/sqreen/backport/original_name.rb +0 -88
@@ -0,0 +1,57 @@
|
|
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/kit/signals/point'
|
7
|
+
require 'sqreen/kit/signals/dto_helper'
|
8
|
+
|
9
|
+
# reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/attack/2020-01-01T00_00_00_000Z/schema.cue
|
10
|
+
|
11
|
+
module Sqreen
|
12
|
+
module Kit
|
13
|
+
module Signals
|
14
|
+
module Specialized
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Sqreen::Kit::Signals::Specialized::Attack < Sqreen::Kit::Signals::Point
|
21
|
+
add_mandatory_attrs :source, :time, :payload
|
22
|
+
|
23
|
+
validate_str_attr :signal_name, /\Asq\.agent\.attack\..+\z/
|
24
|
+
validate_str_attr :source, /\Asqreen:rule:[a-f0-9]{40}:.+\z/
|
25
|
+
|
26
|
+
def initialize(values = {})
|
27
|
+
self.payload_schema = Payload::SCHEMA_VERSION
|
28
|
+
self.time = values[:time] || Time.now
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def payload=(payload)
|
33
|
+
unless payload.is_a?(Payload)
|
34
|
+
raise ArgumentError, "Payload should be a #{Payload}"
|
35
|
+
end
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
class Payload
|
40
|
+
include Sqreen::Kit::Signals::DtoHelper
|
41
|
+
|
42
|
+
SCHEMA_VERSION = 'attack/2020-01-01T00:00:00.000Z'.freeze
|
43
|
+
|
44
|
+
add_mandatory_attrs :test, :block, :infos
|
45
|
+
|
46
|
+
# all are mandatory
|
47
|
+
|
48
|
+
# @return [Boolean]
|
49
|
+
attr_accessor :test
|
50
|
+
|
51
|
+
# @return [Boolean]
|
52
|
+
attr_accessor :block
|
53
|
+
|
54
|
+
# @return [Hash{String|Symbol=>Object}]
|
55
|
+
attr_accessor :infos
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,76 @@
|
|
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/kit/signals/metric'
|
7
|
+
require 'sqreen/kit/signals/dto_helper'
|
8
|
+
|
9
|
+
# reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/binning_metric/2020-01-01T00_00_00_000Z/schema.cue
|
10
|
+
|
11
|
+
module Sqreen
|
12
|
+
module Kit
|
13
|
+
module Signals
|
14
|
+
module Specialized
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Sqreen::Kit::Signals::Specialized::BinningMetric < Sqreen::Kit::Signals::Metric
|
21
|
+
add_mandatory_attrs :source, :time, :payload
|
22
|
+
|
23
|
+
validate_str_attr :signal_name, /\Asq\.agent\.metric\..+\z/
|
24
|
+
|
25
|
+
def initialize(args)
|
26
|
+
self.payload_schema = Payload::SCHEMA_VERSION
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
class Payload
|
31
|
+
include Sqreen::Kit::Signals::DtoHelper
|
32
|
+
|
33
|
+
SCHEMA_VERSION = 'metric_binning/2020-01-01T00:00:00.000Z'.freeze
|
34
|
+
|
35
|
+
add_mandatory_attrs :capture_interval_s,
|
36
|
+
:date_started,
|
37
|
+
:date_ended,
|
38
|
+
:max, :base, :unit, :bins
|
39
|
+
|
40
|
+
# mandatory
|
41
|
+
# @return [Integer]
|
42
|
+
attr_accessor :capture_interval_s
|
43
|
+
|
44
|
+
# mandatory
|
45
|
+
# @param [Time]
|
46
|
+
# @return [String]
|
47
|
+
attr_accessor_time :date_started
|
48
|
+
|
49
|
+
# mandatory
|
50
|
+
# @param [Time]
|
51
|
+
# @return [String]
|
52
|
+
attr_accessor_time :date_ended
|
53
|
+
|
54
|
+
# mandatory
|
55
|
+
# @return [Float]
|
56
|
+
attr_accessor :max
|
57
|
+
|
58
|
+
# mandatory
|
59
|
+
# @return [Float]
|
60
|
+
attr_accessor :base
|
61
|
+
|
62
|
+
# mandatory
|
63
|
+
# @return [Float]
|
64
|
+
attr_accessor :unit
|
65
|
+
|
66
|
+
# mandatory
|
67
|
+
# @return [Hash{Integer=>Integer}]
|
68
|
+
attr_accessor :bins
|
69
|
+
|
70
|
+
def to_h
|
71
|
+
{
|
72
|
+
kind: 'binning',
|
73
|
+
}.merge(super)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,26 @@
|
|
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/kit/signals/trace'
|
7
|
+
require 'sqreen/kit/signals/context/http_context'
|
8
|
+
require 'sqreen/kit/signals/dto_helper'
|
9
|
+
|
10
|
+
module Sqreen
|
11
|
+
module Kit
|
12
|
+
module Signals
|
13
|
+
module Specialized
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Sqreen::Kit::Signals::Specialized::HttpTrace < Sqreen::Kit::Signals::Trace
|
20
|
+
add_mandatory_attrs :context
|
21
|
+
|
22
|
+
def initialize(values = {})
|
23
|
+
self.context_schema = ::Sqreen::Kit::Signals::Context::HttpContext::SCHEMA_VERSION
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
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/kit/signals/point'
|
7
|
+
require 'sqreen/kit/signals/dto_helper'
|
8
|
+
|
9
|
+
# reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/sdk_call/2020-01-01T00_00_00_000Z/schema.cue
|
10
|
+
|
11
|
+
module Sqreen
|
12
|
+
module Kit
|
13
|
+
module Signals
|
14
|
+
module Specialized
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Sqreen::Kit::Signals::Specialized::SdkTrackCall < Sqreen::Kit::Signals::Point
|
21
|
+
add_mandatory_attrs :source, :time, :payload
|
22
|
+
|
23
|
+
validate_str_attr :signal_name, /\Asq\.sdk\..+\z/
|
24
|
+
|
25
|
+
def initialize(values = {})
|
26
|
+
self.payload_schema = Payload::SCHEMA_VERSION
|
27
|
+
self.source = "sqreen:sdk:track"
|
28
|
+
self.time = values[:time] || Time.now
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def payload=(payload)
|
33
|
+
unless payload.is_a?(Payload)
|
34
|
+
raise ArgumentError, "Payload should be a #{Payload}"
|
35
|
+
end
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
class Payload
|
40
|
+
include Sqreen::Kit::Signals::DtoHelper
|
41
|
+
|
42
|
+
SCHEMA_VERSION = 'track_event/2020-01-01T00:00:00.000Z'.freeze
|
43
|
+
|
44
|
+
# @return [Hash{String|Symbol=>Object}]
|
45
|
+
attr_accessor :properties
|
46
|
+
|
47
|
+
# @return [Hash{String|Symbol=>String}]
|
48
|
+
attr_accessor :user_identifiers
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,57 @@
|
|
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/kit/signals/point'
|
7
|
+
require 'sqreen/kit/signals/dto_helper'
|
8
|
+
|
9
|
+
# reference: https://github.com/sqreen/SignalsSchemas/blob/master/schemas/payload/sqreen_exception/2020-01-01T00_00_00_000Z/schema.cue
|
10
|
+
|
11
|
+
module Sqreen
|
12
|
+
module Kit
|
13
|
+
module Signals
|
14
|
+
module Specialized
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Sqreen::Kit::Signals::Specialized::SqreenException < Sqreen::Kit::Signals::Point
|
21
|
+
PAYLOAD_SCHEMA_VERSION = 'sqreen_exception/2020-01-01T00:00:00.000Z'.freeze
|
22
|
+
|
23
|
+
# @return [Hash]
|
24
|
+
attr_accessor :infos
|
25
|
+
|
26
|
+
# @return [Exception]
|
27
|
+
attr_accessor :ruby_exception
|
28
|
+
|
29
|
+
add_mandatory_attrs :source, :time, :ruby_exception
|
30
|
+
|
31
|
+
validate_str_attr :source, /\A(?:sqreen:rule:[a-f0-9]{40}:.+)|(?:sqreen:agent:.+)\z/
|
32
|
+
|
33
|
+
def self.attributes_for_to_h_self
|
34
|
+
[] # don't include ruby_exception in list of attributes for to_h
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(values = {})
|
38
|
+
self.payload_schema = PAYLOAD_SCHEMA_VERSION
|
39
|
+
self.signal_name = 'sq.agent.exception'
|
40
|
+
self.time = values[:time] || Time.now
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
def payload
|
45
|
+
return nil unless @ruby_exception
|
46
|
+
compact_hash({
|
47
|
+
klass: @ruby_exception.class.to_s,
|
48
|
+
message: @ruby_exception.message,
|
49
|
+
infos: @infos,
|
50
|
+
})
|
51
|
+
end
|
52
|
+
|
53
|
+
def location
|
54
|
+
return nil unless @ruby_exception
|
55
|
+
Sqreen::Kit::Signals::Location.new(exception: @ruby_exception)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,221 @@
|
|
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
|
+
|
10
|
+
module Sqreen
|
11
|
+
module Legacy
|
12
|
+
# see also Sqreen::Signals::SignalsSubmissionStrategy
|
13
|
+
# usage in Sqreen:Session
|
14
|
+
class OldEventSubmissionStrategy
|
15
|
+
include Sqreen::Log::Loggable
|
16
|
+
|
17
|
+
RETRY_MANY = 301
|
18
|
+
|
19
|
+
def initialize(post_proc)
|
20
|
+
@post_proc = post_proc
|
21
|
+
end
|
22
|
+
|
23
|
+
def post_metrics(metrics)
|
24
|
+
return if metrics.nil? || metrics.empty?
|
25
|
+
payload = { metrics: metrics.map { |m| EventToHash.convert_agg_metric(m) } }
|
26
|
+
post('metrics', payload, {}, RETRY_MANY)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param attack [Sqreen::Attack]
|
30
|
+
def post_attack(attack)
|
31
|
+
post('attack', EventToHash.convert_attack(attack), {}, RETRY_MANY)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [Sqreen::RequestRecord] request_record
|
35
|
+
def post_request_record(request_record)
|
36
|
+
rr_hash = EventToHash.convert_request_record(request_record)
|
37
|
+
post('request_record', rr_hash, {}, RETRY_MANY)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Post an exception to Sqreen for analysis
|
41
|
+
# @param exception [RemoteException] Exception and context to be sent over
|
42
|
+
def post_sqreen_exception(exception)
|
43
|
+
data = EventToHash.convert_exception(exception)
|
44
|
+
post('sqreen_exception', data, {}, 5)
|
45
|
+
rescue StandardError => e
|
46
|
+
logger.warn(format('Could not post exception (network down? %s) %s',
|
47
|
+
e.inspect,
|
48
|
+
exception.inspect))
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def post_batch(events)
|
53
|
+
batch = events.map do |event|
|
54
|
+
h = case event
|
55
|
+
when AggregatedMetric
|
56
|
+
logger.warn "Aggregated metric event in non-signal mode. Signals disabled at runtime?"
|
57
|
+
next
|
58
|
+
when Attack # in practice only found inside req rec
|
59
|
+
EventToHash.convert_attack event
|
60
|
+
when RemoteException
|
61
|
+
EventToHash.convert_exception event
|
62
|
+
when RequestRecord
|
63
|
+
EventToHash.convert_request_record event
|
64
|
+
else
|
65
|
+
logger.warn "Unexpected event type: #{event}"
|
66
|
+
next
|
67
|
+
end
|
68
|
+
h['event_type'] = event_kind(event)
|
69
|
+
h
|
70
|
+
end
|
71
|
+
Sqreen.log.debug do
|
72
|
+
tally = Hash[events.group_by(&:class).map { |k, v| [k, v.count] }]
|
73
|
+
"Doing batch with the following tally of event types: #{tally}"
|
74
|
+
end
|
75
|
+
post('batch', { batch: batch }, {}, RETRY_MANY)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# see +Sqreen::Session.post+
|
81
|
+
def post(*args)
|
82
|
+
@post_proc[*args]
|
83
|
+
end
|
84
|
+
|
85
|
+
def event_kind(event)
|
86
|
+
case event
|
87
|
+
when Sqreen::RemoteException then 'sqreen_exception'
|
88
|
+
when Sqreen::Attack then 'attack'
|
89
|
+
when Sqreen::RequestRecord then 'request_record'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
module EventToHash
|
95
|
+
class << self
|
96
|
+
# @param attack [Sqreen::Attack]
|
97
|
+
def convert_attack(attack)
|
98
|
+
payload = attack.payload
|
99
|
+
res = {}
|
100
|
+
rule_p = payload['rule']
|
101
|
+
request_p = payload['request']
|
102
|
+
res[:rule_name] = rule_p['name'] if rule_p && rule_p['name']
|
103
|
+
res[:rulespack_id] = rule_p['rulespack_id'] if rule_p && rule_p['rulespack_id']
|
104
|
+
res[:test] = rule_p['test'] if rule_p && rule_p['test']
|
105
|
+
res[:infos] = payload['infos'] if payload['infos']
|
106
|
+
res[:time] = attack.time
|
107
|
+
res[:client_ip] = request_p[:addr] if request_p && request_p[:addr]
|
108
|
+
res[:request] = request_p if request_p
|
109
|
+
res[:params] = payload['params'] if payload['params']
|
110
|
+
res[:context] = payload['context'] if payload['context']
|
111
|
+
res[:headers] = payload['headers'] if payload['headers']
|
112
|
+
res
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param [Sqreen::RequestRecord] rr
|
116
|
+
def convert_request_record(rr)
|
117
|
+
res = { :version => '20171208' }
|
118
|
+
payload = rr.payload
|
119
|
+
|
120
|
+
if payload[:observed]
|
121
|
+
res[:observed] = payload[:observed].dup
|
122
|
+
rulespack = nil
|
123
|
+
if rr.observed[:attacks]
|
124
|
+
res[:observed][:attacks] = rr.observed[:attacks].map do |att|
|
125
|
+
natt = att.dup
|
126
|
+
[:attack_type, :block].each { |k| natt.delete(k) } # signals stuff
|
127
|
+
rulespack = natt.delete(:rulespack_id) || rulespack
|
128
|
+
natt
|
129
|
+
end
|
130
|
+
end
|
131
|
+
if rr.observed[:sqreen_exceptions]
|
132
|
+
res[:observed][:sqreen_exceptions] = rr.observed[:sqreen_exceptions].map do |exc|
|
133
|
+
nex = exc.dup
|
134
|
+
excp = nex.delete(:exception)
|
135
|
+
if excp
|
136
|
+
nex[:message] = excp.message
|
137
|
+
nex[:klass] = excp.class.name
|
138
|
+
end
|
139
|
+
rulespack = nex.delete(:rulespack_id) || rulespack
|
140
|
+
nex
|
141
|
+
end
|
142
|
+
end
|
143
|
+
res[:rulespack_id] = rulespack unless rulespack.nil?
|
144
|
+
if rr.observed[:observations]
|
145
|
+
res[:observed][:observations] = rr.observed[:observations].map do |cat, key, value, time|
|
146
|
+
{ :category => cat, :key => key, :value => value, :time => time }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
if rr.observed[:sdk] # rubocop:disable Style/IfUnlessModifier
|
150
|
+
res[:observed][:sdk] = rr.processed_sdk_calls
|
151
|
+
end
|
152
|
+
end
|
153
|
+
res[:local] = payload['local'] if payload['local']
|
154
|
+
if payload['request']
|
155
|
+
res[:request] = payload['request'].dup
|
156
|
+
res[:client_ip] = res[:request].delete(:client_ip) if res[:request][:client_ip]
|
157
|
+
else
|
158
|
+
res[:request] = {}
|
159
|
+
end
|
160
|
+
if payload['response']
|
161
|
+
res[:response] = payload['response'].dup
|
162
|
+
else
|
163
|
+
res[:response] = {}
|
164
|
+
end
|
165
|
+
|
166
|
+
res[:request][:parameters] = payload['params'] if payload['params']
|
167
|
+
res[:request][:headers] = payload['headers'] if payload['headers']
|
168
|
+
|
169
|
+
res = Sqreen::EncodingSanitizer.sanitize(res)
|
170
|
+
|
171
|
+
if rr.redactor
|
172
|
+
res[:request], redacted = rr.redactor.redact(res[:request])
|
173
|
+
redacted = redacted.uniq
|
174
|
+
if redacted.any? && res[:observed] && res[:observed][:attacks]
|
175
|
+
res[:observed][:attacks] = WafRedactions.redact_attacks!(res[:observed][:attacks], redacted)
|
176
|
+
end
|
177
|
+
if redacted.any? && res[:observed] && res[:observed][:sqreen_exceptions]
|
178
|
+
res[:observed][:sqreen_exceptions] = WafRedactions.redact_exceptions!(res[:observed][:sqreen_exceptions], redacted)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
res
|
183
|
+
end
|
184
|
+
|
185
|
+
# @param exception_evt [Sqreen::RemoteException]
|
186
|
+
def convert_exception(exception_evt)
|
187
|
+
payload = exception_evt.payload
|
188
|
+
exception = payload['exception']
|
189
|
+
ev = {
|
190
|
+
:klass => exception.class.name,
|
191
|
+
:message => exception.message,
|
192
|
+
:params => payload['request_params'],
|
193
|
+
:time => payload['time'],
|
194
|
+
:infos => {
|
195
|
+
:client_ip => payload['client_ip'],
|
196
|
+
},
|
197
|
+
:request => payload['request_infos'],
|
198
|
+
:headers => payload['headers'],
|
199
|
+
:rule_name => payload['rule_name'],
|
200
|
+
:rulespack_id => payload['rulespack_id'],
|
201
|
+
}
|
202
|
+
|
203
|
+
ev[:infos].merge!(payload['infos']) if payload['infos']
|
204
|
+
return ev unless exception.backtrace
|
205
|
+
ev[:context] = { :backtrace => exception.backtrace.map(&:to_s) }
|
206
|
+
ev
|
207
|
+
end
|
208
|
+
|
209
|
+
# @param [Sqreen::AggregatedMetric] agg_metric
|
210
|
+
def convert_agg_metric(agg_metric)
|
211
|
+
{
|
212
|
+
name: agg_metric.name,
|
213
|
+
observation: agg_metric.data,
|
214
|
+
start: agg_metric.start,
|
215
|
+
finish: agg_metric.finish,
|
216
|
+
}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|