canvas_statsd 1.0.8 → 2.0.0
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/lib/canvas_statsd.rb +44 -44
- data/lib/canvas_statsd/block_stat.rb +37 -0
- data/lib/canvas_statsd/block_tracking.rb +47 -0
- data/lib/canvas_statsd/counter.rb +16 -7
- data/lib/canvas_statsd/default_tracking.rb +19 -55
- data/lib/canvas_statsd/request_logger.rb +5 -15
- data/lib/canvas_statsd/request_stat.rb +13 -21
- data/lib/canvas_statsd/request_tracking.rb +27 -0
- data/lib/canvas_statsd/sql_tracker.rb +10 -14
- data/spec/canvas_statsd/block_stat_spec.rb +11 -0
- data/spec/canvas_statsd/block_tracking_spec.rb +37 -0
- data/spec/canvas_statsd/counter_spec.rb +12 -17
- data/spec/canvas_statsd/request_logger_spec.rb +26 -10
- data/spec/canvas_statsd/request_stat_spec.rb +6 -21
- data/spec/canvas_statsd/request_tracking_spec.rb +14 -0
- data/spec/canvas_statsd/sql_tracker_spec.rb +33 -14
- metadata +26 -5
- data/spec/canvas_statsd/default_tracking_spec.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 951dbaa395f7d79e76329d841ca6ce9aa8716a07
|
4
|
+
data.tar.gz: 2b9f6679bc1dd954fbe53ddf22fe43febde61380
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9465805cde29e5584b03fd173fb1b9d08cd21892413c6e1c8a06daa5a6295b45ea32ef8acdcab8a05bb967e8f385b9013e8e98b54dcb9a1922f9d849f6734b8d
|
7
|
+
data.tar.gz: a2c901c2764ff4a5690a6704a0f68c28da6ea13f97b52a5237caaeb3c15ff38fa9d05509fb1a345bce2a0dfc278af3104f64b39d81e91eb0ddd828bcd09abdba
|
data/lib/canvas_statsd.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'statsd'
|
2
|
-
require "aroi" if defined?(ActiveRecord)
|
3
2
|
|
4
3
|
module CanvasStatsd
|
5
4
|
VALID_SETTINGS = [:host, :port, :namespace, :append_hostname]
|
@@ -7,59 +6,60 @@ module CanvasStatsd
|
|
7
6
|
class ConfigurationError < StandardError; end
|
8
7
|
|
9
8
|
require "canvas_statsd/statsd"
|
9
|
+
require "canvas_statsd/block_stat"
|
10
|
+
require "canvas_statsd/block_tracking"
|
10
11
|
require "canvas_statsd/request_stat"
|
11
12
|
require "canvas_statsd/counter"
|
12
13
|
require "canvas_statsd/sql_tracker"
|
13
14
|
require "canvas_statsd/default_tracking"
|
14
15
|
require "canvas_statsd/request_logger"
|
16
|
+
require "canvas_statsd/request_tracking"
|
15
17
|
require "canvas_statsd/null_logger"
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
19
|
+
class << self
|
20
|
+
def settings
|
21
|
+
@settings || env_settings
|
22
|
+
end
|
23
|
+
|
24
|
+
def settings=(value)
|
25
|
+
@settings = validate_settings(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_settings(value)
|
29
|
+
return nil if value.nil?
|
30
|
+
|
31
|
+
validated = {}
|
32
|
+
value.each do |k,v|
|
33
|
+
if !VALID_SETTINGS.include?(k.to_sym)
|
34
|
+
raise CanvasStatsd::ConfigurationError, "Invalid key: #{k}"
|
35
|
+
end
|
36
|
+
validated[k.to_sym] = v
|
32
37
|
end
|
33
|
-
|
38
|
+
|
39
|
+
env_settings.merge(validated)
|
34
40
|
end
|
35
|
-
|
36
|
-
env_settings
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
raise CanvasStatsd::ConfigurationError, message
|
41
|
+
|
42
|
+
def env_settings(env=ENV)
|
43
|
+
config = {
|
44
|
+
host: env.fetch('CANVAS_STATSD_HOST', nil),
|
45
|
+
port: env.fetch('CANVAS_STATSD_PORT', nil),
|
46
|
+
namespace: env.fetch('CANVAS_STATSD_NAMESPACE', nil),
|
47
|
+
append_hostname: env.fetch('CANVAS_STATSD_APPEND_HOSTNAME', nil),
|
48
|
+
}
|
49
|
+
config.delete_if {|k,v| v.nil?}
|
50
|
+
convert_bool(config, :append_hostname)
|
51
|
+
config[:host] ? config : {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def convert_bool(hash, key)
|
55
|
+
value = hash[key]
|
56
|
+
return if value.nil?
|
57
|
+
unless ['true', 'True', 'false', 'False', true, false].include?(value)
|
58
|
+
message = "#{key} must be a boolean, or the string representation of a boolean, got: #{value}"
|
59
|
+
raise CanvasStatsd::ConfigurationError, message
|
60
|
+
end
|
61
|
+
hash[key] = ['true', 'True', true].include?(value)
|
57
62
|
end
|
58
|
-
hash[key] = ['true', 'True', true].include?(value)
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.track_default_metrics options={}
|
62
|
-
CanvasStatsd::DefaultTracking.track_default_metrics options
|
63
63
|
end
|
64
64
|
|
65
65
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CanvasStatsd
|
2
|
+
class BlockStat
|
3
|
+
|
4
|
+
attr_accessor :stats
|
5
|
+
attr_accessor :common_key
|
6
|
+
|
7
|
+
def initialize(common_key, statsd=CanvasStatsd::Statsd)
|
8
|
+
self.common_key = common_key
|
9
|
+
@statsd = statsd
|
10
|
+
@stats = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def subtract_exclusives(stats)
|
14
|
+
@exclusives ||= {}
|
15
|
+
stats.each do |(key, value)|
|
16
|
+
@exclusives[key] ||= 0.0
|
17
|
+
@exclusives[key] += value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def exclusive_stats
|
22
|
+
return nil unless @exclusives
|
23
|
+
stats.map { |key, value| [key, value - (@exclusives[key] || 0.0)] }.to_h
|
24
|
+
end
|
25
|
+
|
26
|
+
def report
|
27
|
+
if common_key
|
28
|
+
stats.each do |(key, value)|
|
29
|
+
@statsd.timing("#{common_key}.#{key}", value)
|
30
|
+
end
|
31
|
+
exclusive_stats&.each do |(key, value)|
|
32
|
+
@statsd.timing("#{common_key}.exclusive.#{key}", value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
module CanvasStatsd
|
4
|
+
class BlockTracking
|
5
|
+
class << self
|
6
|
+
attr_accessor :logger
|
7
|
+
|
8
|
+
def track(key, category: nil, statsd: CanvasStatsd::Statsd, only: nil)
|
9
|
+
cookies = if only
|
10
|
+
Array(only).map { |name| [name, Counter.counters[name].start] }
|
11
|
+
else
|
12
|
+
Counter.counters.map { |(name, counter)| [name, counter.start] }
|
13
|
+
end
|
14
|
+
block_stat = CanvasStatsd::BlockStat.new(key, statsd)
|
15
|
+
stack(category).push(block_stat) if category
|
16
|
+
|
17
|
+
result = nil
|
18
|
+
elapsed = Benchmark.realtime do
|
19
|
+
result = yield
|
20
|
+
end
|
21
|
+
# to be consistent with ActionPack, measure in milliseconds
|
22
|
+
elapsed *= 1000
|
23
|
+
|
24
|
+
block_stat.stats = cookies.map { |(name, cookie)| [name, Counter.counters[name].finalize_count(cookie)] }.to_h
|
25
|
+
block_stat.stats['total'] = elapsed
|
26
|
+
# we need to make sure to report exclusive timings, even if nobody called us re-entrantly
|
27
|
+
block_stat.subtract_exclusives({}) if category
|
28
|
+
block_stat.report
|
29
|
+
logger.log(block_stat, "STATSD #{key}") if logger
|
30
|
+
# -1 is ourselves; we want to subtract from the block above us
|
31
|
+
stack(category)[-2].subtract_exclusives(block_stat.stats) if category && stack(category)[-2]
|
32
|
+
|
33
|
+
result
|
34
|
+
ensure
|
35
|
+
stack(category).pop if category && stack(category) == block_stat
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def stack(category)
|
41
|
+
Thread.current[:stats_block_stack] ||= {}
|
42
|
+
Thread.current[:stats_block_stack][category] ||= []
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -1,5 +1,14 @@
|
|
1
1
|
module CanvasStatsd
|
2
2
|
class Counter
|
3
|
+
class << self
|
4
|
+
def counters
|
5
|
+
@counters ||= {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def register(counter)
|
9
|
+
counters[counter.key] = counter
|
10
|
+
end
|
11
|
+
end
|
3
12
|
|
4
13
|
attr_reader :key
|
5
14
|
attr_reader :blocked_names
|
@@ -7,24 +16,24 @@ module CanvasStatsd
|
|
7
16
|
def initialize(key, blocked_names=[])
|
8
17
|
@blocked_names = blocked_names
|
9
18
|
@key = key
|
19
|
+
@tls_key = "statsd.#{key}"
|
20
|
+
self.class.register(self)
|
10
21
|
end
|
11
22
|
|
12
23
|
def start
|
13
|
-
Thread.current[
|
24
|
+
Thread.current[@tls_key] ||= 0
|
14
25
|
end
|
15
26
|
|
16
27
|
def track(name)
|
17
|
-
Thread.current[
|
28
|
+
Thread.current[@tls_key] += 1 if Thread.current[@tls_key] && accepted_name?(name)
|
18
29
|
end
|
19
30
|
|
20
|
-
def finalize_count
|
21
|
-
|
22
|
-
Thread.current[key] = 0
|
23
|
-
final_count
|
31
|
+
def finalize_count(cookie)
|
32
|
+
Thread.current[@tls_key] - cookie
|
24
33
|
end
|
25
34
|
|
26
35
|
def count
|
27
|
-
Thread.current[
|
36
|
+
Thread.current[@tls_key]
|
28
37
|
end
|
29
38
|
|
30
39
|
def accepted_name?(name)
|
@@ -1,78 +1,42 @@
|
|
1
|
+
require "active_support"
|
2
|
+
|
1
3
|
module CanvasStatsd
|
2
4
|
class DefaultTracking
|
3
|
-
|
4
|
-
@ar_counter = CanvasStatsd::Counter.new('ar_counter')
|
5
|
-
@cache_read_counter = CanvasStatsd::Counter.new('cache_read_counter')
|
6
|
-
@sql_tracker = CanvasStatsd::SqlTracker.new(blocked_names: ['SCHEMA'])
|
7
|
-
|
8
|
-
def self.track_default_metrics(options={})
|
9
|
-
options = {sql: true, active_record: true, cache: true, logger: nil}.merge(options)
|
10
|
-
@logger = RequestLogger.new(options[:logger])
|
11
|
-
track_timing
|
12
|
-
track_sql if !!options[:sql]
|
13
|
-
track_active_record if !!options[:active_record]
|
14
|
-
track_cache if !!options[:cache]
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.subscribe type, &block
|
18
|
-
ActiveSupport::Notifications.subscribe type, &block
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def self.instrument_active_record_creation
|
24
|
-
::Aroi::Instrumentation.instrument_creation!
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.track_timing
|
28
|
-
subscribe(/start_processing\.action_controller/) {|*args| start_processing(*args)}
|
29
|
-
subscribe(/process_action\.action_controller/) {|*args| finalize_processing(*args)}
|
30
|
-
end
|
31
|
-
|
32
5
|
def self.track_sql
|
33
|
-
|
34
|
-
|
6
|
+
return if @sql_tracker
|
7
|
+
@sql_tracker = CanvasStatsd::SqlTracker.new(blocked_names: ['SCHEMA'])
|
8
|
+
ActiveSupport::Notifications.subscribe(/sql\.active_record/) {|*args| update_sql_count(*args)}
|
35
9
|
end
|
36
10
|
|
37
11
|
def self.track_active_record
|
38
|
-
|
39
|
-
|
40
|
-
|
12
|
+
return if @ar_counter
|
13
|
+
require 'aroi'
|
14
|
+
|
15
|
+
::Aroi::Instrumentation.instrument_creation!
|
16
|
+
@ar_counter = CanvasStatsd::Counter.new('active_record')
|
17
|
+
ActiveSupport::Notifications.subscribe(/instance\.active_record/) {|*args| update_active_record_count(*args)}
|
41
18
|
end
|
42
19
|
|
43
20
|
def self.track_cache
|
44
|
-
|
45
|
-
subscribe(/cache_read\.active_support/) {|*args| update_cache_read_count(*args)}
|
46
|
-
end
|
21
|
+
return if @cache_read_counter
|
47
22
|
|
48
|
-
|
49
|
-
|
50
|
-
@ar_counter.start
|
51
|
-
@cache_read_counter.start
|
23
|
+
@cache_read_counter = CanvasStatsd::Counter.new('cache.read')
|
24
|
+
ActiveSupport::Notifications.subscribe(/cache_read\.active_support/) {|*args| update_cache_read_count(*args)}
|
52
25
|
end
|
53
26
|
|
54
|
-
|
27
|
+
private
|
28
|
+
|
29
|
+
def self.update_sql_count(_name, _start, _finish, _id, payload)
|
55
30
|
@sql_tracker.track payload.fetch(:name), payload.fetch(:sql)
|
56
31
|
end
|
57
32
|
|
58
|
-
def self.update_active_record_count
|
33
|
+
def self.update_active_record_count(_name, _start, _finish, _id, payload)
|
59
34
|
@ar_counter.track payload.fetch(:name, '')
|
60
35
|
end
|
61
36
|
|
62
|
-
def self.update_cache_read_count
|
37
|
+
def self.update_cache_read_count(_name, _start, _finish, _id, _payload)
|
63
38
|
@cache_read_counter.track "read"
|
64
39
|
end
|
65
40
|
|
66
|
-
def self.finalize_processing *args
|
67
|
-
request_stat = CanvasStatsd::RequestStat.new(*args)
|
68
|
-
request_stat.ar_count = @ar_counter.finalize_count if @tracking_active_record
|
69
|
-
request_stat.sql_read_count = @sql_tracker.num_reads if @tracking_sql
|
70
|
-
request_stat.sql_write_count = @sql_tracker.num_writes if @tracking_sql
|
71
|
-
request_stat.sql_cache_count = @sql_tracker.num_caches if @tracking_sql
|
72
|
-
request_stat.cache_read_count = @cache_read_counter.finalize_count if @tracking_cache
|
73
|
-
request_stat.report
|
74
|
-
@logger.log(request_stat)
|
75
|
-
end
|
76
|
-
|
77
41
|
end
|
78
42
|
end
|
@@ -1,18 +1,6 @@
|
|
1
1
|
module CanvasStatsd
|
2
2
|
class RequestLogger
|
3
3
|
|
4
|
-
VALUES_MAP = {
|
5
|
-
total: :ms,
|
6
|
-
view: :view_runtime,
|
7
|
-
db: :db_runtime,
|
8
|
-
sql_read: :sql_read_count,
|
9
|
-
sql_write: :sql_write_count,
|
10
|
-
sql_cache: :sql_cache_count,
|
11
|
-
active_record: :ar_count,
|
12
|
-
cache_read: :cache_read_count,
|
13
|
-
}.freeze
|
14
|
-
|
15
|
-
|
16
4
|
def initialize(logger)
|
17
5
|
@logger = logger || CanvasStatsd::NullLogger.new
|
18
6
|
end
|
@@ -24,9 +12,11 @@ module CanvasStatsd
|
|
24
12
|
def build_log_message(request_stat, header=nil)
|
25
13
|
header ||= "STATSD"
|
26
14
|
message = "[#{header}]"
|
27
|
-
|
28
|
-
|
29
|
-
|
15
|
+
request_stat.stats.each do |(name, value)|
|
16
|
+
message += " (#{name.to_s.gsub('.', '_')}: #{"%.2f" % value})"
|
17
|
+
end
|
18
|
+
request_stat.exclusive_stats&.each do |(name, value)|
|
19
|
+
message += " (exclusive_#{name.to_s.gsub('.', '_')}: #{"%.2f" % value})"
|
30
20
|
end
|
31
21
|
message
|
32
22
|
end
|
@@ -1,33 +1,25 @@
|
|
1
1
|
module CanvasStatsd
|
2
|
-
class RequestStat
|
3
|
-
|
4
|
-
attr_accessor :sql_read_count
|
5
|
-
attr_accessor :sql_write_count
|
6
|
-
attr_accessor :sql_cache_count
|
7
|
-
attr_accessor :ar_count
|
8
|
-
attr_accessor :cache_read_count
|
9
|
-
|
2
|
+
class RequestStat < BlockStat
|
10
3
|
def initialize(name, start, finish, id, payload, statsd=CanvasStatsd::Statsd)
|
4
|
+
super(nil, statsd)
|
11
5
|
@name = name
|
12
6
|
@start = start
|
13
7
|
@finish = finish
|
14
8
|
@id = id
|
15
9
|
@payload = payload
|
16
|
-
|
10
|
+
end
|
11
|
+
|
12
|
+
def common_key
|
13
|
+
common_key = super
|
14
|
+
return common_key if common_key
|
15
|
+
self.common_key = "request.#{controller}.#{action}" if controller && action
|
17
16
|
end
|
18
17
|
|
19
18
|
def report
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@statsd.timing("#{common_key}.db", db_runtime) if db_runtime
|
25
|
-
@statsd.timing("#{common_key}.sql.read", sql_read_count) if sql_read_count
|
26
|
-
@statsd.timing("#{common_key}.sql.write", sql_write_count) if sql_write_count
|
27
|
-
@statsd.timing("#{common_key}.sql.cache", sql_cache_count) if sql_cache_count
|
28
|
-
@statsd.timing("#{common_key}.active_record", ar_count) if ar_count
|
29
|
-
@statsd.timing("#{common_key}.cache.read", cache_read_count) if cache_read_count
|
30
|
-
end
|
19
|
+
stats['total'] = total
|
20
|
+
stats['view'] = view_runtime if view_runtime
|
21
|
+
stats['db'] = db_runtime if db_runtime
|
22
|
+
super
|
31
23
|
end
|
32
24
|
|
33
25
|
def db_runtime
|
@@ -46,7 +38,7 @@ module CanvasStatsd
|
|
46
38
|
@payload.fetch(:params, {})['action']
|
47
39
|
end
|
48
40
|
|
49
|
-
def
|
41
|
+
def total
|
50
42
|
if (!@finish || !@start)
|
51
43
|
return 0
|
52
44
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CanvasStatsd
|
2
|
+
class RequestTracking
|
3
|
+
|
4
|
+
def self.enable(logger: nil)
|
5
|
+
@logger = RequestLogger.new(logger)
|
6
|
+
track_timing
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def self.track_timing
|
12
|
+
ActiveSupport::Notifications.subscribe(/start_processing\.action_controller/, &method(:start_processing))
|
13
|
+
ActiveSupport::Notifications.subscribe(/process_action\.action_controller/, &method(:finalize_processing))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.start_processing(*_args)
|
17
|
+
@cookies = Counter.counters.map { |(name, counter)| [name, counter.start] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.finalize_processing *args
|
21
|
+
request_stat = CanvasStatsd::RequestStat.new(*args)
|
22
|
+
request_stat.stats = @cookies.map { |(name, cookie)| [name, Counter.counters[name].finalize_count(cookie)] }.to_h
|
23
|
+
request_stat.report
|
24
|
+
@logger.log(request_stat)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -6,13 +6,13 @@ module CanvasStatsd
|
|
6
6
|
def initialize(opts=nil)
|
7
7
|
opts ||= {}
|
8
8
|
@blocked_names = opts.fetch(:blocked_names, [])
|
9
|
-
@read_counts = opts.fetch(:read_counter, CanvasStatsd::Counter.new('
|
10
|
-
@write_counts = opts.fetch(:write_counter, CanvasStatsd::Counter.new('
|
11
|
-
@cache_counts = opts.fetch(:cache_counter, CanvasStatsd::Counter.new('
|
9
|
+
@read_counts = opts.fetch(:read_counter, CanvasStatsd::Counter.new('sql.read'))
|
10
|
+
@write_counts = opts.fetch(:write_counter, CanvasStatsd::Counter.new('sql.write'))
|
11
|
+
@cache_counts = opts.fetch(:cache_counter, CanvasStatsd::Counter.new('sql.cache'))
|
12
12
|
end
|
13
13
|
|
14
14
|
def start
|
15
|
-
[read_counts, write_counts, cache_counts].
|
15
|
+
[read_counts, write_counts, cache_counts].map(&:start)
|
16
16
|
end
|
17
17
|
|
18
18
|
def track name, sql
|
@@ -27,16 +27,12 @@ module CanvasStatsd
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def num_caches
|
39
|
-
cache_counts.finalize_count
|
30
|
+
def finalize_counts(cookies)
|
31
|
+
[
|
32
|
+
read_counts.finalize_count(cookies[0]),
|
33
|
+
write_counts.finalize_count(cookies[1]),
|
34
|
+
cache_counts.finalize_count(cookies[2])
|
35
|
+
]
|
40
36
|
end
|
41
37
|
|
42
38
|
private
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CanvasStatsd::BlockStat do
|
4
|
+
it "track exclusives correctly" do
|
5
|
+
stat = CanvasStatsd::BlockStat.new("key")
|
6
|
+
stat.stats['total'] = 5.0
|
7
|
+
stat.subtract_exclusives("total" => 1.5)
|
8
|
+
stat.subtract_exclusives("total" => 2.1)
|
9
|
+
expect(stat.exclusive_stats).to eql("total" => 1.4)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CanvasStatsd::BlockTracking do
|
4
|
+
before(:all) do
|
5
|
+
CanvasStatsd::DefaultTracking.track_sql
|
6
|
+
end
|
7
|
+
|
8
|
+
it "works" do
|
9
|
+
statsd = double()
|
10
|
+
allow(statsd).to receive(:timing).with('mykey.total', anything)
|
11
|
+
expect(statsd).to receive(:timing).with("mykey.sql.read", 1)
|
12
|
+
|
13
|
+
CanvasStatsd::BlockTracking.track("mykey", statsd: statsd, only: 'sql.read') do
|
14
|
+
ActiveSupport::Notifications.instrument('sql.active_record', name: "LOAD", sql: "SELECT * FROM users") {}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "keeps track of exclusive stats too" do
|
19
|
+
statsd = double()
|
20
|
+
expect(statsd).to receive(:timing).with("mykey.sql.read", 2).ordered
|
21
|
+
expect(statsd).to receive(:timing).with('mykey.total', anything).ordered
|
22
|
+
expect(statsd).to receive(:timing).with("mykey.exclusive.sql.read", 2).ordered
|
23
|
+
expect(statsd).to receive(:timing).with('mykey.exclusive.total', anything).ordered
|
24
|
+
expect(statsd).to receive(:timing).with("mykey.sql.read", 3).ordered
|
25
|
+
expect(statsd).to receive(:timing).with('mykey.total', anything).ordered
|
26
|
+
expect(statsd).to receive(:timing).with("mykey.exclusive.sql.read", 1).ordered
|
27
|
+
expect(statsd).to receive(:timing).with('mykey.exclusive.total', anything).ordered
|
28
|
+
|
29
|
+
CanvasStatsd::BlockTracking.track("mykey", category: :nested, statsd: statsd, only: 'sql.read') do
|
30
|
+
ActiveSupport::Notifications.instrument('sql.active_record', name: "LOAD", sql: "SELECT * FROM users") {}
|
31
|
+
CanvasStatsd::BlockTracking.track("mykey", category: :nested, statsd: statsd, only: 'sql.read') do
|
32
|
+
ActiveSupport::Notifications.instrument('sql.active_record', name: "LOAD", sql: "SELECT * FROM users") {}
|
33
|
+
ActiveSupport::Notifications.instrument('sql.active_record', name: "LOAD", sql: "SELECT * FROM users") {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -22,41 +22,36 @@ describe CanvasStatsd::Counter do
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
describe "#start" do
|
26
|
-
it 'should reset count to zero' do
|
27
|
-
subject.start
|
28
|
-
expect(subject.count).to eq 0
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
25
|
describe "#track" do
|
33
26
|
it 'should increment when given allowed names' do
|
34
|
-
subject.start
|
27
|
+
cookie = subject.start
|
35
28
|
subject.track('bar')
|
36
29
|
subject.track('baz')
|
37
|
-
expect(subject.
|
30
|
+
expect(subject.finalize_count(cookie)).to eq 2
|
38
31
|
end
|
39
32
|
|
40
33
|
it 'should not increment when given a blocked name' do
|
41
|
-
subject.start
|
34
|
+
cookie = subject.start
|
42
35
|
subject.track('foo') #shouldn't count as foo is a blocked name
|
43
36
|
subject.track('name')
|
44
|
-
expect(subject.
|
37
|
+
expect(subject.finalize_count(cookie)).to eq 1
|
45
38
|
end
|
46
39
|
end
|
47
40
|
|
48
41
|
describe "#finalize_count" do
|
49
42
|
it 'should return the current count' do
|
50
|
-
subject.start
|
43
|
+
cookie = subject.start
|
51
44
|
subject.track('bar')
|
52
|
-
expect(subject.finalize_count).to eq 1
|
45
|
+
expect(subject.finalize_count(cookie)).to eq 1
|
53
46
|
end
|
54
47
|
|
55
|
-
it 'should
|
56
|
-
subject.start
|
48
|
+
it 'should not interfere with multiple people using the object' do
|
49
|
+
cookie1 = subject.start
|
50
|
+
subject.track('bar')
|
51
|
+
cookie2 = subject.start
|
57
52
|
subject.track('bar')
|
58
|
-
subject.finalize_count
|
59
|
-
expect(subject.
|
53
|
+
expect(subject.finalize_count(cookie1)).to eq 2
|
54
|
+
expect(subject.finalize_count(cookie2)).to eq 1
|
60
55
|
end
|
61
56
|
end
|
62
57
|
|
@@ -9,42 +9,55 @@ describe CanvasStatsd::RequestLogger do
|
|
9
9
|
end
|
10
10
|
it 'includes the supplied header' do
|
11
11
|
request_stat = double('request_stat')
|
12
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(nil)
|
13
|
+
allow(request_stat).to receive(:stats).and_return({})
|
12
14
|
results = @logger.build_log_message(request_stat, 'FOO_STATS')
|
13
15
|
expect(results).to eq("[FOO_STATS]")
|
14
16
|
end
|
15
17
|
it 'falls back to the default header' do
|
16
18
|
request_stat = double('request_stat')
|
19
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(nil)
|
20
|
+
allow(request_stat).to receive(:stats).and_return({})
|
17
21
|
results = @logger.build_log_message(request_stat)
|
18
22
|
expect(results).to eq("[STATSD]")
|
19
23
|
end
|
20
24
|
it 'includes stats that are available' do
|
21
25
|
request_stat = double('request_stat')
|
22
|
-
allow(request_stat).to receive(:
|
23
|
-
allow(request_stat).to receive(:
|
26
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(nil)
|
27
|
+
allow(request_stat).to receive(:stats).and_return(
|
28
|
+
"total" => 100.21,
|
29
|
+
"active.record" => 24)
|
24
30
|
results = @logger.build_log_message(request_stat)
|
25
31
|
expect(results).to eq("[STATSD] (total: 100.21) (active_record: 24.00)")
|
26
32
|
end
|
27
|
-
|
33
|
+
|
34
|
+
it 'includes exclusive_stats if there are any' do
|
28
35
|
request_stat = double('request_stat')
|
29
|
-
allow(request_stat).to receive(:
|
30
|
-
|
36
|
+
allow(request_stat).to receive(:stats).and_return(
|
37
|
+
"total" => 100.21,
|
38
|
+
"active.record" => 24)
|
39
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(
|
40
|
+
"total" => 54.32,
|
41
|
+
"active.record" => 1)
|
31
42
|
results = @logger.build_log_message(request_stat)
|
32
|
-
expect(results).to eq("[STATSD] (total: 100.
|
43
|
+
expect(results).to eq("[STATSD] (total: 100.21) (active_record: 24.00) (exclusive_total: 54.32) (exclusive_active_record: 1.00)")
|
33
44
|
end
|
34
45
|
|
35
46
|
describe 'decimal precision' do
|
36
47
|
it 'forces 2 decimal precision' do
|
37
48
|
request_stat = double('request_stat')
|
38
|
-
allow(request_stat).to receive(:
|
49
|
+
allow(request_stat).to receive(:stats).and_return(total: 72.1)
|
50
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(nil)
|
39
51
|
results = @logger.build_log_message(request_stat)
|
40
52
|
expect(results).to eq("[STATSD] (total: 72.10)")
|
41
53
|
end
|
42
54
|
it 'rounds values to 2 decimals' do
|
43
55
|
request_stat = double('request_stat')
|
44
|
-
allow(request_stat).to receive(:
|
56
|
+
allow(request_stat).to receive(:stats).and_return(total: 72.1382928)
|
57
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(nil)
|
45
58
|
results = @logger.build_log_message(request_stat)
|
46
59
|
expect(results).to eq("[STATSD] (total: 72.14)")
|
47
|
-
allow(request_stat).to receive(:
|
60
|
+
allow(request_stat).to receive(:stats).and_return(total: 72.1348209)
|
48
61
|
results = @logger.build_log_message(request_stat)
|
49
62
|
expect(results).to eq("[STATSD] (total: 72.13)")
|
50
63
|
end
|
@@ -58,6 +71,8 @@ describe CanvasStatsd::RequestLogger do
|
|
58
71
|
logger = CanvasStatsd::RequestLogger.new(std_out_logger)
|
59
72
|
expect(std_out_logger).to receive(:info)
|
60
73
|
request_stat = double('request_stat')
|
74
|
+
allow(request_stat).to receive(:stats).and_return({})
|
75
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(nil)
|
61
76
|
logger.log(request_stat)
|
62
77
|
end
|
63
78
|
it 'sends info method with build_log_message output if logger exists' do
|
@@ -65,7 +80,8 @@ describe CanvasStatsd::RequestLogger do
|
|
65
80
|
logger = CanvasStatsd::RequestLogger.new(std_out_logger)
|
66
81
|
expect(std_out_logger).to receive(:info).with("[DEFAULT_METRICS] (total: 100.20)")
|
67
82
|
request_stat = double('request_stat')
|
68
|
-
allow(request_stat).to receive(:
|
83
|
+
allow(request_stat).to receive(:stats).and_return(total: 100.2)
|
84
|
+
allow(request_stat).to receive(:exclusive_stats).and_return(nil)
|
69
85
|
logger.log(request_stat, "DEFAULT_METRICS")
|
70
86
|
end
|
71
87
|
end
|
@@ -68,18 +68,18 @@ describe CanvasStatsd::RequestStat do
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
describe '#
|
71
|
+
describe '#total' do
|
72
72
|
it 'correctly calcuates milliseconds from start, finish' do
|
73
73
|
rs = create_subject({params: {}})
|
74
74
|
# start and finish are in seconds
|
75
|
-
expect(rs.
|
75
|
+
expect(rs.total).to eq 1000
|
76
76
|
end
|
77
77
|
|
78
78
|
it 'defaults to zero if either start or finish are nil' do
|
79
79
|
rs = CanvasStatsd::RequestStat.new('name', nil, 1001, 1111, {params: {}})
|
80
|
-
expect(rs.
|
80
|
+
expect(rs.total).to eq 0
|
81
81
|
rs = CanvasStatsd::RequestStat.new('name', 1, nil, 1111, {params: {}})
|
82
|
-
expect(rs.
|
82
|
+
expect(rs.total).to eq 0
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -142,7 +142,7 @@ describe CanvasStatsd::RequestStat do
|
|
142
142
|
}
|
143
143
|
}
|
144
144
|
@rs = create_subject(payload, @statsd)
|
145
|
-
@rs.
|
145
|
+
@rs.stats['cache.read'] = 25
|
146
146
|
expect(@statsd).to receive(:timing).with('request.foo.index.cache.read', 25)
|
147
147
|
end
|
148
148
|
|
@@ -155,26 +155,11 @@ describe CanvasStatsd::RequestStat do
|
|
155
155
|
end
|
156
156
|
|
157
157
|
it 'sends sql_read_count when present' do
|
158
|
-
@rs.
|
158
|
+
@rs.stats['sql.read'] = 10
|
159
159
|
allow(@statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
160
160
|
expect(@statsd).to receive(:timing).with('request.foo.index.sql.read', 10)
|
161
161
|
@rs.report
|
162
162
|
end
|
163
|
-
|
164
|
-
it 'sends sql_read_count when present' do
|
165
|
-
@rs.sql_write_count = 3
|
166
|
-
allow(@statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
167
|
-
expect(@statsd).to receive(:timing).with('request.foo.index.sql.write', 3)
|
168
|
-
@rs.report
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'sends sql_cache_count when present' do
|
172
|
-
@rs.sql_cache_count = 1
|
173
|
-
allow(@statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
174
|
-
expect(@statsd).to receive(:timing).with('request.foo.index.sql.cache', 1)
|
175
|
-
@rs.report
|
176
|
-
end
|
177
|
-
|
178
163
|
end
|
179
164
|
|
180
165
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CanvasStatsd::RequestTracking do
|
4
|
+
|
5
|
+
describe '#enable' do
|
6
|
+
it 'should delegate log messages to the optional logger' do
|
7
|
+
log_double = double()
|
8
|
+
expect(log_double).to receive(:info)
|
9
|
+
CanvasStatsd::RequestTracking.enable logger: log_double
|
10
|
+
CanvasStatsd::RequestTracking.start_processing
|
11
|
+
CanvasStatsd::RequestTracking.finalize_processing('name', 1000, 10001, 1234, {})
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -8,48 +8,67 @@ module CanvasStatsd
|
|
8
8
|
subject = SqlTracker.new
|
9
9
|
subject.start
|
10
10
|
subject.track 'CACHE', 'SELECT * FROM some_table'
|
11
|
-
subject.start
|
12
|
-
expect(subject.
|
13
|
-
expect(subject.num_reads).to eq(0)
|
14
|
-
expect(subject.num_writes).to eq(0)
|
11
|
+
cookies = subject.start
|
12
|
+
expect(subject.finalize_counts(cookies)).to eq([0, 0, 0])
|
15
13
|
end
|
16
14
|
end
|
17
15
|
|
18
16
|
describe '#track' do
|
19
17
|
before :each do
|
20
18
|
@subject = SqlTracker.new
|
21
|
-
@subject.start
|
19
|
+
@cookies = @subject.start
|
20
|
+
end
|
21
|
+
|
22
|
+
def finish
|
23
|
+
if @num_reads.nil?
|
24
|
+
@num_reads, @num_writes, @num_caches = @subject.finalize_counts(@cookies)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def num_reads
|
29
|
+
finish
|
30
|
+
@num_reads
|
31
|
+
end
|
32
|
+
|
33
|
+
def num_writes
|
34
|
+
finish
|
35
|
+
@num_writes
|
36
|
+
end
|
37
|
+
|
38
|
+
def num_caches
|
39
|
+
finish
|
40
|
+
@num_caches
|
22
41
|
end
|
23
42
|
|
24
43
|
it 'considers CACHE above all' do
|
25
44
|
@subject.track 'CACHE', 'SELECT * FROM some_table'
|
26
|
-
expect(
|
27
|
-
expect(
|
45
|
+
expect(num_caches).to eq(1)
|
46
|
+
expect(num_reads).to eq(0)
|
28
47
|
end
|
29
48
|
|
30
49
|
it 'marks as read when select is in the first 15 chars of the sql' do
|
31
50
|
@subject.track 'LOAD', ' SELECT "context_external_tools".* FROM'
|
32
|
-
expect(
|
33
|
-
expect(
|
51
|
+
expect(num_reads).to eq(1)
|
52
|
+
expect(num_writes).to eq(0)
|
34
53
|
end
|
35
54
|
|
36
55
|
it 'marks as read with no select, but a LOAD name' do
|
37
56
|
@subject.track 'LOAD', 'WITH RECURSIVE t AS'
|
38
|
-
expect(
|
39
|
-
expect(
|
57
|
+
expect(num_reads).to eq(1)
|
58
|
+
expect(num_writes).to eq(0)
|
40
59
|
end
|
41
60
|
|
42
61
|
it 'doesnt track names set as blocked' do
|
43
62
|
tracker = SqlTracker.new(blocked_names: ['SCHEMA'])
|
44
|
-
tracker.start
|
63
|
+
cookies = tracker.start
|
45
64
|
tracker.track 'SCHEMA', 'SELECT * FROM some_table'
|
46
|
-
expect(tracker.
|
65
|
+
expect(tracker.finalize_counts(cookies)[0]).to eq(0)
|
47
66
|
end
|
48
67
|
|
49
68
|
it 'doesnt track nil names or sql values' do
|
50
69
|
@subject.track nil, 'SELECT *'
|
51
70
|
@subject.track 'CACHE', nil
|
52
|
-
expect(
|
71
|
+
expect(num_reads).to eq(0)
|
53
72
|
end
|
54
73
|
|
55
74
|
it 'passes full sql to counter.track calls for reads' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: canvas_statsd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Cloward
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-01-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: statsd-ruby
|
@@ -53,6 +53,20 @@ dependencies:
|
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '1.5'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: byebug
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: rake
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,19 +104,24 @@ extensions: []
|
|
90
104
|
extra_rdoc_files: []
|
91
105
|
files:
|
92
106
|
- lib/canvas_statsd.rb
|
107
|
+
- lib/canvas_statsd/block_stat.rb
|
108
|
+
- lib/canvas_statsd/block_tracking.rb
|
93
109
|
- lib/canvas_statsd/counter.rb
|
94
110
|
- lib/canvas_statsd/default_tracking.rb
|
95
111
|
- lib/canvas_statsd/null_logger.rb
|
96
112
|
- lib/canvas_statsd/request_logger.rb
|
97
113
|
- lib/canvas_statsd/request_stat.rb
|
114
|
+
- lib/canvas_statsd/request_tracking.rb
|
98
115
|
- lib/canvas_statsd/sql_tracker.rb
|
99
116
|
- lib/canvas_statsd/statsd.rb
|
117
|
+
- spec/canvas_statsd/block_stat_spec.rb
|
118
|
+
- spec/canvas_statsd/block_tracking_spec.rb
|
100
119
|
- spec/canvas_statsd/canvas_statsd_spec.rb
|
101
120
|
- spec/canvas_statsd/counter_spec.rb
|
102
|
-
- spec/canvas_statsd/default_tracking_spec.rb
|
103
121
|
- spec/canvas_statsd/null_logger_spec.rb
|
104
122
|
- spec/canvas_statsd/request_logger_spec.rb
|
105
123
|
- spec/canvas_statsd/request_stat_spec.rb
|
124
|
+
- spec/canvas_statsd/request_tracking_spec.rb
|
106
125
|
- spec/canvas_statsd/sql_tracker_spec.rb
|
107
126
|
- spec/canvas_statsd/statsd_spec.rb
|
108
127
|
- spec/spec_helper.rb
|
@@ -128,17 +147,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
147
|
version: '0'
|
129
148
|
requirements: []
|
130
149
|
rubyforge_project:
|
131
|
-
rubygems_version: 2.6.
|
150
|
+
rubygems_version: 2.6.10
|
132
151
|
signing_key:
|
133
152
|
specification_version: 4
|
134
153
|
summary: Statsd for Canvas
|
135
154
|
test_files:
|
155
|
+
- spec/canvas_statsd/block_stat_spec.rb
|
156
|
+
- spec/canvas_statsd/block_tracking_spec.rb
|
136
157
|
- spec/canvas_statsd/canvas_statsd_spec.rb
|
137
158
|
- spec/canvas_statsd/counter_spec.rb
|
138
|
-
- spec/canvas_statsd/default_tracking_spec.rb
|
139
159
|
- spec/canvas_statsd/null_logger_spec.rb
|
140
160
|
- spec/canvas_statsd/request_logger_spec.rb
|
141
161
|
- spec/canvas_statsd/request_stat_spec.rb
|
162
|
+
- spec/canvas_statsd/request_tracking_spec.rb
|
142
163
|
- spec/canvas_statsd/sql_tracker_spec.rb
|
143
164
|
- spec/canvas_statsd/statsd_spec.rb
|
144
165
|
- spec/spec_helper.rb
|
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'aroi' # ensure aroi is loaded (its only conditionally loaded by default)
|
3
|
-
|
4
|
-
describe CanvasStatsd::DefaultTracking do
|
5
|
-
|
6
|
-
describe '#track_default_metrics' do
|
7
|
-
it 'should track timing, sql, and active_record by default' do
|
8
|
-
expect(CanvasStatsd::DefaultTracking).to receive(:track_timing)
|
9
|
-
expect(CanvasStatsd::DefaultTracking).to receive(:track_sql)
|
10
|
-
expect(CanvasStatsd::DefaultTracking).to receive(:track_active_record)
|
11
|
-
CanvasStatsd::DefaultTracking.track_default_metrics
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'should not track sql when sql: false option' do
|
15
|
-
expect(CanvasStatsd::DefaultTracking).not_to receive(:track_sql)
|
16
|
-
CanvasStatsd::DefaultTracking.track_default_metrics sql: false
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'should not track active_record when active_record: false option' do
|
20
|
-
expect(CanvasStatsd::DefaultTracking).not_to receive(:track_active_record)
|
21
|
-
CanvasStatsd::DefaultTracking.track_default_metrics active_record: false
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'should not track cache when cache: false option' do
|
25
|
-
expect(CanvasStatsd::DefaultTracking).not_to receive(:track_cache)
|
26
|
-
CanvasStatsd::DefaultTracking.track_default_metrics cache: false
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'should delegate log messages to the optional logger' do
|
30
|
-
log_double = double()
|
31
|
-
expect(log_double).to receive(:info)
|
32
|
-
CanvasStatsd::DefaultTracking.track_default_metrics logger: log_double
|
33
|
-
CanvasStatsd::DefaultTracking.finalize_processing('name', 1000, 10001, 1234, {})
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe '#track_active_record' do
|
38
|
-
it 'should turn on active record instrumentation' do
|
39
|
-
expect(CanvasStatsd::DefaultTracking).to receive(:instrument_active_record_creation)
|
40
|
-
CanvasStatsd::DefaultTracking.send(:track_active_record)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe '#subscribe' do
|
45
|
-
it 'should subscribe via ActiveSupport::Notifications' do
|
46
|
-
target = double()
|
47
|
-
CanvasStatsd::DefaultTracking.subscribe(/test\.notification/) {|*args| target.callback(*args)}
|
48
|
-
expect(target).to receive(:callback)
|
49
|
-
ActiveSupport::Notifications.instrument('test.notification') {}
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|