pd_metrics 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/README.md +44 -0
- data/Rakefile +10 -0
- data/lib/pd_metrics.rb +236 -0
- data/lib/pd_metrics/version.rb +3 -0
- data/pd_metrics.gemspec +25 -0
- data/test/pd_metrics_test.rb +102 -0
- data/test/test_helper.rb +11 -0
- metadata +122 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# PdMetrics
|
2
|
+
|
3
|
+
Library to send metrics to Logstash, which then delivers them to PagerDuty's
|
4
|
+
metric systems. This is pretty much only useful if you're a PagerDuty employee.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'pd_metrics'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install pd_metrics
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
# Captures timing metrics for a block of Ruby code.
|
23
|
+
PdMetrics.time('api', 'receive_email', account: 'Netflix') do
|
24
|
+
# process the email
|
25
|
+
end
|
26
|
+
|
27
|
+
# Captures an increase/decrease in a counter.
|
28
|
+
PdMetrics.incr('emails', 'bytes_received', email_bytes.size, account: 'Netflix')
|
29
|
+
|
30
|
+
# Captures the current value for a metric.
|
31
|
+
PdMetrics.gauge('ruby', 'live_objects', ObjectSpace.live_objects)
|
32
|
+
|
33
|
+
# Captures statistical metrics for a set of values within a given timeframe.
|
34
|
+
# This is very similar to the time method, but it's genericized for use in
|
35
|
+
# arbitrary values.
|
36
|
+
PdMetrics.histogram('api', 'payload_size', payload.size, account: 'Netflix')
|
37
|
+
|
38
|
+
## Contributing
|
39
|
+
|
40
|
+
1. Fork it
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
44
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/pd_metrics.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
5
|
+
|
6
|
+
class PdMetrics
|
7
|
+
cattr_accessor :settings
|
8
|
+
self.settings ||= {host: '127.0.0.1', port: 5959}
|
9
|
+
|
10
|
+
cattr_accessor :logger
|
11
|
+
self.logger ||= Logger.new('/dev/null')
|
12
|
+
|
13
|
+
# Logs an event to metric backend. In general, you can log any key value pairs.
|
14
|
+
#
|
15
|
+
# PdMetrics.send_event('api', account: 'Netflix', wait_delta: 0.01, run_delta: 0.1)
|
16
|
+
#
|
17
|
+
# This will result in the following line being logged in SumoLogic. No data will be sent to DataDog.
|
18
|
+
#
|
19
|
+
# api #account=Netflix|#run_delta=0.1|#wait_delta=0.01|
|
20
|
+
#
|
21
|
+
# In order to support aggregated graphs in DataDog, you'll need to mark the
|
22
|
+
# type of any numerical metrics you want aggregated.
|
23
|
+
#
|
24
|
+
# PdMetrics.send_event('api', wait_delta: (0.01).histogram, run_delta: (0.1).histogram)
|
25
|
+
#
|
26
|
+
# This extra bit of detail is needed to let DataDog know how to aggregate multiple events in a single timeslice.
|
27
|
+
#
|
28
|
+
# counter - adds together multiple data points. Use this for things like visits, errors, etc.
|
29
|
+
# gauge - takes the last value. Use this for things like free memory, connections to database, etc.
|
30
|
+
# histogram - derives count, avg, median, max, min, 95th percentile from a single value. Use this for this like latency, bytes written, etc.
|
31
|
+
#
|
32
|
+
# Note that when Datadog metrics are supplied, any non-metric data is passed
|
33
|
+
# to DataDog as tags. Depending on how many tags you have, this can be
|
34
|
+
# counterproductive in DataDog. To have additional data logged only to
|
35
|
+
# Sumologic, pass it in the additional_data paramter.
|
36
|
+
#
|
37
|
+
# PdMetrics.send_event('api', {wait_delta: (0.01).histogram, run_delta: (0.1).histogram}, account: 'Netflix')
|
38
|
+
#
|
39
|
+
def self.send_event(namespace, metrics_and_tags = {}, additional_data = {})
|
40
|
+
logger.debug { "send_event #{namespace} #{metrics_and_tags.inspect} #{additional_data.inspect}" }
|
41
|
+
metrics_and_tags ||= {}
|
42
|
+
additional_data ||= {}
|
43
|
+
|
44
|
+
send_datadog_format(namespace, metrics_and_tags)
|
45
|
+
send_sumologic_format(namespace, metrics_and_tags, additional_data)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Captures timing metrics for a block of Ruby code.
|
49
|
+
#
|
50
|
+
# PdMetrics.time('api', 'receive_email', account: 'Netflix') do
|
51
|
+
# # process the email
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Assuming the request took 2 seconds to process, the following log message will be written in SumoLogic.
|
55
|
+
#
|
56
|
+
# api #account=Netflix|#receive_email=2.0|#failed=false|
|
57
|
+
#
|
58
|
+
# Additionally, the following histogram metrics will be captured in DataDog
|
59
|
+
#
|
60
|
+
# api.receive_email.count
|
61
|
+
# api.receive_email.avg
|
62
|
+
# api.receive_email.median
|
63
|
+
# api.receive_email.max
|
64
|
+
# api.receive_email.95percentile
|
65
|
+
#
|
66
|
+
# In addition to capturing latency of the request, the success or failure of
|
67
|
+
# the block of code is captured as well. It is considered failed if an
|
68
|
+
# exception is thrown.
|
69
|
+
#
|
70
|
+
def self.time(namespace, key, tags = {}, additional_data = {})
|
71
|
+
failed = false
|
72
|
+
start = Time.now
|
73
|
+
yield
|
74
|
+
rescue
|
75
|
+
failed = true
|
76
|
+
raise
|
77
|
+
ensure
|
78
|
+
timing_data = tags || {}
|
79
|
+
timing_data[key] = (Time.now - start).histogram
|
80
|
+
timing_data['failed'] = failed
|
81
|
+
send_event(namespace, timing_data)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Captures an increase/decrease in a counter.
|
85
|
+
#
|
86
|
+
# You can use this to capture metrics that should be added together when viewed on a graph.
|
87
|
+
#
|
88
|
+
# PdMetrics.incr('logins', 'success')
|
89
|
+
# PdMetrics.incr('emails', 'bytes_received', email_bytes.size, account: 'Netflix')
|
90
|
+
#
|
91
|
+
# That will produce the following line in SumoLogic.
|
92
|
+
#
|
93
|
+
# logins #success=1
|
94
|
+
# emails #account=Netflix|#bytes_received=1234|
|
95
|
+
#
|
96
|
+
# Additionally, the following metrics will be defined in DataDog
|
97
|
+
#
|
98
|
+
# logins.success
|
99
|
+
# emails.bytes_received
|
100
|
+
#
|
101
|
+
def self.incr(namespace, key, increment_by = 1, tags = {}, additional_data = {})
|
102
|
+
incr_data = tags || {}
|
103
|
+
incr_data[key] = increment_by.counter
|
104
|
+
send_event(namespace, incr_data, additional_data)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Captures the current value for a metric.
|
108
|
+
#
|
109
|
+
# Unlike a counter, this value cannot be combined with itself in a meaningful
|
110
|
+
# way, so only the last reported value with a certain sampling frequency
|
111
|
+
# (normally every 10 seconds) is recorded in DataDog.
|
112
|
+
#
|
113
|
+
# You can use this method to capture metrics that change over time, like
|
114
|
+
# amount of memory used. Usually, this sampling occurs at a regular frequency
|
115
|
+
# via a timer.
|
116
|
+
#
|
117
|
+
# PdMetrics.gauge('ruby', 'live_objects', ObjectSpace.live_objects)
|
118
|
+
#
|
119
|
+
# The following line will be printed in SumoLogic for each call to gauge.
|
120
|
+
#
|
121
|
+
# ruby #live_objects=30873|
|
122
|
+
#
|
123
|
+
# Additionally, the following metric will be available in DataDog.
|
124
|
+
#
|
125
|
+
# ruby.live_objects
|
126
|
+
#
|
127
|
+
def self.gauge(namespace, key, value, tags = {}, additional_data = {})
|
128
|
+
gauge_data = tags || {}
|
129
|
+
gauge_data[key] = value.gauge
|
130
|
+
send_event(namespace, gauge_data, additional_data)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Captures statistical metrics for a set of values within a given timeframe.
|
134
|
+
# This is very similar to the time method, but it's genericized for use in
|
135
|
+
# arbitrary values.
|
136
|
+
#
|
137
|
+
# An example usage would be calculating the size of JSON payloads received by
|
138
|
+
# an API. You could use a counter, but that wouldn't tease out what the
|
139
|
+
# average and median payload sizes are.
|
140
|
+
#
|
141
|
+
# PdMetrics.histogram('api', 'payload_size', payload.size, account: 'Netflix')
|
142
|
+
#
|
143
|
+
# The following line will be printed in SumoLogic for every payload.
|
144
|
+
#
|
145
|
+
# api #account=Netflix|#payload_size=1234
|
146
|
+
# api #account=Netflix|#payload_size=0
|
147
|
+
#
|
148
|
+
# Additionally, DataDog will have the following metrics available. Note,
|
149
|
+
# these metrics are captured every 10 seconds, so they likely represent
|
150
|
+
# multiple requests within that time window.
|
151
|
+
#
|
152
|
+
# api.payload_size.count
|
153
|
+
# api.payload_size.avg
|
154
|
+
# api.payload_size.median
|
155
|
+
# api.payload_size.max
|
156
|
+
# api.payload_size.95percentile
|
157
|
+
#
|
158
|
+
def self.histogram(namespace, key, value, tags = {}, additional_data = {})
|
159
|
+
histogram_data = tags || {}
|
160
|
+
histogram_data[key] = value.histogram
|
161
|
+
send_event(namespace, histogram_data, additional_data)
|
162
|
+
end
|
163
|
+
|
164
|
+
class NumericMetric
|
165
|
+
attr_reader :value
|
166
|
+
|
167
|
+
def initialize(value)
|
168
|
+
@value = value
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_s
|
172
|
+
@value.to_s
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
class Gauge < NumericMetric
|
177
|
+
def statsd_format
|
178
|
+
"#{value}|g"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class Counter < NumericMetric
|
183
|
+
def statsd_format
|
184
|
+
"#{value}|c"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class Histogram < NumericMetric
|
189
|
+
def statsd_format
|
190
|
+
"#{value}|h"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def self.send_datadog_format(namespace, metrics_and_tags)
|
197
|
+
metrics, tag_pairs = metrics_and_tags.partition {|key, value| value.is_a?(NumericMetric) }
|
198
|
+
tags = tag_pairs.map {|d| "#{d[0]}:#{d[1]}" }.join(',')
|
199
|
+
metrics.each do |name, value|
|
200
|
+
msg = "#{namespace}.#{name}:#{value.statsd_format}"
|
201
|
+
msg += "|##{tags}" unless tags.empty?
|
202
|
+
msg += "\n"
|
203
|
+
write_to_socket(msg)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.send_sumologic_format(namespace, metrics_and_tags, additional_data)
|
208
|
+
sumologic_data = metrics_and_tags.merge(additional_data).sort {|a,b| a[0].to_s <=> b[0].to_s }
|
209
|
+
msg = "#{namespace} #{sumologic_data.map {|e| "##{e[0]}=#{e[1]}|" }.join}\n"
|
210
|
+
write_to_socket(msg)
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.write_to_socket(msg)
|
214
|
+
@delivery_socket ||= Socket.new(Socket::PF_INET, Socket::SOCK_DGRAM)
|
215
|
+
@destination_addr ||= Socket.pack_sockaddr_in(settings[:port], settings[:host])
|
216
|
+
@delivery_socket.send(msg, 0, @destination_addr)
|
217
|
+
rescue => e
|
218
|
+
logger.warn { "Ignoring PdMetrics exception: #{e.class} #{e.message}" }
|
219
|
+
end
|
220
|
+
|
221
|
+
module NumericExtensions
|
222
|
+
def gauge
|
223
|
+
PdMetrics::Gauge.new(self)
|
224
|
+
end
|
225
|
+
|
226
|
+
def counter
|
227
|
+
PdMetrics::Counter.new(self)
|
228
|
+
end
|
229
|
+
|
230
|
+
def histogram
|
231
|
+
PdMetrics::Histogram.new(self)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
Numeric.send(:include, NumericExtensions)
|
236
|
+
end
|
data/pd_metrics.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pd_metrics/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "pd_metrics"
|
8
|
+
gem.version = PdMetrics::VERSION
|
9
|
+
gem.authors = ["Doug Barth"]
|
10
|
+
gem.email = ["doug@pagerduty.com"]
|
11
|
+
gem.description = %q{Library to send metrics to Logstash, which then delivers them to PagerDuty's metric systems}
|
12
|
+
gem.summary = %q{PagerDuty's metrics integration library}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'activesupport', '~> 3.2'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'timecop'
|
23
|
+
gem.add_development_dependency 'mocha'
|
24
|
+
gem.add_development_dependency 'minitest'
|
25
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'pd_metrics'
|
3
|
+
|
4
|
+
class PdMetrics
|
5
|
+
cattr_accessor :writes
|
6
|
+
self.writes = []
|
7
|
+
|
8
|
+
@original_write_to_socket = method(:write_to_socket)
|
9
|
+
def self.write_to_socket(msg)
|
10
|
+
writes << msg
|
11
|
+
@original_write_to_socket.call(msg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class PdMetricsTest < ActiveSupport::TestCase
|
16
|
+
def setup
|
17
|
+
PdMetrics.writes = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
Timecop.return
|
22
|
+
end
|
23
|
+
|
24
|
+
test "writes events in the SumoLogic format" do
|
25
|
+
PdMetrics.send_event('api', call: 1.counter, account: 'foo', wait_delta: (0.01).histogram)
|
26
|
+
assert_written_to_socket "api #account=foo|#call=1|#wait_delta=0.01|\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
test "writes events in DataDog format" do
|
30
|
+
PdMetrics.send_event('api', call: 1.counter, account: 'foo', wait_delta: (0.01).histogram)
|
31
|
+
assert_written_to_socket "api.call:1|c|#account:foo\n"
|
32
|
+
assert_written_to_socket "api.wait_delta:0.01|h|#account:foo\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
test "handles events with no DataDog compatible data" do
|
36
|
+
PdMetrics.send_event('api', account: 'foo', class: 'Blah')
|
37
|
+
assert_written_to_socket "api #account=foo|#class=Blah|\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
test "handles multiple non-DataDog event data pairs in DataDog messages" do
|
41
|
+
PdMetrics.send_event('api', call: 1.counter, account: 'foo', class: 'Blah')
|
42
|
+
assert_written_to_socket "api.call:1|c|#account:foo,class:Blah\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
test "ignores exceptions when writing to socket" do
|
46
|
+
Socket.any_instance.expects(:send).at_least_once.raises(RuntimeError, 'KABOOM')
|
47
|
+
PdMetrics.send_event('api', call: 1.counter, account: 'foo', class: 'Blah')
|
48
|
+
end
|
49
|
+
|
50
|
+
test "sends additional_data only to SumoLogic" do
|
51
|
+
PdMetrics.send_event('api', {call: 1.counter, account: 'foo'}, txn_id: 'abc123')
|
52
|
+
assert_written_to_socket "api.call:1|c|#account:foo\n"
|
53
|
+
assert_written_to_socket "api #account=foo|#call=1|#txn_id=abc123|\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
test "time captures latency of a block" do
|
57
|
+
Timecop.freeze do
|
58
|
+
PdMetrics.time('api', 'create_event', account: 'foo') do
|
59
|
+
Timecop.freeze(2)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
assert_written_to_socket "api #account=foo|#create_event=2.0|#failed=false|\n"
|
63
|
+
assert_written_to_socket "api.create_event:2.0|h|#account:foo,failed:false\n"
|
64
|
+
end
|
65
|
+
|
66
|
+
class ExpectedError < RuntimeError; end
|
67
|
+
|
68
|
+
test "time captures failures if exception is thrown" do
|
69
|
+
assert_raise(ExpectedError) do
|
70
|
+
Timecop.freeze do
|
71
|
+
PdMetrics.time('api', 'create_event', account: 'foo') do
|
72
|
+
Timecop.freeze(2)
|
73
|
+
raise ExpectedError
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
assert_written_to_socket "api #account=foo|#create_event=2.0|#failed=true|\n"
|
78
|
+
assert_written_to_socket "api.create_event:2.0|h|#account:foo,failed:true\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
test "incr is a convenience method for counters" do
|
82
|
+
PdMetrics.incr('api', 'requests')
|
83
|
+
assert_written_to_socket "api #requests=1|\n"
|
84
|
+
assert_written_to_socket "api.requests:1|c\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
test "gauge is a convenience method for gauges" do
|
88
|
+
PdMetrics.gauge('ruby', 'live_objects', 30873)
|
89
|
+
assert_written_to_socket "ruby #live_objects=30873|\n"
|
90
|
+
assert_written_to_socket "ruby.live_objects:30873|g\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
test "histogram is a convenience method for statical metrics for a set of data" do
|
94
|
+
PdMetrics.histogram('api', 'payload_size', 1234, account: "Netflix")
|
95
|
+
assert_written_to_socket "api #account=Netflix|#payload_size=1234|\n"
|
96
|
+
assert_written_to_socket "api.payload_size:1234|h|#account:Netflix\n"
|
97
|
+
end
|
98
|
+
|
99
|
+
def assert_written_to_socket(msg)
|
100
|
+
assert_include PdMetrics.writes, msg
|
101
|
+
end
|
102
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pd_metrics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Doug Barth
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: timecop
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mocha
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: minitest
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Library to send metrics to Logstash, which then delivers them to PagerDuty's
|
79
|
+
metric systems
|
80
|
+
email:
|
81
|
+
- doug@pagerduty.com
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- Gemfile
|
88
|
+
- README.md
|
89
|
+
- Rakefile
|
90
|
+
- lib/pd_metrics.rb
|
91
|
+
- lib/pd_metrics/version.rb
|
92
|
+
- pd_metrics.gemspec
|
93
|
+
- test/pd_metrics_test.rb
|
94
|
+
- test/test_helper.rb
|
95
|
+
homepage: ''
|
96
|
+
licenses: []
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.24
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: PagerDuty's metrics integration library
|
119
|
+
test_files:
|
120
|
+
- test/pd_metrics_test.rb
|
121
|
+
- test/test_helper.rb
|
122
|
+
has_rdoc:
|