evenitron 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,65 @@
1
+ # Setting up with delayed_job
2
+
3
+ There are two options for setting up Evenitron to monitor your delayed_job tasks.
4
+
5
+ Firstly, add the gem to your Gemfile:
6
+
7
+ ```ruby
8
+ gem 'evenitron-ruby', :git => 'git@github.com:evenitron/evenitron-ruby.git'
9
+ ```
10
+
11
+ *We'll use the git references if possible until things are public.*
12
+
13
+ Then you need to configure it within your `boot.rb` for Rails (I have no idea if this is the right file but it works). You can either do this explicitly:
14
+
15
+ ```ruby
16
+ require 'evenitron/delayed_job'
17
+
18
+ Evenitron::DelayedJob.configure do
19
+ system_key 'benchmarking'
20
+ # collector_url 'http://localhost:5000/'
21
+ end
22
+ ```
23
+
24
+ Or you can rely on the environment variables `EVENITRON_SYSTEM_KEY` and `EVENITRON_COLLECTOR_URL`:
25
+
26
+ ```ruby
27
+ require 'evenitron/delayed_job'
28
+
29
+ Evenitron::DelayedJob.configure
30
+ ```
31
+
32
+ You must specify your system key either through configuration or the `EVENITRON_SYSTEM_KEY` environment variable but you will normally rely on the default collector URL of `https://collector.evenitron.com/`.
33
+
34
+ # Setting up with resque
35
+
36
+ There are two options for setting up Evenitron to monitor your delayed_job tasks.
37
+
38
+ Firstly, add the gem to your Gemfile:
39
+
40
+ ```ruby
41
+ gem 'evenitron-ruby', :git => 'git@github.com:evenitron/evenitron-ruby.git'
42
+ ```
43
+
44
+ *We'll use the git references if possible until things are public.*
45
+
46
+ Then you need to configure it within your `boot.rb` for Rails (I have no idea if this is the right file but it works). You can either do this explicitly:
47
+
48
+ ```ruby
49
+ require 'evenitron/resque'
50
+
51
+ Evenitron::Resque.configure do
52
+ system_key 'benchmarking'
53
+ # collector_url 'http://localhost:5000/'
54
+ end
55
+ ```
56
+
57
+ Or you can rely on the environment variables `EVENITRON_SYSTEM_KEY` and `EVENITRON_COLLECTOR_URL`:
58
+
59
+ ```ruby
60
+ require 'evenitron/resque'
61
+
62
+ Evenitron::Resque.configure
63
+ ```
64
+
65
+ You must specify your system key either through configuration or the `EVENITRON_SYSTEM_KEY` environment variable but you will normally rely on the default collector URL of `https://collector.evenitron.com/`.
@@ -0,0 +1,76 @@
1
+ module Evenitron
2
+
3
+ class CollectorClient
4
+
5
+ include Evenitron::Logger
6
+
7
+ DEFAULT_URL = "https://collector.evenitron.com/"
8
+
9
+ def initialize(system_key, url = DEFAULT_URL, component = nil)
10
+ @system_key = system_key
11
+ @url = url
12
+ @user_agent = 'evenitron-ruby 0.1'
13
+ @user_agent = "#{@user_agent} (#{component})" if component
14
+ @simulate_requests = ENV['EVENITRON_SIMULATE_REQUESTS'] == 'true'
15
+ log.warn 'Simulating Evenitron messages' if @simulate_requests
16
+ end
17
+
18
+ def job_queued(args)
19
+ send_message :queued, args
20
+ end
21
+
22
+ def job_started(args)
23
+ send_message :started, args
24
+ end
25
+
26
+ def job_failed(args)
27
+ send_message :failed, args
28
+ end
29
+
30
+ def job_complete(args)
31
+ send_message :complete, args
32
+ end
33
+
34
+ private
35
+
36
+ def send_message(stage, args)
37
+ send message(stage, args)
38
+ end
39
+
40
+ def send(msg)
41
+ json = msg.to_json
42
+
43
+ if @simulate_requests
44
+ log.info "Simulating message for #{@system_key} to #{@url} - #{json}"
45
+ return nil
46
+ else
47
+ log.debug "Sending message for #{@system_key} to #{@url} - #{json}"
48
+ end
49
+
50
+ Thread.new do
51
+ begin
52
+ RestClient.post @url, json, :content_type => nil, 'Evenitron-Key' => @system_key, 'User-Agent' => @user_agent
53
+ rescue => e
54
+ log.error "Failed to send message for #{@system_key} to #{@url} - #{json}"
55
+ log.debug e
56
+ end
57
+ end
58
+
59
+ nil
60
+ end
61
+
62
+ def message(stage, args)
63
+ msg = args.dup
64
+ msg[:stage] = stage
65
+ msg[:version] = 1
66
+ msg[:timestamp] = since_epoch(Time.now)
67
+ msg
68
+ end
69
+
70
+ def since_epoch(time)
71
+ (time.to_f * 1000).to_i
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,112 @@
1
+ require_relative '../evenitron'
2
+ require 'delayed_job'
3
+
4
+ module Evenitron
5
+
6
+ module DelayedJob
7
+
8
+ def self.configure(&block)
9
+ dj = Evenitron::DelayedJobCollector.new
10
+ dj.configure &block unless block.nil?
11
+ dj.start!
12
+ nil
13
+ end
14
+
15
+ end
16
+
17
+ class DelayedJobCollector
18
+
19
+ include Evenitron::Logger
20
+
21
+ def initialize
22
+ @collector_url = ENV['EVENITRON_COLLECTOR_URL']
23
+ @system_key = ENV['EVENITRON_SYSTEM_KEY']
24
+ end
25
+
26
+ def configure(&block)
27
+ instance_eval &block
28
+ end
29
+
30
+ def system_key(key)
31
+ @system_key = key
32
+ end
33
+
34
+ def api_key(key)
35
+ @api_key = key
36
+ end
37
+
38
+ def collector_url(url)
39
+ @collector_url = url
40
+ end
41
+
42
+ def start!
43
+ log.info "Registering delayed_job evenitron collector with system key #{@system_key} to send messages to #{@collector_url}"
44
+ @client = Evenitron::CollectorClient.new @system_key, @collector_url, 'dj'
45
+ register_lifecycle_hooks
46
+ end
47
+
48
+ private
49
+
50
+ def lifecycle
51
+ Delayed::Worker.lifecycle
52
+ end
53
+
54
+ def register_lifecycle_hooks
55
+ log.info "Registering delayed_job job queued callback"
56
+ lifecycle.around :enqueue do |job, &block|
57
+ result = block.call(job)
58
+ job_queued job
59
+ result
60
+ end
61
+
62
+ log.info "Registering delayed_job job failed callback"
63
+ lifecycle.before :failure do |worker, job|
64
+ job_failed job
65
+ end
66
+
67
+ log.info "Registering delayed_job job requeued callback"
68
+ lifecycle.after :error do |worker, job|
69
+ job_queued job unless job.failed? or job.frozen?
70
+ end
71
+
72
+ log.info "Registering delayed_job job started callback"
73
+ log.info "Registering delayed_job job complete callback"
74
+ lifecycle.around :perform do |worker, job, &block|
75
+ job_started job, worker
76
+ result = block.call(worker, job)
77
+ job_complete job if result
78
+ result
79
+ end
80
+ end
81
+
82
+ def job_queued(job)
83
+ id = get_id(job)
84
+ log.info "Sending job queued message for job #{job.id} (evenitron id #{id})"
85
+ @client.job_queued :id => id, :queue => job.class.table_name, :task => job.name
86
+ end
87
+
88
+ def job_started(job, worker)
89
+ id = get_id(job)
90
+ log.info "Sending job started message for job #{job.id} (evenitron id #{id})"
91
+ @client.job_started :id => id, :worker => worker.name, :task => job.name
92
+ end
93
+
94
+ def job_failed(job)
95
+ id = get_id(job)
96
+ log.info "Sending job failed message for job #{job.id} (evenitron id #{id})"
97
+ @client.job_failed :id => id, :task => job.name
98
+ end
99
+
100
+ def job_complete(job)
101
+ id = get_id(job)
102
+ log.info "Sending job complete message for job #{job.id} (evenitron id #{id})"
103
+ @client.job_complete :id => id, :task => job.name
104
+ end
105
+
106
+ def get_id(job)
107
+ Evenitron.encode job.class.table_name, job.id
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,32 @@
1
+ module Evenitron
2
+
3
+ def self.logger
4
+ @@logger ||= default_logger
5
+ end
6
+
7
+ def self.logger=(logger)
8
+ @@logger = logger
9
+ end
10
+
11
+ def self.encode(*values)
12
+ value = values.join ':'
13
+ cleanse_base64 Base64.encode64(value)
14
+ end
15
+
16
+ def self.generate_id(length = 64)
17
+ cleanse_base64 SecureRandom.base64(length)
18
+ end
19
+
20
+ private
21
+
22
+ def self.default_logger
23
+ logger = ::Logger.new(STDOUT)
24
+ logger.level = ::Logger::INFO
25
+ logger
26
+ end
27
+
28
+ def self.cleanse_base64(value)
29
+ value.gsub /[\s=]/, ''
30
+ end
31
+
32
+ end
@@ -0,0 +1,11 @@
1
+ module Evenitron
2
+
3
+ module Logger
4
+
5
+ def log
6
+ Evenitron.logger
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,149 @@
1
+ require_relative '../evenitron'
2
+ require 'resque'
3
+
4
+ module Evenitron
5
+
6
+ class NullResqueCollector
7
+
8
+ include Evenitron::Logger
9
+
10
+ def job_queued(queue, item)
11
+ log.warn "EVENITRON NOT CONFIGURED : Not sending job queued message for job #{item['evenitron_id']}"
12
+ end
13
+
14
+ def job_started(job)
15
+ log.info "EVENITRON NOT CONFIGURED : Not sending job started message for job #{job.evenitron_id}"
16
+ end
17
+
18
+ def job_failed(job)
19
+ log.info "EVENITRON NOT CONFIGURED : Not sending job failed message for job #{job.evenitron_id}"
20
+ end
21
+
22
+ def job_complete(job)
23
+ log.info "EVENITRON NOT CONFIGURED : Not sending job complete message for job #{job.evenitron_id}"
24
+ end
25
+
26
+ end
27
+
28
+ module Resque
29
+
30
+ @@collector = Evenitron::NullResqueCollector.new
31
+
32
+ def self.configure(&block)
33
+ rc = Evenitron::ResqueCollector.new
34
+ rc.configure &block unless block.nil?
35
+ rc.start!
36
+ @@collector = rc
37
+ nil
38
+ end
39
+
40
+ def self.collector
41
+ @@collector
42
+ end
43
+
44
+ end
45
+
46
+ class ResqueCollector
47
+
48
+ include Evenitron::Logger
49
+
50
+ def initialize
51
+ @collector_url = ENV['EVENITRON_COLLECTOR_URL']
52
+ @system_key = ENV['EVENITRON_SYSTEM_KEY']
53
+ end
54
+
55
+ def configure(&block)
56
+ instance_eval &block
57
+ end
58
+
59
+ def system_key(key)
60
+ @system_key = key
61
+ end
62
+
63
+ def api_key(key)
64
+ @api_key = key
65
+ end
66
+
67
+ def collector_url(url)
68
+ @collector_url = url
69
+ end
70
+
71
+ def start!
72
+ log.info "Registering resque evenitron collector with system key #{@system_key} to send messages to #{@collector_url}"
73
+ @client = Evenitron::CollectorClient.new @system_key, @collector_url, 'resque'
74
+ end
75
+
76
+ def job_queued(queue, item)
77
+ id = get_id(item)
78
+ log.info "Sending job queued message for job #{id}"
79
+ @client.job_queued :id => id, :queue => queue, :task => item[:class]
80
+ end
81
+
82
+ def job_started(job)
83
+ log.info "Sending job started message for job #{job.evenitron_id}"
84
+ @client.job_started :id => job.evenitron_id, :worker => job.worker.to_s, :task => job.payload['class']
85
+ end
86
+
87
+ def job_failed(job)
88
+ log.info "Sending job failed message for job #{job.evenitron_id}"
89
+ @client.job_failed :id => job.evenitron_id, :task => job.payload['class']
90
+ end
91
+
92
+ def job_complete(job)
93
+ log.info "Sending job complete message for job #{job.evenitron_id}"
94
+ @client.job_complete :id => job.evenitron_id, :task => job.payload['class']
95
+ end
96
+
97
+ private
98
+
99
+ def get_id(item)
100
+ item['evenitron_id']
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ module Resque
108
+
109
+ class << self
110
+
111
+ alias :base_push :push
112
+
113
+ def push(queue, item)
114
+ item['evenitron_id'] ||= Evenitron.generate_id
115
+ base_push(queue, item)
116
+ Evenitron::Resque.collector.job_queued queue, item
117
+ end
118
+
119
+ end
120
+
121
+ class Job
122
+
123
+ alias :base_perform :perform
124
+
125
+ def perform
126
+ begin
127
+ Evenitron.logger.debug "Processing job #{evenitron_id}"
128
+ Evenitron::Resque.collector.job_started self
129
+
130
+ result = base_perform
131
+ Evenitron::Resque.collector.job_complete self
132
+
133
+ Evenitron.logger.debug "Processed job #{evenitron_id}, result was #{result}"
134
+ result
135
+ rescue => e
136
+ Evenitron.logger.fatal "Exception occurred whilst processing job #{evenitron_id}"
137
+ Evenitron.logger.debug e.inspect
138
+ Evenitron::Resque.collector.job_failed self
139
+ raise e
140
+ end
141
+ end
142
+
143
+ def evenitron_id
144
+ @payload['evenitron_id']
145
+ end
146
+
147
+ end
148
+
149
+ end
data/lib/evenitron.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler/setup'
2
+ require 'base64'
3
+ require 'rest_client'
4
+ require 'securerandom'
5
+ require 'logger'
6
+ require 'json'
7
+
8
+ module Evenitron ; end
9
+
10
+ require_relative 'evenitron/evenitron'
11
+ require_relative 'evenitron/logger'
12
+ require_relative 'evenitron/collector_client'
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: evenitron
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Evenitron
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-25 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rest-client
16
+ requirement: &70228307216040 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.7
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70228307216040
25
+ description: Ruby client for Evenitron with hooks for delayed_job and resque
26
+ email:
27
+ - support@evenitron.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - lib/evenitron/collector_client.rb
33
+ - lib/evenitron/delayed_job.rb
34
+ - lib/evenitron/evenitron.rb
35
+ - lib/evenitron/logger.rb
36
+ - lib/evenitron/resque.rb
37
+ - lib/evenitron.rb
38
+ - README.markdown
39
+ homepage: https://github.com/evenitron/evenitron-ruby
40
+ licenses: []
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project: evenitron-ruby
59
+ rubygems_version: 1.8.15
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Ruby client for Evenitron
63
+ test_files: []
64
+ has_rdoc: