datadog-ci 1.14.0 → 1.16.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -2
  3. data/lib/datadog/ci/async_writer.rb +112 -0
  4. data/lib/datadog/ci/configuration/components.rb +36 -6
  5. data/lib/datadog/ci/configuration/settings.rb +17 -0
  6. data/lib/datadog/ci/contrib/activesupport/configuration/settings.rb +25 -0
  7. data/lib/datadog/ci/contrib/activesupport/ext.rb +14 -0
  8. data/lib/datadog/ci/contrib/activesupport/integration.rb +43 -0
  9. data/lib/datadog/ci/contrib/activesupport/logs_formatter.rb +41 -0
  10. data/lib/datadog/ci/contrib/activesupport/patcher.rb +50 -0
  11. data/lib/datadog/ci/contrib/lograge/configuration/settings.rb +25 -0
  12. data/lib/datadog/ci/contrib/lograge/ext.rb +14 -0
  13. data/lib/datadog/ci/contrib/lograge/integration.rb +43 -0
  14. data/lib/datadog/ci/contrib/lograge/log_subscriber.rb +41 -0
  15. data/lib/datadog/ci/contrib/lograge/patcher.rb +32 -0
  16. data/lib/datadog/ci/contrib/minitest/runner.rb +4 -1
  17. data/lib/datadog/ci/contrib/parallel_tests/cli.rb +84 -0
  18. data/lib/datadog/ci/contrib/parallel_tests/configuration/settings.rb +32 -0
  19. data/lib/datadog/ci/contrib/parallel_tests/ext.rb +16 -0
  20. data/lib/datadog/ci/contrib/parallel_tests/integration.rb +42 -0
  21. data/lib/datadog/ci/contrib/parallel_tests/patcher.rb +23 -0
  22. data/lib/datadog/ci/contrib/rspec/example.rb +7 -0
  23. data/lib/datadog/ci/contrib/rspec/example_group.rb +18 -8
  24. data/lib/datadog/ci/contrib/rspec/helpers.rb +18 -0
  25. data/lib/datadog/ci/contrib/rspec/runner.rb +2 -0
  26. data/lib/datadog/ci/contrib/semantic_logger/configuration/settings.rb +25 -0
  27. data/lib/datadog/ci/contrib/semantic_logger/ext.rb +14 -0
  28. data/lib/datadog/ci/contrib/semantic_logger/integration.rb +42 -0
  29. data/lib/datadog/ci/contrib/semantic_logger/logger.rb +32 -0
  30. data/lib/datadog/ci/contrib/semantic_logger/patcher.rb +32 -0
  31. data/lib/datadog/ci/ext/settings.rb +3 -0
  32. data/lib/datadog/ci/ext/test.rb +23 -2
  33. data/lib/datadog/ci/ext/transport.rb +2 -0
  34. data/lib/datadog/ci/git/local_repository.rb +1 -1
  35. data/lib/datadog/ci/git/tree_uploader.rb +9 -0
  36. data/lib/datadog/ci/logs/component.rb +46 -0
  37. data/lib/datadog/ci/logs/transport.rb +73 -0
  38. data/lib/datadog/ci/readonly_test_module.rb +28 -0
  39. data/lib/datadog/ci/readonly_test_session.rb +31 -0
  40. data/lib/datadog/ci/remote/component.rb +43 -16
  41. data/lib/datadog/ci/test.rb +10 -0
  42. data/lib/datadog/ci/test_management/component.rb +34 -1
  43. data/lib/datadog/ci/test_management/tests_properties.rb +2 -1
  44. data/lib/datadog/ci/test_optimisation/component.rb +31 -5
  45. data/lib/datadog/ci/test_retries/driver/retry_new.rb +1 -1
  46. data/lib/datadog/ci/test_session.rb +7 -1
  47. data/lib/datadog/ci/test_visibility/component.rb +82 -28
  48. data/lib/datadog/ci/test_visibility/context.rb +77 -29
  49. data/lib/datadog/ci/test_visibility/null_component.rb +7 -1
  50. data/lib/datadog/ci/test_visibility/store/{local.rb → fiber_local.rb} +1 -1
  51. data/lib/datadog/ci/test_visibility/store/{global.rb → process.rb} +23 -18
  52. data/lib/datadog/ci/test_visibility/transport.rb +1 -2
  53. data/lib/datadog/ci/transport/api/agentless.rb +8 -1
  54. data/lib/datadog/ci/transport/api/base.rb +4 -0
  55. data/lib/datadog/ci/transport/api/builder.rb +5 -1
  56. data/lib/datadog/ci/transport/api/evp_proxy.rb +4 -0
  57. data/lib/datadog/ci/transport/http.rb +1 -1
  58. data/lib/datadog/ci/utils/file_storage.rb +57 -0
  59. data/lib/datadog/ci/utils/stateful.rb +52 -0
  60. data/lib/datadog/ci/version.rb +1 -1
  61. data/lib/datadog/ci.rb +7 -3
  62. metadata +32 -6
  63. data/lib/datadog/ci/test_optimisation/coverage/writer.rb +0 -116
  64. data/lib/datadog/ci/test_visibility/capabilities.rb +0 -36
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../ext/test"
4
+ require_relative "../rspec/ext"
5
+
6
+ module Datadog
7
+ module CI
8
+ module Contrib
9
+ module ParallelTests
10
+ module CLI
11
+ def self.included(base)
12
+ base.prepend(InstanceMethods)
13
+ end
14
+
15
+ module InstanceMethods
16
+ def run_tests_in_parallel(num_processes, options)
17
+ # only rspec runner is supported for now
18
+ return super if @runner != ::ParallelTests::RSpec::Runner
19
+
20
+ begin
21
+ test_session = test_visibility_component.start_test_session(
22
+ tags: {
23
+ CI::Ext::Test::TAG_FRAMEWORK => CI::Contrib::RSpec::Ext::FRAMEWORK,
24
+ CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_extract_rspec_version
25
+ },
26
+ service: datadog_configuration[:service_name],
27
+ estimated_total_tests_count: 10_000, # temporary value, updated by child processes
28
+ distributed: true
29
+ )
30
+ test_module = test_visibility_component.start_test_module("rspec")
31
+
32
+ options[:env] ||= {}
33
+ options[:env][CI::Ext::Settings::ENV_TEST_VISIBILITY_DRB_SERVER_URI] = test_visibility_component.context_service_uri
34
+
35
+ super
36
+ ensure
37
+ test_module&.finish
38
+ test_session&.finish
39
+ end
40
+ end
41
+
42
+ def any_test_failed?(test_results)
43
+ res = super
44
+
45
+ test_session = test_visibility_component.active_test_session
46
+ test_module = test_visibility_component.active_test_module
47
+ if res
48
+ test_module&.failed!
49
+ test_session&.failed!
50
+ else
51
+ test_module&.passed!
52
+ test_session&.passed!
53
+ end
54
+
55
+ res
56
+ end
57
+
58
+ def datadog_extract_rspec_version
59
+ # Try to find either 'rspec' or 'rspec-core' gem
60
+ if Gem.loaded_specs["rspec"]
61
+ Gem.loaded_specs["rspec"].version.to_s
62
+ elsif Gem.loaded_specs["rspec-core"]
63
+ Gem.loaded_specs["rspec-core"].version.to_s
64
+ else
65
+ "0.0.0"
66
+ end
67
+ rescue => e
68
+ Datadog.logger.debug("Error extracting RSpec version: #{e.class.name} - #{e.message}")
69
+ "0.0.0"
70
+ end
71
+
72
+ def datadog_configuration
73
+ Datadog.configuration.ci[:paralleltests]
74
+ end
75
+
76
+ def test_visibility_component
77
+ Datadog.send(:components).test_visibility
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../ext"
4
+ require_relative "../../settings"
5
+ require_relative "../../../utils/configuration"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module ParallelTests
11
+ module Configuration
12
+ # Custom settings for the ParallelTests integration
13
+ # @public_api
14
+ class Settings < Datadog::CI::Contrib::Settings
15
+ option :enabled do |o|
16
+ o.type :bool
17
+ o.env Ext::ENV_ENABLED
18
+ o.default true
19
+ end
20
+
21
+ option :service_name do |o|
22
+ o.type :string
23
+ o.default do
24
+ Utils::Configuration.fetch_service_name(Ext::DEFAULT_SERVICE_NAME)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module Contrib
6
+ module ParallelTests
7
+ # Datadog ParallelTests integration constants
8
+ module Ext
9
+ ENV_ENABLED = "DD_TRACE_PARALLEL_TESTS_ENABLED"
10
+
11
+ DEFAULT_SERVICE_NAME = "parallel_tests"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../integration"
4
+ require_relative "configuration/settings"
5
+ require_relative "patcher"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module ParallelTests
11
+ # Description of ParallelTests integration
12
+ class Integration < Datadog::CI::Contrib::Integration
13
+ MINIMUM_VERSION = Gem::Version.new("4.0")
14
+
15
+ def version
16
+ Gem.loaded_specs["parallel_tests"]&.version
17
+ end
18
+
19
+ def loaded?
20
+ !defined?(::ParallelTests).nil? && !defined?(::ParallelTests::CLI).nil?
21
+ end
22
+
23
+ def compatible?
24
+ super && version >= MINIMUM_VERSION
25
+ end
26
+
27
+ def late_instrument?
28
+ false
29
+ end
30
+
31
+ def new_configuration
32
+ Configuration::Settings.new
33
+ end
34
+
35
+ def patcher
36
+ Patcher
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "cli"
4
+ require_relative "../patcher"
5
+
6
+ module Datadog
7
+ module CI
8
+ module Contrib
9
+ module ParallelTests
10
+ # Patcher enables patching of parallel_tests module
11
+ module Patcher
12
+ include Datadog::CI::Contrib::Patcher
13
+
14
+ module_function
15
+
16
+ def patch
17
+ ::ParallelTests::CLI.include(CLI)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -97,6 +97,13 @@ module Datadog
97
97
  )
