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.
- checksums.yaml +7 -0
- data/.drone.yml +14 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +95 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +74 -0
- data/README.md +369 -0
- data/Rakefile +6 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/instrument_all_the_things.gemspec +42 -0
- data/lib/instrument_all_the_things/clients/stat_reporter/datadog.rb +12 -0
- data/lib/instrument_all_the_things/clients/tracer/blackhole.rb +22 -0
- data/lib/instrument_all_the_things/context.rb +23 -0
- data/lib/instrument_all_the_things/helpers.rb +55 -0
- data/lib/instrument_all_the_things/instrumentors/all.rb +6 -0
- data/lib/instrument_all_the_things/instrumentors/error_logging.rb +48 -0
- data/lib/instrument_all_the_things/instrumentors/execution_count_and_timing.rb +20 -0
- data/lib/instrument_all_the_things/instrumentors/gc_stats.rb +49 -0
- data/lib/instrument_all_the_things/instrumentors/tracing.rb +30 -0
- data/lib/instrument_all_the_things/method_instrumentor.rb +54 -0
- data/lib/instrument_all_the_things/method_proxy.rb +54 -0
- data/lib/instrument_all_the_things/testing/rspec_matchers.rb +97 -0
- data/lib/instrument_all_the_things/testing/stat_tracker.rb +43 -0
- data/lib/instrument_all_the_things/testing/trace_tracker.rb +25 -0
- data/lib/instrument_all_the_things/version.rb +3 -0
- data/lib/instrument_all_the_things.rb +70 -0
- data/logo.jpg +0 -0
- data/vendor/cache/ast-2.4.0.gem +0 -0
- data/vendor/cache/benchmark-ips-2.7.2.gem +0 -0
- data/vendor/cache/coderay-1.1.2.gem +0 -0
- data/vendor/cache/ddtrace-0.32.0.gem +0 -0
- data/vendor/cache/diff-lcs-1.3.gem +0 -0
- data/vendor/cache/docile-1.3.2.gem +0 -0
- data/vendor/cache/dogstatsd-ruby-4.6.0.gem +0 -0
- data/vendor/cache/jaro_winkler-1.5.4.gem +0 -0
- data/vendor/cache/method_source-0.9.2.gem +0 -0
- data/vendor/cache/msgpack-1.3.3.gem +0 -0
- data/vendor/cache/parallel-1.19.1.gem +0 -0
- data/vendor/cache/parser-2.7.0.2.gem +0 -0
- data/vendor/cache/pry-0.12.2.gem +0 -0
- data/vendor/cache/rainbow-3.0.0.gem +0 -0
- data/vendor/cache/rake-10.5.0.gem +0 -0
- data/vendor/cache/rexml-3.2.4.gem +0 -0
- data/vendor/cache/rspec-3.9.0.gem +0 -0
- data/vendor/cache/rspec-core-3.9.1.gem +0 -0
- data/vendor/cache/rspec-expectations-3.9.0.gem +0 -0
- data/vendor/cache/rspec-mocks-3.9.1.gem +0 -0
- data/vendor/cache/rspec-support-3.9.2.gem +0 -0
- data/vendor/cache/rubocop-0.80.0.gem +0 -0
- data/vendor/cache/ruby-progressbar-1.10.1.gem +0 -0
- data/vendor/cache/simplecov-0.18.1.gem +0 -0
- data/vendor/cache/simplecov-html-0.11.0.gem +0 -0
- data/vendor/cache/unicode-display_width-1.6.1.gem +0 -0
- 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
data/.rspec
ADDED
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
data/Gemfile
ADDED
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
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,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
|