canvas_statsd 1.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 +7 -0
- data/lib/canvas_statsd.rb +37 -0
- data/lib/canvas_statsd/counter.rb +35 -0
- data/lib/canvas_statsd/default_tracking.rb +63 -0
- data/lib/canvas_statsd/request_stat.rb +55 -0
- data/lib/canvas_statsd/sql_tracker.rb +54 -0
- data/lib/canvas_statsd/statsd.rb +104 -0
- data/spec/canvas_statsd/canvas_statsd_spec.rb +125 -0
- data/spec/canvas_statsd/counter_spec.rb +63 -0
- data/spec/canvas_statsd/default_tracking_spec.rb +41 -0
- data/spec/canvas_statsd/request_stat_spec.rb +170 -0
- data/spec/canvas_statsd/sql_tracker_spec.rb +77 -0
- data/spec/canvas_statsd/statsd_spec.rb +74 -0
- data/spec/spec_helper.rb +9 -0
- data/test.sh +16 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 13ba02ddc541f1ef3eebb7aee35f60022eea1e24
|
4
|
+
data.tar.gz: 55456a369e6654ca2320832c9478971644107774
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b5af0a665e04b9b1979c65fbbb65be875e5e5fa12aec4fbf7dfa0f4f4cc655ea8514f20d88369078204450efc5cf247a6025cf7275be63f05709d5839c13113
|
7
|
+
data.tar.gz: dd77f76a3c1be35b3c0e9af4cee153e399c70d98331328c31d4a0557191578dd3d1d35d698e3adbaabd95b9418f0cb619baf1677b28affb81e0fb1582443affb
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'statsd'
|
2
|
+
require "aroi"
|
3
|
+
|
4
|
+
module CanvasStatsd
|
5
|
+
|
6
|
+
require "canvas_statsd/statsd"
|
7
|
+
require "canvas_statsd/request_stat"
|
8
|
+
require "canvas_statsd/counter"
|
9
|
+
require "canvas_statsd/sql_tracker"
|
10
|
+
require "canvas_statsd/default_tracking"
|
11
|
+
|
12
|
+
def self.settings
|
13
|
+
@settings || env_settings
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.settings=(value)
|
17
|
+
@settings = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.env_settings(env=ENV)
|
21
|
+
config = {
|
22
|
+
host: env.fetch('CANVAS_STATSD_HOST', nil),
|
23
|
+
port: env.fetch('CANVAS_STATSD_PORT', nil),
|
24
|
+
namespace: env.fetch('CANVAS_STATSD_NAMESPACE', nil),
|
25
|
+
append_hostname: env.fetch('CANVAS_STATSD_APPEND_HOSTNAME', nil),
|
26
|
+
}
|
27
|
+
config.delete_if {|k,v| v.nil?}
|
28
|
+
config[:append_hostname] = false if config[:append_hostname] == 'false';
|
29
|
+
config[:append_hostname] = true if config[:append_hostname] == 'true';
|
30
|
+
config[:host] ? config : {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.track_default_metrics options={}
|
34
|
+
CanvasStatsd::DefaultTracking.track_default_metrics options
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module CanvasStatsd
|
2
|
+
class Counter
|
3
|
+
|
4
|
+
attr_reader :key
|
5
|
+
attr_reader :blocked_names
|
6
|
+
|
7
|
+
def initialize(key, blocked_names=[])
|
8
|
+
@blocked_names = blocked_names
|
9
|
+
@key = key
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
Thread.current[key] = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def track(name)
|
17
|
+
Thread.current[key] += 1 if Thread.current[key] && accepted_name?(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def finalize_count
|
21
|
+
final_count = count
|
22
|
+
Thread.current[key] = 0
|
23
|
+
final_count
|
24
|
+
end
|
25
|
+
|
26
|
+
def count
|
27
|
+
Thread.current[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def accepted_name?(name)
|
31
|
+
!blocked_names.include?(name)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module CanvasStatsd
|
2
|
+
class DefaultTracking
|
3
|
+
|
4
|
+
@ar_counter = CanvasStatsd::Counter.new('ar_counter')
|
5
|
+
@sql_tracker = CanvasStatsd::SqlTracker.new(blocked_names: ['SCHEMA'])
|
6
|
+
|
7
|
+
def self.track_default_metrics(options={})
|
8
|
+
options = {sql: true, active_record: true}.merge(options)
|
9
|
+
track_timing
|
10
|
+
track_sql if !!options[:sql]
|
11
|
+
track_active_record if !!options[:active_record]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.subscribe type, &block
|
15
|
+
ActiveSupport::Notifications.subscribe type, &block
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.instrument_active_record_creation
|
21
|
+
::Aroi::Instrumentation.instrument_creation!
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.track_timing
|
25
|
+
subscribe(/start_processing.action_controller/) {|*args| start_processing(*args)}
|
26
|
+
subscribe(/process_action.action_controller/) {|*args| finalize_processing(*args)}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.track_sql
|
30
|
+
@tracking_sql = true
|
31
|
+
subscribe(/sql.active_record/) {|*args| update_sql_count(*args)}
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.track_active_record
|
35
|
+
instrument_active_record_creation
|
36
|
+
@tracking_active_record = true
|
37
|
+
subscribe(/instance.active_record/) {|*args| update_active_record_count(*args)}
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.start_processing *args
|
41
|
+
@sql_tracker.start
|
42
|
+
@ar_counter.start
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.update_sql_count name, start, finish, id, payload
|
46
|
+
@sql_tracker.track payload.fetch(:name), payload.fetch(:sql)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.update_active_record_count name, start, finish, id, payload
|
50
|
+
@ar_counter.track payload.fetch(:name, '')
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.finalize_processing *args
|
54
|
+
request_stat = CanvasStatsd::RequestStat.new(*args)
|
55
|
+
request_stat.ar_count = @ar_counter.finalize_count if @tracking_active_record
|
56
|
+
request_stat.sql_read_count = @sql_tracker.num_reads if @tracking_sql
|
57
|
+
request_stat.sql_write_count = @sql_tracker.num_writes if @tracking_sql
|
58
|
+
request_stat.sql_cache_count = @sql_tracker.num_caches if @tracking_sql
|
59
|
+
request_stat.report
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,55 @@
|
|
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
|
+
|
9
|
+
def initialize(name, start, finish, id, payload, statsd=CanvasStatsd::Statsd)
|
10
|
+
@name = name
|
11
|
+
@start = start
|
12
|
+
@finish = finish
|
13
|
+
@id = id
|
14
|
+
@payload = payload
|
15
|
+
@statsd = statsd
|
16
|
+
end
|
17
|
+
|
18
|
+
def report
|
19
|
+
if controller && action
|
20
|
+
common_key = "request.#{controller}.#{action}"
|
21
|
+
@statsd.timing("#{common_key}.total", ms)
|
22
|
+
@statsd.timing("#{common_key}.view", view_runtime) if view_runtime
|
23
|
+
@statsd.timing("#{common_key}.db", db_runtime) if db_runtime
|
24
|
+
@statsd.timing("#{common_key}.sql.read", sql_read_count) if sql_read_count
|
25
|
+
@statsd.timing("#{common_key}.sql.write", sql_write_count) if sql_write_count
|
26
|
+
@statsd.timing("#{common_key}.sql.cache", sql_cache_count) if sql_cache_count
|
27
|
+
@statsd.timing("#{common_key}.active_record", ar_count) if ar_count
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def db_runtime
|
32
|
+
@payload.fetch(:db_runtime, nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
def view_runtime
|
36
|
+
@payload.fetch(:view_runtime, nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def controller
|
40
|
+
@payload.fetch(:params, {})['controller']
|
41
|
+
end
|
42
|
+
|
43
|
+
def action
|
44
|
+
@payload.fetch(:params, {})['action']
|
45
|
+
end
|
46
|
+
|
47
|
+
def ms
|
48
|
+
if (!@finish || !@start)
|
49
|
+
return 0
|
50
|
+
end
|
51
|
+
(@finish - @start) * 1000
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module CanvasStatsd
|
2
|
+
class SqlTracker
|
3
|
+
|
4
|
+
attr_reader :blocked_names, :read_counts, :write_counts, :cache_counts
|
5
|
+
|
6
|
+
def initialize(opts=nil)
|
7
|
+
opts ||= {}
|
8
|
+
@blocked_names = opts.fetch(:blocked_names, [])
|
9
|
+
@read_counts = opts.fetch(:read_counter, CanvasStatsd::Counter.new('sql_read_counter'))
|
10
|
+
@write_counts = opts.fetch(:write_counter, CanvasStatsd::Counter.new('sql_write_counter'))
|
11
|
+
@cache_counts = opts.fetch(:cache_counter, CanvasStatsd::Counter.new('sql_cache_counter'))
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
[read_counts, write_counts, cache_counts].each(&:start)
|
16
|
+
end
|
17
|
+
|
18
|
+
def track name, sql
|
19
|
+
return unless sql && accepted_name?(name)
|
20
|
+
|
21
|
+
if name.match(/CACHE/)
|
22
|
+
cache_counts.track name
|
23
|
+
elsif truncate(sql).match(/SELECT/) || name.match(/LOAD/)
|
24
|
+
read_counts.track(sql)
|
25
|
+
else
|
26
|
+
write_counts.track(sql)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def num_reads
|
31
|
+
read_counts.finalize_count
|
32
|
+
end
|
33
|
+
|
34
|
+
def num_writes
|
35
|
+
write_counts.finalize_count
|
36
|
+
end
|
37
|
+
|
38
|
+
def num_caches
|
39
|
+
cache_counts.finalize_count
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def accepted_name?(name)
|
45
|
+
!!(name && !blocked_names.include?(name))
|
46
|
+
end
|
47
|
+
|
48
|
+
def truncate(sql, length=15)
|
49
|
+
sql ||= ''
|
50
|
+
sql.strip[0..length]
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2012 Instructure, Inc.
|
3
|
+
#
|
4
|
+
# This file is part of Canvas.
|
5
|
+
#
|
6
|
+
# Canvas is free software: you can redistribute it and/or modify it under
|
7
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
8
|
+
# Software Foundation, version 3 of the License.
|
9
|
+
#
|
10
|
+
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
11
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
12
|
+
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
13
|
+
# details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License along
|
16
|
+
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
# Proxy class to communicate messages to statsd
|
20
|
+
# Available statsd messages are described in:
|
21
|
+
# https://github.com/etsy/statsd/blob/master/README.md
|
22
|
+
# https://github.com/reinh/statsd/blob/master/lib/statsd.rb
|
23
|
+
#
|
24
|
+
# So for instance:
|
25
|
+
# ms = Benchmark.ms { ..code.. }
|
26
|
+
# CanvasStatsd::Statsd.timing("my_stat", ms)
|
27
|
+
#
|
28
|
+
# Configured in config/statsd.yml, see config/statsd.yml.example
|
29
|
+
# At least a host needs to be defined for the environment, all other config is optional
|
30
|
+
#
|
31
|
+
# If a namespace is defined in statsd.yml, it'll be prepended to the stat name.
|
32
|
+
# The hostname of the server will be appended to the stat name, unless `append_hostname: false` is specified in the config.
|
33
|
+
# So if the namespace is "canvas" and the hostname is "app01", the final stat name of "my_stat" would be "stats.canvas.my_stat.app01"
|
34
|
+
# (assuming the default statsd/graphite configuration)
|
35
|
+
#
|
36
|
+
# If statsd isn't configured and enabled, then calls to CanvasStatsd::Statsd.* will do nothing and return nil
|
37
|
+
|
38
|
+
module CanvasStatsd
|
39
|
+
module Statsd
|
40
|
+
# replace "." in key names with another character to avoid creating spurious sub-folders in graphite
|
41
|
+
def self.escape(str, replacement = '_')
|
42
|
+
str.gsub('.', replacement)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.hostname
|
46
|
+
@hostname ||= Socket.gethostname.split(".").first
|
47
|
+
end
|
48
|
+
|
49
|
+
%w(increment decrement count gauge timing).each do |method|
|
50
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
51
|
+
def self.#{method}(stat, *args)
|
52
|
+
if self.instance
|
53
|
+
if Array === stat
|
54
|
+
stat.each do |st|
|
55
|
+
self.#{method}(st, *args)
|
56
|
+
end
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
if self.append_hostname?
|
61
|
+
stat_name = "\#{stat}.\#{hostname}"
|
62
|
+
else
|
63
|
+
stat_name = stat.to_s
|
64
|
+
end
|
65
|
+
self.instance.#{method}(stat_name, *args)
|
66
|
+
else
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
RUBY
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.time(stat, sample_rate=1)
|
74
|
+
start = Time.now
|
75
|
+
result = yield
|
76
|
+
self.timing(stat, ((Time.now - start) * 1000).round, sample_rate)
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.instance
|
81
|
+
if !defined?(@statsd)
|
82
|
+
statsd_settings = CanvasStatsd.settings
|
83
|
+
|
84
|
+
if statsd_settings && statsd_settings[:host]
|
85
|
+
@statsd = ::Statsd.new(statsd_settings[:host])
|
86
|
+
@statsd.port = statsd_settings[:port] if statsd_settings[:port]
|
87
|
+
@statsd.namespace = statsd_settings[:namespace] if statsd_settings[:namespace]
|
88
|
+
@append_hostname = !statsd_settings.key?(:append_hostname) || !!statsd_settings[:append_hostname]
|
89
|
+
else
|
90
|
+
@statsd = nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
@statsd
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.append_hostname?
|
97
|
+
@append_hostname
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.reset_instance
|
101
|
+
remove_instance_variable(:@statsd) if defined?(@statsd)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2014 Instructure, Inc.
|
3
|
+
#
|
4
|
+
# This file is part of Canvas.
|
5
|
+
#
|
6
|
+
# Canvas is free software: you can redistribute it and/or modify it under
|
7
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
8
|
+
# Software Foundation, version 3 of the License.
|
9
|
+
#
|
10
|
+
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
11
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
12
|
+
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
13
|
+
# details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License along
|
16
|
+
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'spec_helper'
|
20
|
+
|
21
|
+
describe CanvasStatsd do
|
22
|
+
before(:each) do
|
23
|
+
CanvasStatsd.settings = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
after(:each) do
|
27
|
+
[
|
28
|
+
'CANVAS_STATSD_HOST',
|
29
|
+
'CANVAS_STATSD_NAMESPACE',
|
30
|
+
'CANVAS_STATSD_PORT',
|
31
|
+
'CANVAS_STATSD_APPEND_HOST_NAME',
|
32
|
+
].each {|k| ENV.delete k}
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ".settings" do
|
36
|
+
it "have default settings" do
|
37
|
+
expect(CanvasStatsd.settings).to eq({})
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can be assigned a new value" do
|
41
|
+
settings = {foo: 'bar', baz: 'apple'}
|
42
|
+
CanvasStatsd.settings = settings
|
43
|
+
|
44
|
+
expect(CanvasStatsd.settings).to eq settings
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'pulls from ENV if not already set' do
|
48
|
+
ENV['CANVAS_STATSD_HOST'] = 'statsd.example.org'
|
49
|
+
ENV['CANVAS_STATSD_NAMESPACE'] = 'canvas'
|
50
|
+
|
51
|
+
expected = {
|
52
|
+
host: 'statsd.example.org',
|
53
|
+
namespace: 'canvas',
|
54
|
+
}
|
55
|
+
expect(CanvasStatsd.settings).to eq(expected)
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'configured settings take precedence over ENV settings' do
|
60
|
+
ENV['CANVAS_STATSD_HOST'] = 'statsd.example.org'
|
61
|
+
ENV['CANVAS_STATSD_NAMESPACE'] = 'canvas'
|
62
|
+
|
63
|
+
settings = {foo: 'bar', baz: 'apple'}
|
64
|
+
CanvasStatsd.settings = settings
|
65
|
+
expect(CanvasStatsd.settings).to eq settings
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ".env_settings" do
|
70
|
+
it 'returns empty hash when no CANVAS_STATSD_HOST found' do
|
71
|
+
env = {
|
72
|
+
'CANVAS_STATSD_NAMESPACE' => 'canvas'
|
73
|
+
}
|
74
|
+
expect(CanvasStatsd.env_settings(env)).to eq({})
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'builds settings hash from environment vars' do
|
78
|
+
env = {
|
79
|
+
'CANVAS_STATSD_HOST' => 'statsd.example.org',
|
80
|
+
'CANVAS_STATSD_NAMESPACE' => 'canvas',
|
81
|
+
}
|
82
|
+
expected = {
|
83
|
+
host: 'statsd.example.org',
|
84
|
+
namespace: 'canvas',
|
85
|
+
}
|
86
|
+
expect(CanvasStatsd.env_settings(env)).to eq(expected)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'uses ENV if env argument hash not passed' do
|
90
|
+
ENV['CANVAS_STATSD_HOST'] = 'statsd.example.org'
|
91
|
+
ENV['CANVAS_STATSD_NAMESPACE'] = 'canvas'
|
92
|
+
|
93
|
+
expected = {
|
94
|
+
host: 'statsd.example.org',
|
95
|
+
namespace: 'canvas',
|
96
|
+
}
|
97
|
+
expect(CanvasStatsd.env_settings).to eq(expected)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'converts env append_hostname "false" to boolean' do
|
101
|
+
env = {
|
102
|
+
'CANVAS_STATSD_HOST' => 'statsd.example.org',
|
103
|
+
'CANVAS_STATSD_APPEND_HOSTNAME' => 'false',
|
104
|
+
}
|
105
|
+
expected = {
|
106
|
+
host: 'statsd.example.org',
|
107
|
+
append_hostname: false,
|
108
|
+
}
|
109
|
+
expect(CanvasStatsd.env_settings(env)).to eq(expected)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'converts env append_hostname "true" to boolean' do
|
113
|
+
env = {
|
114
|
+
'CANVAS_STATSD_HOST' => 'statsd.example.org',
|
115
|
+
'CANVAS_STATSD_APPEND_HOSTNAME' => 'true',
|
116
|
+
}
|
117
|
+
expected = {
|
118
|
+
host: 'statsd.example.org',
|
119
|
+
append_hostname: true,
|
120
|
+
}
|
121
|
+
expect(CanvasStatsd.env_settings(env)).to eq(expected)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CanvasStatsd::Counter do
|
4
|
+
|
5
|
+
let(:subject) { CanvasStatsd::Counter.new('test', ['foo']) }
|
6
|
+
|
7
|
+
describe "#accepted_name?" do
|
8
|
+
it 'should return true for names not in blocked_names' do
|
9
|
+
expect(subject.accepted_name?('bar')).to eq true
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should return false for names in blocked_names' do
|
13
|
+
expect(subject.accepted_name?('foo')).to eq false
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should return true for empty string names' do
|
17
|
+
expect(subject.accepted_name?('')).to eq true
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should return true for empty nil names' do
|
21
|
+
expect(subject.accepted_name?(nil)).to eq true
|
22
|
+
end
|
23
|
+
end
|
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
|
+
describe "#track" do
|
33
|
+
it 'should increment when given allowed names' do
|
34
|
+
subject.start
|
35
|
+
subject.track('bar')
|
36
|
+
subject.track('baz')
|
37
|
+
expect(subject.count).to eq 2
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should not increment when given a blocked name' do
|
41
|
+
subject.start
|
42
|
+
subject.track('foo') #shouldn't count as foo is a blocked name
|
43
|
+
subject.track('name')
|
44
|
+
expect(subject.count).to eq 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#finalize_count" do
|
49
|
+
it 'should return the current count' do
|
50
|
+
subject.start
|
51
|
+
subject.track('bar')
|
52
|
+
expect(subject.finalize_count).to eq 1
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should reset the current count to 0' do
|
56
|
+
subject.start
|
57
|
+
subject.track('bar')
|
58
|
+
subject.finalize_count
|
59
|
+
expect(subject.count).to eq 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CanvasStatsd::DefaultTracking do
|
4
|
+
|
5
|
+
describe '#track_default_metrics' do
|
6
|
+
it 'should track timing, sql, and active_record by default' do
|
7
|
+
expect(CanvasStatsd::DefaultTracking).to receive(:track_timing)
|
8
|
+
expect(CanvasStatsd::DefaultTracking).to receive(:track_sql)
|
9
|
+
expect(CanvasStatsd::DefaultTracking).to receive(:track_active_record)
|
10
|
+
CanvasStatsd::DefaultTracking.track_default_metrics
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should not track sql when sql: false option' do
|
14
|
+
expect(CanvasStatsd::DefaultTracking).not_to receive(:track_sql)
|
15
|
+
CanvasStatsd::DefaultTracking.track_default_metrics sql: false
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should not track active_record when active_record: false option' do
|
19
|
+
expect(CanvasStatsd::DefaultTracking).not_to receive(:track_active_record)
|
20
|
+
CanvasStatsd::DefaultTracking.track_default_metrics active_record: false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#track_active_record' do
|
25
|
+
it 'should turn on active record instrumentation' do
|
26
|
+
expect(CanvasStatsd::DefaultTracking).to receive(:instrument_active_record_creation)
|
27
|
+
CanvasStatsd::DefaultTracking.send(:track_active_record)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#subscribe' do
|
32
|
+
it 'should subscribe via ActiveSupport::Notifications' do
|
33
|
+
target = double()
|
34
|
+
CanvasStatsd::DefaultTracking.subscribe(/test.notification/) {|*args| target.callback(*args)}
|
35
|
+
expect(target).to receive(:callback)
|
36
|
+
ActiveSupport::Notifications.instrument('test.notification') {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
def create_subject(payload={}, statsd=nil)
|
5
|
+
args = ['name', 1000, 1001, 1234, payload]
|
6
|
+
args << statsd if statsd
|
7
|
+
CanvasStatsd::RequestStat.new(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
describe CanvasStatsd::RequestStat do
|
12
|
+
|
13
|
+
describe '#db_runtime' do
|
14
|
+
it 'should return the payload db_runtime' do
|
15
|
+
rs = create_subject({db_runtime: 11.11})
|
16
|
+
expect(rs.db_runtime).to eq 11.11
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should return nil when payload db_runtime key doesnt exists' do
|
20
|
+
rs = create_subject
|
21
|
+
expect(rs.db_runtime).to eq nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#view_runtime' do
|
26
|
+
it 'should return the payload view_runtime' do
|
27
|
+
rs = create_subject(view_runtime: 11.11)
|
28
|
+
expect(rs.view_runtime).to eq 11.11
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return nil when payload view_runtime key doesnt exists' do
|
32
|
+
rs = create_subject
|
33
|
+
expect(rs.view_runtime).to eq nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#controller' do
|
38
|
+
it "should return params['controller']" do
|
39
|
+
rs = create_subject({params: {'controller' => 'foo'}})
|
40
|
+
expect(rs.controller).to eq 'foo'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should return nil if no params are available' do
|
44
|
+
rs = create_subject
|
45
|
+
expect(rs.controller).to eq nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should return nil if no controller is available on params' do
|
49
|
+
rs = create_subject({params: {}})
|
50
|
+
expect(rs.controller).to eq nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#action' do
|
55
|
+
it "should return params['action']" do
|
56
|
+
rs = create_subject({params: {'action' => 'index'}})
|
57
|
+
expect(rs.action).to eq 'index'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should return nil if no params are available' do
|
61
|
+
rs = create_subject
|
62
|
+
expect(rs.action).to eq nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should return nil if no action is available on params' do
|
66
|
+
rs = create_subject({params: {}})
|
67
|
+
expect(rs.action).to eq nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#ms' do
|
72
|
+
it 'correctly calcuates milliseconds from start, finish' do
|
73
|
+
rs = create_subject({params: {}})
|
74
|
+
# start and finish are in seconds
|
75
|
+
expect(rs.ms).to eq 1000
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'defaults to zero if either start or finish are nil' do
|
79
|
+
rs = CanvasStatsd::RequestStat.new('name', nil, 1001, 1111, {params: {}})
|
80
|
+
expect(rs.ms).to eq 0
|
81
|
+
rs = CanvasStatsd::RequestStat.new('name', 1, nil, 1111, {params: {}})
|
82
|
+
expect(rs.ms).to eq 0
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#report' do
|
87
|
+
it 'doesnt send stats when no controller or action' do
|
88
|
+
statsd = double
|
89
|
+
rs = create_subject({params: {}}, statsd)
|
90
|
+
expect(statsd).to_not receive(:timing).with('request.foo.index', 1000)
|
91
|
+
rs.report
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'sends total timing when controller && action are present, doesnt send db, or view if they are not' do
|
95
|
+
statsd = double
|
96
|
+
payload = {
|
97
|
+
params: {
|
98
|
+
'controller' => 'foo',
|
99
|
+
'action' => 'index'
|
100
|
+
}
|
101
|
+
}
|
102
|
+
rs = create_subject(payload, statsd)
|
103
|
+
expect(statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
104
|
+
rs.report
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'sends view_runtime and db_runtime when present' do
|
108
|
+
statsd = double
|
109
|
+
payload = {
|
110
|
+
view_runtime: 70.1,
|
111
|
+
db_runtime: 100.2,
|
112
|
+
params: {
|
113
|
+
'controller' => 'foo',
|
114
|
+
'action' => 'index'
|
115
|
+
}
|
116
|
+
}
|
117
|
+
rs = create_subject(payload, statsd)
|
118
|
+
allow(statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
119
|
+
expect(statsd).to receive(:timing).with('request.foo.index.view', 70.1)
|
120
|
+
expect(statsd).to receive(:timing).with('request.foo.index.db', 100.2)
|
121
|
+
rs.report
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'sql stats' do
|
125
|
+
|
126
|
+
before :each do
|
127
|
+
@statsd = double
|
128
|
+
payload = {
|
129
|
+
params: {
|
130
|
+
'controller' => 'foo',
|
131
|
+
'action' => 'index'
|
132
|
+
}
|
133
|
+
}
|
134
|
+
@rs = create_subject(payload, @statsd)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'doesnt send sql stats when they dont exist' do
|
138
|
+
allow(@statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
139
|
+
expect(@statsd).to_not receive(:timing).with('request.foo.index.sql.read', kind_of(Numeric))
|
140
|
+
expect(@statsd).to_not receive(:timing).with('request.foo.index.sql.write', kind_of(Numeric))
|
141
|
+
expect(@statsd).to_not receive(:timing).with('request.foo.index.sql.cache', kind_of(Numeric))
|
142
|
+
@rs.report
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'sends sql_read_count when present' do
|
146
|
+
@rs.sql_read_count = 10
|
147
|
+
allow(@statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
148
|
+
expect(@statsd).to receive(:timing).with('request.foo.index.sql.read', 10)
|
149
|
+
@rs.report
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'sends sql_read_count when present' do
|
153
|
+
@rs.sql_write_count = 3
|
154
|
+
allow(@statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
155
|
+
expect(@statsd).to receive(:timing).with('request.foo.index.sql.write', 3)
|
156
|
+
@rs.report
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'sends sql_cache_count when present' do
|
160
|
+
@rs.sql_cache_count = 1
|
161
|
+
allow(@statsd).to receive(:timing).with('request.foo.index.total', 1000)
|
162
|
+
expect(@statsd).to receive(:timing).with('request.foo.index.sql.cache', 1)
|
163
|
+
@rs.report
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CanvasStatsd
|
4
|
+
describe SqlTracker do
|
5
|
+
|
6
|
+
describe '#start' do
|
7
|
+
it 'resets values to zero' do
|
8
|
+
subject = SqlTracker.new
|
9
|
+
subject.start
|
10
|
+
subject.track 'CACHE', 'SELECT * FROM some_table'
|
11
|
+
subject.start
|
12
|
+
expect(subject.num_caches).to eq(0)
|
13
|
+
expect(subject.num_reads).to eq(0)
|
14
|
+
expect(subject.num_writes).to eq(0)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#track' do
|
19
|
+
before :each do
|
20
|
+
@subject = SqlTracker.new
|
21
|
+
@subject.start
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'considers CACHE above all' do
|
25
|
+
@subject.track 'CACHE', 'SELECT * FROM some_table'
|
26
|
+
expect(@subject.num_caches).to eq(1)
|
27
|
+
expect(@subject.num_reads).to eq(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'marks as read when select is in the first 15 chars of the sql' do
|
31
|
+
@subject.track 'LOAD', ' SELECT "context_external_tools".* FROM'
|
32
|
+
expect(@subject.num_reads).to eq(1)
|
33
|
+
expect(@subject.num_writes).to eq(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'marks as read with no select, but a LOAD name' do
|
37
|
+
@subject.track 'LOAD', 'WITH RECURSIVE t AS'
|
38
|
+
expect(@subject.num_reads).to eq(1)
|
39
|
+
expect(@subject.num_writes).to eq(0)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'doesnt track names set as blocked' do
|
43
|
+
tracker = SqlTracker.new(blocked_names: ['SCHEMA'])
|
44
|
+
tracker.start
|
45
|
+
tracker.track 'SCHEMA', 'SELECT * FROM some_table'
|
46
|
+
expect(tracker.num_reads).to eq(0)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'doesnt track nil names or sql values' do
|
50
|
+
@subject.track nil, 'SELECT *'
|
51
|
+
@subject.track 'CACHE', nil
|
52
|
+
expect(@subject.num_reads).to eq(0)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'passes full sql to counter.track calls for reads' do
|
56
|
+
sql = ' SELECT \'context_external_tools\'.* FROM'
|
57
|
+
read_counter = double()
|
58
|
+
allow(read_counter).to receive(:start)
|
59
|
+
expect(read_counter).to receive(:track).with sql
|
60
|
+
tracker = SqlTracker.new(read_counter: read_counter)
|
61
|
+
tracker.start
|
62
|
+
tracker.track 'LOAD', sql
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'passes full sql to counter.track calls for writes' do
|
66
|
+
sql = ' UPDATE \'context_external_tools\'.* FROM'
|
67
|
+
write_counter = double()
|
68
|
+
allow(write_counter).to receive(:start)
|
69
|
+
expect(write_counter).to receive(:track).with sql
|
70
|
+
tracker = SqlTracker.new(write_counter: write_counter)
|
71
|
+
tracker.start
|
72
|
+
tracker.track 'UPDATE', sql
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2012 Instructure, Inc.
|
3
|
+
#
|
4
|
+
# This file is part of Canvas.
|
5
|
+
#
|
6
|
+
# Canvas is free software: you can redistribute it and/or modify it under
|
7
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
8
|
+
# Software Foundation, version 3 of the License.
|
9
|
+
#
|
10
|
+
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
11
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
12
|
+
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
13
|
+
# details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License along
|
16
|
+
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'spec_helper'
|
20
|
+
|
21
|
+
describe CanvasStatsd::Statsd do
|
22
|
+
METHODS = %w(increment decrement count gauge timing)
|
23
|
+
|
24
|
+
it "appends the hostname to stat names by default" do
|
25
|
+
CanvasStatsd::Statsd.stub(:hostname).and_return("testhost")
|
26
|
+
statsd = double
|
27
|
+
CanvasStatsd::Statsd.stub(:instance).and_return(statsd)
|
28
|
+
CanvasStatsd::Statsd.stub(:append_hostname?).and_return(true)
|
29
|
+
METHODS.each do |method|
|
30
|
+
expect(statsd).to receive(method).with("test.name.testhost", "test")
|
31
|
+
CanvasStatsd::Statsd.send(method, "test.name", "test")
|
32
|
+
end
|
33
|
+
expect(statsd).to receive("timing").with("test.name.testhost", anything, anything)
|
34
|
+
expect(CanvasStatsd::Statsd.time("test.name") { "test" }).to eq "test"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "omits hostname if specified in config" do
|
38
|
+
expect(CanvasStatsd::Statsd).to receive(:hostname).never
|
39
|
+
statsd = double
|
40
|
+
CanvasStatsd::Statsd.stub(:instance).and_return(statsd)
|
41
|
+
CanvasStatsd::Statsd.stub(:append_hostname?).and_return(false)
|
42
|
+
METHODS.each do |method|
|
43
|
+
expect(statsd).to receive(method).with("test.name", "test")
|
44
|
+
CanvasStatsd::Statsd.send(method, "test.name", "test")
|
45
|
+
end
|
46
|
+
expect(statsd).to receive("timing").with("test.name", anything, anything)
|
47
|
+
expect(CanvasStatsd::Statsd.time("test.name") { "test" }).to eq "test"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "ignores all calls if statsd isn't enabled" do
|
51
|
+
CanvasStatsd::Statsd.stub(:instance).and_return(nil)
|
52
|
+
METHODS.each do |method|
|
53
|
+
expect(CanvasStatsd::Statsd.send(method, "test.name")).to be_nil
|
54
|
+
end
|
55
|
+
expect(CanvasStatsd::Statsd.time("test.name") { "test" }).to eq "test"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "configures a statsd instance" do
|
59
|
+
expect(CanvasStatsd::Statsd.instance).to be_nil
|
60
|
+
|
61
|
+
CanvasStatsd.settings = { :host => "testhost", :namespace => "test", :port => 1234 }
|
62
|
+
CanvasStatsd::Statsd.reset_instance
|
63
|
+
|
64
|
+
instance = CanvasStatsd::Statsd.instance
|
65
|
+
expect(instance).to be_a ::Statsd
|
66
|
+
expect(instance.host).to eq "testhost"
|
67
|
+
expect(instance.port).to eq 1234
|
68
|
+
expect(instance.namespace).to eq "test"
|
69
|
+
end
|
70
|
+
|
71
|
+
after do
|
72
|
+
CanvasStatsd::Statsd.reset_instance
|
73
|
+
end
|
74
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/test.sh
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
result=0
|
3
|
+
|
4
|
+
echo "################ canvas_statsd ################"
|
5
|
+
echo "################ Running tests against Rails 3 ################"
|
6
|
+
bundle check || bundle install
|
7
|
+
bundle exec rspec spec
|
8
|
+
let result=$result+$?
|
9
|
+
|
10
|
+
if [ $result -eq 0 ]; then
|
11
|
+
echo "SUCCESS"
|
12
|
+
else
|
13
|
+
echo "FAILURE"
|
14
|
+
fi
|
15
|
+
|
16
|
+
exit $result
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: canvas_statsd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Cloward
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: statsd-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aroi
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- ncloward@instructure.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/canvas_statsd.rb
|
91
|
+
- lib/canvas_statsd/counter.rb
|
92
|
+
- lib/canvas_statsd/default_tracking.rb
|
93
|
+
- lib/canvas_statsd/request_stat.rb
|
94
|
+
- lib/canvas_statsd/sql_tracker.rb
|
95
|
+
- lib/canvas_statsd/statsd.rb
|
96
|
+
- spec/canvas_statsd/canvas_statsd_spec.rb
|
97
|
+
- spec/canvas_statsd/counter_spec.rb
|
98
|
+
- spec/canvas_statsd/default_tracking_spec.rb
|
99
|
+
- spec/canvas_statsd/request_stat_spec.rb
|
100
|
+
- spec/canvas_statsd/sql_tracker_spec.rb
|
101
|
+
- spec/canvas_statsd/statsd_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
- test.sh
|
104
|
+
homepage: https://github.com/instructure/canvas_statsd
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.4.6
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Statsd for Canvas
|
128
|
+
test_files:
|
129
|
+
- spec/canvas_statsd/canvas_statsd_spec.rb
|
130
|
+
- spec/canvas_statsd/counter_spec.rb
|
131
|
+
- spec/canvas_statsd/default_tracking_spec.rb
|
132
|
+
- spec/canvas_statsd/request_stat_spec.rb
|
133
|
+
- spec/canvas_statsd/sql_tracker_spec.rb
|
134
|
+
- spec/canvas_statsd/statsd_spec.rb
|
135
|
+
- spec/spec_helper.rb
|
136
|
+
has_rdoc:
|