quark 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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: []