quark 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.
- data/bin/quark +53 -0
- data/lib/quark.rb +66 -0
- data/lib/quark/commands/fetch.rb +45 -0
- data/lib/quark/commands/observe.rb +11 -0
- data/lib/quark/commands/peek.rb +28 -0
- data/lib/quark/config.rb +18 -0
- metadata +107 -0
data/bin/quark
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'quark'
|
4
|
+
require 'quark/config'
|
5
|
+
|
6
|
+
module Quark
|
7
|
+
class CLI
|
8
|
+
class<<self
|
9
|
+
def run()
|
10
|
+
@options = {}
|
11
|
+
|
12
|
+
(OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: quark [options]"
|
14
|
+
|
15
|
+
# -----------------------------------------------------------------------------
|
16
|
+
opts.on('-H', '--redis-host HOST', 'Hostname of the Redis server to write to') do |host|
|
17
|
+
Quark::Config.set("redis.host", host)
|
18
|
+
end
|
19
|
+
|
20
|
+
# -----------------------------------------------------------------------------
|
21
|
+
opts.on('-P', '--redis-port PORT', 'Hostname of the Redis server to write to') do |port|
|
22
|
+
Quark::Config.set("redis.port", port)
|
23
|
+
end
|
24
|
+
|
25
|
+
# -----------------------------------------------------------------------------
|
26
|
+
opts.on('-N', '--redis-prefix PREFIX', 'The string used to prefix keys in Redis') do |prefix|
|
27
|
+
Quark::Config.set("redis.prefix", prefix)
|
28
|
+
end
|
29
|
+
|
30
|
+
# -----------------------------------------------------------------------------
|
31
|
+
opts.on('-S', '--socket FILE', 'Location of the Unix socket to accept commands from') do |file|
|
32
|
+
Quark::Config.set("quark.socket", file)
|
33
|
+
end
|
34
|
+
|
35
|
+
# -----------------------------------------------------------------------------
|
36
|
+
opts.on('-a', '--listen-address ADDR', 'The IP address to listen on') do |addr|
|
37
|
+
Quark::Config.set("quark.address", addr)
|
38
|
+
end
|
39
|
+
|
40
|
+
# -----------------------------------------------------------------------------
|
41
|
+
opts.on('-p', '--listen-port PORT', 'The TCP port to listen on') do |port|
|
42
|
+
Quark::Config.set("quark.port", port)
|
43
|
+
end
|
44
|
+
|
45
|
+
end).parse!
|
46
|
+
|
47
|
+
Quark::Server.run()
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Quark::CLI.run()
|
data/lib/quark.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'redis'
|
4
|
+
require 'hiredis'
|
5
|
+
require 'em-synchrony'
|
6
|
+
require 'multi_json'
|
7
|
+
require 'quark/config'
|
8
|
+
|
9
|
+
|
10
|
+
module Quark
|
11
|
+
module SocketServer
|
12
|
+
Dir[File.join(File.dirname(__FILE__), "quark", "commands", "*.rb")].each do |rb|
|
13
|
+
require "#{rb.gsub(/\.rb$/,'')}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def post_init()
|
17
|
+
@redis = Redis.new({
|
18
|
+
:host => Quark::Config.get("redis.host", "127.0.0.1"),
|
19
|
+
:port => Quark::Config.get("redis.port", 6379).to_i,
|
20
|
+
:db => Quark::Config.get("redis.db", 0).to_i
|
21
|
+
})
|
22
|
+
|
23
|
+
@_prefix = Quark::Config.get("redis.prefix", "quark")
|
24
|
+
end
|
25
|
+
|
26
|
+
def receive_data(data)
|
27
|
+
command, arguments = data.chomp.split(' ',2)
|
28
|
+
|
29
|
+
if methods.include?(:"process_command_#{command.downcase}")
|
30
|
+
rv = send(:"process_command_#{command.downcase}", arguments)
|
31
|
+
if rv.nil?
|
32
|
+
send_data(MultiJson.dump({
|
33
|
+
:success => true
|
34
|
+
})+"\n")
|
35
|
+
else
|
36
|
+
send_data(MultiJson.dump({
|
37
|
+
:success => true,
|
38
|
+
:command => data.chomp,
|
39
|
+
:results => rv
|
40
|
+
})+"\n")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def unbind()
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Server
|
50
|
+
def self.run()
|
51
|
+
@address = Quark::Config.get("quark.address", "127.0.0.1")
|
52
|
+
@port = Quark::Config.get("quark.port", 12161)
|
53
|
+
@filename = Quark::Config.get("quark.socket")
|
54
|
+
|
55
|
+
EM.run do
|
56
|
+
if not @filename.nil? and File.writable?(File.dirname(@filename))
|
57
|
+
puts "Starting server at #{@filename}..."
|
58
|
+
EM::start_server(@filename, Quark::SocketServer)
|
59
|
+
else
|
60
|
+
puts "Starting server on #{@address}:#{@port}..."
|
61
|
+
EM::start_server(@address, @port, Quark::SocketServer)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Quark
|
2
|
+
module SocketServer
|
3
|
+
def process_command_fetch(data)
|
4
|
+
rv = {}
|
5
|
+
keys, from, to = data.split(' ')
|
6
|
+
now = (Time.now.to_f * 1000).to_i
|
7
|
+
|
8
|
+
# negative from/to values are treated as relative number of seconds from now
|
9
|
+
if from.to_i < 0
|
10
|
+
from = (now - (-1 * from.to_i * 1000))
|
11
|
+
end
|
12
|
+
|
13
|
+
if to.to_i < 0
|
14
|
+
to = (now - (-1 * to.to_i * 1000))
|
15
|
+
end
|
16
|
+
|
17
|
+
# if not set, to defaults to now
|
18
|
+
to ||= now
|
19
|
+
|
20
|
+
# find all matching keys from all blocks
|
21
|
+
@redis.keys("#{@_prefix}:#{keys}:*").each do |key|
|
22
|
+
x, name, block = key.split(':',3)
|
23
|
+
block = block.to_i
|
24
|
+
|
25
|
+
# skip keys whose block falls outside of the given time range (if given)
|
26
|
+
next if block > 0 and not from.nil? and block < (from.to_i/1000/3600).to_i
|
27
|
+
next if block > 0 and not to.nil? and block > (to.to_i/1000/3600).to_i
|
28
|
+
|
29
|
+
# get all values, sort by timestamp ascending, and append to ouput (within range, if given)
|
30
|
+
@redis.hgetall(key).sort{|a,b|
|
31
|
+
a[0] <=> b[0]
|
32
|
+
}.each do |timestamp, value|
|
33
|
+
timestamp = timestamp.to_i
|
34
|
+
|
35
|
+
if timestamp >= from.to_i and timestamp <= to.to_i
|
36
|
+
rv[name] ||= []
|
37
|
+
rv[name] << [timestamp, value.to_f]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
return rv
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Quark
|
2
|
+
module SocketServer
|
3
|
+
def process_command_observe(data)
|
4
|
+
id, value, timestamp = data.chomp.split(' ')
|
5
|
+
block = Integer(timestamp.to_i / 1000 / 3600)
|
6
|
+
|
7
|
+
@redis.hset("#{@_prefix}:#{id}:#{block}", timestamp, value)
|
8
|
+
return nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Quark
|
2
|
+
module SocketServer
|
3
|
+
def process_command_peek(data)
|
4
|
+
rv = {}
|
5
|
+
keys = @redis.keys("#{@_prefix}:#{data}:*")
|
6
|
+
peek_keys = []
|
7
|
+
|
8
|
+
# get unique metric names
|
9
|
+
keys.collect{|i|
|
10
|
+
i.split(':')[1]
|
11
|
+
}.uniq().each do |name|
|
12
|
+
# get most recent bucket for this metric
|
13
|
+
peek_keys << keys.select{|i|
|
14
|
+
i.split(':')[1] == name
|
15
|
+
}.sort.last
|
16
|
+
end
|
17
|
+
|
18
|
+
# now that we have the most recent bucket, pull the most recent value from that bucket
|
19
|
+
peek_keys.each do |peek|
|
20
|
+
rv[peek.split(':')[1]] = @redis.hgetall(peek).sort{|a,b|
|
21
|
+
a[0] <=> b[0]
|
22
|
+
}.last
|
23
|
+
end
|
24
|
+
|
25
|
+
return rv
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/quark/config.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Quark
|
2
|
+
class Config
|
3
|
+
require 'hashlib'
|
4
|
+
@_options = Hash.new()
|
5
|
+
|
6
|
+
def self.get(key, fallback=nil)
|
7
|
+
@_options.rget(key, fallback)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.set(key, value)
|
11
|
+
@_options.rset(key, value)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.all()
|
15
|
+
@_options
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quark
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gary Hetzel
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-05-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: multi_json
|
16
|
+
requirement: &14255880 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.7.9
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *14255880
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: eventmachine
|
27
|
+
requirement: &14253980 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *14253980
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: redis
|
38
|
+
requirement: &14252900 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 3.0.0
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *14252900
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: hiredis
|
49
|
+
requirement: &14251500 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.5.2
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *14251500
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: em-synchrony
|
60
|
+
requirement: &14265760 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 1.0.3
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *14265760
|
69
|
+
description: A small service for logging and retrieving timeseries metrics into a
|
70
|
+
Redis server
|
71
|
+
email: garyhetzel@gmail.com
|
72
|
+
executables:
|
73
|
+
- quark
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- lib/quark.rb
|
78
|
+
- lib/quark/config.rb
|
79
|
+
- lib/quark/commands/peek.rb
|
80
|
+
- lib/quark/commands/fetch.rb
|
81
|
+
- lib/quark/commands/observe.rb
|
82
|
+
- bin/quark
|
83
|
+
homepage: https://github.com/ghetzel/quark
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.11
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: A miniscule timeseries metrics logging service
|
107
|
+
test_files: []
|