instrument_all_the_things 0.9.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yml +14 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +95 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +7 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +74 -0
  10. data/README.md +369 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +11 -0
  13. data/bin/setup +8 -0
  14. data/instrument_all_the_things.gemspec +42 -0
  15. data/lib/instrument_all_the_things/clients/stat_reporter/datadog.rb +12 -0
  16. data/lib/instrument_all_the_things/clients/tracer/blackhole.rb +22 -0
  17. data/lib/instrument_all_the_things/context.rb +23 -0
  18. data/lib/instrument_all_the_things/helpers.rb +55 -0
  19. data/lib/instrument_all_the_things/instrumentors/all.rb +6 -0
  20. data/lib/instrument_all_the_things/instrumentors/error_logging.rb +48 -0
  21. data/lib/instrument_all_the_things/instrumentors/execution_count_and_timing.rb +20 -0
  22. data/lib/instrument_all_the_things/instrumentors/gc_stats.rb +49 -0
  23. data/lib/instrument_all_the_things/instrumentors/tracing.rb +30 -0
  24. data/lib/instrument_all_the_things/method_instrumentor.rb +54 -0
  25. data/lib/instrument_all_the_things/method_proxy.rb +54 -0
  26. data/lib/instrument_all_the_things/testing/rspec_matchers.rb +97 -0
  27. data/lib/instrument_all_the_things/testing/stat_tracker.rb +43 -0
  28. data/lib/instrument_all_the_things/testing/trace_tracker.rb +25 -0
  29. data/lib/instrument_all_the_things/version.rb +3 -0
  30. data/lib/instrument_all_the_things.rb +70 -0
  31. data/logo.jpg +0 -0
  32. data/vendor/cache/ast-2.4.0.gem +0 -0
  33. data/vendor/cache/benchmark-ips-2.7.2.gem +0 -0
  34. data/vendor/cache/coderay-1.1.2.gem +0 -0
  35. data/vendor/cache/ddtrace-0.32.0.gem +0 -0
  36. data/vendor/cache/diff-lcs-1.3.gem +0 -0
  37. data/vendor/cache/docile-1.3.2.gem +0 -0
  38. data/vendor/cache/dogstatsd-ruby-4.6.0.gem +0 -0
  39. data/vendor/cache/jaro_winkler-1.5.4.gem +0 -0
  40. data/vendor/cache/method_source-0.9.2.gem +0 -0
  41. data/vendor/cache/msgpack-1.3.3.gem +0 -0
  42. data/vendor/cache/parallel-1.19.1.gem +0 -0
  43. data/vendor/cache/parser-2.7.0.2.gem +0 -0
  44. data/vendor/cache/pry-0.12.2.gem +0 -0
  45. data/vendor/cache/rainbow-3.0.0.gem +0 -0
  46. data/vendor/cache/rake-10.5.0.gem +0 -0
  47. data/vendor/cache/rexml-3.2.4.gem +0 -0
  48. data/vendor/cache/rspec-3.9.0.gem +0 -0
  49. data/vendor/cache/rspec-core-3.9.1.gem +0 -0
  50. data/vendor/cache/rspec-expectations-3.9.0.gem +0 -0
  51. data/vendor/cache/rspec-mocks-3.9.1.gem +0 -0
  52. data/vendor/cache/rspec-support-3.9.2.gem +0 -0
  53. data/vendor/cache/rubocop-0.80.0.gem +0 -0
  54. data/vendor/cache/ruby-progressbar-1.10.1.gem +0 -0
  55. data/vendor/cache/simplecov-0.18.1.gem +0 -0
  56. data/vendor/cache/simplecov-html-0.11.0.gem +0 -0
  57. data/vendor/cache/unicode-display_width-1.6.1.gem +0 -0
  58. metadata +227 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 13757eb83b8ab7dfb82354e91666927cac3cb03027dc52e67604c1de51b4f748
