graphite-metric 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,96 +2,143 @@ If you're sending data to Graphite from Ruby, GraphiteMetric will help you get
2
2
  it in the right format. For now, it only supports the plaintext protocol, but
3
3
  pickle should be relatively easy to add. Contributions are welcome.
4
4
 
5
- At [GoSquared](http://www.gosquared.com/), we're using graphite extensively.
6
- Some services use it directly via TCP, others via UDP (direct or via
7
- aggregators such as statsd). When metrics are critical, we push them into our
8
- RabbitMQ cluster. Carbon agents support AMQP natively, thus an efficient and
9
- fault-tolerant setup is achievable with little effort.
5
+ If you're loading data from graphite-web, you can use the raw protocol
6
+ and apply a few transformations before consuming it.
7
+
8
+ At [GoSquared] [1], we're using graphite extensively. Some services use
9
+ it directly via TCP, others via UDP (direct or via aggregators such as
10
+ statsd). When metrics delivery is critical, we push them into our
11
+ RabbitMQ cluster. Carbon agent supports AMQP natively.
10
12
 
11
13
  ## USAGE
12
14
 
13
- What is a `GraphiteMetric::Plaintext`?
15
+ ### GraphiteMetric::Plaintext
16
+
17
+ #### From values
18
+
19
+ ```ruby
20
+ graphite_metric = GraphiteMetric::Plaintext.new("visitors", 2)
21
+
22
+ graphite_metric.key
23
+ => "visitors"
24
+
25
+ graphite_metric.value
26
+ => 2
27
+
28
+ graphite_metric.timestamp
29
+ => 1331043095
30
+
31
+ graphite_metric.to_s
32
+ => "visitors 2 1331043095"
33
+
34
+ "#{graphite_metric}"
35
+ => "visitors 2 1331043095"
36
+ ```
37
+
38
+ #### From hash
39
+
40
+ ```ruby
41
+ gmp = GraphiteMetric::Plaintext.from_hash(
42
+ :key => "visitors",
43
+ :value => 2,
44
+ :timestamp => Time.now.to_i
45
+ )
14
46
 
15
- GraphiteMetric::Plaintext.ancestors
16
- => [GraphiteMetric::Plaintext,
17
- Struct,
18
- Enumerable,
19
- Object,
20
- TestHelpers,
21
- MiniTest::Expectations,
22
- PP::ObjectMixin,
23
- Kernel,
24
- BasicObject]
47
+ "#{gmp}"
48
+ => "visitors 2 1331039853"
49
+ ```
25
50
 
26
- ### From values
51
+ #### From array (of hashes)
27
52
 
28
- graphite_metric = GraphiteMetric::Plaintext.new("visitors", 2)
29
- => #<struct GraphiteMetric::Plaintext
30
- key="visitors",
31
- value=2,
32
- timestamp=1331043095>
53
+ ```ruby
54
+ gmps = GraphiteMetric::Plaintext.from_array([
55
+ {
56
+ :key => "visitors",
57
+ :value => 1,
58
+ :timestamp => Time.now.to_i
59
+ },
60
+ {
61
+ :key => "visitors",
62
+ :value => 5
63
+ }
64
+ ]
33
65
 
34
- graphite_metric.key
35
- => "visitors"
36
- graphite_metric.value
37
- => 2
38
- graphite_metric.timestamp
39
- => 1331043095
66
+ gmps.map(&:to_s)
67
+ ["visitors 1 1331039914", "visitors 5 1331043514"]
68
+ ```
40
69
 
41
- graphite_metric.to_s
42
- => "visitors 2 1331043095"
70
+ ### GraphiteMetric::Raw
43
71
 
44
- "#{graphite_metric}"
45
- => "visitors 2 1331043095"
72
+ It supports single as well as multiple raw metrics (multiple targets).
73
+ For simplicity's, all examples will use single metrics:
46
74
 
47
- `GraphiteMetric::Plaintext` is the same as `GMP`. The following examples will
48
- use the abbreviated form.
75
+ ```ruby
76
+ gmr = GraphiteMetric::Raw.new("my.metric,1336559725,1336559845,60|34.999,35.10,33.0")
49
77
 
50
- ### From hash
78
+ gmr.metrics
79
+ => [{:key=>"my.metric", :timestamp=>1336559725, :value=>34.999},
80
+ {:key=>"my.metric", :timestamp=>1336559785, :value=>35.1},
81
+ {:key=>"my.metric", :timestamp=>1336559845, :value=>33.0}]
82
+ ```
51
83
 
52
- gmp = GMP.from_hash(
53
- :key => "visitors",
54
- :value => 2,
55
- :timestamp => Time.now.to_i
56
- )
57
- => #<struct GraphiteMetric::Plaintext
58
- key="visitors",
59
- value=2,
60
- timestamp=1331039853>
84
+ #### Round all values
61
85
 
62
- "#{gmp}"
63
- => "visitors 2 1331039853"
86
+ ```ruby
87
+ gmr.round
88
+ => #<GraphiteMetric::Raw:0x00000104054fb0
89
+ @metrics=
90
+ [{:key=>"my.metric", :timestamp=>1336559725, :value=>35},
91
+ {:key=>"my.metric", :timestamp=>1336559785, :value=>35},
92
+ {:key=>"my.metric", :timestamp=>1336559845, :value=>33}],
93
+ @raw="my.metric,1336559725,1336559845,60|34.999,35.10,33.0\n">
94
+ ```
64
95
 
65
- ### From array (of hashes)
96
+ #### Timeshift timestamps
66
97
 
67
- gmps = GMP.from_array([
68
- {
69
- :key => "visitors",
70
- :value => 1,
71
- :timestamp => Time.now.to_i
72
- },
73
- {
74
- :key => "visitors",
75
- :value => 5
76
- }
77
- ]
78
- => [#<struct GraphiteMetric::Plaintext
79
- key="visitors",
80
- value=1,
81
- timestamp=1331039914>,
82
- #<struct GraphiteMetric::Plaintext
83
- key="visitors",
84
- value=5,
85
- timestamp=1331043514>]
98
+ [Graphite 0.9.9 didn't support positive timeShifts] [2]. Until 0.9.10 is
99
+ released:
86
100
 
87
- gmps.map(&:to_s)
88
- ["visitors 1 1331039914", "visitors 5 1331043514"]
101
+ ```ruby
102
+ gmr.timeshift(3600)
103
+ => #<GraphiteMetric::Raw:0x00000104054fb0
104
+ @metrics=
105
+ [{:key=>"my.metric", :timestamp=>1336563325, :value=>35},
106
+ {:key=>"my.metric", :timestamp=>1336563385, :value=>35},
107
+ {:key=>"my.metric", :timestamp=>1336563445, :value=>33}],
108
+ @raw="my.metric,1336559725,1336559845,60|34.999,35.10,33.0\n">
109
+ ```
89
110
 
111
+ #### Group metrics by key
90
112
 
113
+ ```ruby
114
+ gmr.grouped_metrics
115
+ => {"my.metric"=>
116
+ [{:timestamp=>1336563325, :value=>35},
117
+ {:timestamp=>1336563385, :value=>35},
118
+ {:timestamp=>1336563445, :value=>33}]}
119
+ ```
91
120
 
92
- ## COMPATIBILITY
121
+ #### Reset metrics
93
122
 
94
- Tested against Ruby 1.9.2. Used in production on both 1.9.2 & 1.9.3.
123
+ Both timeshifting and rounding modify the internal @metrics. If you need
124
+ to reset them back to their original state:
125
+
126
+ ```ruby
127
+ gmr.populate_from_raw
128
+ => #<GraphiteMetric::Raw:0x00000101fc0ab0
129
+ @metrics=
130
+ [{:key=>"my.metric", :timestamp=>1336559725, :value=>34.999},
131
+ {:key=>"my.metric", :timestamp=>1336559785, :value=>35.1},
132
+ {:key=>"my.metric", :timestamp=>1336559845, :value=>33.0}],
133
+ @raw="my.metric,1336559725,1336559845,60|34.999,35.10,33.0\n">
134
+ ```
135
+
136
+
137
+
138
+ ## Ruby 1.8
139
+
140
+ All our development and production systems run 1.9. We have no intention
141
+ of making this 1.8 compatible.
95
142
 
96
143
 
97
144
 
@@ -118,3 +165,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
118
165
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
119
166
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
120
167
  SOFTWARE.
168
+
169
+ [1]: http://www.gosquared.com/
170
+ [2]: https://bugs.launchpad.net/graphite/+bug/903675
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
8
8
  s.authors = ["Gerhard Lazu"]
9
9
  s.email = ["gerhard@lazu.co.uk"]
10
10
  s.homepage = ""
11
- s.summary = %q{Generates strings that graphite understands}
12
- s.description = %q{Converts hashes and arrays into graphite plaintext format}
11
+ s.summary = %q{Generates strings that graphite understands. Loads raw graphite data.}
12
+ s.description = %q{Converts hashes and arrays into graphite plaintext format. Creates hashes from graphite raw format.}
13
13
 
14
14
  #s.rubyforge_project = "graphite-metric"
15
15
 
@@ -18,8 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.add_development_dependency 'guard-minitest'
22
- s.add_development_dependency 'minitest'
23
- s.add_development_dependency 'pry'
24
- s.add_development_dependency 'turn'
21
+ s.add_development_dependency 'guard-minitest', '~> 0.5'
22
+ s.add_development_dependency 'minitest', '~> 3.0'
23
+ s.add_development_dependency 'pry', '~> 0.9'
24
+ s.add_development_dependency 'turn', '~> 0.9'
25
25
  end
@@ -0,0 +1,53 @@
1
+ module GraphiteMetric
2
+ class Raw
3
+ attr_reader :raw, :metrics
4
+
5
+ def initialize(raw)
6
+ @raw = raw
7
+ populate_from_raw
8
+ end
9
+
10
+ def timeshift(offset)
11
+ @metrics.each { |metric| metric[:timestamp] += offset }
12
+ self
13
+ end
14
+
15
+ def round
16
+ @metrics.each { |metric| metric[:value] = metric[:value].round }
17
+ self
18
+ end
19
+
20
+ # Cannot be chained unlike the previous methods because the
21
+ # @metrics structure gets changed. Not memoizing on purpose, the
22
+ # other methods might modify @metrics.
23
+ def grouped_metrics
24
+ @metrics.inject({}) do |result, metric|
25
+ (result[metric[:key]] ||= []) << {
26
+ :timestamp => metric[:timestamp],
27
+ :value => metric[:value]
28
+ }
29
+ result
30
+ end
31
+ end
32
+
33
+ def populate_from_raw
34
+ @metrics = []
35
+ @raw.split("\n").each do |raw|
36
+ raw_headers, raw_metrics = raw.split("|")
37
+
38
+ metric_name, from, to, step = raw_headers.split(",")
39
+
40
+ current_step = 0
41
+ raw_metrics.split(",").each do |raw_metric|
42
+ @metrics << {
43
+ :key => metric_name,
44
+ :timestamp => from.to_i + current_step,
45
+ :value => Float(raw_metric)
46
+ }
47
+ current_step += step.to_i
48
+ end
49
+ end
50
+ self
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,3 @@
1
1
  module GraphiteMetric
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,7 +1,6 @@
1
1
  require 'graphite-metric/version'
2
2
  require 'graphite-metric/plaintext'
3
+ require 'graphite-metric/raw'
3
4
 
4
5
  module GraphiteMetric
5
6
  end
6
-
7
- GMP = GraphiteMetric::Plaintext
@@ -0,0 +1,2 @@
1
+ my.first.metric,1336559725,1336559845,60|10.0,9.21,8.333
2
+ my.second.metric,1336559725,1336559845,60|1.1,2.999,3.50
@@ -0,0 +1 @@
1
+ my.metric,1336559725,1336559845,60|34.999,35.10,33.0
@@ -0,0 +1,38 @@
1
+ require_relative '../test_helper'
2
+ require 'graphite-metric/raw'
3
+
4
+ include TestHelpers
5
+
6
+ module GraphiteMetric
7
+ describe Raw do
8
+ it "converts raw graphite single metric to hashes" do
9
+ gmr = GraphiteMetric::Raw.new(single_metric_raw)
10
+ gmr.metrics.must_equal single_metric_converted
11
+ end
12
+
13
+ it "converts raw graphite multiple metrics to hashes" do
14
+ gmr = GraphiteMetric::Raw.new(multiple_metrics_raw)
15
+ gmr.metrics.must_equal multiple_metrics_converted
16
+ end
17
+
18
+ it "timeshifts with a positive offset" do
19
+ gmr = GraphiteMetric::Raw.new(single_metric_raw)
20
+ gmr.timeshift(3600).metrics.first[:timestamp].must_equal 1336559725 + 3600
21
+ end
22
+
23
+ it "timeshifts with a negatice offset" do
24
+ gmr = GraphiteMetric::Raw.new(single_metric_raw)
25
+ gmr.timeshift(-3600).metrics.first[:timestamp].must_equal 1336559725 - 3600
26
+ end
27
+
28
+ it "rounds values" do
29
+ gmr = GraphiteMetric::Raw.new(single_metric_raw)
30
+ gmr.round.metrics.first[:value].must_equal 35
31
+ end
32
+
33
+ it "groups metrics by keys" do
34
+ gmr = GraphiteMetric::Raw.new(multiple_metrics_raw)
35
+ gmr.grouped_metrics.must_equal multiple_metrics_grouped
36
+ end
37
+ end
38
+ end
data/test/test_helper.rb CHANGED
@@ -1,10 +1,106 @@
1
- require 'bundler'
2
- Bundler.setup
3
1
  require 'pry'
4
2
  require 'turn/autorun'
5
3
 
4
+ BASE_PATH = File.expand_path('../../', __FILE__)
5
+
6
6
  module TestHelpers
7
7
  def utc_now
8
8
  Time.now.utc.to_i
9
9
  end
10
+
11
+ def single_metric_raw
12
+ @single_metric_raw ||= File.read("#{BASE_PATH}/test/fixtures/single_metric.txt")
13
+ end
14
+
15
+ def single_metric_converted
16
+ [
17
+ {
18
+ :key => "my.metric",
19
+ :timestamp => 1336559725,
20
+ :value => 34.999
21
+ },
22
+ {
23
+ :key => "my.metric",
24
+ :timestamp => 1336559785,
25
+ :value => 35.10
26
+ },
27
+ {
28
+ :key => "my.metric",
29
+ :timestamp => 1336559845,
30
+ :value => 33.0
31
+ }
32
+ ]
33
+ end
34
+
35
+ def multiple_metrics_raw
36
+ @multiple_metrics_raw ||= File.read("#{BASE_PATH}/test/fixtures/multiple_metrics.txt")
37
+ end
38
+
39
+ def multiple_metrics_converted
40
+ [
41
+ {
42
+ :key => "my.first.metric",
43
+ :timestamp => 1336559725,
44
+ :value => 10.0
45
+ },
46
+ {
47
+ :key => "my.first.metric",
48
+ :timestamp => 1336559785,
49
+ :value => 9.21
50
+ },
51
+ {
52
+ :key => "my.first.metric",
53
+ :timestamp => 1336559845,
54
+ :value => 8.333
55
+ },
56
+ {
57
+ :key => "my.second.metric",
58
+ :timestamp => 1336559725,
59
+ :value => 1.1
60
+ },
61
+ {
62
+ :key => "my.second.metric",
63
+ :timestamp => 1336559785,
64
+ :value => 2.999
65
+ },
66
+ {
67
+ :key => "my.second.metric",
68
+ :timestamp => 1336559845,
69
+ :value => 3.50
70
+ }
71
+ ]
72
+ end
73
+
74
+ def multiple_metrics_grouped
75
+ {
76
+ "my.first.metric" => [
77
+ {
78
+ :timestamp => 1336559725,
79
+ :value => 10.0
80
+ },
81
+ {
82
+ :timestamp => 1336559785,
83
+ :value => 9.21
84
+ },
85
+ {
86
+ :timestamp => 1336559845,
87
+ :value => 8.333
88
+ }
89
+ ],
90
+ "my.second.metric" => [
91
+ {
92
+ :timestamp => 1336559725,
93
+ :value => 1.1
94
+ },
95
+ {
96
+ :timestamp => 1336559785,
97
+ :value => 2.999
98
+ },
99
+ {
100
+ :timestamp => 1336559845,
101
+ :value => 3.50
102
+ }
103
+ ]
104
+ }
105
+ end
10
106
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphite-metric
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,53 +9,74 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-08 00:00:00.000000000Z
12
+ date: 2012-05-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: guard-minitest
16
- requirement: &2158012620 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: '0.5'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2158012620
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.5'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: minitest
27
- requirement: &2158012200 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
- - - ! '>='
35
+ - - ~>
31
36
  - !ruby/object:Gem::Version
32
- version: '0'
37
+ version: '3.0'
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *2158012200
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: pry
38
- requirement: &2158011780 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
- - - ! '>='
51
+ - - ~>
42
52
  - !ruby/object:Gem::Version
43
- version: '0'
53
+ version: '0.9'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *2158011780
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: turn
49
- requirement: &2158011360 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
- - - ! '>='
67
+ - - ~>
53
68
  - !ruby/object:Gem::Version
54
- version: '0'
69
+ version: '0.9'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *2158011360
58
- description: Converts hashes and arrays into graphite plaintext format
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '0.9'
78
+ description: Converts hashes and arrays into graphite plaintext format. Creates hashes
79
+ from graphite raw format.
59
80
  email:
60
81
  - gerhard@lazu.co.uk
61
82
  executables: []
@@ -71,10 +92,13 @@ files:
71
92
  - graphite-metric.gemspec
72
93
  - lib/graphite-metric.rb
73
94
  - lib/graphite-metric/plaintext.rb
95
+ - lib/graphite-metric/raw.rb
74
96
  - lib/graphite-metric/util.rb
75
97
  - lib/graphite-metric/version.rb
76
- - test/graphite-metric-test.rb
98
+ - test/fixtures/multiple_metrics.txt
99
+ - test/fixtures/single_metric.txt
77
100
  - test/graphite-metric/plaintext_test.rb
101
+ - test/graphite-metric/raw_test.rb
78
102
  - test/graphite-metric/util_test.rb
79
103
  - test/test_helper.rb
80
104
  homepage: ''
@@ -97,12 +121,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
121
  version: '0'
98
122
  requirements: []
99
123
  rubyforge_project:
100
- rubygems_version: 1.8.10
124
+ rubygems_version: 1.8.23
101
125
  signing_key:
102
126
  specification_version: 3
103
- summary: Generates strings that graphite understands
127
+ summary: Generates strings that graphite understands. Loads raw graphite data.
104
128
  test_files:
105
- - test/graphite-metric-test.rb
129
+ - test/fixtures/multiple_metrics.txt
130
+ - test/fixtures/single_metric.txt
106
131
  - test/graphite-metric/plaintext_test.rb
132
+ - test/graphite-metric/raw_test.rb
107
133
  - test/graphite-metric/util_test.rb
108
134
  - test/test_helper.rb
@@ -1,8 +0,0 @@
1
- require_relative('./test_helper')
2
- require 'graphite-metric'
3
-
4
- describe GraphiteMetric do
5
- it "Plainext has an abbreviated form" do
6
- GMP.new.must_be_instance_of GraphiteMetric::Plaintext
7
- end
8
- end