Skip to content

Commit e4b9a17

Browse files
committed
added logs and metrics
1 parent 0204fd2 commit e4b9a17

39 files changed

Lines changed: 2016 additions & 3 deletions

Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source 'https://rubygems.org'
2+
3+
# Specify your gem's dependencies in stackify.gemspec
4+
gemspec

Gemfile.lock

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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!

README.md

Lines changed: 156 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,157 @@
1-
stackify-api-ruby
2-
=================
1+
# Stackify
32

4-
Stackify API for Ruby
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

Rakefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require 'bundler/gem_tasks'
2+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/stackify-api-ruby.rb

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 commit comments

Comments
 (0)