datadog-ci 1.2.0 → 1.3.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 +4 -4
- data/CHANGELOG.md +24 -2
- data/lib/datadog/ci/configuration/components.rb +30 -3
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +13 -9
- data/lib/datadog/ci/contrib/minitest/runnable.rb +5 -1
- data/lib/datadog/ci/contrib/minitest/runner.rb +6 -2
- data/lib/datadog/ci/contrib/minitest/test.rb +7 -3
- data/lib/datadog/ci/contrib/rspec/example.rb +6 -2
- data/lib/datadog/ci/contrib/rspec/example_group.rb +5 -1
- data/lib/datadog/ci/contrib/rspec/knapsack_pro/runner.rb +6 -2
- data/lib/datadog/ci/contrib/rspec/runner.rb +6 -2
- data/lib/datadog/ci/ext/environment/providers/appveyor.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/aws_code_pipeline.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/azure.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/bitbucket.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/bitrise.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/buddy.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/buildkite.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/circleci.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/codefresh.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/github_actions.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/gitlab.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/jenkins.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/teamcity.rb +1 -1
- data/lib/datadog/ci/ext/environment/providers/travis.rb +1 -1
- data/lib/datadog/ci/ext/environment.rb +17 -0
- data/lib/datadog/ci/ext/telemetry.rb +120 -0
- data/lib/datadog/ci/git/local_repository.rb +116 -25
- data/lib/datadog/ci/git/search_commits.rb +20 -1
- data/lib/datadog/ci/git/telemetry.rb +37 -0
- data/lib/datadog/ci/git/tree_uploader.rb +7 -0
- data/lib/datadog/ci/git/upload_packfile.rb +22 -1
- data/lib/datadog/ci/test.rb +5 -0
- data/lib/datadog/ci/test_optimisation/component.rb +16 -2
- data/lib/datadog/ci/test_optimisation/coverage/transport.rb +10 -1
- data/lib/datadog/ci/test_optimisation/skippable.rb +24 -0
- data/lib/datadog/ci/test_optimisation/telemetry.rb +56 -0
- data/lib/datadog/ci/test_visibility/component.rb +95 -235
- data/lib/datadog/ci/test_visibility/context.rb +274 -0
- data/lib/datadog/ci/test_visibility/{context → store}/global.rb +1 -1
- data/lib/datadog/ci/test_visibility/{context → store}/local.rb +1 -1
- data/lib/datadog/ci/test_visibility/telemetry.rb +69 -0
- data/lib/datadog/ci/test_visibility/transport.rb +8 -9
- data/lib/datadog/ci/transport/adapters/net.rb +42 -11
- data/lib/datadog/ci/transport/adapters/net_http_client.rb +17 -0
- data/lib/datadog/ci/transport/adapters/telemetry_webmock_safe_adapter.rb +28 -0
- data/lib/datadog/ci/transport/event_platform_transport.rb +42 -5
- data/lib/datadog/ci/transport/http.rb +49 -21
- data/lib/datadog/ci/transport/remote_settings_api.rb +39 -1
- data/lib/datadog/ci/transport/telemetry.rb +93 -0
- data/lib/datadog/ci/utils/identity.rb +20 -0
- data/lib/datadog/ci/utils/parsing.rb +2 -1
- data/lib/datadog/ci/utils/telemetry.rb +23 -0
- data/lib/datadog/ci/version.rb +1 -1
- data/lib/datadog/ci.rb +30 -0
- metadata +16 -6
@@ -0,0 +1,274 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "datadog/tracing"
|
4
|
+
require "datadog/tracing/contrib/component"
|
5
|
+
require "datadog/tracing/trace_digest"
|
6
|
+
|
7
|
+
require_relative "store/global"
|
8
|
+
require_relative "store/local"
|
9
|
+
|
10
|
+
require_relative "../ext/app_types"
|
11
|
+
require_relative "../ext/environment"
|
12
|
+
require_relative "../ext/test"
|
13
|
+
|
14
|
+
require_relative "../span"
|
15
|
+
require_relative "../test"
|
16
|
+
require_relative "../test_session"
|
17
|
+
require_relative "../test_module"
|
18
|
+
require_relative "../test_suite"
|
19
|
+
|
20
|
+
module Datadog
|
21
|
+
module CI
|
22
|
+
module TestVisibility
|
23
|
+
# Manages current in-memory context for test visibility (such as active test session, suite, test, etc.).
|
24
|
+
# Its responsibility includes building domain models for test visibility as well.
|
25
|
+
# Internally it uses Datadog::Tracing module to create spans.
|
26
|
+
class Context
|
27
|
+
def initialize
|
28
|
+
@local_context = Store::Local.new
|
29
|
+
@global_context = Store::Global.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_test_session(service: nil, tags: {})
|
33
|
+
@global_context.fetch_or_activate_test_session do
|
34
|
+
tracer_span = start_datadog_tracer_span(
|
35
|
+
"test.session", build_tracing_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
|
36
|
+
)
|
37
|
+
set_session_context(tags, tracer_span)
|
38
|
+
|
39
|
+
build_test_session(tracer_span, tags)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def start_test_module(test_module_name, service: nil, tags: {})
|
44
|
+
@global_context.fetch_or_activate_test_module do
|
45
|
+
set_inherited_globals(tags)
|
46
|
+
set_session_context(tags)
|
47
|
+
|
48
|
+
tracer_span = start_datadog_tracer_span(
|
49
|
+
test_module_name, build_tracing_span_options(service, Ext::AppTypes::TYPE_TEST_MODULE)
|
50
|
+
)
|
51
|
+
set_module_context(tags, tracer_span)
|
52
|
+
|
53
|
+
build_test_module(tracer_span, tags)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def start_test_suite(test_suite_name, service: nil, tags: {})
|
58
|
+
@global_context.fetch_or_activate_test_suite(test_suite_name) do
|
59
|
+
set_inherited_globals(tags)
|
60
|
+
set_session_context(tags)
|
61
|
+
set_module_context(tags)
|
62
|
+
|
63
|
+
tracer_span = start_datadog_tracer_span(
|
64
|
+
test_suite_name, build_tracing_span_options(service, Ext::AppTypes::TYPE_TEST_SUITE)
|
65
|
+
)
|
66
|
+
set_suite_context(tags, span: tracer_span)
|
67
|
+
|
68
|
+
build_test_suite(tracer_span, tags)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
|
73
|
+
set_inherited_globals(tags)
|
74
|
+
set_session_context(tags)
|
75
|
+
set_module_context(tags)
|
76
|
+
set_suite_context(tags, name: test_suite_name)
|
77
|
+
|
78
|
+
tags[Ext::Test::TAG_NAME] = test_name
|
79
|
+
tags[Ext::Test::TAG_TYPE] ||= Ext::Test::Type::TEST
|
80
|
+
|
81
|
+
span_options = build_tracing_span_options(
|
82
|
+
service,
|
83
|
+
Ext::AppTypes::TYPE_TEST,
|
84
|
+
# :resource is needed for the agent APM protocol to work correctly (for older agent versions)
|
85
|
+
# :continue_from is required to start a new trace for each test
|
86
|
+
{resource: test_name, continue_from: Datadog::Tracing::TraceDigest.new}
|
87
|
+
)
|
88
|
+
|
89
|
+
if block
|
90
|
+
start_datadog_tracer_span(test_name, span_options) do |tracer_span|
|
91
|
+
test = build_test(tracer_span, tags)
|
92
|
+
|
93
|
+
@local_context.activate_test(test) do
|
94
|
+
block.call(test)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
else
|
98
|
+
tracer_span = start_datadog_tracer_span(test_name, span_options)
|
99
|
+
test = build_test(tracer_span, tags)
|
100
|
+
@local_context.activate_test(test)
|
101
|
+
test
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def trace(span_name, type: "span", tags: {}, &block)
|
106
|
+
span_options = build_tracing_span_options(
|
107
|
+
nil, # service name is completely optional for custom spans
|
108
|
+
type,
|
109
|
+
{resource: span_name}
|
110
|
+
)
|
111
|
+
|
112
|
+
if block
|
113
|
+
start_datadog_tracer_span(span_name, span_options) do |tracer_span|
|
114
|
+
block.call(build_span(tracer_span, tags))
|
115
|
+
end
|
116
|
+
else
|
117
|
+
tracer_span = start_datadog_tracer_span(span_name, span_options)
|
118
|
+
|
119
|
+
build_span(tracer_span, tags)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def active_span
|
124
|
+
tracer_span = Datadog::Tracing.active_span
|
125
|
+
Span.new(tracer_span) if tracer_span
|
126
|
+
end
|
127
|
+
|
128
|
+
def active_test
|
129
|
+
@local_context.active_test
|
130
|
+
end
|
131
|
+
|
132
|
+
def active_test_session
|
133
|
+
@global_context.active_test_session
|
134
|
+
end
|
135
|
+
|
136
|
+
def active_test_module
|
137
|
+
@global_context.active_test_module
|
138
|
+
end
|
139
|
+
|
140
|
+
def active_test_suite(test_suite_name)
|
141
|
+
@global_context.active_test_suite(test_suite_name)
|
142
|
+
end
|
143
|
+
|
144
|
+
def single_active_test_suite
|
145
|
+
@global_context.fetch_single_test_suite
|
146
|
+
end
|
147
|
+
|
148
|
+
def deactivate_test
|
149
|
+
@local_context.deactivate_test
|
150
|
+
end
|
151
|
+
|
152
|
+
def deactivate_test_session
|
153
|
+
@global_context.deactivate_test_session!
|
154
|
+
end
|
155
|
+
|
156
|
+
def deactivate_test_module
|
157
|
+
@global_context.deactivate_test_module!
|
158
|
+
end
|
159
|
+
|
160
|
+
def deactivate_test_suite(test_suite_name)
|
161
|
+
@global_context.deactivate_test_suite!(test_suite_name)
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
# BUILDING DOMAIN MODELS
|
167
|
+
def build_test_session(tracer_span, tags)
|
168
|
+
test_session = TestSession.new(tracer_span)
|
169
|
+
set_initial_tags(test_session, tags)
|
170
|
+
test_session
|
171
|
+
end
|
172
|
+
|
173
|
+
def build_test_module(tracer_span, tags)
|
174
|
+
test_module = TestModule.new(tracer_span)
|
175
|
+
set_initial_tags(test_module, tags)
|
176
|
+
test_module
|
177
|
+
end
|
178
|
+
|
179
|
+
def build_test_suite(tracer_span, tags)
|
180
|
+
test_suite = TestSuite.new(tracer_span)
|
181
|
+
set_initial_tags(test_suite, tags)
|
182
|
+
test_suite
|
183
|
+
end
|
184
|
+
|
185
|
+
def build_test(tracer_span, tags)
|
186
|
+
test = Test.new(tracer_span)
|
187
|
+
set_initial_tags(test, tags)
|
188
|
+
test
|
189
|
+
end
|
190
|
+
|
191
|
+
def build_span(tracer_span, tags)
|
192
|
+
span = Span.new(tracer_span)
|
193
|
+
set_initial_tags(span, tags)
|
194
|
+
span
|
195
|
+
end
|
196
|
+
|
197
|
+
# TAGGING
|
198
|
+
def set_initial_tags(ci_span, tags)
|
199
|
+
@environment_tags ||= Ext::Environment.tags(ENV).freeze
|
200
|
+
|
201
|
+
ci_span.set_default_tags
|
202
|
+
ci_span.set_environment_runtime_tags
|
203
|
+
|
204
|
+
ci_span.set_tags(tags)
|
205
|
+
ci_span.set_tags(@environment_tags)
|
206
|
+
end
|
207
|
+
|
208
|
+
# PROPAGATING CONTEXT FROM TOP-LEVEL TO THE LOWER LEVELS
|
209
|
+
def set_inherited_globals(tags)
|
210
|
+
# this code achieves the same as @global_context.inheritable_session_tags.merge(tags)
|
211
|
+
# but without allocating a new hash
|
212
|
+
@global_context.inheritable_session_tags.each do |key, value|
|
213
|
+
tags[key] = value unless tags.key?(key)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def set_session_context(tags, test_session = nil)
|
218
|
+
test_session ||= active_test_session
|
219
|
+
tags[Ext::Test::TAG_TEST_SESSION_ID] = test_session.id.to_s if test_session
|
220
|
+
end
|
221
|
+
|
222
|
+
def set_module_context(tags, test_module = nil)
|
223
|
+
test_module ||= active_test_module
|
224
|
+
if test_module
|
225
|
+
tags[Ext::Test::TAG_TEST_MODULE_ID] = test_module.id.to_s
|
226
|
+
tags[Ext::Test::TAG_MODULE] = test_module.name
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def set_suite_context(tags, span: nil, name: nil)
|
231
|
+
return if span.nil? && name.nil?
|
232
|
+
|
233
|
+
test_suite = span || active_test_suite(name)
|
234
|
+
|
235
|
+
if test_suite
|
236
|
+
tags[Ext::Test::TAG_TEST_SUITE_ID] = test_suite.id.to_s
|
237
|
+
tags[Ext::Test::TAG_SUITE] = test_suite.name
|
238
|
+
else
|
239
|
+
tags[Ext::Test::TAG_SUITE] = name
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# INTERACTIONS WITH TRACING
|
244
|
+
def start_datadog_tracer_span(span_name, span_options, &block)
|
245
|
+
if block
|
246
|
+
Datadog::Tracing.trace(span_name, **span_options) do |tracer_span, trace|
|
247
|
+
set_trace_origin(trace)
|
248
|
+
|
249
|
+
yield tracer_span
|
250
|
+
end
|
251
|
+
else
|
252
|
+
tracer_span = Datadog::Tracing.trace(span_name, **span_options)
|
253
|
+
trace = Datadog::Tracing.active_trace
|
254
|
+
set_trace_origin(trace)
|
255
|
+
|
256
|
+
tracer_span
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Sets trace's origin to ciapp-test because tracing requires so
|
261
|
+
def set_trace_origin(trace)
|
262
|
+
trace&.origin = Ext::Test::CONTEXT_ORIGIN
|
263
|
+
end
|
264
|
+
|
265
|
+
def build_tracing_span_options(service, type, other_options = {})
|
266
|
+
other_options[:service] = service || @global_context.service
|
267
|
+
other_options[:type] = type
|
268
|
+
|
269
|
+
other_options
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../ext/app_types"
|
4
|
+
require_relative "../ext/environment"
|
5
|
+
require_relative "../ext/telemetry"
|
6
|
+
require_relative "../ext/test"
|
7
|
+
require_relative "../utils/telemetry"
|
8
|
+
|
9
|
+
module Datadog
|
10
|
+
module CI
|
11
|
+
module TestVisibility
|
12
|
+
# Telemetry for test visibility
|
13
|
+
module Telemetry
|
14
|
+
SPAN_TYPE_TO_TELEMETRY_EVENT_TYPE = {
|
15
|
+
Ext::AppTypes::TYPE_TEST => Ext::Telemetry::EventType::TEST,
|
16
|
+
Ext::AppTypes::TYPE_TEST_SUITE => Ext::Telemetry::EventType::SUITE,
|
17
|
+
Ext::AppTypes::TYPE_TEST_MODULE => Ext::Telemetry::EventType::MODULE,
|
18
|
+
Ext::AppTypes::TYPE_TEST_SESSION => Ext::Telemetry::EventType::SESSION
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
def self.event_created(span)
|
22
|
+
Utils::Telemetry.inc(Ext::Telemetry::METRIC_EVENT_CREATED, 1, event_tags_from_span(span))
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.event_finished(span)
|
26
|
+
tags = event_tags_from_span(span)
|
27
|
+
add_browser_tags!(span, tags)
|
28
|
+
Utils::Telemetry.inc(Ext::Telemetry::METRIC_EVENT_FINISHED, 1, tags)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.test_session_started(test_session)
|
32
|
+
Utils::Telemetry.inc(
|
33
|
+
Ext::Telemetry::METRIC_TEST_SESSION,
|
34
|
+
1,
|
35
|
+
{
|
36
|
+
Ext::Telemetry::TAG_AUTO_INJECTED => "false", # ruby doesn't support auto injection yet
|
37
|
+
Ext::Telemetry::TAG_PROVIDER =>
|
38
|
+
test_session.get_tag(Ext::Environment::TAG_PROVIDER_NAME) ||
|
39
|
+
Ext::Telemetry::Provider::UNSUPPORTED
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.event_tags_from_span(span)
|
45
|
+
# base tags for span
|
46
|
+
# @type var tags: Hash[String, String]
|
47
|
+
tags = {
|
48
|
+
Ext::Telemetry::TAG_EVENT_TYPE => SPAN_TYPE_TO_TELEMETRY_EVENT_TYPE.fetch(span.type, "unknown"),
|
49
|
+
Ext::Telemetry::TAG_TEST_FRAMEWORK => span.get_tag(Ext::Test::TAG_FRAMEWORK)
|
50
|
+
}
|
51
|
+
|
52
|
+
# ci provider tag
|
53
|
+
tags[Ext::Telemetry::TAG_IS_UNSUPPORTED_CI] = "true" if span.get_tag(Ext::Environment::TAG_PROVIDER_NAME).nil?
|
54
|
+
|
55
|
+
# codeowner tag
|
56
|
+
tags[Ext::Telemetry::TAG_HAS_CODEOWNER] = "true" if span.get_tag(Ext::Test::TAG_CODEOWNERS)
|
57
|
+
|
58
|
+
tags
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.add_browser_tags!(span, tags)
|
62
|
+
tags[Ext::Telemetry::TAG_IS_RUM] = "true" if span.get_tag(Ext::Test::TAG_IS_RUM_ACTIVE)
|
63
|
+
browser_driver = span.get_tag(Ext::Test::TAG_BROWSER_DRIVER)
|
64
|
+
tags[Ext::Telemetry::TAG_BROWSER_DRIVER] = browser_driver if browser_driver
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -3,8 +3,10 @@
|
|
3
3
|
require "datadog/core/environment/identity"
|
4
4
|
|
5
5
|
require_relative "serializers/factories/test_level"
|
6
|
+
require_relative "../ext/telemetry"
|
6
7
|
require_relative "../ext/transport"
|
7
8
|
require_relative "../transport/event_platform_transport"
|
9
|
+
require_relative "../transport/telemetry"
|
8
10
|
|
9
11
|
module Datadog
|
10
12
|
module CI
|
@@ -31,6 +33,10 @@ module Datadog
|
|
31
33
|
|
32
34
|
private
|
33
35
|
|
36
|
+
def telemetry_endpoint_tag
|
37
|
+
Ext::Telemetry::Endpoint::TEST_CYCLE
|
38
|
+
end
|
39
|
+
|
34
40
|
def send_payload(encoded_payload)
|
35
41
|
api.citestcycle_request(
|
36
42
|
path: Datadog::CI::Ext::Transport::TEST_VISIBILITY_INTAKE_PATH,
|
@@ -46,21 +52,14 @@ module Datadog
|
|
46
52
|
|
47
53
|
def encode_span(trace, span)
|
48
54
|
serializer = serializers_factory.serializer(trace, span, options: {itr_correlation_id: itr&.correlation_id})
|
49
|
-
|
50
55
|
if serializer.valid?
|
51
56
|
encoded = encoder.encode(serializer)
|
52
|
-
|
53
|
-
if encoded.size > max_payload_size
|
54
|
-
# This single event is too large, we can't flush it
|
55
|
-
Datadog.logger.warn("Dropping test event. Payload too large: '#{span.inspect}'")
|
56
|
-
Datadog.logger.warn(encoded)
|
57
|
-
|
58
|
-
return nil
|
59
|
-
end
|
57
|
+
return nil if event_too_large?(span, encoded)
|
60
58
|
|
61
59
|
encoded
|
62
60
|
else
|
63
61
|
Datadog.logger.warn("Invalid event skipped: #{serializer} Errors: #{serializer.validation_errors}")
|
62
|
+
CI::Transport::Telemetry.endpoint_payload_dropped(1, endpoint: telemetry_endpoint_tag)
|
64
63
|
nil
|
65
64
|
end
|
66
65
|
end
|
@@ -3,7 +3,9 @@
|
|
3
3
|
require "datadog/core/transport/response"
|
4
4
|
require "datadog/core/transport/ext"
|
5
5
|
|
6
|
+
require_relative "net_http_client"
|
6
7
|
require_relative "../gzip"
|
8
|
+
require_relative "../../ext/telemetry"
|
7
9
|
require_relative "../../ext/transport"
|
8
10
|
|
9
11
|
module Datadog
|
@@ -26,7 +28,7 @@ module Datadog
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def open(&block)
|
29
|
-
req =
|
31
|
+
req = NetHttpClient.original_net_http.new(hostname, port)
|
30
32
|
|
31
33
|
req.use_ssl = ssl
|
32
34
|
req.open_timeout = req.read_timeout = timeout
|
@@ -63,6 +65,8 @@ module Datadog
|
|
63
65
|
include Datadog::Core::Transport::Response
|
64
66
|
|
65
67
|
attr_reader :http_response
|
68
|
+
# Stats for telemetry
|
69
|
+
attr_accessor :duration_ms, :request_compressed, :request_size
|
66
70
|
|
67
71
|
def initialize(http_response)
|
68
72
|
@http_response = http_response
|
@@ -77,6 +81,10 @@ module Datadog
|
|
77
81
|
@decompressed_payload = Gzip.decompress(http_response.body)
|
78
82
|
end
|
79
83
|
|
84
|
+
def response_size
|
85
|
+
http_response.body.bytesize
|
86
|
+
end
|
87
|
+
|
80
88
|
def header(name)
|
81
89
|
http_response[name]
|
82
90
|
end
|
@@ -86,7 +94,10 @@ module Datadog
|
|
86
94
|
end
|
87
95
|
|
88
96
|
def ok?
|
89
|
-
code
|
97
|
+
http_code = code
|
98
|
+
return false if http_code.nil?
|
99
|
+
|
100
|
+
http_code.between?(200, 299)
|
90
101
|
end
|
91
102
|
|
92
103
|
def unsupported?
|
@@ -98,11 +109,17 @@ module Datadog
|
|
98
109
|
end
|
99
110
|
|
100
111
|
def client_error?
|
101
|
-
code
|
112
|
+
http_code = code
|
113
|
+
return false if http_code.nil?
|
114
|
+
|
115
|
+
http_code.between?(400, 499)
|
102
116
|
end
|
103
117
|
|
104
118
|
def server_error?
|
105
|
-
code
|
119
|
+
http_code = code
|
120
|
+
return false if http_code.nil?
|
121
|
+
|
122
|
+
http_code.between?(500, 599)
|
106
123
|
end
|
107
124
|
|
108
125
|
def gzipped_content?
|
@@ -119,17 +136,31 @@ module Datadog
|
|
119
136
|
first_bytes.b == Ext::Transport::GZIP_MAGIC_NUMBER
|
120
137
|
end
|
121
138
|
|
122
|
-
def
|
123
|
-
|
139
|
+
def error
|
140
|
+
nil
|
124
141
|
end
|
125
|
-
end
|
126
142
|
|
127
|
-
|
143
|
+
def telemetry_error_type
|
144
|
+
return nil if ok?
|
145
|
+
|
146
|
+
case error
|
147
|
+
when nil
|
148
|
+
Ext::Telemetry::ErrorType::STATUS_CODE
|
149
|
+
when Timeout::Error
|
150
|
+
Ext::Telemetry::ErrorType::TIMEOUT
|
151
|
+
else
|
152
|
+
Ext::Telemetry::ErrorType::NETWORK
|
153
|
+
end
|
154
|
+
end
|
128
155
|
|
129
|
-
|
130
|
-
|
156
|
+
# compatibility with Datadog::Tracing transport layer
|
157
|
+
def trace_count
|
158
|
+
0
|
159
|
+
end
|
131
160
|
|
132
|
-
|
161
|
+
def inspect
|
162
|
+
"#{super}, http_response:#{http_response}"
|
163
|
+
end
|
133
164
|
end
|
134
165
|
end
|
135
166
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module Transport
|
6
|
+
module Adapters
|
7
|
+
module NetHttpClient
|
8
|
+
def self.original_net_http
|
9
|
+
return ::Net::HTTP unless defined?(WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP)
|
10
|
+
|
11
|
+
WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "net_http_client"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
module Transport
|
8
|
+
module Adapters
|
9
|
+
module TelemetryWebmockSafeAdapter
|
10
|
+
def self.included(base)
|
11
|
+
base.prepend(InstanceMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
module InstanceMethods
|
15
|
+
def open(&block)
|
16
|
+
req = NetHttpClient.original_net_http.new(@hostname, @port)
|
17
|
+
|
18
|
+
req.use_ssl = @ssl
|
19
|
+
req.open_timeout = req.read_timeout = @timeout
|
20
|
+
|
21
|
+
req.start(&block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -5,6 +5,8 @@ require "msgpack"
|
|
5
5
|
require "datadog/core/encoding"
|
6
6
|
require "datadog/core/chunker"
|
7
7
|
|
8
|
+
require_relative "telemetry"
|
9
|
+
|
8
10
|
module Datadog
|
9
11
|
module CI
|
10
12
|
module Transport
|
@@ -24,12 +26,19 @@ module Datadog
|
|
24
26
|
|
25
27
|
Datadog.logger.debug { "[#{self.class.name}] Sending #{events.count} events..." }
|
26
28
|
|
27
|
-
encoded_events =
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
encoded_events = []
|
30
|
+
# @type var serialization_duration_ms: Float
|
31
|
+
serialization_duration_ms = Core::Utils::Time.measure(:float_millisecond) do
|
32
|
+
encoded_events = encode_events(events)
|
33
|
+
if encoded_events.empty?
|
34
|
+
Datadog.logger.debug { "[#{self.class.name}] Empty encoded events list, skipping send" }
|
35
|
+
return []
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
39
|
+
Telemetry.events_enqueued_for_serialization(encoded_events.count)
|
40
|
+
Telemetry.endpoint_payload_serialization_ms(serialization_duration_ms, endpoint: telemetry_endpoint_tag)
|
41
|
+
|
33
42
|
responses = []
|
34
43
|
|
35
44
|
Datadog::Core::Chunker.chunk_by_size(encoded_events, max_payload_size).map do |chunk|
|
@@ -37,9 +46,28 @@ module Datadog
|
|
37
46
|
Datadog.logger.debug do
|
38
47
|
"[#{self.class.name}] Send chunk of #{chunk.count} events; payload size #{encoded_payload.size}"
|
39
48
|
end
|
49
|
+
Telemetry.endpoint_payload_events_count(chunk.count, endpoint: telemetry_endpoint_tag)
|
40
50
|
|
41
51
|
response = send_payload(encoded_payload)
|
42
52
|
|
53
|
+
Telemetry.endpoint_payload_requests(
|
54
|
+
1,
|
55
|
+
endpoint: telemetry_endpoint_tag, compressed: response.request_compressed
|
56
|
+
)
|
57
|
+
Telemetry.endpoint_payload_requests_ms(response.duration_ms, endpoint: telemetry_endpoint_tag)
|
58
|
+
Telemetry.endpoint_payload_bytes(response.request_size, endpoint: telemetry_endpoint_tag)
|
59
|
+
|
60
|
+
# HTTP layer could send events and exhausted retries (if any)
|
61
|
+
unless response.ok?
|
62
|
+
Telemetry.endpoint_payload_dropped(chunk.count, endpoint: telemetry_endpoint_tag)
|
63
|
+
Telemetry.endpoint_payload_requests_errors(
|
64
|
+
1,
|
65
|
+
endpoint: telemetry_endpoint_tag,
|
66
|
+
error_type: response.telemetry_error_type,
|
67
|
+
status_code: response.code
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
43
71
|
responses << response
|
44
72
|
end
|
45
73
|
|
@@ -48,6 +76,10 @@ module Datadog
|
|
48
76
|
|
49
77
|
private
|
50
78
|
|
79
|
+
def telemetry_endpoint_tag
|
80
|
+
raise NotImplementedError, "must be implemented by the subclass"
|
81
|
+
end
|
82
|
+
|
51
83
|
def encoder
|
52
84
|
Datadog::Core::Encoding::MsgpackEncoder
|
53
85
|
end
|
@@ -65,9 +97,14 @@ module Datadog
|
|
65
97
|
return false unless encoded_event.size > max_payload_size
|
66
98
|
|
67
99
|
# This single event is too large, we can't flush it
|
68
|
-
Datadog.logger.warn(
|
100
|
+
Datadog.logger.warn(
|
101
|
+
"[#{self.class.name}] Dropping test visibility event for endpoint [#{telemetry_endpoint_tag}]. " \
|
102
|
+
"Payload too large: '#{event.inspect}'"
|
103
|
+
)
|
69
104
|
Datadog.logger.warn(encoded_event)
|
70
105
|
|
106
|
+
Telemetry.endpoint_payload_dropped(1, endpoint: telemetry_endpoint_tag)
|
107
|
+
|
71
108
|
true
|
72
109
|
end
|
73
110
|
|