datadog 2.1.0 → 2.2.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -2
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +19 -1
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +41 -0
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1 -1
  6. data/ext/datadog_profiling_native_extension/crashtracker.c +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  8. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +47 -1
  9. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -6
  11. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  12. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  13. data/lib/datadog/appsec/extensions.rb +1 -0
  14. data/lib/datadog/core/configuration/components.rb +6 -3
  15. data/lib/datadog/core/configuration/settings.rb +39 -0
  16. data/lib/datadog/core/configuration.rb +3 -17
  17. data/lib/datadog/core/deprecations.rb +58 -0
  18. data/lib/datadog/core/environment/yjit.rb +5 -0
  19. data/lib/datadog/core/runtime/ext.rb +1 -0
  20. data/lib/datadog/core/runtime/metrics.rb +6 -0
  21. data/lib/datadog/core/telemetry/component.rb +107 -0
  22. data/lib/datadog/core/telemetry/event.rb +100 -25
  23. data/lib/datadog/core/telemetry/ext.rb +2 -0
  24. data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
  25. data/lib/datadog/core/telemetry/metric.rb +167 -0
  26. data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
  27. data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
  28. data/lib/datadog/core/telemetry/request.rb +1 -1
  29. data/lib/datadog/core/telemetry/worker.rb +173 -0
  30. data/lib/datadog/core/utils/only_once_successful.rb +76 -0
  31. data/lib/datadog/core.rb +2 -19
  32. data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
  33. data/lib/datadog/opentelemetry/sdk/span_processor.rb +5 -2
  34. data/lib/datadog/profiling/collectors/code_provenance.rb +18 -5
  35. data/lib/datadog/profiling/component.rb +18 -1
  36. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
  37. data/lib/datadog/profiling.rb +1 -0
  38. data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
  39. data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
  40. data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
  41. data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
  42. data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
  43. data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
  44. data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
  45. data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
  46. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  47. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  48. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  49. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  50. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  51. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  52. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  53. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  54. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  55. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  56. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  57. data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
  58. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
  59. data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
  60. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
  61. data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
  62. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
  63. data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
  64. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
  65. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
  66. data/lib/datadog/tracing/contrib/analytics.rb +5 -0
  67. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
  68. data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
  69. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
  70. data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +25 -0
  71. data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
  72. data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
  73. data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
  74. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
  75. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
  76. data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
  77. data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
  78. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
  79. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
  80. data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
  81. data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
  82. data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
  83. data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
  84. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  85. data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
  86. data/lib/datadog/tracing/distributed/datadog.rb +2 -2
  87. data/lib/datadog/tracing/distributed/propagation.rb +9 -2
  88. data/lib/datadog/tracing/distributed/trace_context.rb +3 -2
  89. data/lib/datadog/tracing/span_operation.rb +3 -2
  90. data/lib/datadog/tracing/trace_operation.rb +7 -3
  91. data/lib/datadog/tracing/trace_segment.rb +4 -1
  92. data/lib/datadog/tracing/tracer.rb +9 -2
  93. data/lib/datadog/tracing.rb +5 -1
  94. data/lib/datadog/version.rb +2 -2
  95. metadata +22 -9
  96. data/lib/datadog/core/telemetry/client.rb +0 -95
  97. data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
@@ -42,6 +42,11 @@ module Datadog
42
42
  o.type :bool
43
43
  o.default false
44
44
  end
45
+
46
+ option :with_unified_tracer do |o|
47
+ o.type :bool
48
+ o.default false
49
+ end
45
50
  end
46
51
  end
47
52
  end
@@ -4,6 +4,7 @@ require_relative '../analytics'
4
4
  require_relative '../patcher'
5
5
  require_relative 'tracing_patcher'
6
6
  require_relative 'trace_patcher'
7
+ require_relative 'unified_trace_patcher'
7
8
 
8
9
  module Datadog
9
10
  module Tracing
@@ -23,10 +24,15 @@ module Datadog
23
24
  if configuration[:with_deprecated_tracer]
24
25
  TracingPatcher.patch!(schemas, trace_options)
25
26
  elsif Integration.trace_supported?
