tabstabs 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +421 -0
  8. data/Rakefile +5 -0
  9. data/lib/tabs_tabs.rb +26 -0
  10. data/lib/tabs_tabs/config.rb +65 -0
  11. data/lib/tabs_tabs/helpers.rb +27 -0
  12. data/lib/tabs_tabs/metrics/counter.rb +69 -0
  13. data/lib/tabs_tabs/metrics/counter/stats.rb +51 -0
  14. data/lib/tabs_tabs/metrics/task.rb +72 -0
  15. data/lib/tabs_tabs/metrics/task/token.rb +89 -0
  16. data/lib/tabs_tabs/metrics/value.rb +91 -0
  17. data/lib/tabs_tabs/metrics/value/stats.rb +55 -0
  18. data/lib/tabs_tabs/resolution.rb +65 -0
  19. data/lib/tabs_tabs/resolutionable.rb +48 -0
  20. data/lib/tabs_tabs/resolutions/day.rb +40 -0
  21. data/lib/tabs_tabs/resolutions/hour.rb +40 -0
  22. data/lib/tabs_tabs/resolutions/minute.rb +40 -0
  23. data/lib/tabs_tabs/resolutions/month.rb +40 -0
  24. data/lib/tabs_tabs/resolutions/week.rb +40 -0
  25. data/lib/tabs_tabs/resolutions/year.rb +40 -0
  26. data/lib/tabs_tabs/storage.rb +105 -0
  27. data/lib/tabs_tabs/tabs_tabs.rb +117 -0
  28. data/lib/tabs_tabs/version.rb +3 -0
  29. data/spec/lib/tabs_tabs/config_spec.rb +60 -0
  30. data/spec/lib/tabs_tabs/metrics/counter/stats_spec.rb +42 -0
  31. data/spec/lib/tabs_tabs/metrics/counter_spec.rb +196 -0
  32. data/spec/lib/tabs_tabs/metrics/task/token_spec.rb +18 -0
  33. data/spec/lib/tabs_tabs/metrics/task_spec.rb +103 -0
  34. data/spec/lib/tabs_tabs/metrics/value/stats_spec.rb +61 -0
  35. data/spec/lib/tabs_tabs/metrics/value_spec.rb +160 -0
  36. data/spec/lib/tabs_tabs/resolution_spec.rb +52 -0
  37. data/spec/lib/tabs_tabs/resolutionable_spec.rb +53 -0
  38. data/spec/lib/tabs_tabs/resolutions/day_spec.rb +23 -0
  39. data/spec/lib/tabs_tabs/resolutions/hour_spec.rb +23 -0
  40. data/spec/lib/tabs_tabs/resolutions/minute_spec.rb +23 -0
  41. data/spec/lib/tabs_tabs/resolutions/month_spec.rb +23 -0
  42. data/spec/lib/tabs_tabs/resolutions/week_spec.rb +24 -0
  43. data/spec/lib/tabs_tabs/resolutions/year_spec.rb +23 -0
  44. data/spec/lib/tabs_tabs/storage_spec.rb +138 -0
  45. data/spec/lib/tabs_tabs_spec.rb +223 -0
  46. data/spec/spec_helper.rb +17 -0
  47. data/spec/support/custom_resolutions.rb +40 -0
  48. data/tabs_tabs.gemspec +31 -0
  49. metadata +213 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9fea0109dd26d3768ee616097bfbd79a15f5373d
