ddtrace 0.24.0 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
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