26
- TracePatcher.patch!(schemas, trace_options)
27
+ if configuration[:with_unified_tracer]
28
+ UnifiedTracePatcher.patch!(schemas, trace_options)
29
+ else
30
+ TracePatcher.patch!(schemas, trace_options)
31
+ end
27
32
  else
28
33
  Datadog.logger.warn(
29
- "GraphQL version (#{target_version}) does not support GraphQL::Tracing::DataDogTrace. "\
34
+ "GraphQL version (#{target_version}) does not support GraphQL::Tracing::DataDogTrace"\
35
+ 'or Datadog::Tracing::Contrib::GraphQL::UnifiedTrace.'\
30
36
  'Falling back to GraphQL::Tracing::DataDogTracing.'
31
37
  )
32
38
  TracingPatcher.patch!(schemas, trace_options)
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql/tracing'
4
+
5
+ module Datadog
6
+ module Tracing
7
+ module Contrib
8
+ module GraphQL
9
+ # These methods will be called by the GraphQL runtime to trace the execution of queries.
10
+ # This tracer differs from the upstream one as it follows the unified naming convention specification,
11
+ # which is required to use features such as API Catalog.
12
+ # DEV-3.0: This tracer should be the default one in the next major version.
13
+ module UnifiedTrace
14
+ # @param analytics_enabled [Boolean] Deprecated
15
+ # @param analytics_sample_rate [Float] Deprecated
16
+ # @param service [String|nil] The service name to be set on the spans
17
+ def initialize(*args, analytics_enabled: false, analytics_sample_rate: 1.0, service: nil, **kwargs)
18
+ @analytics_enabled = analytics_enabled
19
+ @analytics_sample_rate = analytics_sample_rate
20
+
21
+ @service_name = service
22
+ @has_prepare_span = respond_to?(:prepare_span)
23
+ super
24
+ end
25
+
26
+ def lex(*args, query_string:, **kwargs)
27
+ trace(proc { super }, 'lex', query_string, query_string: query_string)
28
+ end
29
+
30
+ def parse(*args, query_string:, **kwargs)
31
+ trace(proc { super }, 'parse', query_string, query_string: query_string) do |span|
32
+ span.set_tag('graphql.source', query_string)
33
+ end
34
+ end
35
+
36
+ def validate(*args, query:, validate:, **kwargs)
37
+ trace(proc { super }, 'validate', query.selected_operation_name, query: query, validate: validate) do |span|
38
+ span.set_tag('graphql.source', query.query_string)
39
+ end
40
+ end
41
+
42
+ def analyze_multiplex(*args, multiplex:, **kwargs)
43
+ trace(proc { super }, 'analyze_multiplex', multiplex_resource(multiplex), multiplex: multiplex)
44
+ end
45
+
46
+ def analyze_query(*args, query:, **kwargs)
47
+ trace(proc { super }, 'analyze', query.query_string, query: query)
48
+ end
49
+
50
+ def execute_multiplex(*args, multiplex:, **kwargs)
51
+ trace(proc { super }, 'execute_multiplex', multiplex_resource(multiplex), multiplex: multiplex) do |span|
52
+ span.set_tag('graphql.source', "Multiplex[#{multiplex.queries.map(&:query_string).join(', ')}]")
53
+ end
54
+ end
55
+
56
+ def execute_query(*args, query:, **kwargs)
57
+ trace(proc { super }, 'execute', query.selected_operation_name, query: query) do |span|
58
+ span.set_tag('graphql.source', query.query_string)
59
+ span.set_tag('graphql.operation.type', query.selected_operation.operation_type)
60
+ span.set_tag('graphql.operation.name', query.selected_operation_name) if query.selected_operation_name
61
+ query.variables.instance_variable_get(:@storage).each do |key, value|
62
+ span.set_tag("graphql.variables.#{key}", value)
63
+ end
64
+ end
65
+ end
66
+
67
+ def execute_query_lazy(*args, query:, multiplex:, **kwargs)
68
+ resource = if query
69
+ query.selected_operation_name || fallback_transaction_name(query.context)
70
+ else
71
+ multiplex_resource(multiplex)
72
+ end
73
+ trace(proc { super }, 'execute_lazy', resource, query: query, multiplex: multiplex)
74
+ end
75
+
76
+ def execute_field_span(callable, span_key, **kwargs)
77
+ # @platform_key_cache is initialized upstream, in ::GraphQL::Tracing::PlatformTrace
78
+ platform_key = @platform_key_cache[UnifiedTrace].platform_field_key_cache[kwargs[:field]]
79
+
80
+ if platform_key
81
+ trace(callable, span_key, platform_key, **kwargs) do |span|
82
+ kwargs[:arguments].each do |key, value|
83
+ span.set_tag("graphql.variables.#{key}", value)
84
+ end
85
+ end
86
+ else
87
+ callable.call
88
+ end
89
+ end
90
+
91
+ def execute_field(*args, **kwargs)
92
+ execute_field_span(proc { super }, 'resolve', **kwargs)
93
+ end
94
+
95
+ def execute_field_lazy(*args, **kwargs)
96
+ execute_field_span(proc { super }, 'resolve_lazy', **kwargs)
97
+ end
98
+
99
+ def authorized_span(callable, span_key, **kwargs)
100
+ platform_key = @platform_key_cache[UnifiedTrace].platform_authorized_key_cache[kwargs[:type]]
101
+ trace(callable, span_key, platform_key, **kwargs)
102
+ end
103
+
104
+ def authorized(*args, **kwargs)
105
+ authorized_span(proc { super }, 'authorized', **kwargs)
106
+ end
107
+
108
+ def authorized_lazy(*args, **kwargs)
109
+ authorized_span(proc { super }, 'authorized_lazy', **kwargs)
110
+ end
111
+
112
+ def resolve_type_span(callable, span_key, **kwargs)
113
+ platform_key = @platform_key_cache[UnifiedTrace].platform_resolve_type_key_cache[kwargs[:type]]
114
+ trace(callable, span_key, platform_key, **kwargs)
115
+ end
116
+
117
+ def resolve_type(*args, **kwargs)
118
+ resolve_type_span(proc { super }, 'resolve_type', **kwargs)
119
+ end
120
+
121
+ def resolve_type_lazy(*args, **kwargs)
122
+ resolve_type_span(proc { super }, 'resolve_type_lazy', **kwargs)
123
+ end
124
+
125
+ include ::GraphQL::Tracing::PlatformTrace
126
+
127
+ def platform_field_key(field, *args, **kwargs)
128
+ field.path
129
+ end
130
+
131
+ def platform_authorized_key(type, *args, **kwargs)
132
+ "#{type.graphql_name}.authorized"
133
+ end
134
+
135
+ def platform_resolve_type_key(type, *args, **kwargs)
136
+ "#{type.graphql_name}.resolve_type"
137
+ end
138
+
139
+ private
140
+
141
+ def trace(callable, trace_key, resource, **kwargs)
142
+ Tracing.trace("graphql.#{trace_key}", resource: resource, service: @service_name, type: 'graphql') do |span|
143
+ yield(span) if block_given?
144
+
145
+ prepare_span(trace_key, kwargs, span) if @has_prepare_span
146
+
147
+ callable.call
148
+ end
149
+ end
150
+
151
+ def multiplex_resource(multiplex)
152
+ return nil unless multiplex
153
+
154
+ operations = multiplex.queries.map(&:selected_operation_name).compact.join(', ')
155
+ if operations.empty?
156
+ first_query = multiplex.queries.first
157
+ fallback_transaction_name(first_query && first_query.context)
158
+ else
159
+ operations
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module GraphQL
7
+ # Provides instrumentation for `graphql` through the GraphQL's tracing with methods defined in UnifiedTrace
8
+ module UnifiedTracePatcher
9
+ module_function
10
+
11
+ def patch!(schemas, options)
12
+ require_relative 'unified_trace'
13
+ if schemas.empty?
14
+ ::GraphQL::Schema.trace_with(UnifiedTrace, **options)
15
+ else
16
+ schemas.each do |schema|
17
+ schema.trace_with(UnifiedTrace, **options)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -6,7 +6,7 @@ module Datadog
6
6
  module Kafka
