metricsgeek 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.md ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2013 Evan Chan
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,71 @@
1
+ Easily grok and analyze your application metrics from a cluster, in real time, with this command line tool /
2
+ gem. Compatible with HTTP JSON metrics routes as exposed by Coda Hale's metrics package, Jolokia, ruby-
3
+ metrics, or any metrics package that exposes metrics via an HTTP route as JSON.
4
+
5
+ The only assumption is that the JSON consists of deeper and deeper levels of hashes.
6
+
7
+ * Get metrics in real time, no need to wait minutes for a central service to index your logs
8
+ * Easily see load and traffic imbalances across a cluster
9
+ * Select and filter metrics easily using wildcards
10
+ * Group and summarize metrics
11
+
12
+ Quick start
13
+ ===========
14
+ Install this gem:
15
+
16
+ gem install metricsgeek
17
+
18
+ To get a list of metrics keys from your servers:
19
+
20
+ metricsgeek --list_keys --from "server[1..8].abc.com"
21
+
22
+ To get two metrics, one a pattern, from your servers:
23
+
24
+ metricsgeek --select jvm.uptime,com.abc.*.latency.mean --from "server[1..8].abc.com"
25
+
26
+ Port and Route
27
+ ==============
28
+ The port defaults to 7000 and the route defaults to /metricz. Both of these can be set via the --port and
29
+ --route options.
30
+
31
+ Host selection
32
+ ==============
33
+ You can select multiple hosts to query in two ways.
34
+
35
+ First, individual hosts can be passed, comma separated, to the --from option.
36
+
37
+ Second, you can pass in a range of numbers within brackets, and this is automatically expanded to separate
38
+ hosts. For example,
39
+
40
+ --from server[1..3,5,8..11].dc
41
+
42
+ expands to
43
+
44
+ --from server1.dc,server2.dc,server3.dc,server5.dc,server8.dc,server9.dc,server10.dc,server11.dc
45
+
46
+ Metrics selection
47
+ =================
48
+ You can use the --list_keys option together with --from to list all the metrics keys available for querying.
49
+ This is done by flattening the JSON output from all the routes, with successive levels of JSON separated by
50
+ dots in the flat metric key.
51
+
52
+ The --select option takes one or more metric keys separated by commas. So,
53
+
54
+ --select jvm.uptime,my.app.*.latency
55
+
56
+ Note that you can use the * wildcard character to select multiple metrics keys, as well as [12] style
57
+ character selection.
58
+
59
+ Grouping metrics
60
+ ================
61
+ Intead of displaying metrics from every host on a separate line, you can group the metrics together from all hosts using one of four functions: sum, min, max, avg. For example, to compute the average uptime of all hosts:
62
+
63
+ metricsgeek --select jvm.uptime --from "server[1..8].dc" --group avg
64
+
65
+ Or, to sum up the POST rate from metric com.abc.webservice.posts.m1 across the cluster:
66
+
67
+ metricsgeek --select com.abc.webservice.posts.m1 --from "server[1..8].dc" --group sum
68
+
69
+ How to contribute
70
+ =================
71
+ Pull requests are welcome!
data/bin/metricsgeek ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ require "trollop"
3
+ require "metricsgeek"
4
+
5
+ opts = Trollop::options do
6
+ opt :select, "metrics keys or key glob patterns, comma separated", :type => :string
7
+ opt :from, "host names, comma separated, may contain [1..10] ranges", :type => :string
8
+ opt :port, "port #", :default => 7000
9
+ opt :route, "JSON metrics route", :default => "metricz"
10
+ opt :list_keys, "Just list the keys"
11
+ opt :group, "Group metrics from hosts using min/max/avg/sum", :type => :string
12
+ opt :timeout, "Request timeout in seconds", :type => :int
13
+ end
14
+
15
+ class Array
16
+ def sum
17
+ self.inject(0) { |a, s| a + s }
18
+ end
19
+
20
+ def avg
21
+ self.sum * 1.0 / self.length
22
+ end
23
+ end
24
+
25
+ def format(number)
26
+ "%7.3f" % [number]
27
+ end
28
+
29
+ expanded_hosts = opts[:from].split(",").map { |expr| MetricsDownloader.expand_host_expr(expr) }.flatten
30
+ urls = MetricsDownloader.create_urls_from_host_params(expanded_hosts, opts[:port], opts[:route])
31
+ json_trees = MetricsDownloader.download_and_parse_json_from_urls(urls, opts[:timeout])
32
+
33
+ if opts[:list_keys]
34
+ MetricsParser.list_keys_for_hashes(json_trees).each { |key| puts key }
35
+ else
36
+ keys = opts[:select].split(",")
37
+ host_key_value = expanded_hosts.zip(json_trees).map do |host, json_tree|
38
+ matching_key_values = MetricsParser.glob_key_values(json_tree, keys)
39
+ matching_key_values.map do |flatkey, value|
40
+ {:host => host, :key => flatkey, :value => value}
41
+ end
42
+ end.flatten
43
+ host_key_value.group_by { |hash| hash[:key] }.each do |key, hashes|
44
+ puts "\n\nFor key: #{key}"
45
+ if opts[:group]
46
+ values = hashes.map { |h| h[:value] }
47
+ opts[:group].split(",").each { |func| puts " #{func}: #{format(values.send(func.to_sym))}" }
48
+ else
49
+ hashes.each { |h| puts " #{h[:host]}\t#{format(h[:value])}"}
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,48 @@
1
+ require "json"
2
+ require "yaml"
3
+ require "rest_client"
4
+
5
+ module MetricsDownloader
6
+ # @returns a list of parsed JSON hashes from each url
7
+ def self.download_and_parse_json_from_urls(urls, timeout = 5)
8
+ threads = urls.map do |url|
9
+ Thread.new do
10
+ Thread.current[:output] = begin
11
+ body = RestClient::Request.execute(:method => :get, :url => url,
12
+ :timeout => timeout,
13
+ :open_timeout => timeout)
14
+ JSON.parse(body)
15
+ rescue
16
+ # TODO: log the exception?
17
+ STDERR.puts "WARNING: Unable to parse JSON from #{url}"
18
+ {}
19
+ end
20
+ end
21
+ end
22
+
23
+ threads.map { |t| t.join; t[:output] }
24
+ end
25
+
26
+ # @param host_list A list of host strings, without the port, optionally with http:// prefix
27
+ # @route the string after the / for getting metrics, defaults to "metricz/"
28
+ # @returns a list of URLs from which to retrieve data
29
+ def self.create_urls_from_host_params(host_list, port, route = "metricz/")
30
+ host_list.map do |host|
31
+ host = host[7..-1] if host.start_with?("http:")
32
+ "http://#{host}:#{port}/#{route}"
33
+ end
34
+ end
35
+
36
+ # @param host_expr A host string with an embedded range [1..18] or individual numbers,
37
+ # [1..5,7,9] => 1,2,3,4,5,7,9 will be substituted
38
+ # @returns An expanded list of hosts, basically with the range expanded into numbers.
39
+ def self.expand_host_expr(host_expr)
40
+ if host_expr =~ /(\[([0-9.,]+)\])/
41
+ embedded_expr = $1
42
+ numbers = $2.split(",").map { |item| item.include?("..") ? eval(item).to_a : [item.to_i] }.flatten
43
+ numbers.map { |n| host_expr.gsub(embedded_expr, n.to_s) }
44
+ else
45
+ [host_expr]
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,41 @@
1
+ # Core functions for parsing metrics from JSON web output, such as that by Coda Hale's metrics library
2
+
3
+ module MetricsParser
4
+ # @param tree A hash tree such as that generated by JSON.parse()
5
+ # @param prefix A list of namespace strings to prefix the generated keys by
6
+ # @returns A flattened hash where the key is each level of the tree joined by '.'
7
+ def self.flatten_tree(json_tree, prefix=[])
8
+ flat_tree = {}
9
+ pairs = json_tree.each do |key, value|
10
+ newkey = (prefix + [key]).join(".")
11
+ flat_tree[newkey] = value
12
+ if value.is_a?(Hash)
13
+ flat_tree.update(MetricsParser.flatten_tree(value, prefix + [key]))
14
+ end
15
+ end
16
+ flat_tree
17
+ end
18
+
19
+ # @returns a list of sorted, flattened hash keys for the input JSON hash tree
20
+ def self.list_keys_for_hash(json_tree)
21
+ self.flatten_tree(json_tree).keys.sort
22
+ end
23
+
24
+ # @returns a set of flattened hash keys that's the union of the individual flat hash keys for each JSON blob
25
+ def self.list_keys_for_hashes(json_trees)
26
+ json_trees.map { |tree| self.flatten_tree(tree).keys }.flatten.uniq.sort
27
+ end
28
+
29
+ # @param json_tree As returned by JSON,parse()
30
+ # @param key_patterns a list of glob pattern strings for matching keys
31
+ # @returns a hash of flat_key to value for all keys matching any of the key_patterns
32
+ def self.glob_key_values(json_tree, key_patterns)
33
+ matches = {}
34
+ flat_tree = flatten_tree(json_tree)
35
+ key_patterns.each do |key_pattern|
36
+ matching_keys = flat_tree.keys.select { |key| File.fnmatch(key_pattern, key) }
37
+ matches.merge!(Hash[ matching_keys.map { |key| [key, flat_tree[key]] } ])
38
+ end
39
+ matches
40
+ end
41
+ end
@@ -0,0 +1,2 @@
1
+ require "metrics_downloader"
2
+ require "metrics_parser"
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metricsgeek
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Evan Chan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-16 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: ! 'Easily grok and analyze your application metrics from a cluster, in
15
+ real time, with this command line tool /
16
+
17
+ gem. Compatible with HTTP JSON metrics routes as exposed by Coda Hale''s metrics
18
+ package, Jolokia, ruby-
19
+
20
+ metrics, or any metrics package that exposes metrics via an HTTP route as JSON.
21
+
22
+ '
23
+ email: velvia@gmail.com
24
+ executables:
25
+ - metricsgeek
26
+ extensions: []
27
+ extra_rdoc_files: []
28
+ files:
29
+ - lib/metrics_downloader.rb
30
+ - lib/metrics_parser.rb
31
+ - lib/metricsgeek.rb
32
+ - bin/metricsgeek
33
+ - LICENSE.md
34
+ - README.md
35
+ homepage: http://github.com/velvia/metricsgeek
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 1.8.10
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Easily grok and analyze application metrics from a cluster.
59
+ test_files: []