98
98
  end
99
99
 
100
+ def datadog_fqn_test_id
101
+ @datadog_fqn_test_id ||= Utils::TestRun.datadog_test_id(
102
+ datadog_test_name,
103
+ datadog_test_suite_name
104
+ )
105
+ end
106
+
100
107
  def datadog_unskippable?
101
108
  !!metadata[CI::Ext::Test::ITR_UNSKIPPABLE_OPTION]
102
109
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../../ext/test"
4
4
  require_relative "ext"
5
+ require_relative "helpers"
5
6
 
6
7
  module Datadog
7
8
  module CI
@@ -28,13 +29,17 @@ module Datadog
28
29
  return super unless top_level?
29
30
 
30
31
  suite_name = "#{description} at #{file_path}"
31
- test_suite = test_visibility_component&.start_test_suite(
32
- suite_name,
33
- tags: {
34
- CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(metadata[:file_path]),
35
- CI::Ext::Test::TAG_SOURCE_START => metadata[:line_number].to_s
36
- }
37
- )
32
+ suite_tags = {
33
+ CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(metadata[:file_path]),
34
+ CI::Ext::Test::TAG_SOURCE_START => metadata[:line_number].to_s
35
+ }
36
+
37
+ test_suite =
38
+ test_visibility_component&.start_test_suite(
39
+ suite_name,
40
+ tags: suite_tags,
41
+ service: datadog_configuration[:service_name]
42
+ )
38
43
 