7
7
  # Defines basic behaviors for an event for a consumer.
8
8
  module ConsumerEvent
9
- def process(span, _event, _id, payload)
9
+ def on_start(span, _event, _id, payload)
10
10
  super
11
11
 
12
12
  span.set_tag(Ext::TAG_GROUP, payload[:group_id])
@@ -6,7 +6,7 @@ module Datadog
6
6
  module Kafka
7
7
  # Defines basic behaviors for an event for a consumer group.
8
8
  module ConsumerGroupEvent
9
- def process(span, _event, _id, payload)
9
+ def on_start(span, _event, _id, payload)
10
10
  super
11
11
 
12
12
  span.resource = payload[:group_id]
@@ -29,7 +29,7 @@ module Datadog
29
29
  Datadog.configuration.tracing[:kafka]
30
30
  end
31
31
 
32
- def process(span, _event, _id, payload)
32
+ def on_start(span, _event, _id, payload)
33
33
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
34
34
  span.set_tag(Contrib::Ext::Messaging::TAG_SYSTEM, Ext::TAG_MESSAGING_SYSTEM)
35
35
 
@@ -15,7 +15,9 @@ module Datadog
15
15
 
16
16
  EVENT_NAME = 'request.connection.kafka'
17
17
 
18
- def self.process(span, _event, _id, payload)
18
+ module_function
19
+
20
+ def on_start(span, _event, _id, payload)
19
21
  super
