ddtrace 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.env +11 -0
  3. data/.gitignore +56 -0
  4. data/.rubocop.yml +43 -0
  5. data/Appraisals +65 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +24 -0
  8. data/README.md +119 -0
  9. data/Rakefile +103 -0
  10. data/circle.yml +68 -0
  11. data/ddtrace.gemspec +41 -0
  12. data/docker-compose.yml +33 -0
  13. data/docs/GettingStarted +352 -0
  14. data/gemfiles/contrib.gemfile +9 -0
  15. data/gemfiles/rails3_mysql2.gemfile +11 -0
  16. data/gemfiles/rails3_postgres.gemfile +10 -0
  17. data/gemfiles/rails3_postgres_redis.gemfile +11 -0
  18. data/gemfiles/rails4_mysql2.gemfile +9 -0
  19. data/gemfiles/rails4_postgres.gemfile +9 -0
  20. data/gemfiles/rails4_postgres_redis.gemfile +10 -0
  21. data/gemfiles/rails5_mysql2.gemfile +8 -0
  22. data/gemfiles/rails5_postgres.gemfile +8 -0
  23. data/gemfiles/rails5_postgres_redis.gemfile +9 -0
  24. data/lib/ddtrace.rb +63 -0
  25. data/lib/ddtrace/buffer.rb +77 -0
  26. data/lib/ddtrace/contrib/elasticsearch/core.rb +56 -0
  27. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +35 -0
  28. data/lib/ddtrace/contrib/elasticsearch/quantize.rb +22 -0
  29. data/lib/ddtrace/contrib/rails/action_controller.rb +75 -0
  30. data/lib/ddtrace/contrib/rails/action_view.rb +121 -0
  31. data/lib/ddtrace/contrib/rails/active_record.rb +44 -0
  32. data/lib/ddtrace/contrib/rails/active_support.rb +115 -0
  33. data/lib/ddtrace/contrib/rails/core_extensions.rb +89 -0
  34. data/lib/ddtrace/contrib/rails/framework.rb +107 -0
  35. data/lib/ddtrace/contrib/rails/utils.rb +42 -0
  36. data/lib/ddtrace/contrib/redis/core.rb +72 -0
  37. data/lib/ddtrace/contrib/redis/patcher.rb +36 -0
  38. data/lib/ddtrace/contrib/redis/quantize.rb +30 -0
  39. data/lib/ddtrace/contrib/redis/tags.rb +19 -0
  40. data/lib/ddtrace/encoding.rb +65 -0
  41. data/lib/ddtrace/ext/app_types.rb +9 -0
  42. data/lib/ddtrace/ext/cache.rb +7 -0
  43. data/lib/ddtrace/ext/errors.rb +9 -0
  44. data/lib/ddtrace/ext/http.rb +11 -0
  45. data/lib/ddtrace/ext/net.rb +8 -0
  46. data/lib/ddtrace/ext/redis.rb +16 -0
  47. data/lib/ddtrace/ext/sql.rb +8 -0
  48. data/lib/ddtrace/monkey.rb +60 -0
  49. data/lib/ddtrace/pin.rb +62 -0
  50. data/lib/ddtrace/span.rb +163 -0
  51. data/lib/ddtrace/tracer.rb +180 -0
  52. data/lib/ddtrace/transport.rb +149 -0
  53. data/lib/ddtrace/utils.rb +9 -0
  54. data/lib/ddtrace/version.rb +9 -0
  55. data/lib/ddtrace/workers.rb +109 -0
  56. data/lib/ddtrace/writer.rb +119 -0
  57. metadata +187 -0