39
44
  success = super
40
45
  return success unless test_suite
@@ -56,7 +61,8 @@ module Datadog
56
61
 
57
62
  def all_examples_skipped_by_datadog?
58
63
  descendant_filtered_examples.all? do |example|
59
- !example.datadog_unskippable? && test_optimisation_component&.skippable?(example)
64
+ !example.datadog_unskippable? && test_optimisation_component&.skippable?(example.datadog_test_id) &&
65
+ !test_management_component&.attempt_to_fix?(example.datadog_fqn_test_id)
60
66
  end
61
67
  end
62
68
 
@@ -71,6 +77,10 @@ module Datadog
71
77
  def test_optimisation_component
72
78
  Datadog.send(:components).test_optimisation
73
79
  end
80
+
81
+ def test_management_component
82
+ Datadog.send(:components).test_management
83
+ end
74
84
  end
75
85
  end
76
86
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module Contrib
6
+ module RSpec
7
+ # Helper methods for RSpec instrumentation
8
+ module Helpers
9
+ module_function
10
+
11
+ def parallel_tests?
12
+ !!ENV.fetch("TEST_ENV_NUMBER", nil) && !!ENV.fetch("PARALLEL_TEST_GROUPS", nil)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -3,6 +3,7 @@
3
3
  require_relative "../../ext/test"
