librato-rack 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +3 -0
- data/README.md +43 -20
- data/lib/librato/collector/aggregator.rb +45 -25
- data/lib/librato/collector/counter_cache.rb +38 -24
- data/lib/librato/collector.rb +9 -3
- data/lib/librato/rack/configuration.rb +24 -16
- data/lib/librato/rack/errors.rb +2 -0
- data/lib/librato/rack/tracker.rb +11 -16
- data/lib/librato/rack/validating_queue.rb +8 -6
- data/lib/librato/rack/version.rb +1 -1
- data/lib/librato/rack.rb +6 -12
- data/test/apps/custom.ru +5 -1
- data/test/integration/custom_test.rb +20 -6
- data/test/integration/no_stats_test.rb +2 -3
- data/test/integration/no_suites_test.rb +4 -5
- data/test/integration/queue_wait_test.rb +4 -10
- data/test/integration/request_test.rb +20 -23
- data/test/integration/suites_test.rb +5 -7
- data/test/remote/tracker_test.rb +53 -45
- data/test/support/environment_helpers.rb +1 -1
- data/test/test_helper.rb +1 -1
- data/test/unit/collector/aggregator_test.rb +70 -31
- data/test/unit/collector/counter_cache_test.rb +73 -33
- data/test/unit/collector/group_test.rb +20 -16
- data/test/unit/collector_test.rb +6 -5
- data/test/unit/rack/configuration_test.rb +19 -8
- data/test/unit/rack/tracker_test.rb +41 -5
- data.tar.gz.sig +0 -0
- metadata +83 -42
- metadata.gz.sig +1 -0
- checksums.yaml +0 -7
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -49,7 +49,7 @@ If you don't have a Metrics account already, [sign up](https://metrics.librato.c
|
|
49
49
|
|
50
50
|
By default you can use `LIBRATO_USER` and `LIBRATO_TOKEN` to pass your account data to the middleware. While these are the only required variables, there are a few more optional environment variables you may find useful.
|
51
51
|
|
52
|
-
* `
|
52
|
+
* `LIBRATO_TAGS` - the default tags to use for submitted metrics. Format is comma-separated key=value pairs, e.g. `region=us-east,az=b`. If not set, `host` of the executing machine is detected and set as default tag
|
53
53
|
* `LIBRATO_SUITES` - manage which metrics librato-rack will report. See more in [metrics suites](#metric-suites).
|
54
54
|
* `LIBRATO_PREFIX` - a prefix which will be prepended to all metric names
|
55
55
|
* `LIBRATO_LOG_LEVEL` - see logging section for more
|
@@ -74,19 +74,36 @@ See the [configuration class](https://github.com/librato/librato-rack/blob/maste
|
|
74
74
|
|
75
75
|
If you are using the [Librato Metrics Heroku addon](https://addons.heroku.com/librato), your `LIBRATO_USER` and `LIBRATO_TOKEN` environment variables will already be set in your Heroku environment. If you are running without the addon you will need to provide them yourself.
|
76
76
|
|
77
|
-
|
77
|
+
NOTE: if Heroku idles your application no measurements will be sent until it receives another request and is restarted. If you see intermittent gaps in your measurements during periods of low traffic this is the most likely cause.
|
78
78
|
|
79
|
-
|
79
|
+
## Default Tags
|
80
80
|
|
81
|
-
|
81
|
+
Librato Metrics supports tagged measurements that are associated with a metric, one or more tag pairs, and a point in time. For more information on tagged measurements, visit our [API documentation](https://www.librato.com/docs/api/#measurements).
|
82
|
+
|
83
|
+
##### Detected Tags
|
84
|
+
|
85
|
+
By default, `host` is detected and applied as a default tag for submitted measurements. Optionally, you can override the detected values, e.g. `LIBRATO_TAGS=host=myapp-prod-1`
|
86
|
+
|
87
|
+
##### Custom Tags
|
88
|
+
|
89
|
+
In addition to the default tags, you can also provide custom tags:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
config = Librato::Rack::Configuration.new
|
93
|
+
config.user = 'myuser@mysite.com'
|
94
|
+
config.token = 'mytoken'
|
95
|
+
config.tags = { service: 'myapp', environment: 'production', host: 'myapp-prod-1' }
|
96
|
+
|
97
|
+
use Librato::Rack, :config => config
|
98
|
+
```
|
82
99
|
|
83
100
|
##### Metric Suites
|
84
101
|
|
85
102
|
The metrics recorded by `librato-rack` are organized into named metric suites that can be selectively enabled/disabled:
|
86
103
|
|
87
104
|
* `rack`: The `rack.request.total`, `rack.request.time`, `rack.request.slow`, and `rack.request.queue.time` metrics
|
88
|
-
* `rack_status`:
|
89
|
-
* `rack_method`:
|
105
|
+
* `rack_status`: `rack.request.status` metric with `status` tag name and HTTP status code tag value, e.g. `status=200`
|
106
|
+
* `rack_method`: `rack.request.method` metric with `method` tag name and HTTP method tag value, e.g. `method=POST`
|
90
107
|
|
91
108
|
All three of the metric suites listed above are enabled by default.
|
92
109
|
|
@@ -116,14 +133,23 @@ Tracking anything that interests you is easy with Metrics. There are four primar
|
|
116
133
|
|
117
134
|
Use for tracking a running total of something _across_ requests, examples:
|
118
135
|
|
119
|
-
|
120
|
-
|
136
|
+
```ruby
|
137
|
+
# increment the 'sales_completed' metric by one
|
138
|
+
Librato.increment 'sales.completed'
|
139
|
+
# => {:host=>"myapp-prod-1"}
|
121
140
|
|
122
|
-
|
123
|
-
|
141
|
+
# increment by five
|
142
|
+
Librato.increment 'items.purchased', by: 5
|
143
|
+
# => {:host=>"myapp-prod-1"}
|
124
144
|
|
125
|
-
|
126
|
-
|
145
|
+
# increment with custom per-measurement tags
|
146
|
+
Librato.increment 'user.purchases', tags: { user_id: user.id, currency: 'USD' }
|
147
|
+
# => {:user_id=>43, :currency=>"USD"}
|
148
|
+
|
149
|
+
# increment with custom per-measurement tags and inherited default tags
|
150
|
+
Librato.increment 'user.purchases', tags: { user_id: user.id, currency: 'USD' }, inherit_tags: true
|
151
|
+
# => {:host=>"myapp-prod-1", :user_id=>43, :currency=>"USD"}
|
152
|
+
```
|
127
153
|
|
128
154
|
Other things you might track this way: user signups, requests of a certain type or to a certain route, total jobs queued or processed, emails sent or received
|
129
155
|
|
@@ -131,10 +157,10 @@ Other things you might track this way: user signups, requests of a certain type
|
|
131
157
|
|
132
158
|
Note that `increment` is primarily used for tracking the rate of occurrence of some event. Given this increment metrics are _continuous by default_: after being called on a metric once they will report on every interval, reporting zeros for any interval when increment was not called on the metric.
|
133
159
|
|
134
|
-
Especially with custom
|
160
|
+
Especially with custom per-measurement tags you may want the opposite behavior - reporting a measurement only during intervals where `increment` was called on the metric:
|
135
161
|
|
136
162
|
# report a value for 'user.uploaded_file' only during non-zero intervals
|
137
|
-
Librato.increment 'user.uploaded_file', :
|
163
|
+
Librato.increment 'user.uploaded_file', tags: { user: user.id, bucket: bucket.name }, sporadic: true
|
138
164
|
|
139
165
|
#### measure
|
140
166
|
|
@@ -142,8 +168,8 @@ Use when you want to track an average value _per_-request. Examples:
|
|
142
168
|
|
143
169
|
Librato.measure 'user.social_graph.nodes', 212
|
144
170
|
|
145
|
-
# report from
|
146
|
-
Librato.measure 'jobs.queued', 3, :
|
171
|
+
# report from custom per-measurement tags
|
172
|
+
Librato.measure 'jobs.queued', 3, tags: { priority: 'high', worker: 'worker.12' }
|
147
173
|
|
148
174
|
#### timing
|
149
175
|
|
@@ -205,14 +231,11 @@ If you are using `librato-rack` with sidekiq, [see these notes about setup](http
|
|
205
231
|
|
206
232
|
## Cross-Process Aggregation
|
207
233
|
|
208
|
-
`librato-rack` submits measurements back to the Librato platform on a _per-process_ basis. By default these measurements are then combined into a single measurement per
|
234
|
+
`librato-rack` submits measurements back to the Librato platform on a _per-process_ basis. By default these measurements are then combined into a single measurement per default tags (detects `host`) before persisting the data.
|
209
235
|
|
210
236
|
For example if you have 4 hosts with 8 unicorn instances each (i.e. 32 processes total), on the Metrics site you'll find 4 data streams (1 per host) instead of 32.
|
211
237
|
Current pricing applies after aggregation, so in this case you will be charged for 4 streams instead of 32.
|
212
238
|
|
213
|
-
If you want to report per-process instead, you can set `source_pids` to `true` in
|
214
|
-
your config, which will append the process id to the source name used by each thread.
|
215
|
-
|
216
239
|
## Troubleshooting
|
217
240
|
|
218
241
|
Note that it may take 2-3 minutes for the first results to show up in your Metrics account after you have started your servers with `librato-rack` enabled and the first request has been received.
|
@@ -5,16 +5,19 @@ module Librato
|
|
5
5
|
# maintains storage of timing and measurement type measurements
|
6
6
|
#
|
7
7
|
class Aggregator
|
8
|
-
|
8
|
+
SEPARATOR = "$$"
|
9
9
|
|
10
10
|
extend Forwardable
|
11
11
|
|
12
12
|
def_delegators :@cache, :empty?, :prefix, :prefix=
|
13
13
|
|
14
|
+
attr_reader :default_tags
|
15
|
+
|
14
16
|
def initialize(options={})
|
15
17
|
@cache = Librato::Metrics::Aggregator.new(prefix: options[:prefix])
|
16
18
|
@percentiles = {}
|
17
19
|
@lock = Mutex.new
|
20
|
+
@default_tags = options.fetch(:default_tags, {})
|
18
21
|
end
|
19
22
|
|
20
23
|
def [](key)
|
@@ -26,13 +29,13 @@ module Librato
|
|
26
29
|
def fetch(key, options={})
|
27
30
|
return nil if @cache.empty?
|
28
31
|
return fetch_percentile(key, options) if options[:percentile]
|
29
|
-
|
30
|
-
|
31
|
-
@lock.synchronize {
|
32
|
-
|
32
|
+
measurements = nil
|
33
|
+
tags = options[:tags] || @default_tags
|
34
|
+
@lock.synchronize { measurements = @cache.queued[:measurements] }
|
35
|
+
measurements.each do |metric|
|
33
36
|
if metric[:name] == key.to_s
|
34
|
-
return metric if !
|
35
|
-
return metric if
|
37
|
+
return metric if !tags && !metric[:tags]
|
38
|
+
return metric if tags == metric[:tags]
|
36
39
|
end
|
37
40
|
end
|
38
41
|
nil
|
@@ -89,18 +92,27 @@ module Librato
|
|
89
92
|
if args.length > 1 and args[-1].respond_to?(:each)
|
90
93
|
options = args[-1]
|
91
94
|
end
|
92
|
-
source = options[:source]
|
93
|
-
percentiles = Array(options[:percentile])
|
94
95
|
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
percentiles = Array(options[:percentile])
|
97
|
+
source = options[:source]
|
98
|
+
tags_option = options[:tags]
|
99
|
+
tags_option = { source: source } if source && !tags_option
|
100
|
+
tags =
|
101
|
+
if tags_option && options[:inherit_tags]
|
102
|
+
@default_tags.merge(tags_option)
|
103
|
+
elsif tags_option
|
104
|
+
tags_option
|
98
105
|
else
|
99
|
-
@
|
106
|
+
@default_tags
|
100
107
|
end
|
101
108
|
|
109
|
+
@lock.synchronize do
|
110
|
+
payload = { value: value }
|
111
|
+
payload.merge!({ tags: tags }) if tags
|
112
|
+
@cache.add event => payload
|
113
|
+
|
102
114
|
percentiles.each do |perc|
|
103
|
-
store = fetch_percentile_store(event,
|
115
|
+
store = fetch_percentile_store(event, payload)
|
104
116
|
store[:reservoir] << value
|
105
117
|
track_percentile(store, perc)
|
106
118
|
end
|
@@ -120,30 +132,38 @@ module Librato
|
|
120
132
|
end
|
121
133
|
|
122
134
|
def fetch_percentile(key, options)
|
123
|
-
store = fetch_percentile_store(key, options
|
135
|
+
store = fetch_percentile_store(key, options)
|
124
136
|
return nil unless store
|
125
137
|
store[:reservoir].percentile(options[:percentile])
|
126
138
|
end
|
127
139
|
|
128
|
-
def fetch_percentile_store(event,
|
129
|
-
keyname =
|
140
|
+
def fetch_percentile_store(event, options)
|
141
|
+
keyname = event
|
142
|
+
|
143
|
+
if options[:tags]
|
144
|
+
keyname = Librato::Metrics::Util.build_key_for(keyname, options[:tags])
|
145
|
+
end
|
146
|
+
|
130
147
|
@percentiles[keyname] ||= {
|
148
|
+
name: event,
|
131
149
|
reservoir: Hetchy::Reservoir.new(size: 1000),
|
132
150
|
percs: Set.new
|
133
151
|
}
|
152
|
+
@percentiles[keyname].merge!({ tags: options[:tags] }) if options && options[:tags]
|
153
|
+
@percentiles[keyname]
|
134
154
|
end
|
135
155
|
|
136
156
|
def flush_percentiles(queue, opts)
|
137
157
|
@percentiles.each do |key, val|
|
138
|
-
metric, source = key.split(SOURCE_SEPARATOR)
|
139
158
|
val[:percs].each do |perc|
|
140
159
|
perc_name = perc.to_s[0,5].gsub('.','')
|
141
|
-
payload =
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
160
|
+
payload =
|
161
|
+
if val[:tags]
|
162
|
+
{ value: val[:reservoir].percentile(perc), tags: val[:tags] }
|
163
|
+
else
|
164
|
+
val[:reservoir].percentile(perc)
|
165
|
+
end
|
166
|
+
queue.add "#{val[:name]}.p#{perc_name}" => payload
|
147
167
|
end
|
148
168
|
end
|
149
169
|
end
|
@@ -158,4 +178,4 @@ module Librato
|
|
158
178
|
end
|
159
179
|
|
160
180
|
end
|
161
|
-
end
|
181
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "json"
|
2
|
+
|
1
3
|
module Librato
|
2
4
|
class Collector
|
3
5
|
# maintains storage of a set of incrementable, counter-like
|
@@ -11,10 +13,13 @@ module Librato
|
|
11
13
|
|
12
14
|
def_delegators :@cache, :empty?
|
13
15
|
|
14
|
-
|
16
|
+
attr_reader :default_tags
|
17
|
+
|
18
|
+
def initialize(options={})
|
15
19
|
@cache = {}
|
16
20
|
@lock = Mutex.new
|
17
21
|
@sporadics = Set.new
|
22
|
+
@default_tags = options.fetch(:default_tags, {})
|
18
23
|
end
|
19
24
|
|
20
25
|
# Retrieve the current value for a given metric. This is a short
|
@@ -34,14 +39,15 @@ module Librato
|
|
34
39
|
@lock.synchronize { @cache.clear }
|
35
40
|
end
|
36
41
|
|
37
|
-
|
38
42
|
def fetch(key, options={})
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@
|
44
|
-
|
43
|
+
key = key.to_s
|
44
|
+
key =
|
45
|
+
if options[:tags]
|
46
|
+
Librato::Metrics::Util.build_key_for(key, options[:tags])
|
47
|
+
elsif @default_tags
|
48
|
+
Librato::Metrics::Util.build_key_for(key, @default_tags)
|
49
|
+
end
|
50
|
+
@lock.synchronize { @cache[key] }
|
45
51
|
end
|
46
52
|
|
47
53
|
# transfer all measurements to queue and reset internal status
|
@@ -50,16 +56,13 @@ module Librato
|
|
50
56
|
@lock.synchronize do
|
51
57
|
# work off of a duplicate data set so we block for
|
52
58
|
# as little time as possible
|
53
|
-
|
59
|
+
# requires a deep copy of data set
|
60
|
+
counts = JSON.parse(@cache.dup.to_json, symbolize_names: true)
|
54
61
|
reset_cache unless opts[:preserve]
|
55
62
|
end
|
56
|
-
counts.each do |metric,
|
57
|
-
metric
|
58
|
-
|
59
|
-
queue.add metric => {value: value, source: source}
|
60
|
-
else
|
61
|
-
queue.add metric => value
|
62
|
-
end
|
63
|
+
counts.each do |metric, payload|
|
64
|
+
metric = metric.to_s.split(SEPARATOR).first
|
65
|
+
queue.add metric => payload
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
@@ -75,22 +78,33 @@ module Librato
|
|
75
78
|
# increment :foo, :source => user.id
|
76
79
|
#
|
77
80
|
def increment(counter, options={})
|
81
|
+
metric = counter.to_s
|
78
82
|
if options.is_a?(INTEGER_CLASS)
|
79
83
|
# suppport legacy style
|
80
84
|
options = {by: options}
|
81
85
|
end
|
82
86
|
by = options[:by] || 1
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
source = options[:source]
|
88
|
+
tags_option = options[:tags]
|
89
|
+
tags_option = { source: source } if source && !tags_option
|
90
|
+
tags =
|
91
|
+
if tags_option && options[:inherit_tags]
|
92
|
+
@default_tags.merge(tags_option)
|
93
|
+
elsif tags_option
|
94
|
+
tags_option
|
95
|
+
else
|
96
|
+
@default_tags
|
97
|
+
end
|
98
|
+
metric = Librato::Metrics::Util.build_key_for(metric, tags) if tags
|
88
99
|
if options[:sporadic]
|
89
100
|
make_sporadic(metric)
|
90
101
|
end
|
91
102
|
@lock.synchronize do
|
92
|
-
@cache[metric]
|
93
|
-
@cache[metric]
|
103
|
+
@cache[metric] = {} unless @cache[metric]
|
104
|
+
@cache[metric][:name] ||= metric
|
105
|
+
@cache[metric][:value] ||= 0
|
106
|
+
@cache[metric][:value] += by
|
107
|
+
@cache[metric][:tags] = tags if tags
|
94
108
|
end
|
95
109
|
end
|
96
110
|
|
@@ -105,7 +119,7 @@ module Librato
|
|
105
119
|
@sporadics.each { |metric| @cache.delete(metric) }
|
106
120
|
@sporadics.clear
|
107
121
|
# reset all continuous source/metric pairs to 0
|
108
|
-
@cache.each_key { |key| @cache[key] = 0 }
|
122
|
+
@cache.each_key { |key| @cache[key][:value] = 0 }
|
109
123
|
end
|
110
124
|
|
111
125
|
end
|
data/lib/librato/collector.rb
CHANGED
@@ -10,14 +10,20 @@ module Librato
|
|
10
10
|
def_delegators :counters, :increment
|
11
11
|
def_delegators :aggregate, :measure, :timing
|
12
12
|
|
13
|
+
attr_reader :tags
|
14
|
+
|
15
|
+
def initialize(options={})
|
16
|
+
@tags = options[:tags]
|
17
|
+
end
|
18
|
+
|
13
19
|
# access to internal aggregator object
|
14
20
|
def aggregate
|
15
|
-
@aggregator_cache ||= Aggregator.new(prefix: @prefix)
|
21
|
+
@aggregator_cache ||= Aggregator.new(prefix: @prefix, default_tags: @tags)
|
16
22
|
end
|
17
23
|
|
18
24
|
# access to internal counters object
|
19
25
|
def counters
|
20
|
-
@counter_cache ||= CounterCache.new
|
26
|
+
@counter_cache ||= CounterCache.new(default_tags: @tags)
|
21
27
|
end
|
22
28
|
|
23
29
|
# remove any accumulated but unsent metrics
|
@@ -48,4 +54,4 @@ end
|
|
48
54
|
require_relative 'collector/aggregator'
|
49
55
|
require_relative 'collector/counter_cache'
|
50
56
|
require_relative 'collector/exceptions'
|
51
|
-
require_relative 'collector/group'
|
57
|
+
require_relative 'collector/group'
|
@@ -1,5 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
1
|
module Librato
|
4
2
|
class Rack
|
5
3
|
# Holds configuration for Librato::Rack middleware to use.
|
@@ -16,17 +14,17 @@ module Librato
|
|
16
14
|
|
17
15
|
DEFAULT_SUITES = [:rack, :rack_method, :rack_status]
|
18
16
|
|
19
|
-
attr_accessor :
|
20
|
-
:
|
21
|
-
:
|
22
|
-
|
17
|
+
attr_accessor :api_endpoint, :autorun, :disable_rack_metrics,
|
18
|
+
:flush_interval, :log_level, :log_prefix,
|
19
|
+
:log_target, :proxy, :suites,
|
20
|
+
:tags, :token, :tracker, :user
|
21
|
+
attr_reader :deprecations, :prefix
|
23
22
|
|
24
23
|
def initialize
|
25
24
|
# set up defaults
|
26
25
|
self.tracker = nil
|
27
26
|
self.api_endpoint = Librato::Metrics.api_endpoint
|
28
27
|
self.flush_interval = 60
|
29
|
-
self.source_pids = false
|
30
28
|
self.log_prefix = '[librato-rack] '
|
31
29
|
@listeners = []
|
32
30
|
@deprecations = []
|
@@ -50,8 +48,8 @@ module Librato
|
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
53
|
-
def
|
54
|
-
|
51
|
+
def has_tags?
|
52
|
+
@tags && !@tags.empty?
|
55
53
|
end
|
56
54
|
|
57
55
|
# check environment variables and capture current state
|
@@ -61,7 +59,7 @@ module Librato
|
|
61
59
|
self.token = ENV['LIBRATO_TOKEN']
|
62
60
|
self.autorun = detect_autorun
|
63
61
|
self.prefix = ENV['LIBRATO_PREFIX']
|
64
|
-
self.
|
62
|
+
self.tags = build_tags
|
65
63
|
self.log_level = ENV['LIBRATO_LOG_LEVEL'] || :info
|
66
64
|
self.proxy = ENV['LIBRATO_PROXY'] || ENV['https_proxy'] || ENV['http_proxy']
|
67
65
|
self.event_mode = ENV['LIBRATO_EVENT_MODE']
|
@@ -78,14 +76,9 @@ module Librato
|
|
78
76
|
@listeners << listener
|
79
77
|
end
|
80
78
|
|
81
|
-
def source=(src)
|
82
|
-
@source = src
|
83
|
-
@explicit_source = !!@source
|
84
|
-
end
|
85
|
-
|
86
79
|
def dump
|
87
80
|
fields = {}
|
88
|
-
%w{
|
81
|
+
%w{flush_interval log_level prefix suites tags token user}.each do |field|
|
89
82
|
fields[field.to_sym] = self.send(field)
|
90
83
|
end
|
91
84
|
fields[:metric_suites] = metric_suites.fields
|
@@ -130,6 +123,21 @@ module Librato
|
|
130
123
|
end
|
131
124
|
end
|
132
125
|
|
126
|
+
def build_tags
|
127
|
+
tags = {}
|
128
|
+
tags.tap do
|
129
|
+
if ENV["LIBRATO_TAGS"]
|
130
|
+
ENV["LIBRATO_TAGS"].split(",")
|
131
|
+
.map { |pairs| pairs.split("=") }
|
132
|
+
.map { |k,v| tags[k.to_sym] = v }
|
133
|
+
|
134
|
+
if tags.all? {|k,v| k.nil? || v.nil? }
|
135
|
+
raise InvalidTagConfiguration, "Invalid tag configuration format. Example: foo=bar,baz=qux"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
133
141
|
end
|
134
142
|
end
|
135
143
|
end
|
data/lib/librato/rack/errors.rb
CHANGED
data/lib/librato/rack/tracker.rb
CHANGED
@@ -5,8 +5,6 @@ module Librato
|
|
5
5
|
class Tracker
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
-
SOURCE_REGEX = /\A[-:A-Za-z0-9_.]{1,255}\z/
|
9
|
-
|
10
8
|
def_delegators :collector, :increment, :measure, :timing, :group
|
11
9
|
def_delegators :logger, :log
|
12
10
|
|
@@ -39,7 +37,7 @@ module Librato
|
|
39
37
|
|
40
38
|
# primary collector object used by this tracker
|
41
39
|
def collector
|
42
|
-
@collector ||= Librato::Collector.new
|
40
|
+
@collector ||= Librato::Collector.new(tags: tags)
|
43
41
|
end
|
44
42
|
|
45
43
|
# log a deprecation message
|
@@ -59,11 +57,6 @@ module Librato
|
|
59
57
|
log :error, "submission failed permanently: #{error}"
|
60
58
|
end
|
61
59
|
|
62
|
-
# source including process pid if indicated
|
63
|
-
def qualified_source
|
64
|
-
config.source_pids ? "#{source}.#{$$}" : source
|
65
|
-
end
|
66
|
-
|
67
60
|
# current local instrumentation to be sent on next flush
|
68
61
|
# this is for debugging, don't call rapidly in production as it
|
69
62
|
# may introduce latency
|
@@ -78,10 +71,12 @@ module Librato
|
|
78
71
|
log :debug, 'halting: credentials not present.'
|
79
72
|
elsif config.autorun == false
|
80
73
|
log :debug, 'halting: LIBRATO_AUTORUN disabled startup'
|
81
|
-
elsif
|
82
|
-
log :warn, "halting: '#{
|
83
|
-
elsif
|
84
|
-
log :warn,
|
74
|
+
elsif tags.any? { |k,v| k.to_s !~ ValidatingQueue::TAGS_KEY_REGEX || v.to_s !~ ValidatingQueue::TAGS_VALUE_REGEX }
|
75
|
+
log :warn, "halting: '#{tags}' are invalid tags."
|
76
|
+
elsif tags.keys.length > ValidatingQueue::DEFAULT_TAGS_LIMIT
|
77
|
+
log :warn, "halting: cannot exceed default tags limit of #{ValidatingQueue::DEFAULT_TAGS_LIMIT} tag names per measurement."
|
78
|
+
elsif on_heroku && !config.has_tags?
|
79
|
+
log :warn, 'halting: tags must be provided in configuration.'
|
85
80
|
else
|
86
81
|
return true
|
87
82
|
end
|
@@ -125,12 +120,12 @@ module Librato
|
|
125
120
|
end
|
126
121
|
|
127
122
|
def build_flush_queue(collector, preserve=false)
|
128
|
-
queue = ValidatingQueue.new( client: client,
|
123
|
+
queue = ValidatingQueue.new( client: client,
|
129
124
|
prefix: config.prefix, skip_measurement_times: true )
|
130
125
|
[collector.counters, collector.aggregate].each do |cache|
|
131
126
|
cache.flush_to(queue, preserve: preserve)
|
132
127
|
end
|
133
|
-
queue.add 'rack.processes' => 1
|
128
|
+
queue.add 'rack.processes' => { value: 1, tags: tags }
|
134
129
|
trace_queued(queue.queued) #if should_log?(:trace)
|
135
130
|
queue
|
136
131
|
end
|
@@ -166,8 +161,8 @@ module Librato
|
|
166
161
|
RUBY_DESCRIPTION.split[0]
|
167
162
|
end
|
168
163
|
|
169
|
-
def
|
170
|
-
@
|
164
|
+
def tags
|
165
|
+
@tags ||= config.has_tags? ? config.tags : { host: Socket.gethostname.downcase }
|
171
166
|
end
|
172
167
|
|
173
168
|
# should we spin up a worker? wrap this in a process check
|
@@ -5,21 +5,23 @@ module Librato
|
|
5
5
|
# to work out the kinks
|
6
6
|
#
|
7
7
|
class ValidatingQueue < Librato::Metrics::Queue
|
8
|
+
DEFAULT_TAGS_LIMIT = 4
|
8
9
|
METRIC_NAME_REGEX = /\A[-.:_\w]{1,255}\z/
|
9
|
-
|
10
|
+
TAGS_KEY_REGEX = /\A[-.:_\w]{1,64}\z/
|
11
|
+
TAGS_VALUE_REGEX = /\A[-.:_\w]{1,256}\z/
|
10
12
|
|
11
13
|
attr_accessor :logger
|
12
14
|
|
13
15
|
# screen all measurements for validity before sending
|
14
16
|
def submit
|
15
|
-
@queued[:
|
17
|
+
@queued[:measurements].delete_if do |entry|
|
16
18
|
name = entry[:name].to_s
|
17
|
-
|
19
|
+
tags = entry[:tags]
|
18
20
|
if name !~ METRIC_NAME_REGEX
|
19
21
|
log :warn, "invalid metric name '#{name}', not sending."
|
20
22
|
true # delete
|
21
|
-
elsif
|
22
|
-
log :warn, "
|
23
|
+
elsif tags && tags.any? { |k,v| k.to_s !~ TAGS_KEY_REGEX || v.to_s !~ TAGS_VALUE_REGEX }
|
24
|
+
log :warn, "halting: '#{tags}' are invalid tags."
|
23
25
|
true # delete
|
24
26
|
else
|
25
27
|
false # preserve
|
@@ -38,4 +40,4 @@ module Librato
|
|
38
40
|
|
39
41
|
end
|
40
42
|
end
|
41
|
-
end
|
43
|
+
end
|
data/lib/librato/rack/version.rb
CHANGED
data/lib/librato/rack.rb
CHANGED
@@ -44,21 +44,15 @@ module Librato
|
|
44
44
|
EOS
|
45
45
|
|
46
46
|
RECORD_RACK_STATUS_BODY = <<-'EOS'
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
s.timing "#{status}.time", duration
|
52
|
-
s.timing "#{status.to_s[0]}xx.time", duration
|
53
|
-
end
|
47
|
+
status_tags = { status: status }
|
48
|
+
tracker.increment "rack.request.status", tags: status_tags, inherit_tags: true
|
49
|
+
tracker.timing "rack.request.status.time", duration, tags: status_tags, inherit_tags: true
|
54
50
|
EOS
|
55
51
|
|
56
52
|
RECORD_RACK_METHOD_BODY = <<-'EOS'
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
m.timing "#{method}.time", duration
|
61
|
-
end
|
53
|
+
method_tags = { method: http_method.downcase! }
|
54
|
+
tracker.increment "rack.request.method", tags: method_tags, inherit_tags: true
|
55
|
+
tracker.timing "rack.request.method.time", duration, tags: method_tags, inherit_tags: true
|
62
56
|
EOS
|
63
57
|
|
64
58
|
attr_reader :config, :tracker
|
data/test/apps/custom.ru
CHANGED
@@ -5,6 +5,10 @@ use Librato::Rack
|
|
5
5
|
|
6
6
|
def application(env)
|
7
7
|
case env['PATH_INFO']
|
8
|
+
when '/tags'
|
9
|
+
tags = { region: "us-east-1" }
|
10
|
+
Librato.increment "requests", tags: tags
|
11
|
+
Librato.timing "requests.time", 3, tags: tags
|
8
12
|
when '/increment'
|
9
13
|
Librato.increment :hits
|
10
14
|
when '/measure'
|
@@ -24,4 +28,4 @@ def application(env)
|
|
24
28
|
[200, {"Content-Type" => 'text/html'}, ["Hello!"]]
|
25
29
|
end
|
26
30
|
|
27
|
-
run method(:application)
|
31
|
+
run method(:application)
|