4
+ data.tar.gz: eb981d035be4b2ce63fd169e4dd365797be77d71
5
+ SHA512:
6
+ metadata.gz: 020fabcb549a614424bc49aba23aec060e173999ae773255f52260493d151bfa83d37606cc04b99be5a620190f47f99d36babb37043d8a150c375f4d76c5489e
7
+ data.tar.gz: 7ab0dc4c3508f9123feb3efea1591f21564556f0b7604f8926dba6d7b0b9e995decba93042a3e8cc692145f1408a99c31672df2791d45a6bb426a16586d09a7c
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ dump.rdb
19
+ tags
20
+ .idea/**/*
@@ -0,0 +1 @@
1
+ 2.4.0
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.2
4
+ - 2.4.0
5
+ services:
6
+ - redis-server
7
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 JC Grubbs
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,421 @@
1
+ [![Build Status](https://travis-ci.org/FHG-IMW/tabstabs.svg?branch=master)](https://travis-ci.org/FHG-IMW/tabstabs)
2
+ # TabsTabs
3
+
4
+ TabsTabs is a redis-backed metrics tracker for time-based events that supports counts, sums,
5
+ averages, and min/max, and task based stats sliceable by the minute, hour, day, week, month, and year.
6
+
7
+ This gem is a fork of [Tabs](https://github.com/devmynd/tabs). We want to keep the project alive
8
+ and compatible with resent Ruby and Rails versions.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'TabsTabs'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install TabsTabs
23
+
24
+ ## Usage
25
+
26
+ Metrics come in three flavors: counters, values, and tasks.
27
+
28
+ ### Counter Metrics
29
+
30
+ A counter metric simply records the number of events that occur within a given timeframe. To create a counter metric called ‘website-visits’, simply call:
31
+
32
+ ```ruby
33
+ TabsTabs .create_metric("website-visits", "counter")
34
+ ```
35
+
36
+ TabsTabs will also create a counter metric automatically the first time you
37
+ increment the counter.
38
+
39
+ To increment a metric counter, simply call:
40
+
41
+ ```ruby
42
+ TabsTabs .increment_counter("website-visits")
43
+ ```
44
+
45
+ If you need to retroactively increment the counter for a specific
46
+ timestamp, just pass it in.
47
+
48
+ ```ruby
49
+ TabsTabs .increment_counter("wibsite-visits", Time.now - 2.days)
50
+ ```
51
+
52
+ To retrieve the counts for a given time period just call `TabsTabs #get_stats` with the name of the metric, a range of times defining the period for which you want stats, and the resolution at which the data should be aggregated.
53
+
54
+ ```ruby
55
+ TabsTabs .get_stats("website-visits", 10.days.ago..Time.now, :hour)
56
+ ```
57
+
58
+ This will return stats for the last 10 days by hour in the form of a `TabsTabs ::Metrics::Counter::Stats` object. This object is enumerable so you can iterate through the results like so:
59
+
60
+ ```ruby
61
+ results = TabsTabs .get_stats("website-visits", 10.days.ago..Time.now, :hour)
62
+ results.each { |r| puts r }
63
+
64
+ #=>
65
+ { timestamp: 2000-01-01 00:00:00 UTC, count: 1 }
66
+ { timestamp: 2000-01-01 01:00:00 UTC, count: 0 }
67
+ { timestamp: 2000-01-01 02:00:00 UTC, count: 10 }
68
+ { timestamp: 2000-01-01 03:00:00 UTC, count: 1 }
69
+ { timestamp: 2000-01-01 04:00:00 UTC, count: 0 }
70
+ { timestamp: 2000-01-01 05:00:00 UTC, count: 0 }
71
+ { timestamp: 2000-01-01 06:00:00 UTC, count: 3 }
72
+ { timestamp: 2000-01-01 07:00:00 UTC, count: 0 }
73
+ ...
74
+ ```
75
+
76
+ The results object also provides the following methods:
77
+
78
+ ```ruby
79
+ results.total #=> The count total for the given period
80
+ results.min #=> The min count for any timestamp in the period
81
+ results.max #=> The max count for any timestamp in the period
82
+ results.avg #=> The avg count for timestamps in the period
83
+ results.period #=> The timestamp range that was requested
84
+ results.resolution #=> The resolution requested
85
+ ```
86
+
87
+ Timestamps for the given period in which no events occurred will be "filled in" with a count value to make visualizations easier.
88
+
89
+ The timestamps are also normalized. For example, in hour resolution, the minutes and seconds of the timestamps are set to 00:00. Likewise for the week resolution, the day is set to the first day of the week.
90
+
91
+ Lastly, you can access the overall total for a counter (for all time)
92
+ using the `counter_total` method.
93
+
94
+ ```ruby
95
+ TabsTabs .counter_total("website-visits") #=> 476873
96
+ ```
97
+
98
+ ### Value Metrics
99
+
100
+ Value metrics record a value at a point in time and calculate the min, max, avg, and sum for a given time resolution. Creating a value metric is easy:
101
+
102
+ To record a value, simply call `TabsTabs #record_value`.
103
+
104
+ ```ruby
105
+ TabsTabs .record_value("new-user-age", 32)
106
+ ```
107
+
108
+ If you need to retroactively record a value for a specific
109
+ timestamp, just pass it in.
110
+
111
+ ```ruby
112
+ TabsTabs .increment_counter("new-user-age", 19, Time.now - 2.days)
113
+ ```
114
+
115
+ This will also create a value metric the first time, you can manually create
116
+ a metric as well:
117
+
118
+ ```ruby
119
+ TabsTabs .create_metric("new-user-age", "value")
120
+ ```
121
+
122
+ Retrieving the stats for a value metric is just like retrieving a counter metric.
123
+
124
+ ```ruby
125
+ TabsTabs .get_stats("new-user-age", 6.months.ago..Time.now, :month)
126
+ ```
127
+
128
+ This will return a `TabsTabs ::Metrics::Value::Stats` object. Again, this
129
+ object is enumerable and encapsulates all the timestamps within the
130
+ given period.
131
+
132
+ ```ruby
133
+ results = TabsTabs .get_stats("new-user-age", 6.months.ago..Time.now, :month)
134
+ results.each { |r| puts r }
135
+ #=>
136
+ { timestamp: 2000-01-01 00:00:00, count: 9, min: 19, max: 54, sum: 226, avg: 38 }
137
+ { timestamp: 2000-02-01 01:00:00, count: 0, min: 0, max: 0, sum: 0, avg: 0 }
138
+ { timestamp: 2000-03-01 02:00:00, count: 2, min: 22, max: 34, sum: 180, avg: 26 }
139
+ ...
140
+ ```
141
+
142
+ The results object also provides some aggregates and other methods:
143
+
144
+ ```ruby
145
+ results.count #=> The total count of recorded values for the period
146
+ results.sum #=> The sum of all values for the period
147
+ results.min #=> The min value for any timestamp in the period
148
+ results.max #=> The max value for any timestamp in the period
149
+ results.avg #=> The avg value for timestamps in the period
150
+ results.period #=> The timestamp range that was requested
151
+ results.resolution #=> The resolution requested
152
+ ```
153
+
154
+ ### Task Metrics
155
+
156
+ Task metrics allow you to track the beginning and ending of a process.
157
+ For example, tracking a user who downloads you mobile application and
158
+ later visits your website to make a purchase.
159
+
160
+ ```ruby
161
+ TabsTabs .start_task("mobile-to-purchase", "2g4hj17787s")
162
+ ```
163
+
164
+ The first argument is the metric key and the second is a unique token
165
+ used to identify the given process. You can use any string for the
166
+ token but it needs to be unique. Use the `complete_task` method to
167
+ finish the task:
168
+
169
+ ```ruby
170
+ TabsTabs .complete_task("mobile-to-purchase", "2g4hj17787s")
171
+ ```
172
+
173
+ If you need to retroactively start/complete a task at a specific
174
+ timestamp, just pass it in.
175
+
176
+ ```ruby
177
+ TabsTabs .start_task("mobile-to-purchase", "2g4hj17787s", Time.now - 2.days)
178
+ TabsTabs .complete_task("mobile-to-purchase", "2g4hj17787s", Time.now - 1.days)
179
+ ```
180
+
181
+ Retrieving stats for a task metric is just like the other types:
182
+
183
+ ```ruby
184
+ TabsTabs .get_stats("mobile-to-purchase", 6.hours.ago..Time.now, :minute)
185
+ ```
186
+
187
+ This will return a `TabsTabs ::Metrics::Task::Stats` object:
188
+
189
+ ```ruby
190
+ results = TabsTabs .get_stats("mobile-to-purchase", 6.hours.ago..Time.now, :minute)
191
+ results.started_within_period #=> Number of items started in period
192
+ results.completed_within_period #=> Number of items completed in period
193
+ results.started_and_completed_within_period #=> Items wholly started/completed in period
194
+ results.completion_rate #=> Rate of completion in the given resolution
195
+ results.average_completion_time #=> Average time for the task to be completed
196
+ ```
197
+
198
+ ### Resolutions
199
+
200
+ When TabsTabs increments a counter or records a value it does so for each of the following "resolutions". You may supply any of these as the last argument to the `TabsTabs #get_stats` method.
201
+
202
+ :minute, :hour, :day, :week, :month, :year
203
+
204
+ It automatically aggregates multiple events for the same period. For instance when you increment a counter metric, 1 will be added for each of the resolutions for the current time. Repeating the event 5 minutes later will increment a different minute slot, but the same hour, date, week, etc. When you retrieve metrics, all timestamps will be in UTC.
205
+
206
+ #### Custom Resolutions
207
+
208
+ If the built-in resolutions above don't work you can add your own. All
209
+ that's necessary is a module that conforms to the following protocol:
210
+
211
+ ```ruby
212
+ module SecondResolution
213
+ include TabsTabs ::Resolutionable
214
+ extend self
215
+
216
+ PATTERN = "%Y-%m-%d-%H-%M-%S"
217
+
218
+ def name
219
+ :seconds
220
+ end
221
+
222
+ def serialize(timestamp)
223
+ timestamp.strftime(PATTERN)
224
+ end
225
+
226
+ def deserialize(str)
227
+ dt = DateTime.strptime(str, PATTERN)
228
+ self.normalize(dt)
229
+ end
230
+
231
+ def from_seconds(s)
232
+ s / 1
233
+ end
234
+
235
+ def to_seconds
236
+ 1
237
+ end
238
+
239
+ def add(timestamp, num_of_seconds)
240
+ timestamp + num_of_seconds.seconds
241
+ end
242
+
243
+ def normalize(ts)
244
+ Time.utc(ts.year, ts.month, ts.day, ts.hour, ts.min, ts.sec)
245
+ end
246
+ end
247
+
248
+ ```
249
+
250
+ A little description on each of the above methods:
251
+
252
+ *`name`*: unique symbol used to reference registered resolution
253
+
254
+ *`serialize`*: converts the timestamp to a string. The return value
255
+ here will be used as part of the Redis key storing values associated
256
+ with a given metric.
257
+
258
+ *`deserialize`*: converts the string representation of a timestamp back
259
+ into an actual DateTime value.
260
+
261
+ *`from_seconds`*: should return the number of periods in the given
262
+ number of seconds. For example, there are 60 seconds in a minute.
263
+
264
+ *`to_seconds`*: should return the number of seconds in '1' of these time periods. For example, there are 3600 seconds in an hour.
265
+
266
+ *`add`*: should add the number of seconds in the given resolution to the
267
+ supplied timestamp.
268
+
269
+ *`normalize`*: should simply return the first timestamp for the period.
270
+ For example, the week resolution returns the first hour of the first day
271
+ of the week.
272
+
273
+ *NOTE: If you're doing a custom resolution, you should probably look into
274
+ the code a bit.*
275
+
276
+ Once you have a module that conforms to the resolution protocol you need
277
+ to register it with TabsTabs . You can do this in one of two ways:
278
+
279
+ ```ruby
280
+ tabstabs
281
+ TabsTabs ::Resolution.register(SecondResolution)
282
+
283
+ # or, you can use the config block described below
284
+ ```
285
+
286
+ #### Removing a Resolution
287
+
288
+ You can also remove any resolution (custom or built-in) by calling the `unregister_resolutions` method in the config block (see config section below). Or, you can remove manually by calling:
289
+
290
+ ```ruby
291
+ TabsTabs ::Resolution.unregister(:minute, :hour)
292
+ ```
293
+
294
+ ### Inspecting Metrics
295
+
296
+ You can list all metrics using `list_metrics`:
297
+
298
+ ```ruby
299
+ TabsTabs .list_metrics #=> ["website-visits", "new-user-age"]
300
+ ```
301
+
302
+ You can check a metric's type (counter of value) by calling
303
+ `metric_type`:
304
+
305
+ ```ruby
306
+ TabsTabs .metric_type("website-visits") #=> "counter"
307
+ ```
308
+
309
+ And you can quickly check if a metric exists:
310
+
311
+ ```ruby
312
+ TabsTabs .metric_exists?("foobar") #=> false
313
+ ```
314
+
315
+ ### Drop a Metric
316
+
317
+ To drop a metric, just call `TabsTabs #drop_metric`
318
+
319
+ ```ruby
320
+ TabsTabs .drop_metric!("website-visits")
321
+ ```
322
+
323
+ This will drop all recorded values for the metric so it may not be un-done...be careful.
324
+
325
+ To drop only a specific resolution for a metric, just call `TabsTabs #drop_resolution_for_metric!`
326
+
327
+ ```ruby
328
+ TabsTabs .drop_resolution_for_metric!("website-visits", :minute)
329
+ ```
330
+
331
+ Even more dangerous, you can drop all metrics...be very careful.
332
+
333
+ ```ruby
334
+ TabsTabs .drop_all_metrics!
335
+ ```
336
+ ### Aging Out Old Metrics
337
+
338
+ You can use the expiration features to age out old metrics that may no longer be in your operational data set. For example, you may want to keep monthly or yearly data around but the minute or day level data isn't necessary past a certain date. You can set expirations for any resolution:
339
+
340
+ ```ruby
341
+ TabsTabs .configure do |config|
342
+ config.set_expirations(minute: 1.day, day: 1.week)
343
+ end
344
+ ```
345
+
346
+ The expiration date will start counting at the beginning of the end of the given resolution. Meaning that for a month resolution the given expiration time would start at the end of a given month. A month resolution metric recorded in January with an expiration of 2 weeks would expire after the 2nd week of February.
347
+
348
+ *NOTE: You cannot expire task metrics at this time, only counter and
349
+ values.*
350
+
351
+ ### Configuration
352
+
353
+ TabsTabs just works out of the box. However, if you want to override the default Redis connection or decimal precision, this is how:
354
+
355
+ ```ruby
356
+ TabsTabs .configure do |config|
357
+
358
+ # set it to an existing connection
359
+ config.redis = Redis.current
360
+
361
+ # pass a config hash that will be passed to Redis.new
362
+ config.redis = { :host => 'localhost', :port => 6379 }
363
+
364
+ tabstabs
365
+ tabstabs
366
+ config.prefix = "my_app"
367
+
368
+ # override default decimal precision (5)
369
+ # affects stat averages and task completion rate
370
+ config.decimal_precision = 2
371
+
372
+ # registers a custom resolution
373
+ config.register_resolution :second, SecondResolution
374
+
375
+ # unregisters any resolution
376
+ config.unregister_resolutions(:minute, :hour)
377
+
378
+ # sets TTL for redis keys of specific resolutions
379
+ config.set_expirations({ minute: 1.hour, hour: 1.day })
380
+
381
+ end
382
+ ```
383
+
384
+ #### Prefixing
385
+
386
+ Many applications use a single Redis instance for a number of uses:
387
+ background jobs, ephemeral data, TabsTabs , etc. To avoid key collisions,
388
+ and to make it easier to drop all of your TabsTabs data without affecting
389
+ other parts of your system (or if more than one app shares the Redis
390
+ instance) you can prefix a given 'instance'.
391
+
392
+ Setting the prefix config option will cause all of the keys that TabsTabs
393
+ stores to use this format:
394
+
395
+ ```
396
+ tabstabs:#{prefix}:#{key}..."
397
+ ```
398
+
399
+ ## Change Log & Breaking Changes
400
+
401
+ ### v2.0.0
402
+
403
+ Fork of [Tabs](https://github.com/devmynd/tabs) due to its discontinuation
404
+ and incompatibility with newer Redis versions.
405
+
406
+ - Relaxed redis-rb version requirement
407
+ - Updated specs to new syntax
408
+
409
+ ## Contributing
410
+
411
+ 1. Fork it
412
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
413
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
414
+ 4. Push to the branch (`git push origin my-new-feature`)
415
+ 5. Create new Pull Request
416
+
417
+
418
+ ## Special Thanks
419
+
420
+ Thanks to [@DevMynd](https://github.com/devmynd) for creating the initial version
421
+ of this gem!