ddtrace 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/Appraisals +4 -2
- data/README.md +46 -29
- data/Rakefile +4 -1
- data/docs/GettingStarted.md +182 -14
- data/gemfiles/contrib.gemfile +4 -3
- data/gemfiles/contrib_old.gemfile +2 -1
- data/lib/ddtrace.rb +18 -2
- data/lib/ddtrace/contrib/active_record/patcher.rb +7 -1
- data/lib/ddtrace/contrib/grape/endpoint.rb +164 -0
- data/lib/ddtrace/contrib/grape/patcher.rb +73 -0
- data/lib/ddtrace/contrib/rack/middlewares.rb +103 -0
- data/lib/ddtrace/contrib/rails/action_controller.rb +12 -11
- data/lib/ddtrace/contrib/rails/framework.rb +26 -0
- data/lib/ddtrace/logger.rb +14 -9
- data/lib/ddtrace/monkey.rb +11 -3
- data/lib/ddtrace/span.rb +11 -9
- data/lib/ddtrace/version.rb +2 -2
- metadata +38 -25
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ZTg5OWNhMjdjMDlhNDVlNDMwN2MzODYzNDgyMGM5MTc1MmMwNzFhZg==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 36285521893bb7e1b8ceaa8f28d5d0b2b5103239
|
4
|
+
data.tar.gz: 0108b54ce0043a1ac7acd4412ea8cae915e5fdd2
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ZWU3ODc1MDExNDliNTgzMGM3YzZiOTE4ZTE0YTlkNWJmNjkzMzJlOGRhNzQz
|
11
|
-
MzE2MTRhMDNkNzA3NmNmMmJmZWIzYmEwN2U3NzU1NmVhNjMzYWQ=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MWM3N2I4N2IxNDU5NDZhZTU2YWZmNGU2YmMyYjU2ZmZkNTk4YzgxMGVmZGIz
|
14
|
-
YzM0ZDczYWVhZGMyMjhlNzQ1ZTJjMTBiNjRiNjc4YmIwMTE4ODgyYzE1MDEy
|
15
|
-
NTFhYzFhZGFlNzdmN2M5Y2VhNWQ1ZmJjZDRiNDFjM2QxOWM2YWE=
|
6
|
+
metadata.gz: 1e41922419e9d96624ced8d265301dd1bf4aa2bb487b083a768ffb3282280e5c2f5a8551179016e87b53bd681bd26af9be69bb22e710db1b3bc0d77c3a53ef5d
|
7
|
+
data.tar.gz: c02b8907da2873100ba44f4abeee8ff9c2034869adea96406b69a8ff79357339bfdfc5da8c1d172d92b976f8232de0436d7d14104019405573e8ef7aaa6150fd
|
data/Appraisals
CHANGED
@@ -92,9 +92,11 @@ end
|
|
92
92
|
if RUBY_VERSION >= '2.2.2'
|
93
93
|
appraise 'contrib' do
|
94
94
|
gem 'elasticsearch-transport'
|
95
|
+
gem 'grape'
|
96
|
+
gem 'rack'
|
97
|
+
gem 'rack-test'
|
95
98
|
gem 'redis'
|
96
99
|
gem 'hiredis'
|
97
|
-
gem 'rack-test'
|
98
100
|
gem 'sinatra'
|
99
101
|
gem 'sqlite3'
|
100
102
|
gem 'activerecord'
|
@@ -105,8 +107,8 @@ else
|
|
105
107
|
gem 'elasticsearch-transport'
|
106
108
|
gem 'redis'
|
107
109
|
gem 'hiredis'
|
108
|
-
gem 'rack-test', '0.6.2'
|
109
110
|
gem 'rack', '1.4.7'
|
111
|
+
gem 'rack-test'
|
110
112
|
gem 'sinatra', '1.4.5'
|
111
113
|
gem 'sqlite3'
|
112
114
|
gem 'activerecord', '3.2.22.5'
|
data/README.md
CHANGED
@@ -4,9 +4,9 @@
|
|
4
4
|
|
5
5
|
## Documentation
|
6
6
|
|
7
|
-
You can find the latest documentation
|
7
|
+
You can find the latest documentation on [rubydoc.info][docs]
|
8
8
|
|
9
|
-
[docs]: http://
|
9
|
+
[docs]: http://www.rubydoc.info/github/DataDog/dd-trace-rb/
|
10
10
|
|
11
11
|
## Getting started
|
12
12
|
|
@@ -25,6 +25,12 @@ If you're using ``Bundler``, just update your ``Gemfile`` as follows:
|
|
25
25
|
gem 'ddtrace'
|
26
26
|
```
|
27
27
|
|
28
|
+
To use a development/preview version, use:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'ddtrace', :github => 'DataDog/dd-trace-rb', :branch => 'me/my-feature-branch'
|
32
|
+
```
|
33
|
+
|
28
34
|
### Quickstart (manual instrumentation)
|
29
35
|
|
30
36
|
If you aren't using a supported framework instrumentation, you may want to to manually instrument your code.
|
@@ -34,7 +40,7 @@ to trace requests to the home page:
|
|
34
40
|
```ruby
|
35
41
|
require 'ddtrace'
|
36
42
|
require 'sinatra'
|
37
|
-
require '
|
43
|
+
require 'active_record'
|
38
44
|
|
39
45
|
# a generic tracer that you can use across your application
|
40
46
|
tracer = Datadog.tracer
|
@@ -59,29 +65,30 @@ to trace requests to the home page:
|
|
59
65
|
end
|
60
66
|
```
|
61
67
|
|
62
|
-
###
|
68
|
+
### Quickstart (integration)
|
63
69
|
|
64
|
-
|
65
|
-
|
66
|
-
or `Datadog::Monkey.patch_module`
|
67
|
-
|
68
|
-
This ultimately allows you to enable or disable tracing on a per-library basis.
|
69
|
-
|
70
|
-
The example below shows the Redis case, but any other non-rails library
|
71
|
-
should work the same way:
|
70
|
+
Instead of doing the above manually, whenever an integration is available,
|
71
|
+
you can activate it. The example above would become:
|
72
72
|
|
73
73
|
```ruby
|
74
|
-
|
75
|
-
require 'redis'
|
76
74
|
require 'ddtrace'
|
75
|
+
require 'sinatra'
|
76
|
+
require 'active_record'
|
77
77
|
|
78
|
-
Datadog::Monkey.patch_all #
|
78
|
+
Datadog::Monkey.patch_all # monkey patch all available integrations
|
79
79
|
|
80
|
-
# now
|
81
|
-
|
82
|
-
|
80
|
+
# now write your code naturally, it's traced automatically
|
81
|
+
get '/' do
|
82
|
+
@posts = Posts.order(created_at: :desc).limit(10)
|
83
|
+
erb :index
|
84
|
+
end
|
83
85
|
```
|
84
86
|
|
87
|
+
To know if a given framework or lib is supported by our client,
|
88
|
+
please consult our [integrations][contrib] list.
|
89
|
+
|
90
|
+
[contrib]: http://www.rubydoc.info/github/DataDog/dd-trace-rb/Datadog/Contrib
|
91
|
+
|
85
92
|
## Development
|
86
93
|
|
87
94
|
### Testing
|
@@ -91,21 +98,31 @@ Configure your environment through:
|
|
91
98
|
$ bundle install
|
92
99
|
$ appraisal install
|
93
100
|
|
94
|
-
You can launch tests using the following
|
101
|
+
You can launch tests using the following Rake commands:
|
95
102
|
|
96
|
-
$ rake test:main
|
97
|
-
$ appraisal rails<version>-<database>rake test:rails # tests Rails matrix
|
98
|
-
$ appraisal contrib rake test:redis
|
99
|
-
|
103
|
+
$ rake test:main # tracer tests
|
104
|
+
$ appraisal rails<version>-<database> rake test:rails # tests Rails matrix
|
105
|
+
$ appraisal contrib rake test:redis # tests Redis integration
|
106
|
+
...
|
100
107
|
|
101
|
-
|
108
|
+
Run ``rake --tasks`` for the list of available Rake tasks.
|
102
109
|
|
103
|
-
|
104
|
-
* ``rails{3,4,5}-mysql2``: Rails with MySQL
|
105
|
-
* ``contrib``: Other contrib libraries (Redis, ...)
|
110
|
+
Available appraisals are:
|
106
111
|
|
107
|
-
|
108
|
-
|
112
|
+
* ``contrib``: default for integrations
|
113
|
+
* ``contrib-old``: default for integrations, with version suited for old Ruby (possibly unmaintained) versions
|
114
|
+
* ``rails3-mysql2``: Rails3 with Mysql
|
115
|
+
* ``rails3-postgres``: Rails 3 with Postgres
|
116
|
+
* ``rails3-postgres-redis``: Rails 3 with Postgres and Redis
|
117
|
+
* ``rails3-postgres-sidekiq``: Rails 3 with Postgres and Sidekiq
|
118
|
+
* ``rails4-mysql2``: Rails4 with Mysql
|
119
|
+
* ``rails4-postgres``: Rails 4 with Postgres
|
120
|
+
* ``rails4-postgres-redis``: Rails 4 with Postgres and Redis
|
121
|
+
* ``rails4-postgres-sidekiq``: Rails 4 with Postgres and Sidekiq
|
122
|
+
* ``rails5-mysql2``: Rails5 with Mysql
|
123
|
+
* ``rails5-postgres``: Rails 5 with Postgres
|
124
|
+
* ``rails5-postgres-redis``: Rails 5 with Postgres and Redis
|
125
|
+
* ``rails5-postgres-sidekiq``: Rails 5 with Postgres and Sidekiq
|
109
126
|
|
110
127
|
The test suite requires many backing services (PostgreSQL, MySQL, Redis, ...) and we're using
|
111
128
|
``docker`` and ``docker-compose`` to start these services in the CI.
|
data/Rakefile
CHANGED
@@ -44,7 +44,7 @@ namespace :test do
|
|
44
44
|
t.test_files = FileList['test/contrib/rails/**/*active_job*_test.rb']
|
45
45
|
end
|
46
46
|
|
47
|
-
[:elasticsearch, :http, :redis, :sinatra, :sidekiq].each do |contrib|
|
47
|
+
[:elasticsearch, :http, :redis, :sinatra, :sidekiq, :rack, :grape].each do |contrib|
|
48
48
|
Rake::TestTask.new(contrib) do |t|
|
49
49
|
t.libs << %w[test lib]
|
50
50
|
t.test_files = FileList["test/contrib/#{contrib}/*_test.rb"]
|
@@ -132,11 +132,14 @@ task :ci do
|
|
132
132
|
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:redis'
|
133
133
|
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:sinatra'
|
134
134
|
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:sidekiq'
|
135
|
+
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:rack'
|
136
|
+
sh 'rvm $MRI_VERSIONS --verbose do appraisal contrib rake test:grape'
|
135
137
|
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:monkey'
|
136
138
|
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:elasticsearch'
|
137
139
|
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:http'
|
138
140
|
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:redis'
|
139
141
|
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:sinatra'
|
142
|
+
sh 'rvm $MRI_OLD_VERSIONS --verbose do appraisal contrib-old rake test:rack'
|
140
143
|
sh 'rvm $SIDEKIQ_OLD_VERSIONS --verbose do appraisal contrib-old rake test:sidekiq'
|
141
144
|
when 2
|
142
145
|
sh 'rvm $RAILS3_VERSIONS --verbose do appraisal rails3-mysql2 rake test:rails'
|
data/docs/GettingStarted.md
CHANGED
@@ -21,10 +21,17 @@ We strongly suggest pinning the version of the library you deploy.
|
|
21
21
|
## Quickstart
|
22
22
|
|
23
23
|
The easiest way to get started with the tracing client is to instrument your web application. ``ddtrace`` gem
|
24
|
-
provides auto instrumentation for the following web frameworks:
|
24
|
+
provides auto instrumentation for the following web frameworks and libraries:
|
25
25
|
|
26
26
|
* [Ruby on Rails](#label-Ruby+on+Rails)
|
27
|
+
* [Sidekiq](#label-Sidekiq)
|
27
28
|
* [Sinatra](#label-Sinatra)
|
29
|
+
* [Rack](#label-Rack)
|
30
|
+
* [Grape](#label-Grape)
|
31
|
+
* [Active Record](#label-Active+Record)
|
32
|
+
* [Elastic Search](#label-Elastic+Search)
|
33
|
+
* [Net/HTTP](#label-Net/HTTP)
|
34
|
+
* [Redis](#label-Redis)
|
28
35
|
|
29
36
|
## Web Frameworks
|
30
37
|
|
@@ -45,6 +52,7 @@ To enable the Rails auto instrumentation, create an initializer file in your ``c
|
|
45
52
|
}
|
46
53
|
|
47
54
|
If you're using Rails 3 or higher, your application will be listed as ``my-rails-app`` in your service list.
|
55
|
+
To integrate Rails instrumentation with third-party libraries such as Grape, please check the available settings below.
|
48
56
|
|
49
57
|
#### Configure the tracer with initializers
|
50
58
|
|
@@ -57,9 +65,11 @@ of the Datadog tracer, you can override the following defaults:
|
|
57
65
|
enabled: true,
|
58
66
|
auto_instrument: false,
|
59
67
|
auto_instrument_redis: false,
|
68
|
+
auto_instrument_grape: false,
|
60
69
|
default_service: 'rails-app',
|
61
|
-
|
70
|
+
default_controller_service: 'rails-controller',
|
62
71
|
default_cache_service: 'rails-cache',
|
72
|
+
default_database_service: 'postgresql',
|
63
73
|
template_base_path: 'views/',
|
64
74
|
tracer: Datadog.tracer,
|
65
75
|
debug: false,
|
@@ -77,10 +87,15 @@ Available settings are:
|
|
77
87
|
with a condition, to enable the auto-instrumentation only for particular environments (production, staging, etc...).
|
78
88
|
* ``auto_instrument_redis``: if set to ``true`` Redis calls will be traced as such. Calls to Redis cache may be
|
79
89
|
still instrumented but you will not have the detail of low-level Redis calls.
|
90
|
+
* ``auto_instrument_grape``: if set to ``true`` and you're using a Grape application, all calls to your endpoints are
|
91
|
+
traced, including filters execution.
|
80
92
|
* ``default_service``: set the service name used when tracing application requests. Defaults to ``rails-app``
|
93
|
+
* ``default_controller_service``: set the service name used when tracing a Rails action controller. Defaults to ``rails-controller``
|
94
|
+
* ``default_cache_service``: set the cache service name used when tracing cache activity. Defaults to ``rails-cache``
|
81
95
|
* ``default_database_service``: set the database service name used when tracing database activity. Defaults to the
|
82
96
|
current adapter name, so if you're using PostgreSQL it will be ``postgres``.
|
83
|
-
* ``
|
97
|
+
* ``default_grape_service``: set the service name used when tracing a Grape application mounted in your Rails router.
|
98
|
+
Defaults to ``grape``
|
84
99
|
* ``template_base_path``: used when the template name is parsed in the auto instrumented code. If you don't store
|
85
100
|
your templates in the ``views/`` folder, you may need to change this value
|
86
101
|
* ``tracer``: is the global tracer used by the tracing application. Usually you don't need to change that value
|
@@ -129,20 +144,82 @@ Available settings are:
|
|
129
144
|
* ``trace_agent_hostname``: set the hostname of the trace agent.
|
130
145
|
* ``trace_agent_port``: set the port the trace agent is listening on.
|
131
146
|
|
147
|
+
### Rack
|
148
|
+
|
149
|
+
The Rack integration provides a middleware that traces all requests before they reach the underlying framework
|
150
|
+
or application. It responds to the Rack minimal interface, providing reasonable values that can be
|
151
|
+
retrieved at the Rack level.
|
152
|
+
To start using the middleware in your generic Rack application, add it to your ``config.ru``:
|
153
|
+
|
154
|
+
# config.ru example
|
155
|
+
use Datadog::Contrib::Rack::TraceMiddleware
|
156
|
+
|
157
|
+
app = proc do |env|
|
158
|
+
[ 200, {'Content-Type' => 'text/plain'}, "OK" ]
|
159
|
+
end
|
160
|
+
|
161
|
+
run app
|
162
|
+
|
163
|
+
#### Configure the tracer
|
164
|
+
|
165
|
+
To modify the default middleware configuration, you can use middleware options as follows:
|
166
|
+
|
167
|
+
# config.ru example
|
168
|
+
use Datadog::Contrib::Rack::TraceMiddleware default_service: 'rack-stack'
|
169
|
+
|
170
|
+
app = proc do |env|
|
171
|
+
[ 200, {'Content-Type' => 'text/plain'}, "OK" ]
|
172
|
+
end
|
173
|
+
|
174
|
+
run app
|
175
|
+
|
176
|
+
Available settings are:
|
177
|
+
|
178
|
+
* ``tracer`` (default: ``Datadog.tracer``): set the tracer to use. Usually you don't need to change that value
|
179
|
+
unless you're already using a different initialized tracer somewhere else. If you need to change some
|
180
|
+
configurations such as the ``hostname``, use the [Tracer#configure](Datadog/Tracer.html#configure-instance_method)
|
181
|
+
method before adding the middleware
|
182
|
+
* ``default_service`` (default: ``rack``): set the service name used when the Rack request is traced
|
183
|
+
|
132
184
|
## Other libraries
|
133
185
|
|
134
|
-
###
|
186
|
+
### Grape
|
135
187
|
|
136
|
-
The
|
188
|
+
The Grape integration adds the instrumentation to Grape endpoints and filters. This integration can work side by side
|
189
|
+
with other integrations like Rack and Rails. To activate your integration, use the ``patch_module`` function before
|
190
|
+
defining your Grape application:
|
137
191
|
|
138
|
-
|
192
|
+
# api.rb
|
193
|
+
require 'grape'
|
139
194
|
require 'ddtrace'
|
140
195
|
|
141
|
-
Datadog::Monkey.patch_module(:
|
196
|
+
Datadog::Monkey.patch_module(:grape)
|
142
197
|
|
143
|
-
#
|
144
|
-
|
145
|
-
|
198
|
+
# then define your application
|
199
|
+
class RackTestingAPI < Grape::API
|
200
|
+
desc 'main endpoint'
|
201
|
+
get :success do
|
202
|
+
'Hello world!'
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
### Active Record
|
207
|
+
|
208
|
+
Most of the time, Active Record is set up as part of a web framework (Rails, Sinatra...)
|
209
|
+
however it can be set up alone:
|
210
|
+
|
211
|
+
require 'tmpdir'
|
212
|
+
require 'sqlite3'
|
213
|
+
require 'active_record'
|
214
|
+
require 'ddtrace'
|
215
|
+
|
216
|
+
Datadog::Monkey.patch_module(:active_record) # explicitly patch it
|
217
|
+
|
218
|
+
Dir::Tmpname.create(['test', '.sqlite']) do |db|
|
219
|
+
conn = ActiveRecord::Base.establish_connection(adapter: 'sqlite3',
|
220
|
+
database: db)
|
221
|
+
conn.connection.execute('SELECT 42') # traced!
|
222
|
+
end
|
146
223
|
|
147
224
|
### Elastic Search
|
148
225
|
|
@@ -152,7 +229,7 @@ in the ``Client`` object:
|
|
152
229
|
require 'elasticsearch/transport'
|
153
230
|
require 'ddtrace'
|
154
231
|
|
155
|
-
Datadog::Monkey.patch_module(:elasticsearch) #
|
232
|
+
Datadog::Monkey.patch_module(:elasticsearch) # explicitly patch it
|
156
233
|
|
157
234
|
# now do your Elastic Search stuff, eg:
|
158
235
|
client = Elasticsearch::Client.new url: 'http://127.0.0.1:9200'
|
@@ -170,7 +247,7 @@ Net::HTTP module.
|
|
170
247
|
require 'net/http'
|
171
248
|
require 'ddtrace'
|
172
249
|
|
173
|
-
Datadog::Monkey.patch_module(:http) #
|
250
|
+
Datadog::Monkey.patch_module(:http) # explicitly patch it
|
174
251
|
|
175
252
|
Net::HTTP.start('127.0.0.1', 8080) do |http|
|
176
253
|
request = Net::HTTP::Get.new '/index'
|
@@ -179,6 +256,19 @@ Net::HTTP module.
|
|
179
256
|
|
180
257
|
content = Net::HTTP.get(URI('http://127.0.0.1/index.html'))
|
181
258
|
|
259
|
+
### Redis
|
260
|
+
|
261
|
+
The Redis integration will trace simple calls as well as pipelines.
|
262
|
+
|
263
|
+
require 'redis'
|
264
|
+
require 'ddtrace'
|
265
|
+
|
266
|
+
Datadog::Monkey.patch_module(:redis) # explicitly patch it
|
267
|
+
|
268
|
+
# now do your Redis stuff, eg:
|
269
|
+
redis = Redis.new
|
270
|
+
redis.set 'foo', 'bar' # traced!
|
271
|
+
|
182
272
|
### Sidekiq
|
183
273
|
|
184
274
|
The Sidekiq integration is a server-side middleware which will trace job
|
@@ -242,7 +332,7 @@ to trace requests to the home page:
|
|
242
332
|
|
243
333
|
require 'ddtrace'
|
244
334
|
require 'sinatra'
|
245
|
-
require '
|
335
|
+
require 'active_record'
|
246
336
|
|
247
337
|
# a generic tracer that you can use across your application
|
248
338
|
tracer = Datadog.tracer
|
@@ -372,7 +462,7 @@ for the first time:
|
|
372
462
|
|
373
463
|
require 'ddtrace'
|
374
464
|
require 'sinatra'
|
375
|
-
require '
|
465
|
+
require 'active_record'
|
376
466
|
|
377
467
|
# enable debug mode
|
378
468
|
Datadog::Tracer.debug_logging = true
|
@@ -389,6 +479,23 @@ for the first time:
|
|
389
479
|
Remember that the debug mode may affect your application performance and so it must not be used
|
390
480
|
in a production environment.
|
391
481
|
|
482
|
+
### Environment and tags
|
483
|
+
|
484
|
+
By default, the trace agent (not this library, but the program running in
|
485
|
+
the background collecting data from various clients) uses the tags
|
486
|
+
set in the agent config file, see our
|
487
|
+
[environments tutorial](https://app.datadoghq.com/apm/docs/tutorials/environments) for details.
|
488
|
+
|
489
|
+
These values can be overridden at the tracer level:
|
490
|
+
|
491
|
+
Datadog.tracer.set_tags('env' => 'prod')
|
492
|
+
|
493
|
+
This enables you to set this value on a per tracer basis, so you can have
|
494
|
+
for example several applications reporting for different environments on the same host.
|
495
|
+
|
496
|
+
Ultimately, tags can be set per span, but `env` should typically be the same
|
497
|
+
for all spans belonging to a given trace.
|
498
|
+
|
392
499
|
### Sampling
|
393
500
|
|
394
501
|
`ddtrace` can perform trace sampling. While the trace agent already samples
|
@@ -401,6 +508,67 @@ overhead.
|
|
401
508
|
sampler = Datadog::RateSampler.new(0.5) # sample 50% of the traces
|
402
509
|
Datadog.tracer.configure(sampler: sampler)
|
403
510
|
|
511
|
+
### Distributed Tracing
|
512
|
+
|
513
|
+
To trace requests across hosts, the spans on the secondary hosts must be linked together by setting ``trace_id`` and ``parent_id``:
|
514
|
+
|
515
|
+
def request_on_secondary_host(parent_trace_id, parent_span_id)
|
516
|
+
tracer.trace('web.request') do |span|
|
517
|
+
span.parent_id = parent_span_id
|
518
|
+
span.trace_id = parent_trace_id
|
519
|
+
|
520
|
+
# perform user code
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
Users can pass along the ``parent_trace_id`` and ``parent_span_id`` via whatever method best matches the RPC framework.
|
525
|
+
|
526
|
+
Below is an example using Net/HTTP and Sinatra, where we bypass the integrations to demo how distributed tracing works.
|
527
|
+
|
528
|
+
On the client:
|
529
|
+
|
530
|
+
require 'net/http'
|
531
|
+
require 'ddtrace'
|
532
|
+
|
533
|
+
# Do *not* monkey patch here, we do it "manually", to demo the feature
|
534
|
+
# Datadog::Monkey.patch_module(:http)
|
535
|
+
|
536
|
+
uri = URI('http://localhost:4567/')
|
537
|
+
|
538
|
+
Datadog.tracer.trace('web.call') do |span|
|
539
|
+
req = Net::HTTP::Get.new(uri)
|
540
|
+
req['x-ddtrace-parent_trace_id'] = span.trace_id.to_s
|
541
|
+
req['x-ddtrace-parent_span_id'] = span.span_id.to_s
|
542
|
+
|
543
|
+
response = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
544
|
+
http.request(req)
|
545
|
+
end
|
546
|
+
|
547
|
+
puts response.body
|
548
|
+
end
|
549
|
+
|
550
|
+
On the server:
|
551
|
+
|
552
|
+
require 'sinatra'
|
553
|
+
require 'ddtrace'
|
554
|
+
|
555
|
+
# Do *not* use Sinatra integration, we do it "manually", to demo the feature
|
556
|
+
# require 'ddtrace/contrib/sinatra/tracer'
|
557
|
+
|
558
|
+
get '/' do
|
559
|
+
parent_trace_id = request.env['HTTP_X_DDTRACE_PARENT_TRACE_ID']
|
560
|
+
parent_span_id = request.env['HTTP_X_DDTRACE_PARENT_SPAN_ID']
|
561
|
+
|
562
|
+
Datadog.tracer.trace('web.work') do |span|
|
563
|
+
if parent_trace_id && parent_span_id
|
564
|
+
span.trace_id = parent_trace_id.to_i
|
565
|
+
span.parent_id = parent_span_id.to_i
|
566
|
+
end
|
567
|
+
|
568
|
+
'Hello world!'
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
404
572
|
### Supported Versions
|
405
573
|
|
406
574
|
#### Ruby interpreters
|
data/gemfiles/contrib.gemfile
CHANGED
@@ -3,13 +3,14 @@
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
5
|
gem "elasticsearch-transport"
|
6
|
+
gem "grape"
|
7
|
+
gem "rack"
|
8
|
+
gem "rack-test"
|
6
9
|
gem "redis"
|
7
10
|
gem "hiredis"
|
8
|
-
gem "rack-test"
|
9
11
|
gem "sinatra"
|
10
|
-
gem
|
12
|
+
gem "sqlite3"
|
11
13
|
gem "activerecord"
|
12
14
|
gem "sidekiq"
|
13
|
-
gem "activerecord"
|
14
15
|
|
15
16
|
gemspec :path => "../"
|
@@ -5,9 +5,10 @@ source "https://rubygems.org"
|
|
5
5
|
gem "elasticsearch-transport"
|
6
6
|
gem "redis"
|
7
7
|
gem "hiredis"
|
8
|
-
gem "rack-test", "0.6.2"
|
9
8
|
gem "rack", "1.4.7"
|
9
|
+
gem "rack-test"
|
10
10
|
gem "sinatra", "1.4.5"
|
11
|
+
gem "sqlite3"
|
11
12
|
gem "activerecord", "3.2.22.5"
|
12
13
|
gem "sidekiq", "4.0.0"
|
13
14
|
|
data/lib/ddtrace.rb
CHANGED
@@ -31,13 +31,29 @@ if defined?(Rails::VERSION)
|
|
31
31
|
require 'ddtrace/contrib/rails/framework'
|
32
32
|
|
33
33
|
module Datadog
|
34
|
-
#
|
35
|
-
# `config/initializers` are executed
|
34
|
+
# Railtie class initializes
|
36
35
|
class Railtie < Rails::Railtie
|
36
|
+
# auto instrument Rails and third party components after
|
37
|
+
# the framework initialization
|
38
|
+
options = {}
|
37
39
|
config.after_initialize do |app|
|
38
40
|
Datadog::Contrib::Rails::Framework.configure(config: app.config)
|
39
41
|
Datadog::Contrib::Rails::Framework.auto_instrument()
|
40
42
|
Datadog::Contrib::Rails::Framework.auto_instrument_redis()
|
43
|
+
Datadog::Contrib::Rails::Framework.auto_instrument_grape()
|
44
|
+
|
45
|
+
# override Rack Middleware configurations with Rails
|
46
|
+
options.update(::Rails.configuration.datadog_trace)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Configure datadog settings before building the middleware stack.
|
50
|
+
# This is required because the middleware stack is frozen after
|
51
|
+
# the initialization and so it's too late to add our tracing
|
52
|
+
# functionalities.
|
53
|
+
initializer :datadog_config, before: :build_middleware_stack do |app|
|
54
|
+
app.config.middleware.insert_before(
|
55
|
+
0, Datadog::Contrib::Rack::TraceMiddleware, options
|
56
|
+
)
|
41
57
|
end
|
42
58
|
end
|
43
59
|
end
|
@@ -18,6 +18,7 @@ module Datadog
|
|
18
18
|
begin
|
19
19
|
require 'ddtrace/contrib/rails/utils'
|
20
20
|
require 'ddtrace/ext/sql'
|
21
|
+
require 'ddtrace/ext/app_types'
|
21
22
|
|
22
23
|
patch_active_record()
|
23
24
|
|
@@ -50,11 +51,16 @@ module Datadog
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def self.tracer
|
54
|
+
return Datadog.tracer unless datadog_trace
|
53
55
|
@tracer ||= datadog_trace.fetch(:tracer)
|
54
56
|
end
|
55
57
|
|
56
58
|
def self.database_service
|
57
|
-
@database_service ||=
|
59
|
+
@database_service ||= if defined?(::Sinatra)
|
60
|
+
datadog_trace.fetch(:default_database_service, adapter_name())
|
61
|
+
else
|
62
|
+
adapter_name()
|
63
|
+
end
|
58
64
|
if @database_service
|
59
65
|
tracer().set_service_info(@database_service, 'sinatra',
|
60
66
|
Datadog::Ext::AppTypes::DB)
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'ddtrace/ext/http'
|
2
|
+
require 'ddtrace/ext/errors'
|
3
|
+
|
4
|
+
module Datadog
|
5
|
+
module Contrib
|
6
|
+
module Grape
|
7
|
+
# rubocop:disable Metrics/ModuleLength
|
8
|
+
# Endpoint module includes a list of subscribers to create
|
9
|
+
# traces when a Grape endpoint is hit
|
10
|
+
module Endpoint
|
11
|
+
KEY_RUN = 'datadog_grape_endpoint_run'.freeze
|
12
|
+
KEY_RENDER = 'datadog_grape_endpoint_render'.freeze
|
13
|
+
|
14
|
+
def self.subscribe
|
15
|
+
# Grape is instrumented only if it's available
|
16
|
+
return unless defined?(::Grape) && defined?(::ActiveSupport::Notifications)
|
17
|
+
|
18
|
+
# subscribe when a Grape endpoint is hit
|
19
|
+
::ActiveSupport::Notifications.subscribe('endpoint_run.grape.start_process') do |*args|
|
20
|
+
endpoint_start_process(*args)
|
21
|
+
end
|
22
|
+
::ActiveSupport::Notifications.subscribe('endpoint_run.grape') do |*args|
|
23
|
+
endpoint_run(*args)
|
24
|
+
end
|
25
|
+
::ActiveSupport::Notifications.subscribe('endpoint_render.grape.start_render') do |*args|
|
26
|
+
endpoint_start_render(*args)
|
27
|
+
end
|
28
|
+
::ActiveSupport::Notifications.subscribe('endpoint_render.grape') do |*args|
|
29
|
+
endpoint_render(*args)
|
30
|
+
end
|
31
|
+
::ActiveSupport::Notifications.subscribe('endpoint_run_filters.grape') do |*args|
|
32
|
+
endpoint_run_filters(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.endpoint_start_process(*)
|
37
|
+
return if Thread.current[KEY_RUN]
|
38
|
+
|
39
|
+
# retrieve the tracer from the PIN object
|
40
|
+
pin = Datadog::Pin.get_from(::Grape)
|
41
|
+
return unless pin && pin.enabled?
|
42
|
+
|
43
|
+
# store the beginning of a trace
|
44
|
+
tracer = pin.tracer
|
45
|
+
service = pin.service
|
46
|
+
type = Datadog::Ext::HTTP::TYPE
|
47
|
+
tracer.trace('grape.endpoint_run', service: service, span_type: type)
|
48
|
+
|
49
|
+
Thread.current[KEY_RUN] = true
|
50
|
+
rescue StandardError => e
|
51
|
+
Datadog::Tracer.log.error(e.message)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.endpoint_run(name, start, finish, id, payload)
|
55
|
+
return unless Thread.current[KEY_RUN]
|
56
|
+
Thread.current[KEY_RUN] = false
|
57
|
+
|
58
|
+
# retrieve the tracer from the PIN object
|
59
|
+
pin = Datadog::Pin.get_from(::Grape)
|
60
|
+
return unless pin && pin.enabled?
|
61
|
+
|
62
|
+
tracer = pin.tracer
|
63
|
+
span = tracer.active_span()
|
64
|
+
return unless span
|
65
|
+
|
66
|
+
begin
|
67
|
+
# collect endpoint details
|
68
|
+
api_view = payload[:endpoint].options[:for].to_s
|
69
|
+
path = payload[:endpoint].options[:path].join('/')
|
70
|
+
resource = "#{api_view}##{path}"
|
71
|
+
span.resource = resource
|
72
|
+
|
73
|
+
# set the request span resource if it's a `rack.request` span
|
74
|
+
request_span = payload[:env][:datadog_rack_request_span]
|
75
|
+
if !request_span.nil? && request_span.name == 'rack.request'
|
76
|
+
request_span.resource = resource
|
77
|
+
end
|
78
|
+
|
79
|
+
# catch thrown exceptions
|
80
|
+
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
|
81
|
+
|
82
|
+
# ovverride the current span with this notification values
|
83
|
+
span.set_tag('grape.route.endpoint', api_view)
|
84
|
+
span.set_tag('grape.route.path', path)
|
85
|
+
ensure
|
86
|
+
span.start_time = start
|
87
|
+
span.finish_at(finish)
|
88
|
+
end
|
89
|
+
rescue StandardError => e
|
90
|
+
Datadog::Tracer.log.error(e.message)
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.endpoint_start_render(*)
|
94
|
+
return if Thread.current[KEY_RENDER]
|
95
|
+
|
96
|
+
# retrieve the tracer from the PIN object
|
97
|
+
pin = Datadog::Pin.get_from(::Grape)
|
98
|
+
return unless pin && pin.enabled?
|
99
|
+
|
100
|
+
# store the beginning of a trace
|
101
|
+
tracer = pin.tracer
|
102
|
+
service = pin.service
|
103
|
+
type = Datadog::Ext::HTTP::TYPE
|
104
|
+
tracer.trace('grape.endpoint_render', service: service, span_type: type)
|
105
|
+
|
106
|
+
Thread.current[KEY_RENDER] = true
|
107
|
+
rescue StandardError => e
|
108
|
+
Datadog::Tracer.log.error(e.message)
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.endpoint_render(name, start, finish, id, payload)
|
112
|
+
return unless Thread.current[KEY_RENDER]
|
113
|
+
Thread.current[KEY_RENDER] = false
|
114
|
+
|
115
|
+
# retrieve the tracer from the PIN object
|
116
|
+
pin = Datadog::Pin.get_from(::Grape)
|
117
|
+
return unless pin && pin.enabled?
|
118
|
+
|
119
|
+
tracer = pin.tracer
|
120
|
+
span = tracer.active_span()
|
121
|
+
return unless span
|
122
|
+
|
123
|
+
# catch thrown exceptions
|
124
|
+
begin
|
125
|
+
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
|
126
|
+
ensure
|
127
|
+
span.start_time = start
|
128
|
+
span.finish_at(finish)
|
129
|
+
end
|
130
|
+
rescue StandardError => e
|
131
|
+
Datadog::Tracer.log.error(e.message)
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.endpoint_run_filters(name, start, finish, id, payload)
|
135
|
+
# retrieve the tracer from the PIN object
|
136
|
+
pin = Datadog::Pin.get_from(::Grape)
|
137
|
+
return unless pin && pin.enabled?
|
138
|
+
|
139
|
+
# safe-guard to prevent submitting empty filters
|
140
|
+
zero_length = (finish - start).zero?
|
141
|
+
filters = payload[:filters]
|
142
|
+
type = payload[:type]
|
143
|
+
return if (!filters || filters.empty?) || !type || zero_length
|
144
|
+
|
145
|
+
tracer = pin.tracer
|
146
|
+
service = pin.service
|
147
|
+
type = Datadog::Ext::HTTP::TYPE
|
148
|
+
span = tracer.trace('grape.endpoint_run_filters', service: service, span_type: type)
|
149
|
+
|
150
|
+
begin
|
151
|
+
# catch thrown exceptions
|
152
|
+
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
|
153
|
+
span.set_tag('grape.filter.type', type.to_s)
|
154
|
+
ensure
|
155
|
+
span.start_time = start
|
156
|
+
span.finish_at(finish)
|
157
|
+
end
|
158
|
+
rescue StandardError => e
|
159
|
+
Datadog::Tracer.log.error(e.message)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Contrib
|
3
|
+
module Grape
|
4
|
+
SERVICE = 'grape'.freeze
|
5
|
+
|
6
|
+
# Patcher that introduces more instrumentation for Grape endpoints, so that
|
7
|
+
# new signals are executed at the beginning of each step (filters, render and run)
|
8
|
+
module Patcher
|
9
|
+
@patched = false
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
def patched?
|
14
|
+
@patched
|
15
|
+
end
|
16
|
+
|
17
|
+
def patch
|
18
|
+
if !@patched && defined?(::Grape)
|
19
|
+
begin
|
20
|
+
# do not require these by default, but only when actually patching
|
21
|
+
require 'ddtrace'
|
22
|
+
require 'ddtrace/ext/app_types'
|
23
|
+
require 'ddtrace/contrib/grape/endpoint'
|
24
|
+
|
25
|
+
@patched = true
|
26
|
+
# patch all endpoints
|
27
|
+
patch_endpoint_run()
|
28
|
+
patch_endpoint_render()
|
29
|
+
|
30
|
+
# attach a PIN object globally and set the service once
|
31
|
+
pin = Datadog::Pin.new(SERVICE, app: 'grape', app_type: Datadog::Ext::AppTypes::WEB)
|
32
|
+
pin.onto(::Grape)
|
33
|
+
if pin.tracer && pin.service
|
34
|
+
pin.tracer.set_service_info(pin.service, 'grape', pin.app_type)
|
35
|
+
end
|
36
|
+
|
37
|
+
# subscribe to ActiveSupport events
|
38
|
+
Datadog::Contrib::Grape::Endpoint.subscribe()
|
39
|
+
rescue StandardError => e
|
40
|
+
Datadog::Tracer.log.error("Unable to apply Grape integration: #{e}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@patched
|
44
|
+
end
|
45
|
+
|
46
|
+
def patch_endpoint_run
|
47
|
+
::Grape::Endpoint.class_eval do
|
48
|
+
alias_method :run_without_datadog, :run
|
49
|
+
def run(*args)
|
50
|
+
::ActiveSupport::Notifications.instrument('endpoint_run.grape.start_process')
|
51
|
+
run_without_datadog(*args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def patch_endpoint_render
|
57
|
+
::Grape::Endpoint.class_eval do
|
58
|
+
class << self
|
59
|
+
alias_method :generate_api_method_without_datadog, :generate_api_method
|
60
|
+
def generate_api_method(*params, &block)
|
61
|
+
method_api = generate_api_method_without_datadog(*params, &block)
|
62
|
+
proc do |*args|
|
63
|
+
::ActiveSupport::Notifications.instrument('endpoint_render.grape.start_render')
|
64
|
+
method_api.call(*args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'ddtrace/ext/app_types'
|
2
|
+
require 'ddtrace/ext/http'
|
3
|
+
|
4
|
+
module Datadog
|
5
|
+
module Contrib
|
6
|
+
# Rack module includes middlewares that are required to trace any framework
|
7
|
+
# and application built on top of Rack.
|
8
|
+
module Rack
|
9
|
+
# TraceMiddleware ensures that the Rack Request is properly traced
|
10
|
+
# from the beginning to the end. The middleware adds the request span
|
11
|
+
# in the Rack environment so that it can be retrieved by the underlying
|
12
|
+
# application. If request tags are not set by the app, they will be set using
|
13
|
+
# information available at the Rack level.
|
14
|
+
class TraceMiddleware
|
15
|
+
DEFAULT_CONFIG = {
|
16
|
+
tracer: Datadog.tracer,
|
17
|
+
default_service: 'rack'
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def initialize(app, options = {})
|
21
|
+
# update options with our configuration, unless it's already available
|
22
|
+
options[:tracer] ||= DEFAULT_CONFIG[:tracer]
|
23
|
+
options[:default_service] ||= DEFAULT_CONFIG[:default_service]
|
24
|
+
|
25
|
+
@app = app
|
26
|
+
@options = options
|
27
|
+
end
|
28
|
+
|
29
|
+
def configure
|
30
|
+
# ensure that the configuration is executed only once
|
31
|
+
return if @tracer && @service
|
32
|
+
|
33
|
+
# retrieve the current tracer and service
|
34
|
+
@tracer = @options.fetch(:tracer)
|
35
|
+
@service = @options.fetch(:default_service)
|
36
|
+
|
37
|
+
# configure the Rack service
|
38
|
+
@tracer.set_service_info(
|
39
|
+
@service,
|
40
|
+
'rack',
|
41
|
+
Datadog::Ext::AppTypes::WEB
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def call(env)
|
46
|
+
# configure the Rack middleware once
|
47
|
+
configure()
|
48
|
+
|
49
|
+
# start a new request span and attach it to the current Rack environment;
|
50
|
+
# we must ensure that the span `resource` is set later
|
51
|
+
request_span = @tracer.trace(
|
52
|
+
'rack.request',
|
53
|
+
service: @service,
|
54
|
+
resource: nil,
|
55
|
+
span_type: Datadog::Ext::HTTP::TYPE
|
56
|
+
)
|
57
|
+
env[:datadog_rack_request_span] = request_span
|
58
|
+
|
59
|
+
# call the rest of the stack
|
60
|
+
status, headers, response = @app.call(env)
|
61
|
+
rescue StandardError => e
|
62
|
+
# catch exceptions that may be raised in the middleware chain
|
63
|
+
# Note: if a middleware catches an Exception without re raising,
|
64
|
+
# the Exception cannot be recorded here
|
65
|
+
request_span.set_error(e)
|
66
|
+
raise e
|
67
|
+
ensure
|
68
|
+
# the source of truth in Rack is the PATH_INFO key that holds the
|
69
|
+
# URL for the current request; some framework may override that
|
70
|
+
# value, especially during exception handling and because of that
|
71
|
+
# we prefer using the `REQUEST_URI` if this is available.
|
72
|
+
# NOTE: `REQUEST_URI` is Rails specific and may not apply for other frameworks
|
73
|
+
url = env['REQUEST_URI'] || env['PATH_INFO']
|
74
|
+
|
75
|
+
# Rack is a really low level interface and it doesn't provide any
|
76
|
+
# advanced functionality like routers. Because of that, we assume that
|
77
|
+
# the underlying framework or application has more knowledge about
|
78
|
+
# the result for this request; `resource` and `tags` are expected to
|
79
|
+
# be set in another level but if they're missing, reasonable defaults
|
80
|
+
# are used.
|
81
|
+
request_span.resource = "#{env['REQUEST_METHOD']} #{status}".strip unless request_span.resource
|
82
|
+
request_span.set_tag('http.method', env['REQUEST_METHOD']) if request_span.get_tag('http.method').nil?
|
83
|
+
request_span.set_tag('http.url', url) if request_span.get_tag('http.url').nil?
|
84
|
+
request_span.set_tag('http.status_code', status) if request_span.get_tag('http.status_code').nil? && status
|
85
|
+
|
86
|
+
# detect if the status code is a 5xx and flag the request span as an error
|
87
|
+
# unless it has been already set by the underlying framework
|
88
|
+
if status.to_s.start_with?('5') && request_span.status.zero?
|
89
|
+
request_span.status = 1
|
90
|
+
# in any case we don't touch the stacktrace if it has been set
|
91
|
+
if request_span.get_tag(Datadog::Ext::Errors::STACK).nil?
|
92
|
+
request_span.set_tag(Datadog::Ext::Errors::STACK, caller().join("\n"))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
request_span.finish()
|
97
|
+
|
98
|
+
[status, headers, response]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -24,9 +24,9 @@ module Datadog
|
|
24
24
|
return if Thread.current[KEY]
|
25
25
|
|
26
26
|
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
27
|
-
service = ::Rails.configuration.datadog_trace.fetch(:
|
27
|
+
service = ::Rails.configuration.datadog_trace.fetch(:default_controller_service)
|
28
28
|
type = Datadog::Ext::HTTP::TYPE
|
29
|
-
tracer.trace('rails.
|
29
|
+
tracer.trace('rails.action_controller', service: service, span_type: type)
|
30
30
|
|
31
31
|
Thread.current[KEY] = true
|
32
32
|
rescue StandardError => e
|
@@ -42,9 +42,14 @@ module Datadog
|
|
42
42
|
return unless span
|
43
43
|
|
44
44
|
begin
|
45
|
-
|
46
|
-
span.
|
47
|
-
|
45
|
+
resource = "#{payload.fetch(:controller)}##{payload.fetch(:action)}"
|
46
|
+
span.resource = resource
|
47
|
+
|
48
|
+
# set the parent resource if it's a `rack.request` span
|
49
|
+
if !span.parent.nil? && span.parent.name == 'rack.request'
|
50
|
+
span.parent.resource = resource
|
51
|
+
end
|
52
|
+
|
48
53
|
span.set_tag('rails.route.action', payload.fetch(:action))
|
49
54
|
span.set_tag('rails.route.controller', payload.fetch(:controller))
|
50
55
|
|
@@ -54,18 +59,14 @@ module Datadog
|
|
54
59
|
status = payload.fetch(:status, '?').to_s
|
55
60
|
if status.starts_with?('5')
|
56
61
|
span.status = 1
|
57
|
-
span.set_tag(Datadog::Ext::Errors::STACK, caller().join(
|
62
|
+
span.set_tag(Datadog::Ext::Errors::STACK, caller().join("\n"))
|
58
63
|
end
|
59
|
-
span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, status)
|
60
64
|
else
|
61
65
|
error = payload[:exception]
|
62
66
|
span.status = 1
|
63
67
|
span.set_tag(Datadog::Ext::Errors::TYPE, error[0])
|
64
68
|
span.set_tag(Datadog::Ext::Errors::MSG, error[1])
|
65
|
-
span.set_tag(Datadog::Ext::Errors::STACK, caller().join(
|
66
|
-
# [manu,christian]: it's right to have a 500? there are cases in Rails that let
|
67
|
-
# user to recover the error after this point?
|
68
|
-
span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, payload.fetch(:status, '500').to_s)
|
69
|
+
span.set_tag(Datadog::Ext::Errors::STACK, caller().join("\n"))
|
69
70
|
end
|
70
71
|
ensure
|
71
72
|
span.start_time = start
|
@@ -1,5 +1,9 @@
|
|
1
|
+
require 'ddtrace/pin'
|
1
2
|
require 'ddtrace/ext/app_types'
|
2
3
|
|
4
|
+
require 'ddtrace/contrib/grape/endpoint'
|
5
|
+
require 'ddtrace/contrib/rack/middlewares'
|
6
|
+
|
3
7
|
require 'ddtrace/contrib/rails/core_extensions'
|
4
8
|
require 'ddtrace/contrib/rails/action_controller'
|
5
9
|
require 'ddtrace/contrib/rails/action_view'
|
@@ -20,8 +24,11 @@ module Datadog
|
|
20
24
|
enabled: true,
|
21
25
|
auto_instrument: false,
|
22
26
|
auto_instrument_redis: false,
|
27
|
+
auto_instrument_grape: false,
|
23
28
|
default_service: 'rails-app',
|
29
|
+
default_controller_service: 'rails-controller',
|
24
30
|
default_cache_service: 'rails-cache',
|
31
|
+
default_grape_service: 'grape',
|
25
32
|
template_base_path: 'views/',
|
26
33
|
tracer: Datadog.tracer,
|
27
34
|
debug: false,
|
@@ -57,6 +64,12 @@ module Datadog
|
|
57
64
|
# set default service details
|
58
65
|
datadog_config[:tracer].set_service_info(
|
59
66
|
datadog_config[:default_service],
|
67
|
+
'rack',
|
68
|
+
Datadog::Ext::AppTypes::WEB
|
69
|
+
)
|
70
|
+
|
71
|
+
datadog_config[:tracer].set_service_info(
|
72
|
+
datadog_config[:default_controller_service],
|
60
73
|
'rails',
|
61
74
|
Datadog::Ext::AppTypes::WEB
|
62
75
|
)
|
@@ -115,6 +128,19 @@ module Datadog
|
|
115
128
|
end
|
116
129
|
end
|
117
130
|
|
131
|
+
def self.auto_instrument_grape
|
132
|
+
return unless ::Rails.configuration.datadog_trace[:auto_instrument_grape]
|
133
|
+
|
134
|
+
# patch the Grape library so that endpoints are traced
|
135
|
+
Datadog::Monkey.patch_module(:grape)
|
136
|
+
|
137
|
+
# update the Grape pin object
|
138
|
+
pin = Datadog::Pin.get_from(::Grape)
|
139
|
+
return unless pin && pin.enabled?
|
140
|
+
pin.tracer = ::Rails.configuration.datadog_trace[:tracer]
|
141
|
+
pin.service = ::Rails.configuration.datadog_trace[:default_grape_service]
|
142
|
+
end
|
143
|
+
|
118
144
|
# automatically instrument all Rails component
|
119
145
|
def self.auto_instrument
|
120
146
|
return unless ::Rails.configuration.datadog_trace[:auto_instrument]
|
data/lib/ddtrace/logger.rb
CHANGED
@@ -13,19 +13,24 @@ module Datadog
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def add(severity, message = nil, progname = nil, &block)
|
16
|
-
return super unless debug?
|
17
|
-
|
18
|
-
# We are in debug mode, add stack trace to help debugging
|
19
16
|
where = ''
|
20
|
-
c = caller
|
21
|
-
where = "(#{c[1]}) " if c.length > 1
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
# We are in debug mode, or this is an error, add stack trace to help debugging
|
19
|
+
if debug? || severity >= ::Logger::ERROR
|
20
|
+
c = caller
|
21
|
+
where = "(#{c[1]}) " if c.length > 1
|
22
|
+
end
|
23
|
+
|
24
|
+
if message.nil?
|
25
|
+
if block_given?
|
26
|
+
super(severity, message, progname) do
|
27
|
+
"[#{self.progname}] #{where}#{yield}"
|
28
|
+
end
|
29
|
+
else
|
30
|
+
super(severity, message, "[#{self.progname}] #{where}#{progname}")
|
26
31
|
end
|
27
32
|
else
|
28
|
-
super(severity,
|
33
|
+
super(severity, "[#{self.progname}] #{where}#{message}")
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
data/lib/ddtrace/monkey.rb
CHANGED
@@ -5,14 +5,21 @@ require 'thread'
|
|
5
5
|
# patching code, which is required on demand, when patching.
|
6
6
|
require 'ddtrace/contrib/active_record/patcher'
|
7
7
|
require 'ddtrace/contrib/elasticsearch/patcher'
|
8
|
-
require 'ddtrace/contrib/
|
8
|
+
require 'ddtrace/contrib/grape/patcher'
|
9
9
|
require 'ddtrace/contrib/redis/patcher'
|
10
|
+
require 'ddtrace/contrib/http/patcher'
|
10
11
|
|
11
12
|
module Datadog
|
12
13
|
# Monkey is used for monkey-patching 3rd party libs.
|
13
14
|
module Monkey
|
14
15
|
@patched = []
|
15
|
-
@autopatch_modules = {
|
16
|
+
@autopatch_modules = {
|
17
|
+
elasticsearch: true,
|
18
|
+
http: true,
|
19
|
+
redis: true,
|
20
|
+
grape: true,
|
21
|
+
active_record: false
|
22
|
+
}
|
16
23
|
# Patchers should expose 2 methods:
|
17
24
|
# - patch, which applies our patch if needed. Should be idempotent,
|
18
25
|
# can be call twice but should just do nothing the second time.
|
@@ -21,6 +28,7 @@ module Datadog
|
|
21
28
|
@patchers = { elasticsearch: Datadog::Contrib::Elasticsearch::Patcher,
|
22
29
|
http: Datadog::Contrib::HTTP::Patcher,
|
23
30
|
redis: Datadog::Contrib::Redis::Patcher,
|
31
|
+
grape: Datadog::Contrib::Grape::Patcher,
|
24
32
|
active_record: Datadog::Contrib::ActiveRecord::Patcher }
|
25
33
|
@mutex = Mutex.new
|
26
34
|
|
@@ -37,7 +45,7 @@ module Datadog
|
|
37
45
|
def patch_module(m)
|
38
46
|
@mutex.synchronize do
|
39
47
|
patcher = @patchers[m]
|
40
|
-
raise
|
48
|
+
raise "Unsupported module #{m}" unless patcher
|
41
49
|
patcher.patch
|
42
50
|
end
|
43
51
|
end
|
data/lib/ddtrace/span.rb
CHANGED
@@ -86,23 +86,25 @@ module Datadog
|
|
86
86
|
@status = 1
|
87
87
|
@meta[Datadog::Ext::Errors::MSG] = e.message if e.respond_to?(:message) && e.message
|
88
88
|
@meta[Datadog::Ext::Errors::TYPE] = e.class.to_s
|
89
|
-
@meta[Datadog::Ext::Errors::STACK] = e.backtrace.join(
|
89
|
+
@meta[Datadog::Ext::Errors::STACK] = e.backtrace.join("\n") if e.respond_to?(:backtrace) && e.backtrace
|
90
90
|
end
|
91
91
|
|
92
92
|
# Mark the span finished at the current time and submit it.
|
93
|
-
def finish
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
# Mark the span finished at the given time and submit it.
|
98
|
-
def finish_at(end_time)
|
99
|
-
@end_time = end_time
|
93
|
+
def finish(finish_time = nil)
|
94
|
+
return if finished?
|
100
95
|
|
96
|
+
@end_time = finish_time.nil? ? Time.now.utc : finish_time
|
101
97
|
@tracer.record(self) unless @tracer.nil?
|
102
|
-
|
103
98
|
self
|
104
99
|
end
|
105
100
|
|
101
|
+
# Proxy function that flag a span as finished with the given
|
102
|
+
# timestamp. This function is used for retro-compatibility.
|
103
|
+
# DEPRECATED: remove this function in the next release
|
104
|
+
def finish_at(finish_time)
|
105
|
+
finish(finish_time)
|
106
|
+
end
|
107
|
+
|
106
108
|
# Return whether the span is finished or not.
|
107
109
|
def finished?
|
108
110
|
!@end_time.nil?
|
data/lib/ddtrace/version.rb
CHANGED
metadata
CHANGED
@@ -1,103 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ddtrace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Datadog, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '10.5'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.47'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.47'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: minitest
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- - ~>
|
59
|
+
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
61
|
version: '5.10'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- - ~>
|
66
|
+
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '5.10'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: appraisal
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- - ~>
|
73
|
+
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
75
|
version: '2.1'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- - ~>
|
80
|
+
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '2.1'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: yard
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - ~>
|
87
|
+
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
89
|
version: '0.9'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - ~>
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0.9'
|
83
|
-
description:
|
84
|
-
requests
|
85
|
-
|
97
|
+
description: |
|
98
|
+
ddtrace is Datadog’s tracing client for Ruby. It is used to trace requests
|
86
99
|
as they flow across web servers, databases and microservices so that developers
|
87
|
-
|
88
100
|
have great visiblity into bottlenecks and troublesome requests.
|
89
|
-
|
90
|
-
'
|
91
101
|
email:
|
92
102
|
- dev@datadoghq.com
|
93
103
|
executables: []
|
94
104
|
extensions: []
|
95
105
|
extra_rdoc_files: []
|
96
106
|
files:
|
97
|
-
- .env
|
98
|
-
- .gitignore
|
99
|
-
- .rubocop.yml
|
100
|
-
- .yardopts
|
107
|
+
- ".env"
|
108
|
+
- ".gitignore"
|
109
|
+
- ".rubocop.yml"
|
110
|
+
- ".yardopts"
|
101
111
|
- Appraisals
|
102
112
|
- Gemfile
|
103
113
|
- LICENSE
|
@@ -126,7 +136,10 @@ files:
|
|
126
136
|
- lib/ddtrace/contrib/active_record/patcher.rb
|
127
137
|
- lib/ddtrace/contrib/elasticsearch/patcher.rb
|
128
138
|
- lib/ddtrace/contrib/elasticsearch/quantize.rb
|
139
|
+
- lib/ddtrace/contrib/grape/endpoint.rb
|
140
|
+
- lib/ddtrace/contrib/grape/patcher.rb
|
129
141
|
- lib/ddtrace/contrib/http/patcher.rb
|
142
|
+
- lib/ddtrace/contrib/rack/middlewares.rb
|
130
143
|
- lib/ddtrace/contrib/rails/action_controller.rb
|
131
144
|
- lib/ddtrace/contrib/rails/action_view.rb
|
132
145
|
- lib/ddtrace/contrib/rails/active_record.rb
|
@@ -169,17 +182,17 @@ require_paths:
|
|
169
182
|
- lib
|
170
183
|
required_ruby_version: !ruby/object:Gem::Requirement
|
171
184
|
requirements:
|
172
|
-
- -
|
185
|
+
- - ">="
|
173
186
|
- !ruby/object:Gem::Version
|
174
187
|
version: 1.9.1
|
175
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
189
|
requirements:
|
177
|
-
- -
|
190
|
+
- - ">="
|
178
191
|
- !ruby/object:Gem::Version
|
179
192
|
version: '0'
|
180
193
|
requirements: []
|
181
194
|
rubyforge_project:
|
182
|
-
rubygems_version: 2.
|
195
|
+
rubygems_version: 2.5.1
|
183
196
|
signing_key:
|
184
197
|
specification_version: 4
|
185
198
|
summary: Datadog tracing code for your Ruby applications
|