instrument_all_the_things 1.0.4

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