4
+ data.tar.gz: e69d10d504e0a017074c4cfb208fe2775508f1adb29c88d9e47e265badcfe0ec
5
+ SHA512:
6
+ metadata.gz: 5743da7853d6563314b209954a090eca836aff00b908392678df98abad8bb62c1fc6fdcf6f64abd5be715781c5281fdcd7f470b40471217e005f16e12dc2ccec
7
+ data.tar.gz: b087b4ef6a124e395023372bc4211e790fe31869e319f6aa491abe1396e282e1c427dd7382dc1479f23ab88f4eb3122d647fb085527cdc80036c6f0904b93e9a
data/.drone.yml ADDED
@@ -0,0 +1,14 @@
1
+ kind: pipeline
2
+ name: default
3
+
4
+ steps:
5
+ - name: test-ruby
6
+ image: getterminus/ruby-ci-image:2.6-je-20190205
7
+ group: bundler
8
+ volumes:
9
+ - name: bundledir
10
+ path: /usr/local/bundle
11
+ commands:
12
+ - bundle check --path=/usr/local/bundle || bundle --local --path=/usr/local/bundle
13
+ - bundle exec rspec
14
+
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,95 @@
1
+ AllCops:
2
+ Exclude:
3
+ - Makefile
4
+ - vendor/**/*
5
+ - bin/*
6
+ - Guardfile
7
+
8
+ Naming/AccessorMethodName:
9
+ Exclude:
10
+ - app/controllers/**/*
11
+
12
+ Layout/EndOfLine:
13
+ Enabled: false
14
+
15
+ Style/DateTime:
16
+ Enabled: false
17
+
18
+ Style/Documentation:
19
+ Enabled: false
20
+
21
+ Lint/Debugger:
22
+ Enabled: true
23
+
24
+ Style/FrozenStringLiteralComment:
25
+ Enabled: true
26
+ EnforcedStyle: always
27
+
28
+ Style/TrailingCommaInHashLiteral:
29
+ Enabled: true
30
+ EnforcedStyleForMultiline: comma
31
+
32
+ Style/TrailingCommaInArrayLiteral:
33
+ Enabled: true
34
+ EnforcedStyleForMultiline: comma
35
+
36
+ Style/TrailingCommaInArguments:
37
+ Enabled: true
38
+ EnforcedStyleForMultiline: comma
39
+
40
+ Lint/UnusedMethodArgument:
41
+ AllowUnusedKeywordArguments: true
42
+
43
+ Layout/LineLength:
44
+ Enabled: true
45
+ Max: 280
46
+ IgnoreCopDirectives: true
47
+ IgnoredPatterns: ['\A#', '\A\s*sig { .* }\Z']
48
+ Exclude:
49
+ - '**/*_pb.rb'
50
+
51
+ Metrics/AbcSize:
52
+ Enabled: true
53
+ Max: 46
54
+
55
+ Metrics/CyclomaticComplexity:
56
+ Max: 7
57
+
58
+ Metrics/MethodLength:
59
+ Enabled: true
60
+ Max: 32
61
+
62
+ Layout/ParameterAlignment:
63
+ Enabled: true
64
+ EnforcedStyle: with_fixed_indentation
65
+
66
+ Naming/MethodParameterName:
67
+ Enabled: true
68
+ AllowedNames: ['io', 'id', 'to', 'by', 'on', 'in', 'at', '_'] # Defaults + _
69
+
70
+ Layout/MultilineMethodCallIndentation:
71
+ Enabled: true
72
+ EnforcedStyle: indented
73
+
74
+ Style/ParallelAssignment:
75
+ Enabled: true
76
+
77
+ Metrics/ClassLength:
78
+ Max: 240
79
+
80
+ Metrics/BlockLength:
81
+ Max: 30
82
+ Exclude:
83
+ - spec/**/*.rb
84
+ - '**/*_pb.rb'
85
+
86
+ Style/BlockDelimiters:
87
+ Enabled: true
88
+ BracesRequiredMethods: ['expect']
89
+
90
+ Metrics/ParameterLists:
91
+ Max: 6
92
+
93
+ Lint/AmbiguousBlockAssociation:
94
+ Exclude:
95
+ - spec/**/*.rb
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.1
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.1
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in instrument_all_the_things.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,74 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ instrument_all_the_things (0.9.0.alpha)
5
+ ddtrace
6
+ dogstatsd-ruby
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.0)
12
+ benchmark-ips (2.7.2)
13
+ coderay (1.1.2)
14
+ ddtrace (0.32.0)
15
+ msgpack
16
+ diff-lcs (1.3)
17
+ docile (1.3.2)
18
+ dogstatsd-ruby (4.6.0)
19
+ jaro_winkler (1.5.4)
20
+ method_source (0.9.2)
21
+ msgpack (1.3.3)
22
+ parallel (1.19.1)
23
+ parser (2.7.0.2)
24
+ ast (~> 2.4.0)
25
+ pry (0.12.2)
26
+ coderay (~> 1.1.0)
27
+ method_source (~> 0.9.0)
28
+ rainbow (3.0.0)
29
+ rake (10.5.0)
30
+ rexml (3.2.4)
31
+ rspec (3.9.0)
32
+ rspec-core (~> 3.9.0)
33
+ rspec-expectations (~> 3.9.0)
34
+ rspec-mocks (~> 3.9.0)
35
+ rspec-core (3.9.1)
36
+ rspec-support (~> 3.9.1)
37
+ rspec-expectations (3.9.0)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.9.0)
40
+ rspec-mocks (3.9.1)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.9.0)
43
+ rspec-support (3.9.2)
44
+ rubocop (0.80.0)
45
+ jaro_winkler (~> 1.5.1)
46
+ parallel (~> 1.10)
47
+ parser (>= 2.7.0.1)
48
+ rainbow (>= 2.2.2, < 4.0)
49
+ rexml
50
+ ruby-progressbar (~> 1.7)
51
+ unicode-display_width (>= 1.4.0, < 1.7)
52
+ ruby-progressbar (1.10.1)
53
+ simplecov (0.18.1)
54
+ docile (~> 1.1)
55
+ simplecov-html (~> 0.11.0)
56
+ simplecov-html (0.11.0)
57
+ unicode-display_width (1.6.1)
58
+
59
+ PLATFORMS
60
+ ruby
61
+ x86_64-darwin-18
62
+
63
+ DEPENDENCIES
64
+ benchmark-ips
65
+ bundler (~> 2.0)
66
+ instrument_all_the_things!
67
+ pry
68
+ rake (~> 10.0)
69
+ rspec (~> 3.0)
70
+ rubocop
71
+ simplecov
72
+
73
+ BUNDLED WITH
74
+ 2.0.2
data/README.md ADDED
@@ -0,0 +1,369 @@
1
+ Visibility into your application is one of the most critical parts of software development. At best, visibility is typically an afterthought and this is a problem. So what do you do?
2
+
3
+ ![Instrument all the things](./logo.jpg?raw=true)
4
+
5
+ # InstrumentAllTheThings
6
+
7
+ At Terminus we use DataDog for our application visibility. InstrumentAllTheThings provides simple ways to quickly and unobtrusively add detailed instrumentation to Datadog metrics and Datadog APM.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'instrument_all_the_things'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install instrument_all_the_things
24
+
25
+ ## Usage
26
+ *Note:* For convenience the InstrumentAllTheThings constant is aliased to IATT.
27
+
28
+
29
+ ## Method Instrumentation
30
+ Method instrumentation both in APM as well as in simple counts & timings is the bread and butter of visibility, and it
31
+ is trivial to implement with IATT.
32
+
33
+ Each measured metric may be individually disabled, and some may be provided additional configuration. All measurments
34
+ default to on, unless otherwise specified. You may disable the specified measurement by providing a falsy value to the
35
+ configuration key when calling `instrument`
36
+
37
+ *Example*
38
+ ```ruby
39
+ class Foo
40
+ include InstrumentAllTheThings
41
+
42
+ instrument config_key: {configuration_option: 123}
43
+ def foo
44
+ end
45
+ end
46
+ ```
47
+ ### Garbage Collection Stats
48
+ _Configuration Key `gc_stats`_
49
+
50
+ Collects the difference between the specified keys during the execution of the method.
51
+
52
+ Stat diffs are added to the active trace span as a tag, and a stat is emitted with the following format
53
+
54
+ `klass_name.(instance|class)_methods.(stat_name)_change`
55
+
56
+ #### Description of default stats
57
+ _GC Stats are not thread local, if your app is multi threaded other threads may be contributing to these stats_
58
+ | Option | Description
59
+ | ----- | ----
60
+ | total_allocated_pages | Total number of memory pages owned by this ruby process. Mature processes tend to see a slowdown in page allocations
61
+ | total_allocated_objects | Total number of objects which have not been garbage collected yet
62
+ | count | Total number of GC runs during this method's exuection
63
+
64
+ #### Options
65
+ | Option | Description | Default
66
+ | ----- | ---- | -----
67
+ | diffed_stats | Stats to diff and record | [:total_allocated_pages, :total_allocated_objects, :count]
68
+
69
+ ### Error Logging
70
+ _Configuration Key `log_errors`_
71
+
72
+ When set to a non falsy value all errors raised in a span will be logged to the configured IATT logger and re-emitted.
73
+ The first traces span where they are seen logs them only, they will not be re-logged by IATT at any future level
74
+
75
+ By default call stacks are logged without all gem paths (as defined by the `Bundler.bundle_path`)
76
+
77
+ | Option | Description | Default
78
+ | ----- | ---- | -----
79
+ | rescue_class | The parent error which should be caught and logged | StandardError
80
+ | exclude_bundle_path | If truthy, remove all entries from the error backtrace which are in the bundle path | true
81
+
82
+ ### Tracing
83
+ _Configuration Key `trace`_
84
+
85
+ When set to a non falsy value, a span for this method will be created. The defaults are listed below. This hash will
86
+ also be passed to the DataDog tracer, and their [options](https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#manual-instrumentation) should also be understood.
87
+
88
+ | Option | Description | Default
89
+ | ----- | ---- | -----
90
+ | service | This is the value which shows up on the [first page of the APM screen](https://app.datadoghq.com/apm/home) this should be set at the entry point of your application or process | `nil`
91
+ | resource | How this method will show up when viewing the service in APM. | For instance methods `ClassName.method_name`<br>For class methods `ClassName#method_name`
92
+ | span_name | You probably don't want to change this | `method.execution`
93
+ | span_type | See DD Docs. | `nil`
94
+ | tags | Set of tags to be added to the span, expected to be a hash | {}
95
+
96
+ ## Testing Support
97
+
98
+ You can setup your test environment by running the following setup:
99
+
100
+ ```ruby
101
+ require 'instrument_all_the_things/testing/stat_tracker'
102
+ require 'instrument_all_the_things/testing/trace_tracker'
103
+
104
+ Datadog.configure do |c|
105
+ c.tracer transport_options: proc { |t|
106
+ t.adapter :test, IATT::Testing::TraceTracker.new
107
+ }
108
+ end
109
+
110
+ IATT.stat_reporter = IATT::Testing::StatTracker.new
111
+
112
+ RSpec.configure do |config|
113
+ config.before(:each) do
114
+ IATT::Testing::TraceTracker.tracker.reset!
115
+ IATT.stat_reporter.reset!
116
+ end
117
+ end
118
+ ```
119
+
120
+ This injects middleware and in the StatsD interface as well as in the Tracer output. By doing this you can start using
121
+ some awesome rspec helpers like so:
122
+
123
+ ```ruby
124
+ let(:klass) do
125
+ Class.new do
126
+ include InstrumentAllTheThings::Helpers
127
+
128
+ instrument
129
+ def foo
130
+ end
131
+
132
+ def self.inspect
133
+ 'KlassName'
134
+ end
135
+ end
136
+ end
137
+
138
+ it 'traces' do
139
+ expect {
140
+ klass.new.foo
141
+
142
+ # Datadog writes trace to the wire and to the test harness asynchronously
143
+ # This helper is provided to force the flush before expectations are stated
144
+ flush_traces
145
+ }.to change{
146
+ emitted_spans(
147
+ filtered_by: {resource: 'KlassName.foo'}
148
+ )
149
+ }.by(1)
150
+ end
151
+ end
152
+ ```
153
+
154
+ ## Stat Transmission
155
+ InstrumentAllTheThings provides no real functionality on top of the build in Datadog statsd utility. The primary goal
156
+ of the IATT library is to allow for easy testing. The following method are provided on the InstrumentAllTheThings module.
157
+
158
+ ### Increment
159
+ Examples:
160
+ ```ruby
161
+ expect{
162
+ InstrumentAllTheThings.increment('my.counter')
163
+ }.to change{ counter_value('my.counter') }.from(0).to(1)
164
+
165
+ expect{
166
+ InstrumentAllTheThings.increment('my.counter', by: 5)
167
+ }.to change { counter_value('my.counter') }.from(0).to(5)
168
+
169
+ # You can also filter by tags
170
+ expect {
171
+ InstrumentAllTheThings.increment('my.counter', by: 3, tags: ['a:b', 'foo:bar'])
172
+ InstrumentAllTheThings.increment('my.counter', by: 2, tags: ['a:b', 'foo:baz'])
173
+ }.to change { counter_value('my.counter') }.from(0).to(5)
174
+ .and change { counter_value('my.counter', with_tags: ['a:b']) }.from(0).to(5)
175
+ .and change { counter_value('my.counter', with_tags: ['foo:bar']) }.from(0).to(3)
176
+ .and change { counter_value('my.counter', with_tags: ['foo:baz']) }.from(0).to(2)
177
+ ```
178
+
179
+ ### Decrement
180
+ See Increment for all examples
181
+ ```ruby
182
+ expect{
183
+ InstrumentAllTheThings.increment('my.counter')
184
+ }.to change{ counter_value('my.counter') }.from(0).to(-1)
185
+ ```
186
+
187
+ ### Count
188
+ Count underlies both increment and decrement, and works in a similar way.
189
+
190
+ ```ruby
191
+ expect {
192
+ InstrumentAllTheThings.count('my.counter', 3, tags: ['a:b', 'foo:bar'])
193
+ InstrumentAllTheThings.count('my.counter', 2, tags: ['a:b', 'foo:baz'])
194
+ }.to change { counter_value('my.counter') }.from(0).to(5)
195
+ .and change { counter_value('my.counter', with_tags: ['a:b']) }.from(0).to(5)
196
+ .and change { counter_value('my.counter', with_tags: ['foo:bar']) }.from(0).to(3)
197
+ .and change { counter_value('my.counter', with_tags: ['foo:baz']) }.from(0).to(2)
198
+ ```
199
+
200
+ ### Gauge
201
+ ```ruby
202
+ expect {
203
+ InstrumentAllTheThings.gauge('my.gauge', 1)
204
+ InstrumentAllTheThings.gauge('my.gauge', 2)
205
+ }.to change { gauge_value('my.gauge') }.from(nil).to(2)
206
+
207
+ expect {
208
+ InstrumentAllTheThings.gauge('my.gauge', 3, tags: ['a:b', 'foo:bar'])
209
+ InstrumentAllTheThings.gauge('my.gauge', 1, tags: ['a:b', 'foo:bar'])
210
+ InstrumentAllTheThings.gauge('my.gauge', 2, tags: ['a:b', 'foo:baz'])
211
+ InstrumentAllTheThings.gauge('my.gauge', 7, tags: ['a:b'])
212
+ }.to change { gauge_value('my.gauge') }.to(7)
213
+ .and change { gauge_value('my.gauge', with_tags: ['a:b']) }.to(7)
214
+ .and change { gauge_value('my.gauge', with_tags: ['foo:bar']) }.to(1)
215
+ .and change { gauge_value('my.gauge', with_tags: ['foo:baz']) }.to(2)
216
+ ```
217
+
218
+ ### Set
219
+ ```ruby
220
+ expect {
221
+ InstrumentAllTheThings.set('my.set', 1)
222
+ InstrumentAllTheThings.set('my.set', 2)
223
+ }.to change { set_value('my.set') }.from(0).to(2)
224
+
225
+ expect {
226
+ InstrumentAllTheThings.set('my.set', 3, tags: ['a:b', 'foo:bar'])
227
+ InstrumentAllTheThings.set('my.set', 3, tags: ['a:b', 'foo:bar'])
228
+ InstrumentAllTheThings.set('my.set', 5, tags: ['a:b', 'foo:bar'])
229
+ InstrumentAllTheThings.set('my.set', 6, tags: ['a:b', 'foo:baz'])
230
+ InstrumentAllTheThings.set('my.set', 9, tags: ['a:b'])
231
+ }.to change { set_value('my.set') }.to(4)
232
+ .and change { set_value('my.set', with_tags: ['a:b']) }.to(4)
233
+ .and change { set_value('my.set', with_tags: ['foo:bar']) }.to(2)
234
+ .and change { set_value('my.set', with_tags: ['foo:baz']) }.to(1)
235
+ ```
236
+
237
+ ### Histogram
238
+ Histogram is a pesudo metric in Datadog based on the StatsD timing metric. InstrumentAllTheThings provides a way to
239
+ test the values emitted, not the statistical calculations derived.
240
+
241
+ ```ruby
242
+ expect {
243
+ InstrumentAllTheThings.histogram('my.histogram', 1)
244
+ InstrumentAllTheThings.histogram('my.histogram', 2)
245
+ }.to change { histogram_values('my.histogram') }.from([]).to([1, 2])
246
+
247
+ expect {
248
+ InstrumentAllTheThings.histogram('my.histogram', 3, tags: ['a:b', 'foo:bar'])
249
+ InstrumentAllTheThings.histogram('my.histogram', 5, tags: ['a:b', 'foo:bar'])
250
+ InstrumentAllTheThings.histogram('my.histogram', 6, tags: ['a:b', 'foo:baz'])
251
+ InstrumentAllTheThings.histogram('my.histogram', 9, tags: ['a:b'])
252
+ }.to change { histogram_values('my.histogram') }.to([3, 5, 6, 9])
253
+ .and change { histogram_values('my.histogram', with_tags: ['a:b']) }.to([3, 5, 6, 9])
254
+ .and change { histogram_values('my.histogram', with_tags: ['foo:bar']) }.to([3, 5])
255
+ .and change { histogram_values('my.histogram', with_tags: ['foo:baz']) }.to([6])
256
+ ```
257
+
258
+ ### Distribution
259
+ ```ruby
260
+ expect {
261
+ InstrumentAllTheThings.distribution('my.distribution', 1)
262
+ InstrumentAllTheThings.distribution('my.distribution', 2)
263
+ }.to change { distribution_values('my.distribution') }.from([]).to([1, 2])
264
+
265
+ expect {
266
+ InstrumentAllTheThings.distribution('my.distribution', 3, tags: ['a:b', 'foo:bar'])
267
+ InstrumentAllTheThings.distribution('my.distribution', 5, tags: ['a:b', 'foo:bar'])
268
+ InstrumentAllTheThings.distribution('my.distribution', 6, tags: ['a:b', 'foo:baz'])
269
+ InstrumentAllTheThings.distribution('my.distribution', 9, tags: ['a:b'])
270
+ }.to change { distribution_values('my.distribution') }.to([3, 5, 6, 9])
271
+ .and change { distribution_values('my.distribution', with_tags: ['a:b']) }.to([3, 5, 6, 9])
272
+ .and change { distribution_values('my.distribution', with_tags: ['foo:bar']) }.to([3, 5])
273
+ .and change { distribution_values('my.distribution', with_tags: ['foo:baz']) }.to([6])
274
+ ```
275
+
276
+ ### Timing
277
+ ```ruby
278
+ expect {
279
+ InstrumentAllTheThings.timing('my.timing', 1)
280
+ InstrumentAllTheThings.timing('my.timing', 2)
281
+ }.to change { timing_values('my.timing') }.from([]).to([1, 2])
282
+
283
+ expect {
284
+ InstrumentAllTheThings.timing('my.timing', 3, tags: ['a:b', 'foo:bar'])
285
+ InstrumentAllTheThings.timing('my.timing', 5, tags: ['a:b', 'foo:bar'])
286
+ InstrumentAllTheThings.timing('my.timing', 6, tags: ['a:b', 'foo:baz'])
287
+ InstrumentAllTheThings.timing('my.timing', 9, tags: ['a:b'])
288
+ }.to change { timing_values('my.timing') }.to([3, 5, 6, 9])
289
+ .and change { timing_values('my.timing', with_tags: ['a:b']) }.to([3, 5, 6, 9])
290
+ .and change { timing_values('my.timing', with_tags: ['foo:bar']) }.to([3, 5])
291
+ .and change { timing_values('my.timing', with_tags: ['foo:baz']) }.to([6])
292
+ ```
293
+
294
+ ### Time
295
+ The time helper wraps a block with reporting to the timing metric time. As such specs should leverage that spec helper.
296
+
297
+ ```ruby
298
+ expect {
299
+ InstrumentAllTheThings.time('my.time') {}
300
+ InstrumentAllTheThings.time('my.time') {}
301
+ }.to change { timing_values('my.time').length }.from(0).to(2)
302
+
303
+ expect {
304
+ InstrumentAllTheThings.time('my.time', tags: ['a:b', 'foo:bar']) {}
305
+ InstrumentAllTheThings.time('my.time', tags: ['a:b', 'foo:bar']) {}
306
+ InstrumentAllTheThings.time('my.time', tags: ['a:b', 'foo:baz']) {}
307
+ InstrumentAllTheThings.time('my.time', tags: ['a:b']) {}
308
+ }.to change { timing_values('my.time').length }.to(4)
309
+ .and change { timing_values('my.time', with_tags: ['a:b']).length }.to(4)
310
+ .and change { timing_values('my.time', with_tags: ['foo:bar']).length }.to(2)
311
+ .and change { timing_values('my.time', with_tags: ['foo:baz']).length }.to(1)
312
+ ```
313
+ ## Performance
314
+ Is it slow? That depends quite a bit on your perspective. We keep a benchmark as part of our specs and monitor the
315
+ overall cost of instrumentation. The current numbers look roughly like:
316
+
317
+ ```
318
+ Calculating -------------------------------------
319
+ uninstrumetned 17.038M (± 3.3%) i/s - 85.164M in 5.004312s
320
+ the_works 7.404k (±12.9%) i/s - 36.936k in 5.100630s
321
+ only_trace 27.968k (±12.7%) i/s - 139.209k in 5.061907s
322
+ only_error_logging 638.098k (± 4.6%) i/s - 3.231M in 5.075275s
323
+ only_gc_stats 12.930k (±13.2%) i/s - 63.865k in 5.070874s
324
+ only_execution_counts 9.847k (±11.1%) i/s - 49.088k in 5.073475s
325
+ ```
326
+
327
+ All of these calculations are performed against an empty method, which ruby executes in 0.000059ms. If we consider
328
+ this the baseline, running all of the included instrumentation in the non-error case has a cost of 0.135ms. If this
329
+ is an expensive calculation is directly proportional to the execution duration of the method. If you are tracing a 1ms
330
+ method, it represents a 13% overhead, but if you are tracing a 100ms method it is only 0.13%.
331
+
332
+ The moral of the story, don't blindly add the works to every method in your stack, instead choose methods who's output
333
+ will be diagnostically meaningful.
334
+
335
+ We are also always looking for patches with will reduce execution time and allocations of the instrumentation, so
336
+ please open some PRs!
337
+
338
+ ## Configuration
339
+ The configuration for IATT is available on the top level InstrumentAllTheThings module.
340
+
341
+ | Config Name | Description | Default
342
+ | ----------- | ----------- | -------
343
+ | stat_prefix | The string to add to all outbound stats (may not be changed after stat transmiter initialization) | `nil`
344
+ | logger | The logger used to report errors and info | If the constant `Rails` is set, use `Rails.logger`. <br>If `App` and it responds to `logger` use `App.logger`. Otherwise create a new `Logger` sent to STDOUT
345
+ | stat_reporter | The class which receives simple stats | If [Datadog::Statsd](https://github.com/DataDog/dogstatsd-ruby) is found, use that, otherwise the Blackhole client is used
346
+ | tracer | The instance of a tracer which will handle all traces | If `Datadog` is defined and responds to `tracer`, use the value returned by that. Otherwise use the Blackhole. [Gem](https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md)
347
+
348
+
349
+ ### Stats Reporters
350
+ #### Datadog
351
+ The default client if the constant `Datadog::Statsd` is found.
352
+
353
+ Initialized with environment variables
354
+ * `DATADOG_HOST` if set, otherwise `localhost`
355
+ * `DATADOG_POST` if set, otherwise `8125`
356
+
357
+ ### Tracers
358
+ #### Datadog
359
+ The default client if the constant `Datadog` is found and has a non-null value for `tracer`.
360
+
361
+ ## Development
362
+
363
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
364
+
365
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
366
+
367
+ ## Contributing
368
+
369
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/instrument_all_the_things.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "instrument_all_the_things"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'instrument_all_the_things/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'instrument_all_the_things'
9
+ spec.version = InstrumentAllTheThings::VERSION
10
+ spec.authors = ['Brian Malinconico']
11
+ spec.email = ['bmalinconico@terminus.com']
12
+
13
+ spec.summary = 'Make instrumentation with DataDog easy peasy'
14
+ spec.description = 'Wrappers to make instrumentation of methods easy and pleasant to read'
15
+ spec.homepage = 'https://github.com/GetTerminus/instrument-all-the-things'
16
+
17
+ spec.metadata['allowed_push_host'] = "https://www.rubygems.org"
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/GetTerminus/instrument-all-the-things'
21
+ # spec.metadata['changelog_uri'] = 'http://google.com'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+
32
+ spec.add_dependency 'ddtrace'
33
+ spec.add_dependency 'dogstatsd-ruby'
34
+
35
+ spec.add_development_dependency 'bundler', '~> 2.0'
36
+ spec.add_development_dependency 'pry'
37
+ spec.add_development_dependency 'rake', '~> 10.0'
38
+ spec.add_development_dependency 'rspec', '~> 3.0'
39
+ spec.add_development_dependency 'simplecov'
40
+ spec.add_development_dependency 'rubocop'
41
+ spec.add_development_dependency 'benchmark-ips'
42
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'datadog/statsd'
4
+
5
+ module InstrumentAllTheThings
6
+ module Clients
7
+ module StatReporter
8
+ class DataDog < Datadog::Statsd
9
+ end
10
+ end
11
+ end
12
+ end