ddtrace 0.24.0 → 0.25.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +6 -6
  3. data/.circleci/images/primary/Dockerfile-1.9.3 +6 -2
  4. data/.circleci/images/primary/Dockerfile-2.0.0 +6 -2
  5. data/.circleci/images/primary/Dockerfile-2.1.10 +6 -2
  6. data/.circleci/images/primary/Dockerfile-2.2.10 +6 -2
  7. data/.circleci/images/primary/{Dockerfile-2.4.4 → Dockerfile-2.3.8} +5 -3
  8. data/.circleci/images/primary/{Dockerfile-2.3.7 → Dockerfile-2.4.6} +2 -2
  9. data/CHANGELOG.md +36 -0
  10. data/docker-compose.yml +6 -6
  11. data/docs/DevelopmentGuide.md +63 -0
  12. data/docs/GettingStarted.md +73 -3
  13. data/lib/ddtrace/configuration.rb +20 -1
  14. data/lib/ddtrace/contrib/http/circuit_breaker.rb +39 -11
  15. data/lib/ddtrace/contrib/http/instrumentation.rb +2 -11
  16. data/lib/ddtrace/ext/forced_tracing.rb +18 -2
  17. data/lib/ddtrace/ext/manual_tracing.rb +9 -0
  18. data/lib/ddtrace/ext/metrics.rb +0 -1
  19. data/lib/ddtrace/ext/runtime.rb +0 -1
  20. data/lib/ddtrace/forced_tracing.rb +3 -3
  21. data/lib/ddtrace/metrics.rb +0 -3
  22. data/lib/ddtrace/runtime/metrics.rb +0 -1
  23. data/lib/ddtrace/sync_writer.rb +1 -1
  24. data/lib/ddtrace/tracer.rb +78 -17
  25. data/lib/ddtrace/transport.rb +9 -0
  26. data/lib/ddtrace/transport/http.rb +85 -0
  27. data/lib/ddtrace/transport/http/adapters/net.rb +112 -0
  28. data/lib/ddtrace/transport/http/adapters/registry.rb +24 -0
  29. data/lib/ddtrace/transport/http/adapters/test.rb +77 -0
  30. data/lib/ddtrace/transport/http/adapters/unix_socket.rb +64 -0
  31. data/lib/ddtrace/transport/http/api.rb +46 -0
  32. data/lib/ddtrace/transport/http/api/endpoint.rb +27 -0
  33. data/lib/ddtrace/transport/http/api/fallbacks.rb +22 -0
  34. data/lib/ddtrace/transport/http/api/instance.rb +29 -0
  35. data/lib/ddtrace/transport/http/api/map.rb +14 -0
  36. data/lib/ddtrace/transport/http/api/spec.rb +15 -0
  37. data/lib/ddtrace/transport/http/builder.rb +165 -0
  38. data/lib/ddtrace/transport/http/client.rb +108 -0
  39. data/lib/ddtrace/transport/http/env.rb +48 -0
  40. data/lib/ddtrace/transport/http/response.rb +22 -0
  41. data/lib/ddtrace/transport/http/traces.rb +140 -0
  42. data/lib/ddtrace/transport/parcel.rb +13 -0
  43. data/lib/ddtrace/transport/request.rb +13 -0
  44. data/lib/ddtrace/transport/response.rb +49 -0
  45. data/lib/ddtrace/transport/statistics.rb +44 -0
  46. data/lib/ddtrace/transport/traces.rb +33 -0
  47. data/lib/ddtrace/version.rb +1 -1
  48. data/lib/ddtrace/workers.rb +6 -2
  49. data/lib/ddtrace/writer.rb +48 -21
  50. metadata +26 -4