4
4
  require_relative "../instrumentation"
5
5
  require_relative "ext"
6
+ require_relative "helpers"
6
7
 
7
8
  module Datadog
8
9
  module CI
@@ -32,6 +33,7 @@ module Datadog
32
33
 
33
34
  result = super
34
35
  return result unless test_module && test_session
36
+ return result if Helpers.parallel_tests?
35
37
 
36
38
  if result != 0
37
39
  test_module.failed!
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../ext"
4
+ require_relative "../../settings"
5
+ require_relative "../../../utils/configuration"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module SemanticLogger
11
+ module Configuration
12
+ # Custom settings for the SemanticLogger integration
13
+ # @public_api
14
+ class Settings < Datadog::CI::Contrib::Settings
15
+ option :enabled do |o|
16
+ o.type :bool
17
+ o.env Ext::ENV_ENABLED
18
+ o.default true
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module Contrib
6
+ module SemanticLogger
7
+ # Datadog SemanticLogger integration constants
8
+ module Ext
9
+ ENV_ENABLED = "DD_CI_SEMANTIC_LOGGER_ENABLED"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../integration"
4
+ require_relative "configuration/settings"
5
+ require_relative "patcher"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module SemanticLogger
11
+ # Description of SemanticLogger integration
12
+ class Integration < Datadog::CI::Contrib::Integration
13
+ MINIMUM_VERSION = Gem::Version.new("4.0")
14
+
15
+ def version
16
+ Gem.loaded_specs["semantic_logger"]&.version
17
+ end
18
+
19
+ def loaded?
20
+ !defined?(::SemanticLogger).nil? && !defined?(::SemanticLogger::Logger).nil?
21
+ end
22
+
23
+ def compatible?
24
+ super && version >= MINIMUM_VERSION
25
+ end
26
+
27
+ def late_instrument?
28
+ true
29
+ end
30
+
31
+ def new_configuration
32
+ Configuration::Settings.new
33
+ end
34
+
35
+ def patcher
36
+ Patcher
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module Contrib
6
+ module SemanticLogger
7
+ module Logger
8
+ def self.included(base)
9
+ base.prepend(InstanceMethods)
10
+ end
11
+
12
+ module InstanceMethods
13
+ def log(log, message = nil, progname = nil, &block)
14
+ return super unless log.is_a?(::SemanticLogger::Log)
15
+ return super unless datadog_logs_component.enabled
16
+
17
+ result = super
18
+
19
+ datadog_logs_component.write(log.to_h.clone)
20
+
21
+ result
22
+ end
23
+
24
+ def datadog_logs_component
25
+ Datadog.send(:components).agentless_logs_submission
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../patcher"
4
+ require_relative "logger"
5
+
6
+ module Datadog
7
+ module CI
8
+ module Contrib
9
+ module SemanticLogger
10
+ # Patcher enables patching of semantic_logger module
11
+ module Patcher
12
+ include Datadog::CI::Contrib::Patcher
13
+
14
+ module_function
15
+
16
+ def patch
17
+ unless datadog_logs_component.enabled
18
+ Datadog.logger.debug("Datadog logs submission is disabled, skipping semantic_logger patching")
19
+ return
20
+ end
21
+
22
+ ::SemanticLogger::Logger.include(Logger)
23
+ end
24
+
25
+ def datadog_logs_component
26
+ Datadog.send(:components).agentless_logs_submission
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -22,6 +22,9 @@ module Datadog
22
22
  ENV_TEST_SESSION_NAME = "DD_TEST_SESSION_NAME"
23
23
  ENV_TEST_MANAGEMENT_ENABLED = "DD_TEST_MANAGEMENT_ENABLED"
