rack-monitor-opentsdb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a9afbff4ac650e92c2dbd3f916be7787e88b00f8
4
+ data.tar.gz: 7ede01a4ebbf70501ca057ea0243bdb14d910df8
5
+ SHA512:
6
+ metadata.gz: 41ebb0204e780f94c16e45d03d47c7fb6c544f9b04af119df80fd17c0cb21e03e92fd63bea8516063c9cfd98607d02a96694bac84e415a95e0777ae8d6bd7f29
7
+ data.tar.gz: 7cc1a7f1246e936cb4f1a503ba38e50447147847e6f237a0ad35a05b5e5d9302bdf7769e50342a56057f7e5edacb17a36b5f4c59f7ba786e472572521df96b2c
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - jruby-18mode # JRuby in 1.8 mode
5
+ - jruby-19mode # JRuby in 1.9 mode
6
+ - rbx-2.1.1
7
+ - 1.8.7
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rack-monitor-opentsdb (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rake (10.3.2)
11
+ rspec (3.1.0)
12
+ rspec-core (~> 3.1.0)
13
+ rspec-expectations (~> 3.1.0)
14
+ rspec-mocks (~> 3.1.0)
15
+ rspec-core (3.1.3)
16
+ rspec-support (~> 3.1.0)
17
+ rspec-expectations (3.1.1)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.1.0)
20
+ rspec-mocks (3.1.0)
21
+ rspec-support (~> 3.1.0)
22
+ rspec-support (3.1.0)
23
+
24
+ PLATFORMS
25
+ java
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ rack-monitor-opentsdb!
30
+ rake (~> 10.3, >= 10.3.2)
31
+ rspec (~> 3.1, >= 3.1.0)
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 FriendScout24 GmbH
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ rack-monitor-opentsdb
2
+ =====================
3
+
4
+ Rack middleware to monitor requests with OpenTSDB.
5
+
6
+ Usage:
7
+ ------
8
+
9
+ require 'sinatra'
10
+ require 'rack/monitor/opentsdb'
11
+
12
+ use Rack::Monitor::OpenTSDB, 'service.rest.hworld',
13
+ :host => 'opentsdb.example.com',
14
+ :port => 4242,
15
+ :report_interval => 60,
16
+ :tags => {
17
+ :host => `hostname`.chomp,
18
+ :env => ENV['RACK_ENV'] || 'development' }
19
+
20
+ get '/' do
21
+ 'Hello World'
22
+ end
23
+
24
+ Links:
25
+ ------
26
+
27
+ - [Homepage](http://friendscout24.github.io/rack-monitor-opentsdb)
28
+ - [Sources](http://github.com/friendscout24/rack-monitor-opentsdb)
29
+ - [Issues](http://github.com/friendscout24/rack-monitor-opentsdb/issues)
30
+ - [![Build Status](https://travis-ci.org/friendscout24/rack-monitor-opentsdb.svg?branch=master)](https://travis-ci.org/friendscout24/rack-monitor-opentsdb)
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake'
4
+
5
+ task :default => :spec
6
+
7
+ begin
8
+ require 'rspec/core/rake_task'
9
+ RSpec::Core::RakeTask.new(:spec)
10
+ rescue LoadError
11
+ task :spec do
12
+ abort 'RSpec is not available'
13
+ end
14
+ end
15
+
16
+ desc 'run console'
17
+ task :console do
18
+ require 'irb'
19
+ require 'irb/completion'
20
+ require 'rack/monitor/opentsdb'
21
+ ARGV.clear
22
+ IRB.start
23
+ end
@@ -0,0 +1,118 @@
1
+ module Rack
2
+ module Monitor
3
+ class OpenTSDB
4
+ class Reporter
5
+
6
+ MAX_CACHE = 1024
7
+
8
+ # stats has the following structure:
9
+ #
10
+ # {
11
+ # 'GET /monitored/path/1': {
12
+ # '<httpstatus>': [<time>, ...],
13
+ # ...
14
+ # },
15
+ # ...
16
+ # }
17
+ #
18
+ # The stats parameter must be extended with MonitorMixin. Like this it
19
+ # can be accessed synchronized.
20
+ def initialize(stats, host, port, key = 'unconfigured.rack.monitor', interval = 60, tags = {})
21
+ raise ArgumentError, "Interval less than zero" if interval < 0
22
+ raise ArgumentError, 'stats does not support synchronize' unless stats.respond_to? :synchronize
23
+
24
+ @interval = interval
25
+ @tags = tags
26
+ @stats = stats
27
+ @host = host
28
+ @port = port
29
+ @key = key
30
+ @tosend = []
31
+ start
32
+ end
33
+
34
+ def start
35
+ return if @run
36
+ @run = true
37
+ @th = Thread.new do
38
+ t = Time.now
39
+ while run?
40
+ t += @interval
41
+ (sleep(t - Time.now) rescue nil) and send_report rescue nil
42
+ end
43
+ end
44
+ end
45
+
46
+ def stop
47
+ @stats.synchronize do
48
+ @run = false
49
+ end
50
+ @th.join
51
+ end
52
+
53
+ private
54
+
55
+ def run?
56
+ @stats.synchronize do
57
+ @run
58
+ end
59
+ end
60
+
61
+ def send_report
62
+ timestamp = Time.now.to_i
63
+ data = copy_and_clear_stats
64
+ data.each do |url, url_stats|
65
+ verb, path = url.split(/ /)
66
+ url_stats.each do |status, timings|
67
+ tags = @tags.merge({:status => status, :verb => verb, :path => path}).map { |key, value| "#{key}=#{value}" }.join(' ')
68
+ @tosend.push("put #{@key}.min #{timestamp} #{timings.min} #{tags}")
69
+ @tosend.push("put #{@key}.max #{timestamp} #{timings.max} #{tags}")
70
+ @tosend.push("put #{@key}.num #{timestamp} #{timings.size} #{tags}")
71
+ @tosend.push("put #{@key}.avg #{timestamp} #{timings.inject(0.0) { |sum, e| sum + e } / timings.size} #{tags}")
72
+ end
73
+ end
74
+
75
+ # cleanup if cannot send anything, only keep MAX_CACHE entries to send
76
+ if @tosend.size > MAX_CACHE
77
+ @tosend.shift(@tosend.length - - MAX_CACHE)
78
+ end
79
+
80
+ send_to_socket
81
+ end
82
+
83
+ def send_to_socket
84
+ begin
85
+ @tosend.delete_if do |line|
86
+ # throws in first go NoMethodError: undefined method `puts' for nil:NilClass
87
+ # this is rescued and the connection is opened for retry
88
+ @socket.puts line
89
+ true
90
+ end
91
+ rescue IOError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE
92
+ # Don't retry immediately (preventing loops), next run will retry
93
+ @socket = nil
94
+ rescue
95
+ begin
96
+ @socket = TCPSocket.new(@host, @port)
97
+ retry
98
+ rescue
99
+ # will do no retry in this case
100
+ end
101
+ end
102
+ end
103
+
104
+ def copy_and_clear_stats
105
+ copy = {}
106
+ @stats.synchronize do
107
+ @stats.each do |url, url_stats|
108
+ copy[url] = url_stats
109
+ end
110
+ @stats.clear
111
+ end
112
+ copy
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Monitor
3
+ class OpenTSDB
4
+ module Version
5
+ MAJOR = 0
6
+ MINOR = 0
7
+ NANO = 1
8
+
9
+ VERSION = "#{MAJOR}.#{MINOR}.#{NANO}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ require 'rack/monitor/opentsdb/reporter'
2
+ require 'monitor' # MonitorMixin
3
+
4
+ module Rack
5
+ module Monitor
6
+ class OpenTSDB
7
+
8
+ class Stats < Hash
9
+ include MonitorMixin
10
+ end
11
+
12
+ def initialize(app, key, options={})
13
+ @app = app
14
+ @options = {
15
+ :host => 'localhost',
16
+ :port => 4242,
17
+ :key => key,
18
+ # If somebody has a better idea than defining a regex and gsub
19
+ # replacement tell me.
20
+ :track_regex => /^(\/[^\/]+).*?$/, # Regex to select trackable string
21
+ :track_replace => '\1', # gsub pattern to select trackable string
22
+ :report_interval => 60,
23
+ :tags => {}
24
+ }.merge(options)
25
+ @stats = Stats.new
26
+ @reporter = Reporter.new(@stats, @options[:host], @options[:port], @options[:key], @options[:report_interval], @options[:tags])
27
+ @reporter.start
28
+ end
29
+
30
+ def call(env, options={})
31
+ track_path = env['REQUEST_METHOD'] + ' ' + env['PATH_INFO'].gsub(@options[:track_regex], @options[:track_replace])
32
+
33
+ beginning_time = Time.now
34
+ status, headers, body = @app.call(env)
35
+ execution_time = (Time.now - beginning_time)
36
+
37
+ @stats.synchronize do
38
+ @stats[track_path] ||= {}
39
+ @stats[track_path][status] ||= []
40
+ @stats[track_path][status] << execution_time
41
+ end
42
+
43
+ [status, headers, body]
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'rack', 'monitor', 'opentsdb', 'version')
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'rack-monitor-opentsdb'
5
+ s.version = Rack::Monitor::OpenTSDB::Version::VERSION
6
+ s.licenses = ['ALv2']
7
+ s.summary = 'Rack Monitoring to OpenTSDB'
8
+ s.description = 'Send monitoring info to OpenTSDB'
9
+ s.authors = ['Rainer Jung']
10
+ s.email = 'rainer.jung@gmail.com'
11
+ s.homepage = 'http://github.com/friendscout24/rack-monitor-opentsdb'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
15
+ s.add_development_dependency 'rake', '~> 10.3', '>= 10.3.2'
16
+ s.add_development_dependency 'rspec', '~> 3.1', '>= 3.1.0'
17
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Monitor::OpenTSDB do
4
+
5
+ before :each do
6
+ @app = double(:app)
7
+ @env = {'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/path'}
8
+ @sut = Rack::Monitor::OpenTSDB.new(@app, 'test.monitor')
9
+ end
10
+
11
+ it 'starts with empty stats' do
12
+ expect(@sut.instance_variable_get('@stats')).to be_empty
13
+ end
14
+
15
+ describe :call do
16
+
17
+ it 'does return apps result' do
18
+ allow(@app).to receive(:call).and_return(['status', 'headers', 'body'])
19
+ expect(@sut.call @env).to eq(['status', 'headers', 'body'])
20
+ end
21
+
22
+ describe 'stats' do
23
+ before do
24
+ allow(@app).to receive(:call).and_return(['200', 'headers', 'body'])
25
+ @sut.call @env
26
+ @stats = @sut.instance_variable_get('@stats')
27
+ end
28
+
29
+ it 'includes path' do
30
+ expect(@stats).to include 'GET /path'
31
+ end
32
+
33
+ it 'includes status' do
34
+ expect(@stats['GET /path']).to include '200'
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ describe 'call with regexable path' do
42
+
43
+ before do
44
+ allow(@app).to receive(:call).and_return(['200', 'headers', 'body'])
45
+ @sut.call({'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/some/longer/path'})
46
+ @stats = @sut.instance_variable_get('@stats')
47
+ end
48
+
49
+ it 'includes path' do
50
+ # Default regex does shorten to first path-segment
51
+ expect(@stats).to include 'GET /some'
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Monitor::OpenTSDB::Reporter do
4
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'rack/monitor/opentsdb'
4
+
5
+ RSpec.configure do |config|
6
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-monitor-opentsdb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rainer Jung
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.3'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 10.3.2
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '10.3'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 10.3.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: rspec
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.1.0
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.1.0
53
+ description: Send monitoring info to OpenTSDB
54
+ email: rainer.jung@gmail.com
55
+ executables: []
56
+ extensions: []
57
+ extra_rdoc_files: []
58
+ files:
59
+ - ".travis.yml"
60
+ - Gemfile
61
+ - Gemfile.lock
62
+ - LICENSE
63
+ - README.md
64
+ - Rakefile
65
+ - lib/rack/monitor/opentsdb.rb
66
+ - lib/rack/monitor/opentsdb/reporter.rb
67
+ - lib/rack/monitor/opentsdb/version.rb
68
+ - rack-monitor-opentsdb.gemspec
69
+ - spec/monitor_spec.rb
70
+ - spec/reporter_spec.rb
71
+ - spec/spec_helper.rb
72
+ homepage: http://github.com/friendscout24/rack-monitor-opentsdb
73
+ licenses:
74
+ - ALv2
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.2.2
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Rack Monitoring to OpenTSDB
96
+ test_files: []