@@ -0,0 +1,112 @@
1
+ require 'ddtrace/transport/response'
2
+
3
+ module Datadog
4
+ module Transport
5
+ module HTTP
6
+ module Adapters
7
+ # Adapter for Net::HTTP
8
+ class Net
9
+ attr_reader \
10
+ :hostname,
11
+ :port,
12
+ :timeout
13
+
14
+ DEFAULT_TIMEOUT = 1
15
+
16
+ def initialize(hostname, port, options = {})
17
+ @hostname = hostname
18
+ @port = port
19
+ @timeout = options[:timeout] || DEFAULT_TIMEOUT
20
+ end
21
+
22
+ def open
23
+ # Open connection
24
+ ::Net::HTTP.start(hostname, port, open_timeout: timeout, read_timeout: timeout) do |http|
25
+ yield(http)
26
+ end
27
+ end
28
+
29
+ def call(env)
30
+ if respond_to?(env.verb)
31
+ send(env.verb, env)
32
+ else
33
+ raise UnknownHTTPMethod, env
34
+ end
35
+ end
36
+
37
+ def post(env)
38
+ post = ::Net::HTTP::Post.new(env.path, env.headers)
39
+ post.body = env.body
40
+
41
+ # Connect and send the request
42
+ http_response = open do |http|
43
+ http.request(post)
44
+ end
45
+
46
+ # Build and return response
47
+ Response.new(http_response)
48
+ end
49
+
50
+ # Raised when called with an unknown HTTP method
51
+ class UnknownHTTPMethod < StandardError
52
+ attr_reader :verb
53
+
54
+ def initialize(verb)
55
+ @verb = verb
56
+ end
57
+
58
+ def message
59
+ "No matching Net::HTTP function for '#{verb}'!"
60
+ end
61
+ end
62
+
63
+ # A wrapped Net::HTTP response that implements the Transport::Response interface
64
+ class Response
65
+ include Datadog::Transport::Response
66
+
67
+ attr_reader :http_response
68
+
69
+ def initialize(http_response)
70
+ @http_response = http_response
71
+ end
72
+
73
+ def payload
74
+ return super if http_response.nil?
75
+ http_response.body
76
+ end
77
+
78
+ def code
79
+ return super if http_response.nil?
80
+ http_response.code.to_i
81
+ end
82
+
83
+ def ok?
84
+ return super if http_response.nil?
85
+ code.between?(200, 299)
86
+ end
87
+
88
+ def unsupported?
89
+ return super if http_response.nil?
90
+ code == 415
91
+ end
92
+
93
+ def not_found?
94
+ return super if http_response.nil?
95
+ code == 404
96
+ end
97
+
98
+ def client_error?
99
+ return super if http_response.nil?
100
+ code.between?(400, 499)
101
+ end
102
+
103
+ def server_error?
104
+ return super if http_response.nil?
105
+ code.between?(500, 599)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,24 @@
1
+ module Datadog
2
+ module Transport
3
+ module HTTP
4
+ module Adapters
5
+ # List of available adapters
6
+ class Registry
7
+ def initialize
8
+ @adapters = {}
9
+ end
10
+
11
+ def get(name)
12
+ @adapters[name]
13
+ end
14
+
15
+ def set(klass, name = nil)
16
+ name ||= klass.to_s
17
+ return if name.nil?
18
+ @adapters[name] = klass
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,77 @@
1
+ require 'ddtrace/transport/response'
2
+
3
+ module Datadog
4
+ module Transport
5
+ module HTTP
6
+ module Adapters
7
+ # Adapter for testing
8
+ class Test
9
+ attr_reader \
10
+ :buffer,
11
+ :status
12
+
13
+ def initialize(buffer = nil)
14
+ @buffer = buffer
15
+ @mutex = Mutex.new
16
+ @status = 200
17
+ end
18
+
19
+ def call(env)
20
+ add_request(env)
21
+ Response.new(status)
22
+ end
23
+
24
+ def buffer?
25
+ !@buffer.nil?
26
+ end
27
+
28
+ def add_request(env)
29
+ @mutex.synchronize { buffer << env } if buffer?
30
+ end
31
+
32
+ def set_status!(status)
33
+ @status = status
34
+ end
35
+
36
+ # Response for test adapter
37
+ class Response
38
+ include Datadog::Transport::Response
39
+
40
+ attr_reader \
41
+ :body,
42
+ :code
43
+
44
+ def initialize(code, body = nil)
45
+ @code = code
46
+ @body = body
47
+ end
48
+
49
+ def payload
50
+ @body
51
+ end
52
+
53
+ def ok?
54
+ code.between?(200, 299)
55
+ end
56
+
57
+ def unsupported?
58
+ code == 415
59
+ end
60
+
61
+ def not_found?
62
+ code == 404
63
+ end
64
+
65
+ def client_error?
66
+ code.between?(400, 499)
67
+ end
68
+
69
+ def server_error?
70
+ code.between?(500, 599)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,64 @@
1
+ require 'net/http'
2
+ require 'ddtrace/transport/http/adapters/net'
3
+
4
+ module Datadog
5
+ module Transport
6
+ module HTTP
7
+ module Adapters
8
+ # Adapter for Unix sockets
9
+ class UnixSocket < Adapters::Net
10
+ DEFAULT_TIMEOUT = 1
11
+
12
+ attr_reader \
13
+ :filepath,
14
+ :timeout
15
+
16
+ def initialize(filepath, options = {})
17
+ @filepath = filepath
18
+ @timeout = options.fetch(:timeout, DEFAULT_TIMEOUT)
19
+ end
20
+
21
+ def open
22
+ # Open connection
23
+ connection = HTTP.new(
24
+ filepath,
25
+ read_timeout: timeout,
26
+ continue_timeout: timeout
27
+ )
28
+
29
+ connection.start do |http|
30
+ yield(http)
31
+ end
32
+ end
33
+
34
+ # Re-implements Net:HTTP with underlying Unix socket
35
+ class HTTP < ::Net::HTTP
36
+ DEFAULT_TIMEOUT = 1
37
+
38
+ attr_reader \
39
+ :filepath,
40
+ :unix_socket
41
+
42
+ def initialize(filepath, options = {})
43
+ super('localhost', 80)
44
+ @filepath = filepath
45
+ @read_timeout = options.fetch(:read_timeout, DEFAULT_TIMEOUT)
46
+ @continue_timeout = options.fetch(:continue_timeout, DEFAULT_TIMEOUT)
47
+ @debug_output = options[:debug_output] if options.key?(:debug_output)
48
+ end
49
+
50
+ def connect
51
+ @unix_socket = UNIXSocket.open(filepath)
52
+ @socket = ::Net::BufferedIO.new(@unix_socket).tap do |socket|
53
+ socket.read_timeout = @read_timeout
54
+ socket.continue_timeout = @continue_timeout
55
+ socket.debug_output = @debug_output
56
+ end
57
+ on_connect
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,46 @@
1
+ require 'ddtrace/encoding'
2
+
3
+ require 'ddtrace/transport/http/api/map'
4
+ require 'ddtrace/transport/http/api/spec'
5
+
6
+ require 'ddtrace/transport/http/traces'
7
+
8
+ module Datadog
9
+ module Transport
10
+ module HTTP
11
+ # Namespace for API components
12
+ module API
13
+ # Default API versions
14
+ V4 = 'v0.4'.freeze
15
+ V3 = 'v0.3'.freeze
16
+ V2 = 'v0.2'.freeze
17
+
18
+ module_function
19
+
20
+ def defaults
21
+ Map[
22
+ V4 => Spec.new do |s|
23
+ s.traces = Traces::API::Endpoint.new(
24
+ '/v0.4/traces'.freeze,
25
+ Encoding::MsgpackEncoder,
26
+ service_rates: true
27
+ )
28
+ end,
29
+ V3 => Spec.new do |s|
30
+ s.traces = Traces::API::Endpoint.new(
31
+ '/v0.3/traces'.freeze,
32
+ Encoding::MsgpackEncoder
33
+ )
34
+ end,
35
+ V2 => Spec.new do |s|
36
+ s.traces = Traces::API::Endpoint.new(
37
+ '/v0.2/traces'.freeze,
38
+ Encoding::JSONEncoder
39
+ )
40
+ end
41
+ ].with_fallbacks(V4 => V3, V3 => V2)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ require 'json'
2
+
3
+ module Datadog
4
+ module Transport
5
+ module HTTP
6
+ module API
7
+ # Endpoint
8
+ class Endpoint
9
+ attr_reader \
10
+ :verb,
11
+ :path
12
+
13
+ def initialize(verb, path)
14
+ @verb = verb
15
+ @path = path
16
+ end
17
+
18
+ def call(env)
19
+ env.verb = verb
20
+ env.path = path
21
+ yield(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ module Datadog
2
+ module Transport
3
+ module HTTP
4
+ module API
5
+ # Extension for Map with adds fallback versions.
6
+ module Fallbacks
7
+ def fallbacks
8
+ @fallbacks ||= {}
9
+ end
10
+
11
+ def with_fallbacks(fallbacks)
12
+ tap { add_fallbacks!(fallbacks) }
13
+ end
14
+
15
+ def add_fallbacks!(fallbacks)
16
+ self.fallbacks.merge!(fallbacks)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ module Datadog
2
+ module Transport
3
+ module HTTP
4
+ module API
5
+ # An API configured with adapter and routes
6
+ class Instance
7
+ attr_reader \
8
+ :adapter,
9
+ :headers,
10
+ :spec
11
+
12
+ def initialize(spec, adapter, options = {})
13
+ @spec = spec
14
+ @adapter = adapter
15
+ @headers = options.fetch(:headers, {})
16
+ end
17
+
18
+ def call(env)
19
+ # Add headers to request env, unless empty.
20
+ env.headers.merge!(headers) unless headers.empty?
21
+
22
+ # Send request env to the adapter.
23
+ adapter.call(env)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ require 'ddtrace/transport/http/api/fallbacks'
2
+
3
+ module Datadog
4
+ module Transport
5
+ module HTTP
6
+ module API
7
+ # A mapping of API version => API Routes/Instance
8
+ class Map < Hash
9
+ include Fallbacks
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Datadog
2
+ module Transport
3
+ module HTTP
4
+ module API
5
+ # Specification for an HTTP API
6
+ # Defines behaviors without specific configuration details.
7
+ class Spec
8
+ def initialize
9
+ yield(self) if block_given?
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,165 @@
1
+ require 'ddtrace/transport/http/adapters/registry'
2
+ require 'ddtrace/transport/http/api/map'
3
+ require 'ddtrace/transport/http/api/instance'
4
+ require 'ddtrace/transport/http/client'
5
+
6
+ module Datadog
7
+ module Transport
8
+ module HTTP
9
+ # Builds new instances of Transport::HTTP::Client
10
+ class Builder
11
+ REGISTRY = Adapters::Registry.new
12
+
13
+ attr_reader \
14
+ :apis,
15
+ :api_options,
16
+ :default_adapter,
17
+ :default_api,
18
+ :default_headers
19
+
20
+ def initialize
21
+ # Global settings
22
+ @default_adapter = nil
23
+ @default_headers = {}
24
+
25
+ # Client settings
26
+ @apis = API::Map.new
27
+ @default_api = nil
28
+
29
+ # API settings
30
+ @api_options = {}
31
+
32
+ yield(self) if block_given?
33
+ end
34
+
35
+ def adapter(type, *args)
36
+ @default_adapter = if type.is_a?(Symbol)
37
+ registry_klass = REGISTRY.get(type)
38
+ raise UnknownAdapterError, type if registry_klass.nil?
39
+ registry_klass.new(*args)
40
+ else
41
+ type
42
+ end
43
+ end
44
+
45
+ def headers(values = {})
46
+ @default_headers.merge!(values)
47
+ end
48
+
49
+ # Adds a new API to the client
50
+ # Valid options:
51
+ # - :adapter
52
+ # - :default
53
+ # - :fallback
54
+ # - :headers
55
+ def api(key, spec, options = {})
56
+ options = options.dup
57
+
58
+ # Copy spec into API map
59
+ @apis[key] = spec
60
+
61
+ # Apply as default API, if specified to do so.
62
+ @default_api = key if options.delete(:default) || @default_api.nil?
63
+
64
+ # Save all other settings for initialization
65
+ (@api_options[key] ||= {}).merge!(options)
66
+ end
67
+
68
+ def default_api=(key)
69
+ raise UnknownApiError, key unless @apis.key?(key)
70
+ @default_api = key
71
+ end
72
+
73
+ def to_client
74
+ raise NoDefaultApiError if @default_api.nil?
75
+
76
+ @client ||= Client.new(
77
+ to_api_instances,
78
+ @default_api
79
+ )
80
+ end
81
+
82
+ def to_api_instances
83
+ raise NoApisError if @apis.empty?
84
+
85
+ @apis.inject(API::Map.new) do |instances, (key, spec)|
86
+ instances.tap do
87
+ api_options = @api_options[key].dup
88
+
89
+ # Resolve the adapter to use for this API
90
+ adapter = api_options.delete(:adapter) || @default_adapter
91
+ raise NoAdapterForApiError, key if adapter.nil?
92
+
93
+ # Resolve fallback and merge headers
94
+ fallback = api_options.delete(:fallback)
95
+ api_options[:headers] = @default_headers.merge((api_options[:headers] || {}))
96
+
97
+ # Add API::Instance with all settings
98
+ instances[key] = API::Instance.new(
99
+ spec,
100
+ adapter,
101
+ api_options
102
+ )
103
+
104
+ # Configure fallback, if provided.
105
+ instances.with_fallbacks(key => fallback) unless fallback.nil?
106
+ end
107
+ end
108
+ end
109
+
110
+ # Raised when the API key does not match known APIs.
111
+ class UnknownApiError < StandardError
112
+ attr_reader :key
113
+
114
+ def initialize(key)
115
+ @key = key
116
+ end
117
+
118
+ def message
119
+ "Unknown transport API '#{key}'!"
120
+ end
121
+ end
122
+
123
+ # Raised when the identifier cannot be matched to an adapter.
124
+ class UnknownAdapterError < StandardError
125
+ attr_reader :type
126
+
127
+ def initialize(type)
128
+ @type = type
129
+ end
130
+
131
+ def message
132
+ "Unknown transport adapter '#{type}'!"
133
+ end
134
+ end
135
+
136
+ # Raised when an adapter cannot be resolved for an API instance.
137
+ class NoAdapterForApiError < StandardError
138
+ attr_reader :key
139
+
140
+ def initialize(key)
141
+ @key = key
142
+ end
143
+
144
+ def message
145
+ "No adapter resolved for transport API '#{key}'!"
146
+ end
147
+ end
148
+
149
+ # Raised when built without defining APIs.
150
+ class NoApisError < StandardError
151
+ def message
152
+ 'No APIs configured for transport!'
153
+ end
154
+ end
155
+
156
+ # Raised when client built without defining a default API.
157
+ class NoDefaultApiError < StandardError
158
+ def message
159
+ 'No default API configured for transport!'
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end