rack-monitor-opentsdb 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 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: []