sidewatch 0.0.1
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/.gitignore +20 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +2 -0
- data/bin/sidewatch +14 -0
- data/lib/sidewatch.rb +19 -0
- data/lib/sidewatch/cli.rb +66 -0
- data/lib/sidewatch/cloudwatch.rb +49 -0
- data/lib/sidewatch/daemon.rb +46 -0
- data/lib/sidewatch/metric.rb +29 -0
- data/lib/sidewatch/sidekiq.rb +72 -0
- data/lib/sidewatch/version.rb +3 -0
- data/sidewatch.gemspec +29 -0
- data/spec/cloudwatch_spec.rb +53 -0
- data/spec/metric_spec.rb +34 -0
- data/spec/sidekiq_spec.rb +95 -0
- data/spec/spec_helper.rb +1 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4b49f4160ae5ce3ac9bf51f9368f526659f841ba
|
4
|
+
data.tar.gz: 533bfa8a7b43ccb239ed218c1dcfeb26e5b1c3bb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b76fa21af012ac9750c1397324ee005fb0389626657cacec615e740ee45b4b46679d0251555e29676b2cdc59f2f88eeb4ec7cc97478a45f64c962fb2849c5639
|
7
|
+
data.tar.gz: 058f477685ebe6800eda1394913a626b702a1d07ec85cf0b93be25afdf3f1f477b1aa0b86f5173cad60a5b6aeb1b015444ee6867958514cbca20c5ec0ee7da77
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Jan Hecking
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Sidewatch
|
2
|
+
|
3
|
+
Sidewatch uploads Sidekiq metrics to AWS CloudWatch in regular intervals.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'sidewatch'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install sidewatch
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
To start sidewatch as a daemon process in the background run
|
22
|
+
|
23
|
+
ruby sidewatch.rb start
|
24
|
+
|
25
|
+
To run sidewatch in the foreground, e.g. for testing, run
|
26
|
+
|
27
|
+
ruby sidewatch.rb run
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/sidewatch
ADDED
data/lib/sidewatch.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "sidewatch/version"
|
2
|
+
require "sidewatch/cli"
|
3
|
+
require "sidewatch/daemon"
|
4
|
+
require "sidewatch/metric"
|
5
|
+
require "sidewatch/sidekiq"
|
6
|
+
require "sidewatch/cloudwatch"
|
7
|
+
|
8
|
+
module Sidewatch
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :config
|
12
|
+
attr_accessor :logger
|
13
|
+
end
|
14
|
+
|
15
|
+
self.config = {}
|
16
|
+
self.logger = Logger.new(STDERR)
|
17
|
+
logger.level = Logger::WARN
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
module Sidewatch
|
4
|
+
class CLI
|
5
|
+
|
6
|
+
attr_accessor :options
|
7
|
+
attr_accessor :config_file
|
8
|
+
|
9
|
+
def initialize(pwd = Dir.pwd)
|
10
|
+
self.working_dir = pwd
|
11
|
+
parse_options
|
12
|
+
parse_config
|
13
|
+
setup_logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
Sidewatch::Daemon.new(Sidewatch.config).run
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_accessor :working_dir
|
23
|
+
|
24
|
+
def drop_daemons_args(args)
|
25
|
+
args = args.drop_while{|arg| arg != "--"}
|
26
|
+
args.drop(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_options(args = ARGV)
|
30
|
+
args = drop_daemons_args(args)
|
31
|
+
options = {}
|
32
|
+
parser = OptionParser.new do |opts|
|
33
|
+
opts.banner = "Usage: #{__FILE__} [start|stop|run] (-- [options])"
|
34
|
+
opts.on("-c", "--config PATH", "path to YAML Config file") do |file|
|
35
|
+
options[:config] = file
|
36
|
+
end
|
37
|
+
opts.on("-l", "--logfile PATH", "path to writeable logfile") do |file|
|
38
|
+
options[:logfile] = file
|
39
|
+
end
|
40
|
+
end
|
41
|
+
parser.parse(args)
|
42
|
+
self.options = options
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_config
|
46
|
+
self.config_file = options.fetch(:config){ "/etc/sidewatch.yml" }
|
47
|
+
self.config_file = File.expand_path(config_file, working_dir)
|
48
|
+
if File.exists?(config_file)
|
49
|
+
config = YAML.load(ERB.new(IO.read(config_file)).result)
|
50
|
+
config.merge(options)
|
51
|
+
Sidewatch.config = config.symbolize_keys
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_logger
|
56
|
+
logfile =
|
57
|
+
if options[:logfile]
|
58
|
+
File.expand_path(options[:logfile], working_dir)
|
59
|
+
elsif Sidewatch.config[:logfile]
|
60
|
+
File.expand_path(Sidewatch.config[:logfile], File.dirname(config_file))
|
61
|
+
end
|
62
|
+
Sidewatch.logger = Logger.new(logfile) if logfile
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "aws-sdk"
|
2
|
+
|
3
|
+
module Sidewatch
|
4
|
+
class Cloudwatch
|
5
|
+
|
6
|
+
attr_accessor :client
|
7
|
+
|
8
|
+
def initialize(config = {})
|
9
|
+
client = config.delete(:client){ Aws::CloudWatch::Client }
|
10
|
+
client = Object.const_get(client) if client.is_a?(String)
|
11
|
+
client = client.new(config) if client.is_a?(Class)
|
12
|
+
self.client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
def send(metrics)
|
16
|
+
metrics.group_by(&:namespace).each do |ns, ns_metrics|
|
17
|
+
data = metric_data(ns, ns_metrics)
|
18
|
+
client.put_metric_data(data)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def metric_data(ns, metrics)
|
25
|
+
{
|
26
|
+
namespace: ns,
|
27
|
+
metric_data: metrics.map{|m|
|
28
|
+
{
|
29
|
+
metric_name: m.name,
|
30
|
+
dimensions: Array(m.dimensions).map{|name, value| {name: name, value: value}},
|
31
|
+
timestamp: m.timestamp,
|
32
|
+
value: m.value,
|
33
|
+
unit: m.unit
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
class MockClient
|
40
|
+
def initialize(config = {})
|
41
|
+
@out = config.fetch(:output){ $stdout }
|
42
|
+
end
|
43
|
+
def put_metric_data(data)
|
44
|
+
@out << data.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "daemons"
|
2
|
+
|
3
|
+
module Sidewatch
|
4
|
+
|
5
|
+
class Daemon
|
6
|
+
|
7
|
+
attr_accessor :sidekiq
|
8
|
+
attr_accessor :cloudwatch
|
9
|
+
attr_accessor :interval
|
10
|
+
|
11
|
+
def initialize(config = {})
|
12
|
+
sidekiq_config = config.fetch(:sidekiq){ Hash.new }.symbolize_keys
|
13
|
+
self.sidekiq = Sidewatch::Sidekiq.new(sidekiq_config)
|
14
|
+
cloudwatch_config = config.fetch(:cloudwatch){ Hash.new }.symbolize_keys
|
15
|
+
self.cloudwatch = Sidewatch::Cloudwatch.new(cloudwatch_config)
|
16
|
+
self.interval = config.fetch(:interval){ 60 }
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
logger.info("Starting #{self.class}...")
|
21
|
+
every(interval) do
|
22
|
+
metrics = sidekiq.metrics
|
23
|
+
logger.info(metrics)
|
24
|
+
cloudwatch.send(metrics)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def every(interval, &block)
|
31
|
+
loop do
|
32
|
+
ts = Time.now
|
33
|
+
block.call
|
34
|
+
remaining = (ts + interval) - Time.now
|
35
|
+
sleep remaining
|
36
|
+
end
|
37
|
+
rescue Interrupt
|
38
|
+
logger.info("Interrupted; exiting.")
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger
|
42
|
+
Sidewatch.logger
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Metric
|
2
|
+
|
3
|
+
attr_accessor :namespace
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :value
|
6
|
+
attr_accessor :unit
|
7
|
+
attr_accessor :dimensions
|
8
|
+
attr_accessor :timestamp
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
attributes.each do |name, value|
|
12
|
+
public_send("#{name}=", value)
|
13
|
+
end
|
14
|
+
self.timestamp ||= Time.now.utc
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
ts = timestamp.iso8601 if timestamp
|
19
|
+
{
|
20
|
+
namespace: namespace,
|
21
|
+
name: name,
|
22
|
+
value: value,
|
23
|
+
unit: unit,
|
24
|
+
dimensions: dimensions,
|
25
|
+
timestamp: ts
|
26
|
+
}.reject{|_,v| v.nil?}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "sidekiq"
|
2
|
+
|
3
|
+
module Sidewatch
|
4
|
+
class Sidekiq
|
5
|
+
|
6
|
+
attr_accessor :namespace
|
7
|
+
|
8
|
+
def initialize(config = {})
|
9
|
+
self.namespace = config.fetch(:namespace){ "Sidekiq" }
|
10
|
+
::Sidekiq.redis = config[:redis].symbolize_keys if config[:redis]
|
11
|
+
end
|
12
|
+
|
13
|
+
def metrics
|
14
|
+
metrics = []
|
15
|
+
metrics += global_metrics
|
16
|
+
metrics += per_queue_metrics
|
17
|
+
metrics
|
18
|
+
end
|
19
|
+
|
20
|
+
def global_metrics
|
21
|
+
stats = sidekiq_stats
|
22
|
+
metrics = []
|
23
|
+
[:failed, :enqueued, :processed, :retry_size, :scheduled_size].each do |stat|
|
24
|
+
name = camelcase(stat)
|
25
|
+
value = stats.send(stat)
|
26
|
+
metrics << Metric.new(namespace: namespace, name: name, value: value, unit: "Count", dimensions: default_dimensions)
|
27
|
+
end
|
28
|
+
metrics
|
29
|
+
end
|
30
|
+
|
31
|
+
def per_queue_metrics
|
32
|
+
metrics = []
|
33
|
+
sidekiq_queues.each do |queue|
|
34
|
+
dimensions = default_dimensions.merge("QueueName" => queue.name)
|
35
|
+
metrics << Metric.new(namespace: namespace, name: "Enqueued", value: queue.size, unit: "Count", dimensions: dimensions)
|
36
|
+
metrics << Metric.new(namespace: namespace, name: "Latency", value: queue.latency, unit: "Count", dimensions: dimensions)
|
37
|
+
end
|
38
|
+
metrics
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def sidekiq_stats
|
44
|
+
::Sidekiq::Stats.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def sidekiq_queues
|
48
|
+
::Sidekiq::Queue.all
|
49
|
+
end
|
50
|
+
|
51
|
+
def redis_uri
|
52
|
+
# is there a simpler way to get this?
|
53
|
+
uri = ::Sidekiq.redis {|redis|
|
54
|
+
redis = redis.redis if redis.is_a?(Redis::Namespace)
|
55
|
+
redis.client.id
|
56
|
+
}
|
57
|
+
uri = URI.parse(uri)
|
58
|
+
uri.password = "*****" if uri.password
|
59
|
+
uri.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_dimensions
|
63
|
+
{ "RedisURI" => redis_uri }
|
64
|
+
end
|
65
|
+
|
66
|
+
def camelcase(term)
|
67
|
+
string = term.to_s
|
68
|
+
string.gsub(/(?:^|_)([a-z\d]*)/){ $1.capitalize }
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/sidewatch.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sidewatch/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sidewatch"
|
8
|
+
spec.version = Sidewatch::VERSION
|
9
|
+
spec.authors = ["Jan Hecking"]
|
10
|
+
spec.email = ["jan@travelmob.com"]
|
11
|
+
spec.description = %q{Uploads Sidekiq metrics to AWS CloudWatch in regular intervals}
|
12
|
+
spec.summary = %q{Uploads Sidekiq metrics to AWS CloudWatch in regular intervals}
|
13
|
+
spec.homepage = "https://github.com/jhecking"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.4"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
24
|
+
|
25
|
+
spec.add_runtime_dependency "daemons", "~> 1.2"
|
26
|
+
spec.add_runtime_dependency "sidekiq", "~> 2.17"
|
27
|
+
spec.add_runtime_dependency "aws-sdk", "~> 2.0"
|
28
|
+
spec.add_runtime_dependency "dotenv", "~> 2.0"
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Sidewatch
|
4
|
+
|
5
|
+
describe Cloudwatch do
|
6
|
+
|
7
|
+
let(:client){ spy("cloudwatch-client") }
|
8
|
+
subject{ described_class.new(client: client) }
|
9
|
+
|
10
|
+
describe "#send" do
|
11
|
+
|
12
|
+
it "sends the metrics using the cloudwatch client" do
|
13
|
+
metrics = []
|
14
|
+
metrics << Metric.new(namespace: "Clocks", name: "Ticks", value: 1234, unit: "Count", dimensions: { "ClockName" => "cuckoo" }, timestamp: Time.at(1440494300))
|
15
|
+
expected = {
|
16
|
+
namespace: "Clocks",
|
17
|
+
metric_data: [
|
18
|
+
{
|
19
|
+
metric_name: "Ticks",
|
20
|
+
dimensions: [
|
21
|
+
{
|
22
|
+
name: "ClockName",
|
23
|
+
value: "cuckoo"
|
24
|
+
}
|
25
|
+
],
|
26
|
+
timestamp: Time.at(1440494300),
|
27
|
+
value: 1234,
|
28
|
+
unit: "Count"
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
32
|
+
|
33
|
+
subject.send(metrics)
|
34
|
+
|
35
|
+
expect(client).to have_received(:put_metric_data).with(expected)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "groups the metrics by namespace" do
|
39
|
+
metrics = []
|
40
|
+
metrics << Metric.new(namespace: "Clocks", name: "Ticks", value: 1234, unit: "Count")
|
41
|
+
metrics << Metric.new(namespace: "Events", name: "Failed", value: 55, unit: "Count")
|
42
|
+
received = []
|
43
|
+
allow(client).to receive(:put_metric_data){ |data| received << data }
|
44
|
+
|
45
|
+
subject.send(metrics)
|
46
|
+
|
47
|
+
expect(received).to contain_exactly(hash_including(namespace: "Clocks"), hash_including(namespace: "Events"))
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/spec/metric_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Sidewatch
|
4
|
+
|
5
|
+
describe Metric do
|
6
|
+
|
7
|
+
let(:namespace){ "Clocks" }
|
8
|
+
let(:metric_name){ "Ticks" }
|
9
|
+
let(:metric_value){ 1234 }
|
10
|
+
let(:metric_unit){ "Count" }
|
11
|
+
let(:dimensions){ { "ClockName" => "cuckoo" } }
|
12
|
+
let(:timestamp){ Time.at(1440494300) }
|
13
|
+
subject {
|
14
|
+
described_class.new(
|
15
|
+
name: metric_name,
|
16
|
+
value: metric_value,
|
17
|
+
unit: metric_unit,
|
18
|
+
dimensions: dimensions,
|
19
|
+
timestamp: timestamp,
|
20
|
+
)
|
21
|
+
}
|
22
|
+
|
23
|
+
describe "#initialize" do
|
24
|
+
|
25
|
+
context "missing timestamp" do
|
26
|
+
let(:timestamp){ nil }
|
27
|
+
it "sets a default timestamp" do
|
28
|
+
expect(subject.timestamp).to_not be_nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Sidewatch
|
4
|
+
|
5
|
+
describe Sidekiq do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
allow(::Sidekiq).to receive(:redis=)
|
9
|
+
allow(subject).to receive(:redis_uri){ "redis://localhost:6379/0" }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#metrics" do
|
13
|
+
|
14
|
+
it "returns global and per-queue metrics" do
|
15
|
+
global = double("global-metric")
|
16
|
+
expect(subject).to receive(:global_metrics).and_return([global])
|
17
|
+
per_queue = double("queue-metric")
|
18
|
+
expect(subject).to receive(:per_queue_metrics).and_return([per_queue])
|
19
|
+
|
20
|
+
metrics = subject.metrics
|
21
|
+
|
22
|
+
expect(metrics).to contain_exactly(global, per_queue)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#global_metrics" do
|
27
|
+
|
28
|
+
let(:stats){
|
29
|
+
double("stats",
|
30
|
+
failed: 111,
|
31
|
+
enqueued: 222,
|
32
|
+
processed: 333,
|
33
|
+
retry_size: 444,
|
34
|
+
scheduled_size: 555,
|
35
|
+
)
|
36
|
+
}
|
37
|
+
before(:each) do
|
38
|
+
allow(subject).to receive(:sidekiq_stats){ stats }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "produces a metric for failed jobs" do
|
42
|
+
failed = subject.global_metrics.find{|m| m.name == "Failed"}
|
43
|
+
expect(failed.value).to eq 111
|
44
|
+
end
|
45
|
+
|
46
|
+
it "produces a metric for enqueued jobs" do
|
47
|
+
failed = subject.global_metrics.find{|m| m.name == "Enqueued"}
|
48
|
+
expect(failed.value).to eq 222
|
49
|
+
end
|
50
|
+
|
51
|
+
it "produces a metric for processed jobs" do
|
52
|
+
failed = subject.global_metrics.find{|m| m.name == "Processed"}
|
53
|
+
expect(failed.value).to eq 333
|
54
|
+
end
|
55
|
+
|
56
|
+
it "produces a metric for retry queue size" do
|
57
|
+
failed = subject.global_metrics.find{|m| m.name == "RetrySize"}
|
58
|
+
expect(failed.value).to eq 444
|
59
|
+
end
|
60
|
+
|
61
|
+
it "produces a metric for scheduled queue size" do
|
62
|
+
failed = subject.global_metrics.find{|m| m.name == "ScheduledSize"}
|
63
|
+
expect(failed.value).to eq 555
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#per_queue_metrics" do
|
69
|
+
|
70
|
+
let(:queues){
|
71
|
+
[
|
72
|
+
double("queue1", name: "high-priority", size: 50, latency: 5),
|
73
|
+
double("queue2", name: "low-priority", size: 100, latency: 10),
|
74
|
+
]
|
75
|
+
}
|
76
|
+
before(:each) do
|
77
|
+
allow(subject).to receive(:sidekiq_queues){ queues }
|
78
|
+
end
|
79
|
+
|
80
|
+
it "produces queue length and latency metrics for each queue" do
|
81
|
+
metrics = subject.per_queue_metrics.map(&:name).uniq
|
82
|
+
|
83
|
+
expect(metrics).to contain_exactly("Enqueued", "Latency")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "produces at least one metric for every queue" do
|
87
|
+
metrics = subject.per_queue_metrics
|
88
|
+
queues = metrics.map{|metric| metric.dimensions["QueueName"]}.uniq
|
89
|
+
|
90
|
+
expect(queues).to contain_exactly("high-priority", "low-priority")
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "sidewatch"
|
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sidewatch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jan Hecking
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.4'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: daemons
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sidekiq
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.17'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.17'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aws-sdk
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: dotenv
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.0'
|
111
|
+
description: Uploads Sidekiq metrics to AWS CloudWatch in regular intervals
|
112
|
+
email:
|
113
|
+
- jan@travelmob.com
|
114
|
+
executables:
|
115
|
+
- sidewatch
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- .gitignore
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/sidewatch
|
125
|
+
- lib/sidewatch.rb
|
126
|
+
- lib/sidewatch/cli.rb
|
127
|
+
- lib/sidewatch/cloudwatch.rb
|
128
|
+
- lib/sidewatch/daemon.rb
|
129
|
+
- lib/sidewatch/metric.rb
|
130
|
+
- lib/sidewatch/sidekiq.rb
|
131
|
+
- lib/sidewatch/version.rb
|
132
|
+
- sidewatch.gemspec
|
133
|
+
- spec/cloudwatch_spec.rb
|
134
|
+
- spec/metric_spec.rb
|
135
|
+
- spec/sidekiq_spec.rb
|
136
|
+
- spec/spec_helper.rb
|
137
|
+
homepage: https://github.com/jhecking
|
138
|
+
licenses:
|
139
|
+
- MIT
|
140
|
+
metadata: {}
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - '>='
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubyforge_project:
|
157
|
+
rubygems_version: 2.4.3
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: Uploads Sidekiq metrics to AWS CloudWatch in regular intervals
|
161
|
+
test_files:
|
162
|
+
- spec/cloudwatch_spec.rb
|
163
|
+
- spec/metric_spec.rb
|
164
|
+
- spec/sidekiq_spec.rb
|
165
|
+
- spec/spec_helper.rb
|