20
22
 
21
23
  span.resource = payload[:api]
@@ -24,8 +26,6 @@ module Datadog
24
26
  span.set_tag(Ext::TAG_RESPONSE_SIZE, payload[:response_size]) if payload.key?(:response_size)
25
27
  end
26
28
 
27
- module_function
28
-
29
29
  def span_name
30
30
  Ext::SPAN_CONNECTION_REQUEST
31
31
  end
@@ -17,7 +17,9 @@ module Datadog
17
17
 
18
18
  EVENT_NAME = 'process_batch.consumer.kafka'
19
19
 
20
- def self.process(span, _event, _id, payload)
20
+ module_function
21
+
22
+ def on_start(span, _event, _id, payload)
21
23
  super
22
24
 
23
25
  span.resource = payload[:topic]
@@ -31,8 +33,6 @@ module Datadog
31
33
  span.set_tag(Ext::TAG_OFFSET_LAG, payload[:offset_lag]) if payload.key?(:offset_lag)
32
34
  end
33
35
 
34
- module_function
35
-
36
36
  def span_name
37
37
  Ext::SPAN_PROCESS_BATCH
38
38
  end
@@ -17,7 +17,9 @@ module Datadog
17
17
 
18
18
  EVENT_NAME = 'process_message.consumer.kafka'
19
19
 
20
- def self.process(span, _event, _id, payload)
20
+ module_function
21
+
22
+ def on_start(span, _event, _id, payload)
21
23
  super
22
24
 
23
25
  span.resource = payload[:topic]
@@ -29,8 +31,6 @@ module Datadog
29
31
  span.set_tag(Ext::TAG_OFFSET_LAG, payload[:offset_lag]) if payload.key?(:offset_lag)
30
32
  end
31
33
 
32
- module_function
33
-
34
34
  def span_name
35
35
  Ext::SPAN_PROCESS_MESSAGE
36
36
  end
@@ -19,7 +19,9 @@ module Datadog
19
19
 
20
20
  EVENT_NAME = 'heartbeat.consumer.kafka'
21
21
 
22
- def self.process(span, _event, _id, payload)
22
+ module_function
23
+
24
+ def on_start(span, _event, _id, payload)
23
25
  super
24
26
 
25
27
  if payload.key?(:topic_partitions)
@@ -29,8 +31,6 @@ module Datadog
29
31
  end
30
32
  end
31
33
 
32
- module_function
33
-
34
34
  def span_name
35
35
  Ext::SPAN_CONSUMER_HEARTBEAT
36
36
  end
@@ -15,7 +15,9 @@ module Datadog
15
15
 
16
16
  EVENT_NAME = 'send_messages.producer.kafka'
17
17
 
18
- def self.process(span, _event, _id, payload)
18
+ module_function
19
+
20
+ def on_start(span, _event, _id, payload)
19
21
  super
20
22
 
21
23
  span.set_tag(Ext::TAG_MESSAGE_COUNT, payload[:message_count]) if payload.key?(:message_count)
@@ -23,8 +25,6 @@ module Datadog
23
25
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_PRODUCER)
24
26
  end
25
27
 
26
- module_function
27
-
28
28
  def span_name