24
24
  ENV_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES = "DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES"
25
+ ENV_TEST_VISIBILITY_DRB_SERVER_URI = "DD_TEST_VISIBILITY_DRB_SERVER_URI"
26
+ ENV_AGENTLESS_LOGS_SUBMISSION_ENABLED = "DD_AGENTLESS_LOG_SUBMISSION_ENABLED"
27
+ ENV_AGENTLESS_LOGS_SUBMISSION_URL = "DD_AGENTLESS_LOG_SUBMISSION_URL"
25
28
 
26
29
  # Source: https://docs.datadoghq.com/getting_started/site/
27
30
  DD_SITE_ALLOWLIST = %w[
@@ -86,6 +86,26 @@ module Datadog
86
86
  TAG_TEST_MANAGEMENT_QUARANTINE = "_dd.library_capabilities.test_management.quarantine"
87
87
  TAG_TEST_MANAGEMENT_DISABLE = "_dd.library_capabilities.test_management.disable"
88
88
  TAG_TEST_MANAGEMENT_ATTEMPT_TO_FIX = "_dd.library_capabilities.test_management.attempt_to_fix"
89
+
90
+ # Version numbers for library capabilities
91
+ module Versions
92
+ TEST_IMPACT_ANALYSIS_VERSION = "1"
93
+ EARLY_FLAKE_DETECTION_VERSION = "1"
94
+ AUTO_TEST_RETRIES_VERSION = "1"
95
+ TEST_MANAGEMENT_QUARANTINE_VERSION = "1"
96
+ TEST_MANAGEMENT_DISABLE_VERSION = "1"
97
+ TEST_MANAGEMENT_ATTEMPT_TO_FIX_VERSION = "2"
98
+ end
99
+
100
+ # Map of capabilities to their versions
101
+ CAPABILITY_VERSIONS = {
102
+ TAG_TEST_IMPACT_ANALYSIS => Versions::TEST_IMPACT_ANALYSIS_VERSION,
103
+ TAG_EARLY_FLAKE_DETECTION => Versions::EARLY_FLAKE_DETECTION_VERSION,
104
+ TAG_AUTO_TEST_RETRIES => Versions::AUTO_TEST_RETRIES_VERSION,
105
+ TAG_TEST_MANAGEMENT_QUARANTINE => Versions::TEST_MANAGEMENT_QUARANTINE_VERSION,
106
+ TAG_TEST_MANAGEMENT_DISABLE => Versions::TEST_MANAGEMENT_DISABLE_VERSION,
107
+ TAG_TEST_MANAGEMENT_ATTEMPT_TO_FIX => Versions::TEST_MANAGEMENT_ATTEMPT_TO_FIX_VERSION
108
+ }.freeze
89
109
  end
90
110
 
91
111
  # internal APM tag to mark a span as a test span
@@ -131,9 +151,10 @@ module Datadog
131
151
 
132
152
  # possible reasons why a test was retried
133
153
  module RetryReason
134
- RETRY_NEW = "efd"
135
- RETRY_FAILED = "atr"
154
+ RETRY_DETECT_FLAKY = "early_flake_detection"
155
+ RETRY_FAILED = "auto_test_retries"
136
156
  RETRY_FLAKY_FIXED = "attempt_to_fix"
157
+ RETRY_EXTERNAL = "external"
137
158
  end
138
159
 
139
160
  # possible reasons why a test was skipped
@@ -28,6 +28,8 @@ module Datadog
28
28
  TEST_COVERAGE_INTAKE_HOST_PREFIX = "citestcov-intake"
29
29
  TEST_COVERAGE_INTAKE_PATH = "/api/v2/citestcov"
30
30
 
31
+ LOGS_INTAKE_HOST_PREFIX = "http-intake.logs"
32
+
31
33
  DD_API_HOST_PREFIX = "api"
32
34
 
33
35
  DD_API_SETTINGS_PATH = "/api/v2/libraries/tests/services/setting"
@@ -142,7 +142,7 @@ module Datadog
142
142
  end
143
143
 
144
144
  def self.git_commit_message
145
- exec_git_command("git show -s --format=%s")
145
+ exec_git_command("git log -n 1 --format=%B")
146
146
  rescue => e
147
147
  log_failure(e, "git commit message")
148
148
  nil
@@ -27,6 +27,11 @@ module Datadog
27
27
  return
28
28
  end
29
29
 
30
+ if test_visibility_component.client_process?
31
+ Datadog.logger.debug("Test visibility component is running in client process, aborting git upload")
32
+ return
33
+ end
34
+
30
35
  Datadog.logger.debug { "Uploading git tree for repository #{repository_url}" }
31
36
 
32
37
  latest_commits = LocalRepository.git_commits
@@ -91,6 +96,10 @@ module Datadog
91
96
  backend_commits.include?(commit)
92
97
  end
93
98
  end
99
+
100
+ def test_visibility_component
101
+ Datadog.send(:components).test_visibility
102
+ end
94
103
  end
95
104
  end
96
105
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/core/environment/platform"
4
+
5
+ module Datadog
6
+ module CI
7
+ module Logs
8
+ class Component
9
+ attr_reader :enabled
10
+
11
+ def initialize(enabled:, writer:)
12
+ @enabled = enabled && !writer.nil?
13
+ @writer = writer
14
+ end
15
+
16
+ def write(event)
17
+ return unless enabled
18
+
19
+ add_common_tags!(event)
20
+ @writer&.write(event)
21
+
22
+ nil
23
+ end
24
+
25
+ def shutdown!
26
+ @writer&.stop
27
+ end
28
+
29
+ private
30
+
31
+ def add_common_tags!(event)
32
+ test_session = test_visibility.active_test_session
33
+
34
+ event[:ddsource] ||= "ruby"
35
+ event[:ddtags] ||= "datadog.product:citest"
36
+ event[:service] ||= test_session&.service
37
+ event[:hostname] ||= Datadog::Core::Environment::Platform.hostname
38
+ end
39
+
40
+ def test_visibility
41
+ ::Datadog.send(:components).test_visibility
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ require "datadog/core/chunker"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Logs
10
+ class Transport
11
+ DEFAULT_MAX_PAYLOAD_SIZE = 4.5 * 1024 * 1024
12
+
13
+ attr_reader :api,
14
+ :max_payload_size
15
+
16
+ def initialize(api:, max_payload_size: DEFAULT_MAX_PAYLOAD_SIZE)
17
+ @api = api
18
+ @max_payload_size = max_payload_size
19
+ end
20
+
21
+ def send_events(events)
22
+ return [] if events.nil? || events.empty?
23
+
24
+ Datadog.logger.debug { "[#{self.class.name}] Sending #{events.count} events..." }
25
+
26
+ encoded_events = events.filter_map do |event|
27
+ encoded_event = event.to_json
28
+ if event_too_large?(event, encoded_event)
29
+ next
30
+ end
31
+
32
+ encoded_event
33
+ end
34
+
35
+ responses = []
36
+ Datadog::Core::Chunker.chunk_by_size(encoded_events, max_payload_size).map do |chunk|
37
+ encoded_payload = pack_events(chunk)
38
+ Datadog.logger.debug do
39
+ "[#{self.class.name}] Send chunk of #{chunk.count} events; payload size #{encoded_payload.size}"
40
+ end
41
+
42
+ response = send_payload(encoded_payload)
43
+
44
+ responses << response
45
+ end
46
+
47
+ responses
48
+ end
49
+
50
+ private
51
+
52
+ def pack_events(encoded_events)
53
+ "[#{encoded_events.join(",")}]"
54
+ end
55
+
56
+ def event_too_large?(event, encoded_event)
57
+ return false unless encoded_event.size > max_payload_size
58
+
59
+ # This single event is too large, we can't flush it
60
+ Datadog.logger.debug(
61
+ "[#{self.class.name}] Dropping event for logs intake. Payload too large: '#{event.inspect}'"
62
+ )
63
+
64
+ true
65
+ end
66
+
67
+ def send_payload(encoded_payload)
68
+ @api.logs_intake_request(path: "/v1/input", payload: encoded_payload)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end