batsd-dash 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.rbenv-version +1 -0
  2. data/Gemfile +3 -0
  3. data/README.md +36 -79
  4. data/batsd-dash.gemspec +5 -9
  5. data/lib/batsd-dash.rb +9 -80
  6. data/lib/batsd-dash/app.rb +113 -0
  7. data/lib/batsd-dash/connection.rb +54 -0
  8. data/lib/batsd-dash/graph.rb +10 -2
  9. data/lib/batsd-dash/params.rb +10 -2
  10. data/lib/{public → batsd-dash/public}/css/d3.css +199 -100
  11. data/lib/{public → batsd-dash/public}/css/datetimepicker.css +0 -0
  12. data/lib/{public → batsd-dash/public}/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  13. data/lib/{public → batsd-dash/public}/css/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  14. data/lib/{public → batsd-dash/public}/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  15. data/lib/{public → batsd-dash/public}/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  16. data/lib/{public → batsd-dash/public}/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  17. data/lib/{public → batsd-dash/public}/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  18. data/lib/{public → batsd-dash/public}/css/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  19. data/lib/{public → batsd-dash/public}/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  20. data/lib/{public → batsd-dash/public}/css/images/ui-icons_222222_256x240.png +0 -0
  21. data/lib/{public → batsd-dash/public}/css/images/ui-icons_2e83ff_256x240.png +0 -0
  22. data/lib/{public → batsd-dash/public}/css/images/ui-icons_454545_256x240.png +0 -0
  23. data/lib/{public → batsd-dash/public}/css/images/ui-icons_888888_256x240.png +0 -0
  24. data/lib/{public → batsd-dash/public}/css/images/ui-icons_cd0a0a_256x240.png +0 -0
  25. data/lib/{public → batsd-dash/public}/css/jquery-ui.css +0 -0
  26. data/lib/batsd-dash/public/css/public.css +419 -0
  27. data/lib/batsd-dash/public/js/d3.js +4 -0
  28. data/lib/batsd-dash/public/js/dash.js +82 -0
  29. data/lib/{public → batsd-dash/public}/js/datetimepicker.js +0 -0
  30. data/lib/{public → batsd-dash/public}/js/datetimepicker.js~ +0 -0
  31. data/lib/{public → batsd-dash/public}/js/jquery-ui.js +0 -0
  32. data/lib/{public → batsd-dash/public}/js/jquery.js +0 -0
  33. data/lib/batsd-dash/public/js/nv.d3.js +5 -0
  34. data/lib/batsd-dash/version.rb +4 -2
  35. data/lib/{views → batsd-dash/views}/layout.haml +24 -6
  36. data/lib/batsd-dash/views/loading.haml +5 -0
  37. data/lib/batsd-dash/views/missing.haml +14 -0
  38. data/lib/batsd-dash/views/root.haml +37 -0
  39. data/lib/batsd-dash/views/view.haml +60 -0
  40. data/lib/sass/_charts.scss +37 -0
  41. data/lib/{batsd-dash/sass → sass}/public.scss +102 -27
  42. data/test/helper.rb +5 -16
  43. data/test/test_dash.rb +65 -69
  44. metadata +121 -101
  45. data/lib/batsd-dash/connection_pool.rb +0 -87
  46. data/lib/public/css/public.css +0 -106
  47. data/lib/public/js/d3.js +0 -4
  48. data/lib/public/js/dash.js +0 -110
  49. data/lib/public/js/flot.js +0 -6
  50. data/lib/public/js/nv.d3.js +0 -4
  51. data/lib/views/missing.haml +0 -6
  52. data/lib/views/root.haml +0 -0
  53. data/lib/views/view.haml +0 -18
  54. data/test/test_connection_pool.rb +0 -20
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ jruby-1.7.0
data/Gemfile CHANGED
@@ -7,3 +7,6 @@ group :test do
7
7
  gem "guard-minitest"
8
8
  end
9
9
 
