metricsgeek 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.md +13 -0
- data/README.md +71 -0
- data/bin/metricsgeek +52 -0
- data/lib/metrics_downloader.rb +48 -0
- data/lib/metrics_parser.rb +41 -0
- data/lib/metricsgeek.rb +2 -0
- metadata +59 -0
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
|
data/lib/metricsgeek.rb
ADDED
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: []
|