instrument_all_the_things 1.0.2

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 +371 -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.rb +70 -0
  16. data/lib/instrument_all_the_things/clients/stat_reporter/datadog.rb +12 -0
  17. data/lib/instrument_all_the_things/clients/tracer/blackhole.rb +22 -0
  18. data/lib/instrument_all_the_things/context.rb +23 -0
  19. data/lib/instrument_all_the_things/helpers.rb +55 -0
  20. data/lib/instrument_all_the_things/instrumentors/all.rb +6 -0
  21. data/lib/instrument_all_the_things/instrumentors/error_logging.rb +48 -0
  22. data/lib/instrument_all_the_things/instrumentors/execution_count_and_timing.rb +20 -0
  23. data/lib/instrument_all_the_things/instrumentors/gc_stats.rb +49 -0
  24. data/lib/instrument_all_the_things/instrumentors/tracing.rb +30 -0
  25. data/lib/instrument_all_the_things/method_instrumentor.rb +57 -0
  26. data/lib/instrument_all_the_things/method_proxy.rb +54 -0
  27. data/lib/instrument_all_the_things/testing/rspec_matchers.rb +97 -0
  28. data/lib/instrument_all_the_things/testing/stat_tracker.rb +43 -0
  29. data/lib/instrument_all_the_things/testing/trace_tracker.rb +25 -0
  30. data/lib/instrument_all_the_things/version.rb +5 -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.33.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.7.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
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8bcc57ca40a267c78c39c37d55711d1ca2ca33637c115b293e9f679b5950b6e7
4
+ data.tar.gz: 720d12fbd72e5c7bb7b82f9088c882037cc6c2ba675f3f7c097bfcfdb03771e5
5
+ SHA512:
6
+ metadata.gz: 2a99e6d00c90458e247bb4a018d2abd23774aec7d2c660bdad9e0e6e22dc9b29058e2d605f4dd0fa126084bb2163721fb1e2e8670b1521c488718d3e1d225010
7
+ data.tar.gz: 9e9ea9639a45dd1f766882bb2ae355a4e9029a388a9421849d681389d8595de5f7cf81ac38f7648add79b4db7582320bc33ccdf057cc352e6634b6ee8c392dad
@@ -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
+
@@ -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
@@ -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
@@ -0,0 +1 @@
1
+ 2.6.1
@@ -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
@@ -0,0 +1,74 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ instrument_all_the_things (1.0.2)
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.33.0)
15
+ msgpack
16
+ diff-lcs (1.3)
17
+ docile (1.3.2)
18
+ dogstatsd-ruby (4.7.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
@@ -0,0 +1,371 @@
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 unless the constant is already defined.
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
+ require 'instrument_all_the_things/testing/rspec_matchers'
104
+
105
+ Datadog.configure do |c|
106
+ c.tracer transport_options: proc { |t|
107
+ t.adapter :test, IATT::Testing::TraceTracker.new
108
+ }
109
+ end
110
+
111
+ IATT.stat_reporter = IATT::Testing::StatTracker.new
112
+
113
+ RSpec.configure do |config|
114
+ config.include InstrumentAllTheThings::Testing::RSpecMatchers
115
+ config.before(:each) do
116
+ IATT::Testing::TraceTracker.tracker.reset!
117
+ IATT.stat_reporter.reset!
118
+ end
119
+ end
120
+ ```
121
+
122
+ This injects middleware and in the StatsD interface as well as in the Tracer output. By doing this you can start using
123
+ some awesome rspec helpers like so:
124
+
125
+ ```ruby
126
+ let(:klass) do
127
+ Class.new do
128
+ include InstrumentAllTheThings::Helpers
129
+
130
+ instrument
131
+ def foo
132
+ end
133
+
134
+ def self.inspect
135
+ 'KlassName'
136
+ end
137
+ end
138
+ end
139
+
140
+ it 'traces' do
141
+ expect {
142
+ klass.new.foo
143
+
144
+ # Datadog writes trace to the wire and to the test harness asynchronously
145
+ # This helper is provided to force the flush before expectations are stated
146
+ flush_traces
147
+ }.to change{
148
+ emitted_spans(
149
+ filtered_by: {resource: 'KlassName.foo'}
150
+ )
151
+ }.by(1)
152
+ end
153
+ end
154
+ ```
155
+
156
+ ## Stat Transmission
157
+ InstrumentAllTheThings provides no real functionality on top of the build in Datadog statsd utility. The primary goal
158
+ of the IATT library is to allow for easy testing. The following method are provided on the InstrumentAllTheThings module.
159
+
160
+ ### Increment
161
+ Examples:
162
+ ```ruby
163
+ expect{
164
+ InstrumentAllTheThings.increment('my.counter')
165
+ }.to change{ counter_value('my.counter') }.from(0).to(1)
166
+
167
+ expect{
168
+ InstrumentAllTheThings.increment('my.counter', by: 5)
169
+ }.to change { counter_value('my.counter') }.from(0).to(5)
170
+
171
+ # You can also filter by tags
172
+ expect {
173
+ InstrumentAllTheThings.increment('my.counter', by: 3, tags: ['a:b', 'foo:bar'])
174
+ InstrumentAllTheThings.increment('my.counter', by: 2, tags: ['a:b', 'foo:baz'])
175
+ }.to change { counter_value('my.counter') }.from(0).to(5)
176
+ .and change { counter_value('my.counter', with_tags: ['a:b']) }.from(0).to(5)
177
+ .and change { counter_value('my.counter', with_tags: ['foo:bar']) }.from(0).to(3)
178
+ .and change { counter_value('my.counter', with_tags: ['foo:baz']) }.from(0).to(2)
179
+ ```
180
+
181
+ ### Decrement
182
+ See Increment for all examples
183
+ ```ruby
184
+ expect{
185
+ InstrumentAllTheThings.increment('my.counter')
186
+ }.to change{ counter_value('my.counter') }.from(0).to(-1)
187
+ ```
188
+
189
+ ### Count
190
+ Count underlies both increment and decrement, and works in a similar way.
191
+
192
+ ```ruby
193
+ expect {
194
+ InstrumentAllTheThings.count('my.counter', 3, tags: ['a:b', 'foo:bar'])
195
+ InstrumentAllTheThings.count('my.counter', 2, tags: ['a:b', 'foo:baz'])
196
+ }.to change { counter_value('my.counter') }.from(0).to(5)
197
+ .and change { counter_value('my.counter', with_tags: ['a:b']) }.from(0).to(5)
198
+ .and change { counter_value('my.counter', with_tags: ['foo:bar']) }.from(0).to(3)
199
+ .and change { counter_value('my.counter', with_tags: ['foo:baz']) }.from(0).to(2)
200
+ ```
201
+
202
+ ### Gauge
203
+ ```ruby
204
+ expect {
205
+ InstrumentAllTheThings.gauge('my.gauge', 1)
206
+ InstrumentAllTheThings.gauge('my.gauge', 2)
207
+ }.to change { gauge_value('my.gauge') }.from(nil).to(2)
208
+
209
+ expect {
210
+ InstrumentAllTheThings.gauge('my.gauge', 3, tags: ['a:b', 'foo:bar'])
211
+ InstrumentAllTheThings.gauge('my.gauge', 1, tags: ['a:b', 'foo:bar'])
212
+ InstrumentAllTheThings.gauge('my.gauge', 2, tags: ['a:b', 'foo:baz'])
213
+ InstrumentAllTheThings.gauge('my.gauge', 7, tags: ['a:b'])
214
+ }.to change { gauge_value('my.gauge') }.to(7)
215
+ .and change { gauge_value('my.gauge', with_tags: ['a:b']) }.to(7)
216
+ .and change { gauge_value('my.gauge', with_tags: ['foo:bar']) }.to(1)
217
+ .and change { gauge_value('my.gauge', with_tags: ['foo:baz']) }.to(2)
218
+ ```
219
+
220
+ ### Set
221
+ ```ruby
222
+ expect {
223
+ InstrumentAllTheThings.set('my.set', 1)
224
+ InstrumentAllTheThings.set('my.set', 2)
225
+ }.to change { set_value('my.set') }.from(0).to(2)
226
+
227
+ expect {
228
+ InstrumentAllTheThings.set('my.set', 3, tags: ['a:b', 'foo:bar'])
229
+ InstrumentAllTheThings.set('my.set', 3, tags: ['a:b', 'foo:bar'])
230
+ InstrumentAllTheThings.set('my.set', 5, tags: ['a:b', 'foo:bar'])
231
+ InstrumentAllTheThings.set('my.set', 6, tags: ['a:b', 'foo:baz'])
232
+ InstrumentAllTheThings.set('my.set', 9, tags: ['a:b'])
233
+ }.to change { set_value('my.set') }.to(4)
234
+ .and change { set_value('my.set', with_tags: ['a:b']) }.to(4)
235
+ .and change { set_value('my.set', with_tags: ['foo:bar']) }.to(2)
236
+ .and change { set_value('my.set', with_tags: ['foo:baz']) }.to(1)
237
+ ```
238
+
239
+ ### Histogram
240
+ Histogram is a pesudo metric in Datadog based on the StatsD timing metric. InstrumentAllTheThings provides a way to
241
+ test the values emitted, not the statistical calculations derived.
242
+
243
+ ```ruby
244
+ expect {
245
+ InstrumentAllTheThings.histogram('my.histogram', 1)
246
+ InstrumentAllTheThings.histogram('my.histogram', 2)
247
+ }.to change { histogram_values('my.histogram') }.from([]).to([1, 2])
248
+
249
+ expect {
250
+ InstrumentAllTheThings.histogram('my.histogram', 3, tags: ['a:b', 'foo:bar'])
251
+ InstrumentAllTheThings.histogram('my.histogram', 5, tags: ['a:b', 'foo:bar'])
252
+ InstrumentAllTheThings.histogram('my.histogram', 6, tags: ['a:b', 'foo:baz'])
253
+ InstrumentAllTheThings.histogram('my.histogram', 9, tags: ['a:b'])
254
+ }.to change { histogram_values('my.histogram') }.to([3, 5, 6, 9])
255
+ .and change { histogram_values('my.histogram', with_tags: ['a:b']) }.to([3, 5, 6, 9])
256
+ .and change { histogram_values('my.histogram', with_tags: ['foo:bar']) }.to([3, 5])
257
+ .and change { histogram_values('my.histogram', with_tags: ['foo:baz']) }.to([6])
258
+ ```
259
+
260
+ ### Distribution
261
+ ```ruby
262
+ expect {
263
+ InstrumentAllTheThings.distribution('my.distribution', 1)
264
+ InstrumentAllTheThings.distribution('my.distribution', 2)
265
+ }.to change { distribution_values('my.distribution') }.from([]).to([1, 2])
266
+
267
+ expect {
268
+ InstrumentAllTheThings.distribution('my.distribution', 3, tags: ['a:b', 'foo:bar'])
269
+ InstrumentAllTheThings.distribution('my.distribution', 5, tags: ['a:b', 'foo:bar'])
270
+ InstrumentAllTheThings.distribution('my.distribution', 6, tags: ['a:b', 'foo:baz'])
271
+ InstrumentAllTheThings.distribution('my.distribution', 9, tags: ['a:b'])
272
+ }.to change { distribution_values('my.distribution') }.to([3, 5, 6, 9])
273
+ .and change { distribution_values('my.distribution', with_tags: ['a:b']) }.to([3, 5, 6, 9])
274
+ .and change { distribution_values('my.distribution', with_tags: ['foo:bar']) }.to([3, 5])
275
+ .and change { distribution_values('my.distribution', with_tags: ['foo:baz']) }.to([6])
276
+ ```
277
+
278
+ ### Timing
279
+ ```ruby
280
+ expect {
281
+ InstrumentAllTheThings.timing('my.timing', 1)
282
+ InstrumentAllTheThings.timing('my.timing', 2)
283
+ }.to change { timing_values('my.timing') }.from([]).to([1, 2])
284
+
285
+ expect {
286
+ InstrumentAllTheThings.timing('my.timing', 3, tags: ['a:b', 'foo:bar'])
287
+ InstrumentAllTheThings.timing('my.timing', 5, tags: ['a:b', 'foo:bar'])
288
+ InstrumentAllTheThings.timing('my.timing', 6, tags: ['a:b', 'foo:baz'])
289
+ InstrumentAllTheThings.timing('my.timing', 9, tags: ['a:b'])
290
+ }.to change { timing_values('my.timing') }.to([3, 5, 6, 9])
291
+ .and change { timing_values('my.timing', with_tags: ['a:b']) }.to([3, 5, 6, 9])
292
+ .and change { timing_values('my.timing', with_tags: ['foo:bar']) }.to([3, 5])
293
+ .and change { timing_values('my.timing', with_tags: ['foo:baz']) }.to([6])
294
+ ```
295
+
296
+ ### Time
297
+ The time helper wraps a block with reporting to the timing metric time. As such specs should leverage that spec helper.
298
+
299
+ ```ruby
300
+ expect {
301
+ InstrumentAllTheThings.time('my.time') {}
302
+ InstrumentAllTheThings.time('my.time') {}
303
+ }.to change { timing_values('my.time').length }.from(0).to(2)
304
+
305
+ expect {
306
+ InstrumentAllTheThings.time('my.time', tags: ['a:b', 'foo:bar']) {}
307
+ InstrumentAllTheThings.time('my.time', tags: ['a:b', 'foo:bar']) {}
308
+ InstrumentAllTheThings.time('my.time', tags: ['a:b', 'foo:baz']) {}
309
+ InstrumentAllTheThings.time('my.time', tags: ['a:b']) {}
310
+ }.to change { timing_values('my.time').length }.to(4)
311
+ .and change { timing_values('my.time', with_tags: ['a:b']).length }.to(4)
312
+ .and change { timing_values('my.time', with_tags: ['foo:bar']).length }.to(2)
313
+ .and change { timing_values('my.time', with_tags: ['foo:baz']).length }.to(1)
314
+ ```
315
+ ## Performance
316
+ Is it slow? That depends quite a bit on your perspective. We keep a benchmark as part of our specs and monitor the
317
+ overall cost of instrumentation. The current numbers look roughly like:
318
+
319
+ ```
320
+ Calculating -------------------------------------
321
+ uninstrumetned 17.038M (± 3.3%) i/s - 85.164M in 5.004312s
322
+ the_works 7.404k (±12.9%) i/s - 36.936k in 5.100630s
323
+ only_trace 27.968k (±12.7%) i/s - 139.209k in 5.061907s
324
+ only_error_logging 638.098k (± 4.6%) i/s - 3.231M in 5.075275s
325
+ only_gc_stats 12.930k (±13.2%) i/s - 63.865k in 5.070874s
326
+ only_execution_counts 9.847k (±11.1%) i/s - 49.088k in 5.073475s
327
+ ```
328
+
329
+ All of these calculations are performed against an empty method, which ruby executes in 0.000059ms. If we consider
330
+ this the baseline, running all of the included instrumentation in the non-error case has a cost of 0.135ms. If this
331
+ is an expensive calculation is directly proportional to the execution duration of the method. If you are tracing a 1ms
332
+ method, it represents a 13% overhead, but if you are tracing a 100ms method it is only 0.13%.
333
+
334
+ The moral of the story, don't blindly add the works to every method in your stack, instead choose methods who's output
335
+ will be diagnostically meaningful.
336
+
337
+ We are also always looking for patches with will reduce execution time and allocations of the instrumentation, so
338
+ please open some PRs!
339
+
340
+ ## Configuration
341
+ The configuration for IATT is available on the top level InstrumentAllTheThings module.
342
+
343
+ | Config Name | Description | Default
344
+ | ----------- | ----------- | -------
345
+ | stat_namespace | The string to add to all outbound stats (may not be changed after stat transmiter initialization) | `nil`
346
+ | 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
347
+ | 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
348
+ | 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)
349
+
350
+
351
+ ### Stats Reporters
352
+ #### Datadog
353
+ The default client if the constant `Datadog::Statsd` is found.
354
+
355
+ Initialized with environment variables
356
+ * `DATADOG_HOST` if set, otherwise `localhost`
357
+ * `DATADOG_POST` if set, otherwise `8125`
358
+
359
+ ### Tracers
360
+ #### Datadog
361
+ The default client if the constant `Datadog` is found and has a non-null value for `tracer`.
362
+
363
+ ## Development
364
+
365
+ 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.
366
+
367
+ 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).
368
+
369
+ ## Contributing
370
+
371
+ Bug reports and pull requests are welcome on GitHub at https://github.com/GetTerminus/instrument_all_the_things.