10
+ group :development do
11
+ gem "compass"
12
+ end
data/README.md CHANGED
@@ -1,91 +1,48 @@
1
- batsd-dash
1
+ Batsd-dash
2
2
  ==========
3
3
 
4
- Configurable dashboard for [batsd-server](https://github.com/noahhl/batsd).
4
+ Batds-dash is a configurable dashboard for [batsd-server](https://github.com/noahhl/batsd).
5
+ The front-end uses [NVD3](http://nvd3.org/) for rendering graphs. The back-end uses
6
+ [Sinatra](github.com/sinatra/sinatra/) for the actual web application.
5
7
 
6
- ## Setup
8
+ The application server is designed to use a threaded [connection pool](https://github.com/mperham/connection_pool).
9
+ Thus, it is suggested that you run Batsd-dash on JRuby or Rubinius in order
10
+ to take full advantage of threaded processing.
7
11
 
8
- ### Install
12
+ ### Documentation
9
13
 
10
- To install batsd-dash, simply install the gem
14
+ * [Installation and
15
+ Configuration](https://github.com/mikeycgto/batsd-dash/wiki/Installation-and-Configuration)
16
+ * [Running the
17
+ Application](https://github.com/mikeycgto/batsd-dash/wiki/Running-the-Application)
18
+ * [Viewing
19
+ Graphs](https://github.com/mikeycgto/batsd-dash/wiki/Viewing-Graphs)
20
+ * [Data API](https://github.com/mikeycgto/batsd-dash/wiki/Data-API)
21
+ * [Custom Pages](https://github.com/mikeycgto/batsd-dash/wiki/Custom-Pages)
22
+ * [Contributing](https://github.com/mikeycgto/batsd-dash/wiki/Contributing)
11
23
 
12
- gem install batsd-dash
24
+ ### About
13
25
 
14
- ### Configuration
26
+ This is project is maintained and developed by [@mikeycgto](https://twitter.com/mikeycgto)
27
+ and [@btoconnor](https://twitter.com/btoconnor) mainly for use on [BreakBase](http://breakbase.com).
15
28
 
16
- Here is a sample rackup file (`config.ru`):
17
-
18
- require 'batsd-dash'
29
+ ### License
19
30
 
20
- # set batsd server setting
21
- BatsdDash::ConnectionPool.settings = { host:'localhost', port: 8127, pool_size: 8 }
31
+ Copyright (c) 2012 Michael J Coyne & Brian O'Connor
22
32
 
23
- # run the app
24
- run BatsdDash::App
33
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
34
+ software and associated documentation files (the "Software"), to deal in the Software
35
+ without restriction, including without limitation the rights to use, copy, modify,
36
+ merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
37
+ permit persons to whom the Software is furnished to do so, subject to the following
38
+ conditions:
25
39
 
26
- Rack is very powerful. You can password protect your batsd-dash instance
27
- by using `Rack::Auth::Basic` or `Rack::Auth::Digest::MD5`.
40
+ The above copyright notice and this permission notice shall be included in all copies
41
+ or substantial portions of the Software.
28
42
 
29
- ## Viewing Graphs
30
-
31
- Graphs are rendered using [nv.d3](http://nvd3.com/), a powerful graph
32
- and visualization library.
33
-
34
- Since rendering is all done on the client, we make use of hash based
35
- navigation in order to reduce the amount of requests while still
36
- maintaining 'linkability'.
37
-
38
- For example, to view a graph for the counter `a.b` statistic, you would need
39
- to make the following request from your browser:
40
-
41
- /graph#counters=a.b
42
-
43
- The graph view will provide you with a date time picker to make selecting
44
- different time ranges easier. Graphs are updated when you press the
45
- 'View' button or when the URL is updated.
46
-
47
- It's possible to view more than one metric at the same time. To do this,
48
- visit the following route from your browser:
49
-
50
- /graph#counters=a.b,c.d
51
-
52
- You can also view different datatypes at the same time:
53
-
54
- /graph#counters=a.b&timers=x.y
55
-
56
- __NOTE__: As of now, a single y-axis is used when datatypes are mixed.
57
- Soon, we will add support for multiple axis when viewing mixed types.
58
-
59
- ## Data API
60
-
61
- The application provides a simple JSON-based API for accessing data from
62
- the batds data server. The data API accepts similar parameters as the
63
- graph view but uses traditional query strings instead:
64
-
65
- /data?counters[]=a.b&counters[]=c.d&timers[]=x.y
66
-
67
- The data API also accepts a `start` and `stop` unix timestamp parameter
68
- for accessing different ranges of data. Note that, the data API will
69
- only respond with JSON if the `Accept` header to set to `application/json`!
70
-
71
- ## Graph and Render Options
72
-
73
- 1. Zerofill:
74
- __TODO__ Add details about zerofill
75
-
76
-
77
- ## Development
78
-
79
- ### Asset Management
80
-
81
- We use Sass for CSS within this project. If you make any changes to the Sass
82
- files, ensure you recompile the CSS. This is done by running:
83
-
84
- compass compile --force --output-style compact --environment production --sass-dir lib/batsd-dash/sass --css-dir lib/public/css
85
-
86
- Additionally, it is highly recommended you use thin for development since this
87
- app uses EventMachine.
88
-
89
- ## About
90
-
91
- This is project is maintained and developed by the people behind [BreakBase](http://breakbase.com) ([@mikeycgto](https://twitter.com/mikeycgto) and [@btoconnor](https://twitter.com/btoconnor))
43
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
44
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
45
+ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
46
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
47
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
48
+ DEALINGS IN THE SOFTWARE.
data/batsd-dash.gemspec CHANGED
@@ -4,7 +4,7 @@ require 'batsd-dash/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "batsd-dash"
7
- s.version = BatsdDash::VERSION
7
+ s.version = Batsd::Dash::VERSION
8
8
 
9
9
  s.authors = ["mikeycgto", "btoconnor"]
10
10
  s.email = ["mikeycgto@gmail.com", "gatzby3jr@gmail.com"]
@@ -21,18 +21,14 @@ Gem::Specification.new do |s|
21
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
22
  s.require_paths = ["lib"]
23
23
 
24
- # specify any dependencies here; for example:
25
- s.add_dependency "sinatra"
26
- s.add_dependency "sinatra-contrib"
27
- s.add_dependency "sinatra-synchrony", "~> 0.3.2"
24
+ s.add_dependency "connection_pool"
28
25
 
26
+ s.add_dependency "sinatra"
29
27
  s.add_dependency "haml"
30
- s.add_dependency "yajl-ruby"
31
28
 
32
29
  s.add_development_dependency "rake"
33
30
  s.add_development_dependency "minitest"
34
- s.add_development_dependency "mocha"
35
- s.add_development_dependency "turn"
36
31
 
37
- s.add_development_dependency "thin"
32
+ s.add_development_dependency "sinatra-contrib"
33
+ s.add_development_dependency "mocha"
38
34
  end
data/lib/batsd-dash.rb CHANGED
@@ -1,83 +1,12 @@
1
- require 'yajl'
2
- require 'sinatra/base'
3
- require 'sinatra/synchrony'
4
- #require 'sinatra/reloader' if ENV['RACK_ENV'] == 'development'
5
-
6
- %w[connection_pool graph params version].each { |file| require "batsd-dash/#{file}" }
7
-
8
- module BatsdDash
9
- class App < Sinatra::Base
10
- #configure(:development) { register Sinatra::Reloader }
11
-
12
- configure do
13
- register Sinatra::Synchrony
14
- helpers ParamsHelper, GraphHelper, ConnectionHelpers
15
-
16
- set :haml, :format => :html5
17
-
18
- EM::Synchrony.next_tick { ConnectionPool::initialize_connection_pool }
19
- end
20
-
21
- helpers do
22
- def render_error(msg)
23
- render_json 400, error: msg
24
- end
25
-
26
- def render_json(code = 200, json)
27
- halt code, String === json ? json : Yajl::Encoder.encode(json)
28
- end
29
- end
30
-
31
- get "/" do
32
- haml :root
33
- end
34
-
35
- get "/version", :provides => :json do
36
- render_json version: BatsdDash::VERSION
37
- end
38
-
39
- get "/available", :provides => :json do
40
- connection_pool.async_available_list.callback do |json|
41
- render_json json
42
- end
43
- end
44
-
45
- # this route renders the template (with codes for the graph)
46
- get "/graph", :provides => :html do
47
- haml :view
48
- end
49
-
50
- # actual data API route
51
- get "/data", :provides => :json do
52
- statistics = parse_statistics
53
- range = parse_time_range
54
-
55
- return render_error('Invalid time range') unless range
56
- return render_error('Invalid metrics') if statistics.empty?
57
-
58
- results = []
59
- options = { range: range.dup.map { |n| n * 1000 } }
60
-
61
- statistics.each do |datatype, metrics|
62
- metrics.each do |metric|
63
- statistic = "#{datatype}:#{metric}"
64
- deferrable = connection_pool.async_values(statistic, range)
65
-
66
- deferrable.errback { |e| return render_error(e.message) }
67
- deferrable.callback do |json|
68
- options[:interval] ||= json['interval']
69
- options[:zero_fill] = !statistic.start_with?('gauges') && params[!:no_zero_fill]
70
-
71
- points = json[statistic] || []
72
- values = values_for_graph(points, options)
73
-
74
- results << { key: metric, type: datatype[0..-2], values: values }
75
- end
76
- end
77
- end
78
-
79
- cache_control :no_cache, :no_store
80
- render_json range: options[:range], interval: options[:interval], results: results
1
+ module Batsd
2
+ module Dash
3
+ class << self
4
+ attr_accessor :config
81
5
  end
82
6
  end
83
7
  end
8
+
9
+ require 'batsd-dash/version'
10
+ require 'batsd-dash/connection'
11
+ require 'batsd-dash/params'
12
+ require 'batsd-dash/graph'
@@ -0,0 +1,113 @@
1
+ require 'sinatra/base'
2
+ require 'haml'
3
+ require 'json'
4
+
5
+ module Batsd::Dash
6
+ class App < Sinatra::Base
7
+ configure(:development) { disable :show_exceptions }
8
+
9
+ configure do
10
+ helpers ParamsHelper, GraphHelper
11
+ set :haml, format: :html5
12
+ set :raise_errors, false
13
+
14
+ config = Batsd::Dash::config.dup || {}
15
+
16
+ set :views, [views, config.delete(:view_path)].compact
17
+ set :host, config.delete(:host) || 'localhost'
18
+ set :port, config.delete(:port) || 8127
19
+
20
+ set :view_names, [:root, :view, :missing, :layout, :loading]
21
+
22
+ @connection_pool = ConnectionPool.new(config) do
23
+ Connection.new(host, port)
24
+ end
25
+ end
26
+
27
+ helpers do
28
+ def find_template(views, name, engine, &block)
29
+ path = settings.view_names.include?(name) ? views.first : views.last
30
+
31
+ super path, name, engine, &block
32
+ end
33
+
34
+ def render_error(msg)
35
+ render_json 400, error: msg
36
+ end
37
+
38
+ def render_json(code = 200, json)
39
+ halt code, String === json ? json : JSON.dump(json)
40
+ end
41
+
42
+ def connection_pool
43
+ @connection_pool ||= self.class.instance_variable_get(:@connection_pool)
44
+ end
45
+ end
46
+
47
+ error Connection::SocketError do
48
+ render_json error: env['sinatra.error'].message
49
+ end
50
+
51
+ get "/", provides: :html do
52
+ haml :root
53
+ end
54
+
55
+ get "/graph", provides: :html do
56
+ haml :view
57
+ end
58
+
59
+ get %r[/([A-Za-z0-9-_]+)$], provides: :html do
60
+ begin
61
+ haml params[:captures].first.to_sym, locals: { user_template: true }
62
+ rescue Errno::ENOENT
63
+ haml :missing
64
+ end
65
+ end
66
+
67
+ get "/version", provides: :json do
68
+ render_json version: BatsdDash::VERSION
69
+ end
70
+
71
+ get "/available", provides: :json do
72
+ connection_pool.with do |conn|
73
+ render_json conn.available
74
+ end
75
+ end
76
+
77
+ # actual data API route
78
+ get "/data", :provides => :json do
79
+ cache_control :no_cache, :no_store
80
+
81
+ statistics = parse_statistics
82
+ range = parse_time_range
83
+
84
+ return render_error('Invalid time range') unless range
85
+ return render_error('Invalid metrics') if statistics.empty?
86
+
87
+ options = { range: range.dup.map { |n| n * 1000 } }
88
+ results = []
89
+
90
+ connection_pool.with do |conn|
91
+ statistics.each do |datatype, metrics|
92
+ metrics.each do |metric|
93
+ statistic = "#{datatype}:#{metric}"
94
+ json = conn.values(statistic, range)
95
+
96
+ next unless json
97
+
98
+ options[:interval] ||= json['interval']
99
+ options[:zero_fill] = statistic !~ /^gauges/i && !params[:no_zero_fill]
100
+
101
+ points = json[statistic] || []
102
+ values = values_for_graph(points, options)
103
+
104
+ results << { key: metric, type: datatype[0..-2], values: values }
105
+ end
106
+ end
107
+ end
108
+
109
+ render_json range: options[:range], interval: options[:interval], results: results
110
+ end
111
+ end
112
+ end
113
+
@@ -0,0 +1,54 @@
1
+ require 'socket'
2
+ require 'connection_pool'
3
+
4
+ module Batsd::Dash
5
+ class Connection
6
+ class SocketError < Exception; end
7
+
8
+ attr_reader :socket
9
+
10
+ def initialize(host, port)
11
+ @host = host
12
+ @port = port
13
+
14
+ connect_socket
15
+ end
16
+
17
+ def available
18
+ query 'available'
19
+ end
20
+
21
+ def values(statistic, range)
22
+ query "values #{statistic} #{range[0]} #{range[1]}"
23
+ end
24
+
25
+ private
26
+
27
+ def reset_socket
28
+ @socket = nil
29
+ end
30
+
31
+ def connect_socket
32
+ @socket = TCPSocket.new(@host, @port)
33
+
34
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT
35
+ reset_socket
36
+ end
37
+
38
+ def query(command, attempts = 0)
39
+ connect_socket unless socket
40
+
41
+ raise SocketError.new('Socket not Connected') if socket.nil?
42
+
43
+ socket.puts command
44
+ JSON.parse socket.gets
45
+
46
+ rescue Errno::EPIPE
47
+ reset_socket
48
+
49
+ if attempts < 5
50
+ query command, attempts + 1
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,10 +1,14 @@
1
1
  # helpers for parsing and validating input
2
- module BatsdDash
2
+ module Batsd::Dash
3
3
  module GraphHelper
4
4
  ##
5
5
  # This method works directly against values. It will tranform all
6
6
  # datapoint to an array where the first element is a milisecond
7
7
  # timestamp and the second is a float value data point.
8
+ #
9
+ # @param [Array] values array of datapoints
10
+ # @param [Hash] opts optional options hash
11
+ # @return [Array] modified values array
8
12
  def values_for_graph(values, opts = {})
9
13
  return values if values.empty?
10
14
 
@@ -37,7 +41,11 @@ module BatsdDash
37
41
  end
38
42
 
39
43
  ##
40
- # Transform a point at a given index
44
+ # Transform a point at a given index. Works against
45
+ # values array
46
+ #
47
+ # @param [Integer] index
48
+ # @param [Array] values array of values
41
49
  def transform_point_at!(index, values)
42
50
  data_pt = values[index]
43
51