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,108 @@
1
+ require 'ddtrace/transport/statistics'
2
+ require 'ddtrace/transport/http/env'
3
+
4
+ module Datadog
5
+ module Transport
6
+ module HTTP
7
+ # Routes, encodes, and sends tracer data to the trace agent via HTTP.
8
+ class Client
9
+ include Transport::Statistics
10
+
11
+ attr_reader \
12
+ :apis,
13
+ :current_api_id
14
+
15
+ def initialize(apis, current_api_id)
16
+ @apis = apis
17
+
18
+ # Activate initial API
19
+ change_api!(current_api_id)
20
+ end
21
+
22
+ def send_request(request, &block)
23
+ # Build request into env
24
+ env = build_env(request)
25
+
26
+ # Get response from API
27
+ response = yield(current_api, env)
28
+
29
+ # Update statistics
30
+ update_stats_from_response!(response)
31
+
32
+ # If API should be downgraded, downgrade and try again.
33
+ if downgrade?(response)
34
+ downgrade!
35
+ response = send_request(request, &block)
36
+ end
37
+
38
+ response
39
+ rescue StandardError => e
40
+ message = "Internal error during HTTP transport request. Cause: #{e.message} Location: #{e.backtrace.first}"
41
+
42
+ # Log error
43
+ if stats.consecutive_errors > 0
44
+ Datadog::Tracer.log.debug(message)
45
+ else
46
+ Datadog::Tracer.log.error(message)
47
+ end
48
+
49
+ # Update statistics
50
+ stats.internal_error += 1
51
+ stats.consecutive_errors += 1
52
+
53
+ InternalErrorResponse.new(e)
54
+ end
55
+
56
+ def build_env(request)
57
+ Env.new(request)
58
+ end
59
+
60
+ def downgrade?(response)
61
+ return false unless apis.fallbacks.key?(current_api_id)
62
+ response.not_found? || response.unsupported?
63
+ end
64
+
65
+ def current_api
66
+ apis[current_api_id]
67
+ end
68
+
69
+ def change_api!(api_id)
70
+ raise UnknownApiVersionError, api_id unless apis.key?(api_id)
71
+ @current_api_id = api_id
72
+ end
73
+
74
+ def downgrade!
75
+ downgrade_api_id = apis.fallbacks[current_api_id]
76
+ raise NoDowngradeAvailableError, current_api_id if downgrade_api_id.nil?
77
+ change_api!(downgrade_api_id)
78
+ end
79
+
80
+ # Raised when configured with an unknown API version
81
+ class UnknownApiVersionError < StandardError
82
+ attr_reader :version
83
+
84
+ def initialize(version)
85
+ @version = version
86
+ end
87
+
88
+ def message
89
+ "No matching transport API for version #{version}!"
90
+ end
91
+ end
92
+
93
+ # Raised when configured with an unknown API version
94
+ class NoDowngradeAvailableError < StandardError
95
+ attr_reader :version
96
+
97
+ def initialize(version)
98
+ @version = version
99
+ end
100
+
101
+ def message
102
+ "No downgrade from transport API version #{version} is available!"
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,48 @@
1
+ module Datadog
2
+ module Transport
3
+ module HTTP
4
+ # Data structure for an HTTP request
5
+ class Env < Hash
6
+ attr_reader \
7
+ :request
8
+
9
+ def initialize(request, options = nil)
10
+ @request = request
11
+ merge!(options) unless options.nil?
12
+ end
13
+
14
+ def verb
15
+ self[:verb]
16
+ end
17
+
18
+ def verb=(value)
19
+ self[:verb] = value
20
+ end
21
+
22
+ def path
23
+ self[:path]
24
+ end
25
+
26
+ def path=(value)
27
+ self[:path] = value
28
+ end
29
+
30
+ def body
31
+ self[:body]
32
+ end
33
+
34
+ def body=(value)
35
+ self[:body] = value
36
+ end
37
+
38
+ def headers
39
+ self[:headers] ||= {}
40
+ end
41
+
42
+ def headers=(value)
43
+ self[:headers] = value
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ require 'forwardable'
2
+ require 'ddtrace/transport/response'
3
+
4
+ module Datadog
5
+ module Transport
6
+ module HTTP
7
+ # Wraps an HTTP response from an adapter.
8
+ #
9
+ # Used by endpoints to wrap responses from adapters with
10
+ # fields or behavior that's specific to that endpoint.
11
+ module Response
12
+ extend ::Forwardable
13
+
14
+ def initialize(http_response)
15
+ @http_response = http_response
16
+ end
17
+
18
+ def_delegators :@http_response, *Transport::Response.instance_methods
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,140 @@
1
+ require 'json'
2
+
3
+ require 'ddtrace/transport/traces'
4
+ require 'ddtrace/transport/http/response'
5
+ require 'ddtrace/transport/http/api/endpoint'
6
+
7
+ module Datadog
8
+ module Transport
9
+ module HTTP
10
+ # HTTP transport behavior for traces
11
+ module Traces
12
+ # Response from HTTP transport for traces
13
+ class Response
14
+ include HTTP::Response
15
+ include Transport::Traces::Response
16
+
17
+ def initialize(http_response, options = {})
18
+ super(http_response)
19
+ @service_rates = options.fetch(:service_rates, nil)
20
+ end
21
+ end
22
+
23
+ # Extensions for HTTP client
24
+ module Client
25
+ def send_traces(traces)
26
+ request = Transport::Traces::Request.new(traces)
27
+
28
+ send_request(request) do |api, env|
29
+ api.send_traces(env)
30
+ end
31
+ end
32
+ end
33
+
34
+ module API
35
+ # Extensions for HTTP API Spec
36
+ module Spec
37
+ attr_reader :traces
38
+
39
+ def traces=(endpoint)
40
+ @traces = endpoint
41
+ end
42
+
43
+ def send_traces(env, &block)
44
+ raise NoTraceEndpointDefinedError, self if traces.nil?
45
+ traces.call(env, &block)
46
+ end
47
+
48
+ # Raised when traces sent but no traces endpoint is defined
49
+ class NoTraceEndpointDefinedError < StandardError
50
+ attr_reader :spec
51
+
52
+ def initialize(spec)
53
+ @spec = spec
54
+ end
55
+
56
+ def message
57
+ 'No trace endpoint is defined for API specification!'
58
+ end
59
+ end
60
+ end
61
+
62
+ # Extensions for HTTP API Instance
63
+ module Instance
64
+ def send_traces(env)
65
+ raise TracesNotSupportedError, spec unless spec.is_a?(Traces::API::Spec)
66
+
67
+ spec.send_traces(env) do |request_env|
68
+ call(request_env)
69
+ end
70
+ end
71
+
72
+ # Raised when traces sent to API that does not support traces
73
+ class TracesNotSupportedError < StandardError
74
+ attr_reader :spec
75
+
76
+ def initialize(spec)
77
+ @spec = spec
78
+ end
79
+
80
+ def message
81
+ 'Traces not supported for this API!'
82
+ end
83
+ end
84
+ end
85
+
86
+ # Endpoint for submitting trace data
87
+ class Endpoint < HTTP::API::Endpoint
88
+ HEADER_CONTENT_TYPE = 'Content-Type'.freeze
89
+ HEADER_TRACE_COUNT = 'X-Datadog-Trace-Count'.freeze
90
+ SERVICE_RATE_KEY = 'rate_by_service'.freeze
91
+
92
+ attr_reader \
93
+ :encoder
94
+
95
+ def initialize(path, encoder, options = {})
96
+ super(:post, path)
97
+ @encoder = encoder
98
+ @service_rates = options.fetch(:service_rates, false)
99
+ end
100
+
101
+ def service_rates?
102
+ @service_rates == true
103
+ end
104
+
105
+ def call(env, &block)
106
+ # Add trace count header
107
+ env.headers[HEADER_TRACE_COUNT] = env.request.parcel.count.to_s
108
+
109
+ # Encode body & type
110
+ env.headers[HEADER_CONTENT_TYPE] = encoder.content_type
111
+ env.body = env.request.parcel.encode_with(encoder)
112
+
113
+ # Query for response
114
+ http_response = super(env, &block)
115
+
116
+ # Process the response
117
+ response_options = {}.tap do |options|
118
+ # Parse service rates, if configured to do so.
119
+ if service_rates? && !http_response.payload.to_s.empty?
120
+ body = JSON.parse(http_response.payload)
121
+ if body.is_a?(Hash) && body.key?(SERVICE_RATE_KEY)
122
+ options[:service_rates] = body[SERVICE_RATE_KEY]
123
+ end
124
+ end
125
+ end
126
+
127
+ # Build and return a trace response
128
+ Traces::Response.new(http_response, response_options)
129
+ end
130
+ end
131
+ end
132
+
133
+ # Add traces behavior to transport components
134
+ HTTP::Client.send(:include, Traces::Client)
135
+ HTTP::API::Spec.send(:include, Traces::API::Spec)
136
+ HTTP::API::Instance.send(:include, Traces::API::Instance)
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,13 @@
1
+ module Datadog
2
+ module Transport
3
+ # Data transfer object for generic data
4
+ module Parcel
5
+ attr_reader \
6
+ :data
7
+
8
+ def initialize(data)
9
+ @data = data
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Datadog
2
+ module Transport
3
+ # Defines request for transport operations
4
+ class Request
5
+ attr_reader \
6
+ :parcel
7
+
8
+ def initialize(parcel)
9
+ @parcel = parcel
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ module Datadog
2
+ module Transport
3
+ # Defines abstract response for transport operations
4
+ module Response
5
+ def payload
6
+ nil
7
+ end
8
+
9
+ def ok?
10
+ nil
11
+ end
12
+
13
+ def unsupported?
14
+ nil
15
+ end
16
+
17
+ def not_found?
18
+ nil
19
+ end
20
+
21
+ def client_error?
22
+ nil
23
+ end
24
+
25
+ def server_error?
26
+ nil
27
+ end
28
+
29
+ def internal_error?
30
+ nil
31
+ end
32
+ end
33
+
34
+ # A generic error response for internal errors
35
+ class InternalErrorResponse
36
+ include Response
37
+
38
+ attr_reader :error
39
+
40
+ def initialize(error)
41
+ @error = error
42
+ end
43
+
44
+ def internal_error?
45
+ true
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,44 @@
1
+ module Datadog
2
+ module Transport
3
+ # Tracks statistics for transports
4
+ module Statistics
5
+ def stats
6
+ @stats ||= Counts.new
7
+ end
8
+
9
+ def update_stats_from_response!(response)
10
+ if response.ok?
11
+ stats.success += 1
12
+ stats.consecutive_errors = 0
13
+ else
14
+ stats.client_error += 1 if response.client_error?
15
+ stats.server_error += 1 if response.server_error?
16
+ stats.internal_error += 1 if response.internal_error?
17
+ stats.consecutive_errors += 1
18
+ end
19
+ end
20
+
21
+ # Stat counts
22
+ class Counts
23
+ attr_accessor \
24
+ :success,
25
+ :client_error,
26
+ :server_error,
27
+ :internal_error,
28
+ :consecutive_errors
29
+
30
+ def initialize
31
+ reset!
32
+ end
33
+
34
+ def reset!
35
+ @success = 0
36
+ @client_error = 0
37
+ @server_error = 0
38
+ @internal_error = 0
39
+ @consecutive_errors = 0
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ require 'ddtrace/transport/parcel'
2
+ require 'ddtrace/transport/request'
3
+
4
+ module Datadog
5
+ module Transport
6
+ module Traces
7
+ # Data transfer object for trace data
8
+ class Parcel
9
+ include Transport::Parcel
10
+
11
+ def count
12
+ data.length
13
+ end
14
+
15
+ def encode_with(encoder)
16
+ encoder.encode_traces(data)
17
+ end
18
+ end
19
+
20
+ # Traces request
21
+ class Request < Transport::Request
22
+ def initialize(traces)
23
+ super(Parcel.new(traces))
24
+ end
25
+ end
26
+
27
+ # Traces response
28
+ module Response
29
+ attr_reader :service_rates
30
+ end
31
+ end
32
+ end
33
+ end