29
29
  Ext::SPAN_SEND_MESSAGES
30
30
  end
@@ -15,7 +15,9 @@ module Datadog
15
15
 
16
16
  EVENT_NAME = 'deliver_messages.producer.kafka'
17
17
 
18
- def self.process(span, _event, _id, payload)
18
+ module_function
19
+
20
+ def on_start(span, _event, _id, payload)
19
21
  super
20
22
 
21
23
  span.set_tag(Ext::TAG_ATTEMPTS, payload[:attempts]) if payload.key?(:attempts)
@@ -26,8 +28,6 @@ module Datadog
26
28
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_PRODUCER)
27
29
  end
28
30
 
29
- module_function
30
-
31
31
  def span_name
32
32
  Ext::SPAN_DELIVER_MESSAGES
33
33
  end
@@ -19,7 +19,7 @@ module Datadog
19
19
  # Class methods for Racecar events.
20
20
  # Note, they share the same process method and before_trace method.
21
21
  module ClassMethods
22
- def subscription(*args)
22
+ def subscription(*args, **kwargs)
23
23
  super.tap do |subscription|
24
24
  subscription.before_trace { ensure_clean_context! }
25
25
  end
@@ -33,7 +33,7 @@ module Datadog
33
33
  Datadog.configuration.tracing[:racecar]
34
34
  end
35
35
 
36
- def process(span, event, _id, payload)
36
+ def on_start(span, event, _id, payload)
37
37
  span.service = configuration[:service_name]
38
38
  span.resource = payload[:consumer_class]
39
39
 
@@ -14,6 +14,15 @@ module Datadog
14
14
  ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_RAILS_ANALYTICS_SAMPLE_RATE'
15
15
  ENV_DISABLE = 'DISABLE_DATADOG_RAILS'
16
16
 
17
+ SPAN_RUNNER_FILE = 'rails.runner.file'
18
+ SPAN_RUNNER_INLINE = 'rails.runner.inline'
19
+ SPAN_RUNNER_STDIN = 'rails.runner.stdin'
20
+ TAG_COMPONENT = 'rails'
21
+ TAG_OPERATION_FILE = 'runner.file'
22
+ TAG_OPERATION_INLINE = 'runner.inline'
23
+ TAG_OPERATION_STDIN = 'runner.stdin'
24
+ TAG_RUNNER_SOURCE = 'source'
25
+
17
26
  # @!visibility private
18
27
  MINIMUM_VERSION = Gem::Version.new('4')
19
28
  end
@@ -5,6 +5,7 @@ require_relative '../rack/middlewares'
5
5
  require_relative 'framework'
6
6
  require_relative 'log_injection'
7
7
  require_relative 'middlewares'
8
+ require_relative 'runner'
8
9
  require_relative 'utils'
9
10
  require_relative '../semantic_logger/patcher'
10
11
 
@@ -28,6 +29,7 @@ module Datadog
28
29
  def patch
29
30
  patch_before_initialize
30
31
  patch_after_initialize
32
+ patch_rails_runner
31
33
  end
32
34
 
33
35
  def patch_before_initialize
@@ -81,6 +83,11 @@ module Datadog
81
83
  def setup_tracer
82
84
  Contrib::Rails::Framework.setup
83
85
  end
86
+
87
+ # Instruments the `bin/rails runner` command.
88
+ def patch_rails_runner
89
+ ::Rails::Command.singleton_class.prepend(Command) if defined?(::Rails::Command)
90
+ end
84
91
  end
85
92
  end
