stackify-api-ruby 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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +30 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +157 -0
  7. data/Rakefile +2 -0
  8. data/lib/generators/stackify/stackify_generator.rb +13 -0
  9. data/lib/generators/stackify/templates/stackify.rb +17 -0
  10. data/lib/stackify-api-ruby.rb +166 -0
  11. data/lib/stackify/authorization/authorizable.rb +61 -0
  12. data/lib/stackify/authorization/authorization_client.rb +31 -0
  13. data/lib/stackify/engine.rb +21 -0
  14. data/lib/stackify/env_details.rb +108 -0
  15. data/lib/stackify/error.rb +56 -0
  16. data/lib/stackify/errors_governor.rb +65 -0
  17. data/lib/stackify/http_client.rb +50 -0
  18. data/lib/stackify/logger_client.rb +71 -0
  19. data/lib/stackify/logger_proxy.rb +35 -0
  20. data/lib/stackify/logs_sender.rb +78 -0
  21. data/lib/stackify/metrics/metric.rb +68 -0
  22. data/lib/stackify/metrics/metric_aggregate.rb +52 -0
  23. data/lib/stackify/metrics/metrics.rb +88 -0
  24. data/lib/stackify/metrics/metrics_client.rb +238 -0
  25. data/lib/stackify/metrics/metrics_queue.rb +26 -0
  26. data/lib/stackify/metrics/metrics_sender.rb +32 -0
  27. data/lib/stackify/metrics/monitor.rb +34 -0
  28. data/lib/stackify/msgs_queue.rb +78 -0
  29. data/lib/stackify/rack/errors_catcher.rb +17 -0
  30. data/lib/stackify/schedule_task.rb +23 -0
  31. data/lib/stackify/scheduler.rb +79 -0
  32. data/lib/stackify/utils/backtrace.rb +36 -0
  33. data/lib/stackify/utils/configuration.rb +78 -0
  34. data/lib/stackify/utils/methods.rb +27 -0
  35. data/lib/stackify/utils/msg_object.rb +22 -0
  36. data/lib/stackify/version.rb +3 -0
  37. data/lib/stackify/workers/add_msg_worker.rb +9 -0
  38. data/lib/stackify/workers/auth_worker.rb +18 -0
  39. data/lib/stackify/workers/logs_sender_worker.rb +17 -0
  40. data/lib/stackify/workers/worker.rb +65 -0
  41. data/spec/spec_helper.rb +17 -0
  42. data/stackify-api-ruby.gemspec +25 -0
  43. metadata +137 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3058e14cea1be8ce39a5f5d8d76c6dd914421dba
