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 CHANGED
@@ -1,3 +1,6 @@
1
+ ### Version 2.0.0
2
+ * Add support for tagged measurements (#54)
3
+
1
4
  ### Version 1.1.0
2
5
  * Fix deprecation warnings in ruby 2.4 (#57, Ben Radler)
3
6
 
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
- * `LIBRATO_SOURCE` - the default source to use for submitted metrics. If this is not set, hostname of the executing machine will be the default source
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
- You must also specify a custom source for your app to track properly. If an explicit source is not set, `librato-rack` will not start. You can set the source in your environment:
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
- heroku config:add LIBRATO_SOURCE=myappname
79
+ ## Default Tags
80
80
 
81
- 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.
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`: All of the `rack.request.status` metrics
89
- * `rack_method`: All of the `rack.request.method` metrics
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
- # increment the 'sales_completed' metric by one
120
- Librato.increment 'sales.completed'
136
+ ```ruby
137
+ # increment the 'sales_completed' metric by one
138
+ Librato.increment 'sales.completed'
139
+ # => {:host=>"myapp-prod-1"}
121
140
 
122
- # increment by five
123
- Librato.increment 'items.purchased', :by => 5
141
+ # increment by five
142
+ Librato.increment 'items.purchased', by: 5
143
+ # => {:host=>"myapp-prod-1"}
124
144
 
125
- # increment with a custom source
126
- Librato.increment 'user.purchases', :source => user.id
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 sources you may want the opposite behavior - reporting a measurement only during intervals where `increment` was called on the metric:
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', :source => user.id, :sporadic => true
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 a custom source
146
- Librato.measure 'jobs.queued', 3, :source => 'worker.12'
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 source (default is your hostname) before persisting the data.
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
- SOURCE_SEPARATOR = '$$'
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
- gauges = nil
30
- source = options[:source]
31
- @lock.synchronize { gauges = @cache.queued[:gauges] }
32
- gauges.each do |metric|
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 !source && !metric[:source]
35
- return metric if source.to_s == metric[:source]
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
- @lock.synchronize do
96
- if source
97
- @cache.add event => {source: source, value: value}
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
- @cache.add event => value
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, source)
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[:source])
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, source)
129
- keyname = source ? "#{event}#{SOURCE_SEPARATOR}#{source}" : event
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 = if source
142
- { value: val[:reservoir].percentile(perc), source: source }
143
- else
144
- val[:reservoir].percentile(perc)
145
- end
146
- queue.add "#{metric}.p#{perc_name}" => payload
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
- def initialize
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
- if options[:source]
40
- key = "#{key}#{SEPARATOR}#{options[:source]}"
41
- end
42
- @lock.synchronize do
43
- @cache[key.to_s]
44
- end
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
- counts = @cache.dup
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, value|
57
- metric, source = metric.split(SEPARATOR)
58
- if source
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
- if options[:source]
84
- metric = "#{counter}#{SEPARATOR}#{options[:source]}"
85
- else
86
- metric = counter.to_s
87
- end
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] ||= 0
93
- @cache[metric] += by
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
@@ -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 :user, :token, :autorun, :api_endpoint, :tracker,
20
- :source_pids, :log_level, :log_prefix, :log_target,
21
- :disable_rack_metrics, :flush_interval, :proxy, :suites
22
- attr_reader :prefix, :source, :deprecations
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 explicit_source?
54
- !!@explicit_source
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.source = ENV['LIBRATO_SOURCE']
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{user token log_level source prefix flush_interval source_pids suites}.each do |field|
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
@@ -5,5 +5,7 @@ module Librato
5
5
 
6
6
  class InvalidSuiteConfiguration < RuntimeError; end
7
7
 
8
+ class InvalidTagConfiguration < RuntimeError; end
9
+
8
10
  end
9
11
  end
@@ -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 qualified_source !~ SOURCE_REGEX
82
- log :warn, "halting: '#{qualified_source}' is an invalid source name."
83
- elsif on_heroku && !config.explicit_source?
84
- log :warn, 'halting: source must be provided in configuration.'
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, source: qualified_source,
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 source
170
- @source ||= (config.source || Socket.gethostname).downcase
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
- SOURCE_NAME_REGEX = /\A[-:A-Za-z0-9_.]{1,255}\z/
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[:gauges].delete_if do |entry|
17
+ @queued[:measurements].delete_if do |entry|
16
18
  name = entry[:name].to_s
17
- source = entry[:source] && entry[:source].to_s
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 source && source !~ SOURCE_NAME_REGEX
22
- log :warn, "invalid source name '#{source}', not sending."
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
@@ -1,5 +1,5 @@
1
1
  module Librato
2
2
  class Rack
3
- VERSION = "1.1.1"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
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
- group.group 'status' do |s|
48
- s.increment status
49
- s.increment "#{status.to_s[0]}xx"
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
- group.group 'method' do |m|
58
- method = http_method.downcase
59
- m.increment method
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)