canvas_statsd 1.0.8 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|