@@ -0,0 +1,41 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ddtrace/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ddtrace"
8
+ spec.version = "#{Datadog::VERSION::STRING}#{ENV['VERSION_SUFFIX']}"
9
+ spec.required_ruby_version = '>= 2.1.0'
10
+ spec.authors = ["Datadog, Inc."]
11
+ spec.email = ["dev@datadoghq.com"]
12
+
13
+ spec.summary = "Datadog tracing code for your Ruby applications"
14
+ spec.description = <<-EOS
15
+ ddtrace is Datadog’s tracing client for Ruby. It is used to trace requests
16
+ as they flow across web servers, databases and microservices so that developers
17
+ have great visiblity into bottlenecks and troublesome requests.
18
+ EOS
19
+
20
+ spec.homepage = "https://github.com/DataDog/dd-trace-rb"
21
+ spec.license = "BSD-3-Clause"
22
+
23
+ if spec.respond_to?(:metadata)
24
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
25
+ else
26
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
27
+ end
28
+
29
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "msgpack"
35
+
36
+ spec.add_development_dependency "bundler", "~> 1.12"
37
+ spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rubocop", "~> 0.43"
39
+ spec.add_development_dependency "minitest", "~> 5.0"
40
+ spec.add_development_dependency "appraisal", "~> 2.1"
41
+ end
@@ -0,0 +1,33 @@
1
+ # remember to use this compose file __ONLY__ for development/testing purposes
2
+ postgres:
3
+ image: postgres:9.6
4
+ environment:
5
+ - POSTGRES_PASSWORD=$TEST_POSTGRES_PASSWORD
6
+ - POSTGRES_USER=$TEST_POSTGRES_USER
7
+ - POSTGRES_DB=$TEST_POSTGRES_DB
8
+ ports:
9
+ - "127.0.0.1:${TEST_POSTGRES_PORT}:5432"
10
+
11
+ mysql:
12
+ image: mysql:5.6
13
+ environment:
14
+ - MYSQL_ROOT_PASSWORD=$TEST_MYSQL_ROOT_PASSWORD
15
+ - MYSQL_PASSWORD=$TEST_MYSQL_PASSWORD
16
+ - MYSQL_USER=$TEST_MYSQL_USER
17
+ ports:
18
+ - "127.0.0.1:${TEST_MYSQL_PORT}:3306"
19
+
20
+ elasticsearch:
21
+ # Note: ES 5.0 dies with error:
22
+ # max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
23
+ # see https://github.com/docker-library/elasticsearch/issues/98 for details
24
+ # For now, just rely on a 2.X server.
25
+ image: elasticsearch:2.4
26
+ ports:
27
+ - "127.0.0.1:${TEST_ELASTICSEARCH_REST_PORT}:9200"
28
+ - "127.0.0.1:${TEST_ELASTICSEARCH_NATIVE_PORT}:9300"
29
+
30
+ redis:
31
+ image: redis:3.0
32
+ ports:
33
+ - "127.0.0.1:${TEST_REDIS_PORT}:6379"
@@ -0,0 +1,352 @@
1
+ = \Datadog Trace Client
2
+
3
+ _ddtrace_ is Datadog’s tracing client for Ruby. It is used to trace requests as they flow across web servers,
4
+ databases and microservices so that developers have great visiblity into bottlenecks and troublesome requests.
5
+
6
+ == Installation
7
+
8
+ Install the tracer with the +gem+ command, but point to Datadog's gems repository:
9
+
10
+ $ gem install ddtrace --source http://gems.datadoghq.com/trace/
11
+
12
+ On the other hand, if you're using +Bundler+, just update your +Gemfile+ as follows:
13
+
14
+ source 'https://rubygems.org'
15
+
16
+ # tracing gem
17
+ gem 'ddtrace', :source => 'http://gems.datadoghq.com/trace/'
18
+
19
+ == Quickstart (Auto Instrumentation)
20
+
21
+ If you are on a {supported integration}[#label-Integrations], you should be able to generate traffic and view
22
+ metrics in your {dashboard}[https://app.datadoghq.com/trace].
23
+
24
+ == Quickstart (Manual Instrumentation)
25
+
26
+ If you aren't using a supported framework instrumentation, you may want to to manually instrument your code.
27
+ Adding tracing to your code is very simple. As an example, let’s imagine we have a web server and we want
28
+ to trace requests to the home page:
29
+
30
+ require 'ddtrace'
31
+ require 'sinatra'
32
+ require 'activerecord'
33
+
34
+ # a generic tracer that you can use across your application
35
+ tracer = Datadog.tracer
36
+
37
+ get '/' do
38
+ tracer.trace('web.request') do |span|
39
+ # set some span metadata
40
+ span.service = 'my-web-site'
41
+ span.resource = '/'
42
+ span.set_tag('http.method', request.request_method)
43
+
44
+ # trace the activerecord call
45
+ tracer.trace('posts.fetch') do
46
+ @posts = Posts.order(created_at: :desc).limit(10)
47
+ end
48
+
49
+ # trace the template rendering
50
+ tracer.trace('template.render') do
51
+ erb :index
52
+ end
53
+ end
54
+ end
55
+
56
+ == Glossary
57
+
58
+ [Service] The name of a set of processes that do the same job. Some examples are +datadog-web-app+ or +datadog-metrics-db+.
59
+
60
+ [Resource] A particular query to a service. For a web application, some examples might be a URL stem like +/user/home+ or a
61
+ handler function like +web.user.home+. For a SQL database, a resource would be the SQL of the query itself like
62
+ <tt>select * from users where id = ?</tt>.
63
+ You can track thousands (not millions or billions) of unique resources per services, so prefer resources like
64
+ +/user/home+ rather than <tt>/user/home?id=123456789</tt>.
65
+
66
+ [Span] A span tracks a unit of work in a service, like querying a database or rendering a template. Spans are associated
67
+ with a service and optionally a resource. Spans have names, start times, durations and optional tags.
68
+
69
+ == Integrations
70
+
71
+ === Ruby on \Rails
72
+
73
+ The \Rails integration will trace requests, database calls, templates rendering and cache read/write/delete
74
+ operations. The integration makes use of the Active Support Instrumentation, listening to the Notification API
75
+ so that any operation instrumented by the API is traced.
76
+
77
+ The supported versions are:
78
+
79
+ * \Rails 3.2 (MRI interpreter, JRuby is experimental)
80
+ * \Rails 4.2 (MRI interpreter, JRuby is experimental)
81
+ * \Rails 5.0 (MRI interpreter)
82
+
83
+ The currently supported web server are:
84
+ * Puma 2.16+ and 3.6+
85
+ * Unicorn 4.8+ and 5.1+
86
+ * Passenger 5.0 (experimental)
87
+
88
+ ==== Installation
89
+
90
+ Add the tracer gem to your +Gemfile+:
91
+
92
+ gem 'ddtrace', :source => 'http://gems.datadoghq.com/trace/'
93
+
94
+ Now you can set your service name, simply creating an initializer file in your +config/+ folder:
95
+
96
+ # config/initializers/datadog-tracer.rb
97
+
98
+ Rails.configuration.datadog_trace = {
99
+ default_service: 'my-rails-app',
100
+ }
101
+
102
+ If you're using \Rails 3 or higher, the auto-instrumentation will be automatically activated and no more configuration
103
+ is required. Your application will be listed as +my-rails-app+ in your {dashboard}[https://app.datadoghq.com/trace].
104
+
105
+ ==== Custom Instrumentation
106
+
107
+ If you need to instrument custom code within your controllers, you can simply:
108
+
109
+ class CustomController < ApplicationController
110
+ def index
111
+ # using auto instrumentation, these calls are already traced
112
+ @values = SomeModel.all
113
+ @counter = Rails.cache.fetch('custom_cache_key')
114
+
115
+ # use the global tracer to instrument your code
116
+ tracer = Datadog.tracer
117
+ tracer.trace('custom.service') do
118
+ data = Something::fetch_data()
119
+ @objects = Something::parse_data(data)
120
+ end
121
+ end
122
+ end
123
+
124
+ With the auto instrumentation turned on, the result trace will include your span correctly nested under the
125
+ +rails.request+ span.
126
+
127
+ ==== Tracer Configuration
128
+
129
+ All tracing settings are namespaced under the +Rails.configuration.datadog_tracer+ hash. To change the default behavior
130
+ of the Datadog tracer, you can override the following defaults:
131
+
132
+ # config/initializers/datadog-tracer.rb
133
+
134
+ Rails.configuration.datadog_trace = {
135
+ enabled: true,
136
+ auto_instrument: true,
137
+ auto_instrument_redis: true,
138
+ default_service: 'rails-app',
139
+ default_cache_service: 'rails-cache',
140
+ template_base_path: 'views/',
141
+ tracer: Datadog.tracer,
142
+ debug: false,
143
+ trace_agent_hostname: 'localhost',
144
+ trace_agent_port: 7777
145
+ }
146
+
147
+ The available settings are:
148
+
149
+ * +enabled+: defines if the +tracer+ is enabled or not. If set to +false+, the code is still instrumented
150
+ but no spans are sent to the local trace agent.
151
+ * +auto_instrument+: if set to false the code will not be instrumented, while the +tracer+ may be active for
152
+ your internal usage. This could be useful if you want to use the \Rails integration, but you want to trace
153
+ only particular functions or views
154
+ * +auto_instrument_redis+: if set to false \Redis calls will not be traced as such. Calls to cache will
155
+ still be instrumented but you will not have the detail of low-level \Redis calls.
156
+ * +default_service+: set the service name used when tracing application requests. Defaults to +rails-app+
157
+ * +default_database_service+: set the database service name used when tracing database activity. Defaults to the
158
+ current adapter name, so if you're using PostgreSQL it will be +postgres+.
159
+ * +default_cache_service+: set the cache service name used when tracing cache activity. Defaults to +rails-cache+
160
+ * +template_base_path+: used when the template name is parsed in the auto instrumented code. If you don't store
161
+ your templates in the +views/+ folder, you may need to change this value
162
+ * +tracer+: is the global tracer used by the tracing application. Usually you don't need to change that value
163
+ unless you're already using a different initialized +tracer+ somewhere else
164
+ * +debug+: set to true to enable debug logging.
165
+ * +trace_agent_hostname+: set the hostname of the trace agent.
166
+ * +trace_agent_port+: set the port the trace agent is listening on.
167
+
168
+ === Redis
169
+
170
+ The \Redis integration will trace simple calls as well as pipelines.
171
+
172
+ require 'redis'
173
+ require 'ddtrace'
174
+
175
+ Datadog::Monkey.patch_module(:redis) # you need to explicitly patch it
176
+
177
+ # now do your Redis stuff, eg:
178
+ redis = Redis.new
179
+ redis.set 'foo', 'bar' # traced!
180
+
181
+ The \patch_module allows you to enable tracing on a per-library basis.
182
+ If you want to enable tracing for all supported integrations, you can simply
183
+ use \patch_all eg:
184
+
185
+ require 'redis'
186
+ require 'ddtrace'
187
+
188
+ Datadog::Monkey.patch_all # enables all available integrations
189
+
190
+ # now do your Redis stuff, eg:
191
+ redis = Redis.new
192
+ redis.set 'foo', 'bar' # traced!
193
+
194
+ === Elastic Search
195
+
196
+ The \Elasticsearch integration will trace any call to \perform_request
197
+ in the \Client object:
198
+
199
+ require 'elasticsearch/transport'
200
+ require 'ddtrace'
201
+
202
+ Datadog::Monkey.patch_module(:elasticsearch) # you need to explicitly patch it
203
+
204
+ # now do your Elastic Search stuff, eg:
205
+ client = Elasticsearch::Client.new url: 'http://127.0.0.1:9200'
206
+ response = client.perform_request 'GET', '_cluster/health'
207
+
208
+ The \patch_module allows you to enable tracing on a per-library basis.
209
+ If you want to enable tracing for all supported integrations, you can simply
210
+ use \patch_all eg:
211
+
212
+ require 'elasticsearch/transport'
213
+ require 'ddtrace'
214
+
215
+ Datadog::Monkey.patch_all # enables all available integrations
216
+
217
+ # now do your Elastic Search stuff, eg:
218
+ client = Elasticsearch::Client.new url: 'http://127.0.0.1:9200'
219
+ response = client.perform_request 'GET', '_cluster/health'
220
+
221
+ == Monkey Patching
222
+
223
+ === Patching methods
224
+
225
+ Integrations such as \Redis or \Elasticsearch use monkey patching.
226
+
227
+ The available methods are:
228
+
229
+ * +autopatch_modules+: returns a \Hash of all modules available for monkey patching,
230
+ the key is the name of the module and the value \true or \false. If it is \true,
231
+ a call to \patch_all will enable the module, if it is \false, it will do nothing.
232
+ * +patch_all+: patches all modules which are supported. Make sure all the necessary
233
+ calls to \require have been done before this is alled, else monkey patching will
234
+ not work.
235
+ * +patch_module+: patches a single module, regardless of its settings in the
236
+ \autopatch_modules list.
237
+ * +patch+: patches some modules, you should pass a hash like the one returned
238
+ by \autopatch_modules
239
+ * +get_patched_modules+: returns the list of patched modules, a module has been
240
+ correctly patched only if it is in this hash, with a value set to \true.
241
+
242
+ Example:
243
+
244
+ require 'ddtrace'
245
+
246
+ puts Datadog::Monkey.autopatch_modules # lists all modules available for monkey patching
247
+ Datadog::Monkey.patch_module(:redis) # patch only one module
248
+ Datadog::Monkey.patch(elasticsearch: false, redis: true) # patch redis, but not elasticsearch
249
+ Datadog::Monkey.patch_all # patch all the available modules
250
+ puts Datadog::Monkey.get_patched_modules # tells wether modules are patched or not
251
+
252
+ It is safe to call \patch_all, \patch_module or \patch several times.
253
+ Make sure the library you want to patch is imported before you call \patch_module.
254
+ In doubt, check with \get_patched_modules.
255
+ Once a module is patched, it is not possible to unpatch it.
256
+
257
+ === Patch Info (PIN)
258
+
259
+ The Patch Info, AKA \Pin object, gives you control on the integration.
260
+
261
+ It has one class method:
262
+
263
+ * +get_from+: returns the Pin object which has been pinned onto some random
264
+ object. It is safe to call \get_from on any object, but it might return \nil.
265
+
266
+ Some instance methods:
267
+
268
+ * +enabled?+: wether tracing is enabled for this object
269
+ * +onto+: applies the patch information to some random object. It is the companion
270
+ function of \get_from.
271
+
272
+ Accessors:
273
+
274
+ * +service+: service, you should typically set some meaningful value for this.
275
+ * +app+: application name
276
+ * +tags+: optional tags
277
+ * +app_type+: application type
278
+ * +name+: span name
279
+ * +tracer+: the tracer object used for tracing
280
+
281
+ By default, a traced integration such as \Redis or \Elasticsearch carries a \Pin object. Eg:
282
+
283
+ require 'redis'
284
+ require 'ddtrace'
285
+
286
+ Datadog::Monkey.patch_all
287
+
288
+ redis = Redis.new
289
+ pin = Datadog::Pin.get_from(redis)
290
+ pin.service = 'my-redis-cache'
291
+ puts redis.get 'my-key' # this will be traced as belonging to 'my-redis-cache' service
292
+ pin.tracer = nil
293
+ puts pin.enabled? # false
294
+ puts redis.get 'my-key' # this won't be traced, tracing has been disabled now
295
+
296
+ You can use this object to instrument your own code:
297
+
298
+ require 'ddtrace'
299
+ require 'ddtrace/ext/app_types'
300
+
301
+ class MyWebSite
302
+ def initialize
303
+ pin = Datadog::Pin.new('my-web-site', app_type: Datadog::Ext::AppTypes::WEB)
304
+ Datadog::Pin.onto(self)
305
+ end
306
+
307
+ def serve(something)
308
+ pin = Datadog::Pin.get_from(self)
309
+ pin.tracer.trace('serve') do |span|
310
+ span.resource = something
311
+ span.service = pin.service
312
+ # serve something here
313
+ end
314
+ end
315
+ end
316
+
317
+ == Debug Mode
318
+
319
+ If you need to check locally what traces and spans are sent after each traced block, you can enable
320
+ a global debug mode for all tracers so that every time a trace is ready to be sent, the content will be
321
+ printed in the +STDOUT+. To enable the debug logging, add this code anywhere before using the tracer
322
+ for the first time:
323
+
324
+ require 'ddtrace'
325
+ require 'sinatra'
326
+ require 'activerecord'
327
+
328
+ # enable debug mode
329
+ Datadog::Tracer.debug_logging = true
330
+
331
+ # use the tracer as usual
332
+ tracer = Datadog.tracer
333
+
334
+ get '/' do
335
+ tracer.trace('web.request') do |span|
336
+ # ...
337
+ end
338
+ end
339
+
340
+ Remember that the debug mode may affect your application performance and so it must not be used
341
+ in a production environment.
342
+
343
+ == Supported Versions
344
+
345
+ The \Datadog Trace Client has been tested with the following Ruby versions:
346
+
347
+ * Ruby MRI 2.1
348
+ * Ruby MRI 2.2
349
+ * Ruby MRI 2.3
350
+ * JRuby 9.1.5 (experimental)
351
+
352
+ Other versions aren't yet officially supported.
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "elasticsearch-transport"
6
+ gem "redis"
7
+ gem "hiredis"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "test-unit"
6
+ gem "rails", "3.2.22.5"
7
+ gem "mysql2", "0.3.21", :platform => :ruby
8
+ gem "activerecord-mysql-adapter", :platform => :ruby
9
+ gem "activerecord-jdbcmysql-adapter", :platform => :jruby
10
+
11
+ gemspec :path => "../"