datadog 2.1.0 → 2.2.0

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