86
93
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module Rails
7
+ # Instruments the `bin/rails runner` command.
8
+ # This command executes the provided code with the host Rails application loaded.
9
+ # The command can be either:
10
+ # * `-`: for code provided through the STDIN.
11
+ # * File path: for code provided through a local file.
12
+ # * `inline code`: for code provided directly as a command line argument.
13
+ # @see https://guides.rubyonrails.org/v6.1/command_line.html#bin-rails-runner
14
+ module Runner
15
+ # Limit the maximum size of the source code captured in the source tag.
16
+ MAX_TAG_VALUE_SIZE = 4096
17
+ private_constant :MAX_TAG_VALUE_SIZE
18
+
19
+ def runner(code_or_file = nil, *_command_argv)
20
+ if code_or_file == '-'
21
+ name = Ext::SPAN_RUNNER_STDIN
22
+ resource = nil
23
+ operation = Ext::TAG_OPERATION_STDIN
24
+ # The source is not yet available for STDIN, but it will be captured in `eval`.
25
+ elsif File.exist?(code_or_file)
26
+ name = Ext::SPAN_RUNNER_FILE
27
+ resource = code_or_file
28
+ operation = Ext::TAG_OPERATION_FILE
29
+ source = File.read(code_or_file)
30
+ else
31
+ name = Ext::SPAN_RUNNER_INLINE
32
+ resource = nil
33
+ operation = Ext::TAG_OPERATION_INLINE
34
+ source = code_or_file
35
+ end
36
+
37
+ Tracing.trace(
38
+ name,
39
+ service: Datadog.configuration.tracing[:rails][:service_name],
40
+ resource: resource,
41
+ tags: {
42
+ Tracing::Metadata::Ext::TAG_COMPONENT => Ext::TAG_COMPONENT,
43
+ Tracing::Metadata::Ext::TAG_OPERATION => operation,
44
+ }
45
+ ) do |span|
46
+ if source
47
+ span.set_tag(
48
+ Ext::TAG_RUNNER_SOURCE,
49
+ Core::Utils.truncate(source, MAX_TAG_VALUE_SIZE)
50
+ )
51
+ end
52
+ Contrib::Analytics.set_rate!(span, Datadog.configuration.tracing[:rails])
53
+
54
+ super
55
+ end
56
+ end
57
+
58
+ # Capture the executed source code when provided from STDIN.
59
+ def eval(*args)
60
+ span = Datadog::Tracing.active_span
61
+ if span.name == Ext::SPAN_RUNNER_STDIN
62
+ source = args[0]
63
+ span.set_tag(
64
+ Ext::TAG_RUNNER_SOURCE,
65
+ Core::Utils.truncate(source, MAX_TAG_VALUE_SIZE)
66
+ )
67
+ end
68
+
69
+ super
70
+ end
71
+
72
+ ruby2_keywords :eval if respond_to?(:ruby2_keywords, true)
73
+ end
74
+
75
+ # The instrumentation target, {Rails::Command::RunnerCommand} is only loaded
76
+ # right before `bin/rails runner` is executed. This means there's not much
77
+ # opportunity to patch it ahead of time.
78
+ # To ensure we can patch it successfully, we patch it's caller, {Rails::Command}
79
+ # and promptly patch {Rails::Command::RunnerCommand} when it is loaded.
80
+ module Command
81
+ def find_by_namespace(*args)
82
+ ret = super
83
+ # Patch RunnerCommand if it is loaded and not already patched.
84
+ if defined?(::Rails::Command::RunnerCommand) && !(::Rails::Command::RunnerCommand < Runner)
85
+ ::Rails::Command::RunnerCommand.prepend(Runner)
86
+ end
87
+ ret
88
+ end
89
+
90
+ ruby2_keywords :find_by_namespace if respond_to?(:ruby2_keywords, true)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -31,7 +31,7 @@ module Datadog
31
31
 
32
32
  # DEV: We need these to be hex encoded
33
33
  data[@trace_id_key] = format('%032x', digest.trace_id)
34
- data[@span_id_key] = format('%016x', digest.span_id)
34
+ data[@span_id_key] = format('%016x', digest.span_id || 0) # # Fall back to zero (invalid) if not present
35
35
 
36
36
  if digest.trace_sampling_priority