4
+ data.tar.gz: 95d0089689b0fc904b05ac1cc8dedd4f3be392cc
5
+ SHA512:
6
+ metadata.gz: f0705aeca8ccfad2067d6ccb429a451a58f86e2d039f48d3de30fe9999f76746b8ce1f87e3ccedd85f9e1b16deaee50884ca47e124d69d96b26daec050b20854
7
+ data.tar.gz: c0a6af825600cb6c52ac130bd14b9841ecea688be13c25779ccc65275329b564982ad322f014bb1db62b319402f4293b817eebcee9e94460713575a067b2797b
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in stackify.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ stackify-api-ruby (1.0.0)
5
+ activesupport (~> 4.1, >= 4.1.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (4.1.4)
11
+ i18n (~> 0.6, >= 0.6.9)
12
+ json (~> 1.7, >= 1.7.7)
13
+ minitest (~> 5.1)
14
+ thread_safe (~> 0.1)
15
+ tzinfo (~> 1.1)
16
+ i18n (0.6.11)
17
+ json (1.8.1)
18
+ minitest (5.4.0)
19
+ rake (0.9.6)
20
+ thread_safe (0.3.4)
21
+ tzinfo (1.2.1)
22
+ thread_safe (~> 0.1)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ bundler (~> 1.6)
29
+ rake (~> 0)
30
+ stackify-api-ruby!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dolgishev Victor
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,157 @@
1
+ # Stackify
2
+
3
+ Stackify Logs and Metrics API for Ruby
4
+
5
+
6
+ Rails Installation
7
+ ------------------
8
+
9
+ ### Rails 3.x/4.x
10
+
11
+ Add the stackify gem to your Gemfile. In Gemfile:
12
+
13
+ gem 'stackify-api-ruby'
14
+
15
+ Run the bundle command to install it:
16
+
17
+ $ bundle
18
+
19
+ Or install it manually:
20
+
21
+ $ gem install stackify-api-ruby
22
+
23
+ After you install stackify-api-ruby you need to run the generator:
24
+
25
+ $ rails g stackify --api_key=your_api_key
26
+
27
+ The generator creates a file under config/initializers/stackify.rb configuring stackify-api-ruby with your API key. You can change default settings there.
28
+
29
+ Usage: Logging
30
+ ------------------
31
+
32
+ ### Rails Environment
33
+
34
+ stackify-api-ruby starts working with start of Rails. Every occured error will be cought and sent to Stackify automatically. The same situation with logs - you just use the Rails logger as usual:
35
+
36
+ Rails.logger.info "Some log message"
37
+
38
+ ### Other Environment
39
+
40
+ For using stackify-api-ruby gem within any Ruby application add to top of your main file:
41
+
42
+ require 'stackify-api-ruby'
43
+
44
+ After that you need to make base configuration:
45
+
46
+ Stackify.setup do |config|
47
+ config.api_key = "your_api_key"
48
+ config.env = :development
49
+ config.app_name = "Your's app name"
50
+ config.app_location = "/somewhere/public"
51
+ end
52
+
53
+ "api_key" - it's you key for Stackify. "app-location" - it's location of your application for Nginx/Apache(for Nginx it's value of 'root', for Apache it's value of 'DocumentRoot' at config files).
54
+
55
+ Gem has 3 modes of work - "both", "logging", "metrics". Mode "both" turns on both parts of gem - logging and metrics.
56
+ If you need ONLY logging or metrics use "logging" or "metrics" mode accordingly.
57
+
58
+ config.mode = :metrics
59
+
60
+ You can set minimal level of logs, which should be caught by gem:
61
+
62
+ config.log_level = :error
63
+
64
+ By default, gem sends logs to Stackify every 60 seconds, you can increase this value to higher if need:
65
+
66
+ config.send_interval = 60 #value in seconds, could not be less than 60 seconds
67
+
68
+ All logs, errors, and metrics are queued within the gem and uploaded on a background thread. By default, the maximum amount of logs is 1,000 log items, you can decrease this value:
69
+
70
+ config.queue_max_size = 600
71
+
72
+ To help prevent flooding the system there is a parameter - max amount of the same error per minute:
73
+
74
+ config.flood_limit = 100
75
+
76
+ If you want to use proxy for sendig request, you can do it in such way:
77
+
78
+ config.with_proxy = true
79
+ config.proxy_host = "127.0.0.1"
80
+ config.proxy_port = "8118"
81
+ config.proxy_user = nil
82
+ config.proxy_pass = nil
83
+
84
+ For logging own work stackify-api-rubystackify-api-rubygem uses such logger:
85
+
86
+ config.logger = Logger.new(File.join(Rails.root, "log", "stackify.log"))
87
+
88
+ After set up of logs you should wrap up your logger:
89
+
90
+ logger = Logger.new('mylog.log')
91
+ logger = Stackify::LoggerProxy.new(logger)
92
+
93
+
94
+ And last thing you need to do - call method "run":
95
+
96
+ Stackify.run #remember that this call is running in the background of your main script
97
+
98
+ Usage: Metrics
99
+ ------------------
100
+
101
+ There are four different types of metrics:
102
+
103
+ - **Gauge**: Keeps track of the last value that was set in the current minute
104
+
105
+ Stackify::Metrics.set_gauge "MyCategory", "MyGauge", 100
106
+
107
+ - **Counter**: Calculates the rate per minute
108
+
109
+ Stackify::Metrics.count "MyCategory", "MyCounter"
110
+
111
+ - **Average**: Calculates the average of all values in the current minute
112
+
113
+ Stackify::Metrics.average "MyCategory", "AverageSpeed", 200
114
+
115
+ - **Timer**: Calculates the average elapsed time for an operation in the current minute
116
+
117
+ t = Time.now
118
+ Stackify::Metrics.time "MyCategory", "ElapsedTime", t
119
+
120
+ - **Counter and Timer**: Composite of the Counter and Timer metrics for convenience
121
+
122
+ t = Time.now
123
+ Stackify::Metrics.count_and_time "Metric", "CounterWithTime", t
124
+
125
+ We can configure every metric with settings:
126
+
127
+ settings = MetricSettings.new
128
+ settings.autoreport_zero_if_nothing_reported = true
129
+ # or
130
+ settings.autoreport_last_value_if_nothing_reported = true
131
+ Stackify::Metrics.set_gauge "MyCategory", "MyGauge", 100 , settings
132
+
133
+ Note, "autoreport_last_value_if_nothing_reported" property has influence only on "average" metric.
134
+
135
+ Also there are two methods for getting last values of metrics:
136
+
137
+ - get_latest - return last value of certain metric
138
+
139
+ ``` Stackify::Metrics.get_latest "MyCategory", "MyCounter" ```
140
+
141
+ - get_latest_all_metrics - return all values of existed metrics
142
+
143
+ ``` Stackify::Metrics.get_latest_all_metrics```
144
+
145
+ ## Requirements
146
+ Ruby: 1.9/2.0/2.1
147
+
148
+ Rails: 3.x/4.x
149
+
150
+ Contributing
151
+ ------------------
152
+
153
+ 1. Fork it ( https://github.com/[my-github-username]/stackify/fork )
154
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
155
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
156
+ 4. Push to the branch (`git push origin my-new-feature`)
157
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+
@@ -0,0 +1,13 @@
1
+ require 'rails/generators/base'
2
+
3
+ class StackifyGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../../stackify/templates', __FILE__)
5
+
6
+ class_option :api_key, type: :string, required: true
7
+
8
+ desc 'Creates a Stackify initializer'
9
+ def copy_initializer
10
+ template 'stackify.rb', 'config/initializers/stackify.rb'
11
+ end
12
+
13
+ end
@@ -0,0 +1,17 @@
1
+ Stackify.setup do |config|
2
+ config.api_key = '<%= options[:api_key] %>'
3
+ #config.mode = :both
4
+ #config.app_name = "Your's app name"
5
+ #config.env = :development
6
+ #config.flood_limit = 100
7
+ #config.queue_max_size = 1000
8
+ #config.send_interval = 60 #sec
9
+ #config.log_level = :error
10
+ #config.logger = Logger.new(File.join(Rails.root, 'log', 'stackify.log'))
11
+ #config.logger.level = Logger::INFO
12
+ #config.with_proxy = false
13
+ #config.proxy_host = '127.0.0.1'
14
+ #config.proxy_port = '8118'
15
+ #config.proxy_user = nil
16
+ #config.proxy_pass = nil
17
+ end
@@ -0,0 +1,166 @@
1
+ require 'stackify/version'
2
+ require 'stackify/utils/methods'
3
+ require 'active_support/core_ext' unless defined? Rails
4
+
5
+ module Stackify
6
+
7
+ INTERNAL_LOG_PREFIX = '[Stackify]'.freeze
8
+ STATUSES = { working: 'working', terminating: 'terminating', terminated: 'terminated'}
9
+ MODES = { logging: :logging, metrics: :metrics, both: :both }
10
+
11
+ autoload :Backtrace, 'stackify/utils/backtrace'
12
+ autoload :MsgObject, 'stackify/utils/msg_object'
13
+ autoload :Configuration, 'stackify/utils/configuration'
14
+ autoload :HttpClient, 'stackify/http_client'
15
+ autoload :Authorizable, 'stackify/authorization/authorizable'
16
+ autoload :EnvDetails, 'stackify/env_details'
17
+ autoload :Scheduler, 'stackify/scheduler'
18
+ autoload :ScheduleTask, 'stackify/schedule_task'
19
+ autoload :Worker, 'stackify/workers/worker'
20
+ autoload :AuthWorker, 'stackify/workers/auth_worker'
21
+ autoload :LogsSenderWorker, 'stackify/workers/logs_sender_worker'
22
+ autoload :AddMsgWorker, 'stackify/workers/add_msg_worker'
23
+ autoload :MsgsQueue, 'stackify/msgs_queue'
24
+ autoload :LoggerClient, 'stackify/logger_client'
25
+ autoload :LogsSender, 'stackify/logs_sender'
26
+ autoload :LoggerProxy, 'stackify/logger_proxy'
27
+ autoload :StackifiedError, 'stackify/error'
28
+ autoload :ErrorsGovernor, 'stackify/errors_governor'
29
+ autoload :Metrics, 'stackify/metrics/metrics'
30
+
31
+ include Authorizable
32
+
33
+ class << self
34
+
35
+ attr_writer :config
36
+
37
+ def configuration
38
+ @config ||= Stackify::Configuration.new
39
+ end
40
+
41
+ def setup
42
+ yield(configuration) if block_given?
43
+ if configuration.is_valid?
44
+ @status = STATUSES[:working]
45
+ @workers = []
46
+ else
47
+ msg = "Stackify's configuration is not valid!"
48
+ configuration.errors.each do |error_msg|
49
+ msg += "\n"+error_msg
50
+ end
51
+ raise msg
52
+ end
53
+
54
+ end
55
+
56
+ def msgs_queue
57
+ @msgs_queue ||= Stackify::MsgsQueue.new
58
+ end
59
+
60
+ def logger_client
61
+ @logger_client ||= Stackify::LoggerClient.new
62
+ end
63
+
64
+ def logs_sender
65
+ @logs_sender ||= Stackify::LogsSender.new
66
+ end
67
+
68
+ def logger
69
+ self.configuration.logger
70
+ end
71
+
72
+ def shutdown_all caller_obj=nil
73
+ @workers.each do |worker|
74
+ worker.shutdown! unless worker.equal? caller_obj
75
+ end
76
+ end
77
+
78
+ def alive_adding_msg_workers
79
+ @workers.select{ |w| w.alive? && w.type == :add_msg }
80
+ end
81
+
82
+ def delete_worker worker
83
+ @workers.delete worker
84
+ end
85
+
86
+ def add_dependant_worker worker
87
+ @workers << worker
88
+ end
89
+
90
+ def status
91
+ @status
92
+ end
93
+
94
+ def log_internal_error msg
95
+ Stackify.logger.error (::Stackify::INTERNAL_LOG_PREFIX){ msg }
96
+ end
97
+
98
+ def internal_log level, msg
99
+ Stackify.logger.send(level.downcase.to_sym, Stackify::INTERNAL_LOG_PREFIX){ msg }
100
+ end
101
+
102
+ def run async = true
103
+ if Stackify.is_valid?
104
+ at_exit { make_remained_job }
105
+ t1 = Thread.new { Stackify.authorize }
106
+ case Stackify.configuration.mode
107
+ when MODES[:both]
108
+ t2 = start_logging
109
+ t3 = start_metrics
110
+ when MODES[:logging]
111
+ t2 = start_logging
112
+ when MODES[:metrics]
113
+ t3 = start_metrics
114
+ end
115
+ unless async
116
+ t1.join
117
+ t2.join if t2
118
+ end
119
+ end
120
+ end
121
+
122
+ def start_logging
123
+ Thread.new { Stackify.logs_sender.start}
124
+ end
125
+
126
+ def start_metrics
127
+ Thread.new { Stackify::Metrics.metrics_client.start }
128
+ end
129
+
130
+ def workers
131
+ @workers
132
+ end
133
+
134
+ def make_remained_job
135
+ @status = STATUSES[:terminating]
136
+ Stackify.msgs_queue.push_remained_msgs
137
+ end
138
+
139
+ def is_valid?
140
+ configuration.is_valid?
141
+ end
142
+
143
+ def terminating?
144
+ @status == STATUSES[:terminating]
145
+ end
146
+
147
+ def terminated?
148
+ @status == STATUSES[:terminated]
149
+ end
150
+
151
+ def working?
152
+ @status == STATUSES[:working]
153
+ end
154
+
155
+ def status= status
156
+ if STATUSES.has_value? status
157
+ @status = status
158
+ else
159
+ raise "method 'status=' should get one of arguments #{STATUSES.values}, not a #{status}"
160
+ end
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ require 'stackify/engine' if defined? Rails
@@ -0,0 +1,61 @@
1
+ module Stackify::Authorizable
2
+ autoload :AuthorizationClient, 'stackify/authorization/authorization_client'
3
+
4
+ def self.included(klass)
5
+ klass.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ @@authorized = false
10
+ @@auth_lock = Mutex.new
11
+ @@auth_client = nil
12
+
13
+ def authorize attempts=3, delay_time = 20
14
+ @@auth_lock.synchronize do
15
+ return unless @@auth_client.nil?
16
+ @@auth_client = Stackify::Authorizable::AuthorizationClient.new
17
+ @@auth_client.auth attempts, delay_time
18
+ end
19
+ end
20
+
21
+ def authorized?
22
+ @@auth_lock.synchronize do
23
+ @@authorized
24
+ end
25
+ end
26
+
27
+ def authorized!
28
+ @@authorized = true
29
+ end
30
+
31
+ def successfull_authorisation response
32
+ Stackify::EnvDetails.instance.update_auth_info JSON.parse(response.body)
33
+ Stackify.internal_log :info, 'Authorisation is finished successfully.'
34
+ end
35
+
36
+ def unsuccessfull_authorisation response, caller
37
+ Stackify.log_internal_error "Authorisation finally failed: #{response_string(response)}"
38
+ Stackify.shutdown_all caller unless @@authorized
39
+ end
40
+
41
+ def if_not_authorized failure_msg, &block
42
+ failure_msg += ', but Stackify module is not authorized'
43
+ if Stackify.authorized?
44
+ begin
45
+ block.call
46
+ rescue => e
47
+ Stackify.log_internal_error e.message
48
+ end
49
+ else
50
+ Stackify.log_internal_error failure_msg
51
+ end
52
+ end
53
+
54
+ def response_string r
55
+ return '' if r.nil?
56
+ "Code: #{r.try(:code)}, Message: '#{r.try(:msg)}'"
57
+ end
58
+
59
+ end
60
+
61
+ end