ddtrace 0.6.2 → 0.7.0
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.
- 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
|