37
37
  sampling_priority = Helpers.clamp_sampling_priority(
@@ -25,8 +25,10 @@ module Datadog
25
25
  def inject!(digest, env)
26
26
  return if digest.nil?
27
27
 
28
+ span_id = digest.span_id || 0 # Fall back to zero (invalid) if not present
29
+
28
30
  # DEV: We need these to be hex encoded
29
- value = "#{format('%032x', digest.trace_id)}-#{format('%016x', digest.span_id)}"
31
+ value = "#{format('%032x', digest.trace_id)}-#{format('%016x', span_id)}"
30
32
 
31
33
  if digest.trace_sampling_priority
32
34
  sampling_priority = Helpers.clamp_sampling_priority(
@@ -42,7 +42,7 @@ module Datadog
42
42
 
43
43
  data[@trace_id_key] = Tracing::Utils::TraceId.to_low_order(digest.trace_id).to_s
44
44
 
45
- data[@parent_id_key] = digest.span_id.to_s
45
+ data[@parent_id_key] = digest.span_id.to_s if digest.span_id
46
46
  data[@sampling_priority_key] = digest.trace_sampling_priority.to_s if digest.trace_sampling_priority
47
47
  data[@origin_key] = digest.trace_origin.to_s if digest.trace_origin
48
48
 
@@ -109,7 +109,7 @@ module Datadog
109
109
 
110
110
  return tags if high_order == 0
111
111
 
112
- tags.merge(Tracing::Metadata::Ext::Distributed::TAG_TID => high_order.to_s(16))
112
+ tags.merge(Tracing::Metadata::Ext::Distributed::TAG_TID => format('%016x', high_order))
113
113
  end
114
114
 
115
115
  # Side effect: Remove high order 64 bit hex-encoded `tid` tag from distributed tags
@@ -32,7 +32,9 @@ module Datadog
32
32
 
33
33
  # inject! populates the env with span ID, trace ID and sampling priority
34
34
  #
35
- # This method will never raise errors, but instead log them to `Datadog.logger`.
35
+ # This method will never raise errors.
36
+ # It can propagate partial data, if deemed useful, instead of failing.
37
+ # In case of unrecoverable errors, it will log them to `Datadog.logger`.
36
38
  #
37
39
  # DEV-2.0: inject! should work without arguments, injecting the active_trace's digest
38
40
  # DEV-2.0: and returning a new Hash with the injected data.
@@ -45,7 +47,7 @@ module Datadog
45
47
  # @param digest [TraceDigest]
46
48
  # @param data [Hash]
47
49
  # @return [Boolean] `true` if injected successfully, `false` if no propagation style is configured
48
- # @return [nil] in case of error, see `Datadog.logger` output for details.
50
+ # @return [nil] in case of unrecoverable errors, see `Datadog.logger` output for details.
49
51
  def inject!(digest, data)
50
52
  if digest.nil?
51
53
  ::Datadog.logger.debug('Cannot inject distributed trace data: digest is nil.')
@@ -54,6 +56,11 @@ module Datadog
54
56
 
55
57
  digest = digest.to_digest if digest.respond_to?(:to_digest)
56
58
 
59
+ if digest.trace_id.nil?
60
+ ::Datadog.logger.debug('Cannot inject distributed trace data: digest.trace_id is nil.')
61
+ return nil
62
+ end
63
+
57
64
  result = false
58
65
 
59
66
  # Inject all configured propagation styles
@@ -107,7 +107,7 @@ module Datadog
107
107
  def build_traceparent(digest)
108
108
  build_traceparent_string(
109
109
  digest.trace_id,
110
- digest.span_id,
110
+ digest.span_id || 0, # Fall back to zero (invalid) if not present
111
111
  build_trace_flags(digest)
112
112
  )
113
113
  end
@@ -198,7 +198,8 @@ module Datadog
198
198
 
199
199
  def last_dd_parent_id(digest)
200
200
  if !digest.span_remote
201
- "p:#{format('%016x', digest.span_id)};"
201
+ span_id = digest.span_id || 0 # Fall back to zero (invalid) if not present
202
+ "p:#{format('%016x', span_id)};"
202
203
  elsif digest.trace_distributed_tags&.key?(Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID)
203
204
  "p:#{digest.trace_distributed_tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID]};"
204
205
  else
@@ -48,7 +48,8 @@ module Datadog
48
48
  tags: nil,
49
49
  trace_id: nil,
50
50
  type: nil,
51
- links: nil
51
+ links: nil,
52
+ id: nil
52
53
  )
53
54
  # Ensure dynamically created strings are UTF-8 encoded.
54
55
  #
@@ -60,7 +61,7 @@ module Datadog
60
61
  self.type = type
61
62
  self.resource = resource
62
63
 
63
- @id = Tracing::Utils.next_id
64
+ @id = id.nil? ? Tracing::Utils.next_id : id
64
65
  @parent_id = parent_id || 0
65
66
  @trace_id = trace_id || Tracing::Utils::TraceId.next_id
66
67