ddtop 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ddtop.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Genki Sugawara
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,47 @@
1
+ # ddtop
2
+
3
+ A tool that send to Datadog the data while execute `top`.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/ddtop.png)](http://badge.fury.io/rb/ddtop)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'ddtop'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ddtop
20
+
21
+ ## Usage
22
+
23
+ ```sh
24
+ $ export DD_API_KEY=...
25
+ $ ddtop # The outputted data is transmitted to Datadog.
26
+ top - 14:12:24 up 4:23, 4 users, load average: 2.57, 2.11, 1.43
27
+ Tasks: 116 total, 3 running, 113 sleeping, 0 stopped, 0 zombie
28
+ Cpu(s): 27.0%us, 6.9%sy, 0.0%ni, 65.9%id, 0.0%wa, 0.0%hi, 0.2%si, 0.0%st
29
+ Mem: 15343072k total, 1582800k used, 13760272k free, 19544k buffers
30
+ Swap: 0k total, 0k used, 0k free, 1124444k cached
31
+
32
+ PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
33
+ 16901 ec2-user 20 0 125m 6000 1984 R 99.8 0.0 0:03.86 perl
34
+ 19501 ec2-user 20 0 4871m 232m 4536 S 69.8 1.5 5:48.33 ruby
35
+ 25238 ec2-user 20 0 107m 2024 1568 S 3.3 0.0 0:13.79 bash
36
+ ...
37
+ ```
38
+
39
+ ![datadog-metrics.png](https://bitbucket.org/winebarrel/ddtop/downloads/datadog-metrics.png)
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it ( http://github.com/<my-github-username>/ddtop/fork )
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bin/ddtop ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
+ require 'ddtop'
4
+
5
+ civis = system('tput civis')
6
+
7
+ trap(:INT) do
8
+ puts "\e[0m"
9
+ system('tput cnorm') if civis
10
+ exit
11
+ end
12
+
13
+ Ddtop.run
data/ddtop.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ddtop/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'ddtop'
8
+ spec.version = Ddtop::VERSION
9
+ spec.authors = ['Genki Sugawara']
10
+ spec.email = ['sgwr_dts@yahoo.co.jp']
11
+ spec.summary = %q{A tool that send to Datadog the data while execute top}
12
+ spec.description = %q{A tool that send to Datadog the data while execute top}
13
+ spec.homepage = 'https://bitbucket.org/winebarrel/ddtop'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'dogapi'
22
+ spec.add_development_dependency 'bundler', '~> 1.5'
23
+ spec.add_development_dependency 'rake'
24
+ end
@@ -0,0 +1,176 @@
1
+ class Ddtop::Ddtop
2
+ TOP_CMD = 'top -b'
3
+ PREFIX = 'ddtop'
4
+
5
+ def initialize
6
+ api_key = ENV['DD_API_KEY']
7
+ host = %x[hostname -f 2> /dev/null].strip
8
+ host = Socket.gethostname if host.empty?
9
+ @dog = Dogapi::Client.new(api_key, nil, host)
10
+ end
11
+
12
+ def run
13
+ summary, fields = nil
14
+ values = []
15
+
16
+ head_hook = proc do
17
+ if summary and not values.empty?
18
+ emit_points(summary, fields, values)
19
+ values.clear
20
+ end
21
+ end
22
+
23
+ top_each_lien(head_hook) do |top, line|
24
+ if head?(line)
25
+ summary, fields = parse_header(top, line)
26
+ elsif not line.empty?
27
+ values << line.split(/\s+/, fields.length)
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def top_each_lien(head_hook)
35
+ cmd = [TOP_CMD, ARGV].flatten.join(' ')
36
+
37
+ IO.popen(cmd) do |top|
38
+ while line = gets_with_print(top, &head_hook)
39
+ yield(top, line)
40
+ end
41
+ end
42
+ end
43
+
44
+ def gets_with_print(top)
45
+ line = top.gets
46
+
47
+ if head?(line)
48
+ @win_rows = win_rows
49
+ @curr_row = 0
50
+ yield if block_given?
51
+ clear_screen
52
+ end
53
+
54
+ @curr_row += 1
55
+
56
+ if @curr_row < @win_rows
57
+ print line.sub(/\s+\Z/, "\n")
58
+ elsif @curr_row == @win_rows
59
+ print line.sub(/\s+\Z/, '')
60
+ end
61
+
62
+ return line.strip
63
+ end
64
+
65
+ def head?(line)
66
+ line =~ /\Atop\b/
67
+ end
68
+
69
+ def parse_header(top, line)
70
+ summary_lines = [line] + (1..4).map { gets_with_print(top) }
71
+ gets_with_print(top)
72
+ fields_line = gets_with_print(top)
73
+
74
+ summary = parse_summary(summary_lines)
75
+ fields = fields_line.split(/\s+/)
76
+
77
+ [summary, fields]
78
+ end
79
+
80
+ def parse_summary(lines)
81
+ summary = {}
82
+ first_line = lines[0].split(/[\s,]+/)
83
+ summary[:time] = parse_time(first_line[2])
84
+ load_avg = first_line.values_at(-3, -2, -1)
85
+ summary[:load_avg] = Hash[*['1m', '5m', '15m'].zip(load_avg).flatten]
86
+ summary[:tasks] = line_to_items(lines[1]) {|i| i.each_slice(2).map {|i, j| [j, i] } }
87
+ summary[:cpu] = line_to_items(lines[2]) {|i| i.map {|j| j.split('%').values_at(1, 0) } }
88
+ summary[:mem] = line_to_items(lines[3]) {|i| i.each_slice(2).map {|i, j| [j, i] } }
89
+ summary[:swap] = line_to_items(lines[4]) {|i| i.each_slice(2).map {|i, j| [j, i] } }
90
+ return summary
91
+ end
92
+
93
+ def parse_time(time)
94
+ now = Time.now
95
+ Time.mktime(now.year, now.mon, now.day, *time.split(':').map {|i| i.to_i })
96
+ end
97
+
98
+ def line_to_items(line)
99
+ items = yield(line.split(/[\s,:]+/).slice(1..-1))
100
+ Hash[*items.flatten]
101
+ end
102
+
103
+ def emit_points(summary, names, values)
104
+ time = summary.delete(:time)
105
+ fields = aggregate_fields(names, values)
106
+ emit_points0(time, summary)
107
+ emit_points0(time, fields)
108
+ end
109
+
110
+ def emit_points0(time, metrics)
111
+ metrics.each do |category, items|
112
+ items.each do |name, value|
113
+ metric_name = [PREFIX, category, name].join('.')
114
+
115
+ Thread.start do
116
+ @dog.emit_point(metric_name, expand_value(value), :timstamp => time)
117
+ end
118
+ end
119
+ end
120
+ rescue Timeout::Error
121
+ # nothing to do
122
+ end
123
+
124
+ def aggregate_fields(names, values)
125
+ aggregated = {}
126
+
127
+ values.map {|v|
128
+ h = Hash[*names.zip(v).flatten]
129
+ %w(PID USER PR NI S).each {|k| h.delete(k) }
130
+ h['TIME+'] = parse_time_plus(h['TIME+'])
131
+ h
132
+ }.sort_by {|h|
133
+ h['COMMAND']
134
+ }.chunk {|h|
135
+ h['COMMAND']
136
+ }.each {|cmd, hs|
137
+ total = {}
138
+
139
+ hs.each do |h|
140
+ h.delete('COMMAND')
141
+
142
+ h.each do |k, v|
143
+ total[k] ||= 0.0
144
+ total[k] += v.to_f
145
+ end
146
+ end
147
+
148
+ aggregated['process.' + cmd] = total
149
+ }
150
+
151
+ return aggregated
152
+ end
153
+
154
+ def parse_time_plus(time)
155
+ t = time.split(/[.:]/).map {|i| i.to_f }
156
+ t[0] * 60 + t[1] + t[2] / 100
157
+ end
158
+
159
+ def win_rows
160
+ `stty -a`.split("\n").first.split(';')[1].strip.split(/\s+/, 2)[1].to_i
161
+ end
162
+
163
+ def clear_screen
164
+ print "\e[2J\n"
165
+ end
166
+
167
+ def expand_value(value)
168
+ n = 0
169
+
170
+ case value
171
+ when /m\Z/ then n = 1
172
+ end
173
+
174
+ value.to_f * (1024 ** n)
175
+ end
176
+ end
@@ -0,0 +1,3 @@
1
+ module Ddtop
2
+ VERSION = '0.0.1'
3
+ end
data/lib/ddtop.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Ddtop
2
+ def self.run
3
+ ::Ddtop::Ddtop.new.run
4
+ end
5
+ end
6
+
7
+ require 'dogapi'
8
+ require 'socket'
9
+ require 'thread'
10
+
11
+ require 'ddtop/version'
12
+ require 'ddtop/ddtop'
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ddtop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Genki Sugawara
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dogapi
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.5'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.5'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A tool that send to Datadog the data while execute top
63
+ email:
64
+ - sgwr_dts@yahoo.co.jp
65
+ executables:
66
+ - ddtop
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - LICENSE.txt
73
+ - README.md
74
+ - Rakefile
75
+ - bin/ddtop
76
+ - ddtop.gemspec
77
+ - lib/ddtop.rb
78
+ - lib/ddtop/ddtop.rb
79
+ - lib/ddtop/version.rb
80
+ homepage: https://bitbucket.org/winebarrel/ddtop
81
+ licenses:
82
+ - MIT
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 1.8.23
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: A tool that send to Datadog the data while execute top
105
+ test_files: []