datadog 2.0.0.beta1 → 2.0.0.rc1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +181 -1
  3. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +1 -1
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +40 -32
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +23 -12
  6. data/ext/datadog_profiling_native_extension/crashtracker.c +108 -0
  7. data/ext/datadog_profiling_native_extension/extconf.rb +9 -23
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +81 -4
  9. data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
  10. data/ext/datadog_profiling_native_extension/http_transport.c +1 -94
  11. data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +86 -0
  12. data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +4 -0
  13. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +2 -12
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +25 -86
  15. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  16. data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -5
  17. data/ext/datadog_profiling_native_extension/stack_recorder.c +161 -62
  18. data/lib/datadog/appsec/contrib/devise/tracking.rb +8 -0
  19. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -13
  20. data/lib/datadog/appsec/event.rb +2 -2
  21. data/lib/datadog/core/configuration/components.rb +2 -1
  22. data/lib/datadog/core/configuration/option.rb +7 -5
  23. data/lib/datadog/core/configuration/settings.rb +34 -79
  24. data/lib/datadog/core/configuration.rb +20 -4
  25. data/lib/datadog/core/environment/platform.rb +7 -1
  26. data/lib/datadog/core/remote/client/capabilities.rb +2 -1
  27. data/lib/datadog/core/remote/client.rb +1 -5
  28. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  29. data/lib/datadog/core/remote/dispatcher.rb +3 -3
  30. data/lib/datadog/core/remote/transport/http/config.rb +5 -5
  31. data/lib/datadog/core/telemetry/client.rb +18 -10
  32. data/lib/datadog/core/telemetry/emitter.rb +9 -13
  33. data/lib/datadog/core/telemetry/event.rb +247 -57
  34. data/lib/datadog/core/telemetry/ext.rb +1 -0
  35. data/lib/datadog/core/telemetry/heartbeat.rb +1 -3
  36. data/lib/datadog/core/telemetry/http/ext.rb +4 -1
  37. data/lib/datadog/core/telemetry/http/response.rb +4 -0
  38. data/lib/datadog/core/telemetry/http/transport.rb +9 -4
  39. data/lib/datadog/core/telemetry/request.rb +59 -0
  40. data/lib/datadog/core/utils/base64.rb +22 -0
  41. data/lib/datadog/opentelemetry/sdk/span_processor.rb +19 -2
  42. data/lib/datadog/opentelemetry/sdk/trace/span.rb +3 -17
  43. data/lib/datadog/profiling/collectors/code_provenance.rb +10 -4
  44. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +25 -0
  45. data/lib/datadog/profiling/component.rb +49 -17
  46. data/lib/datadog/profiling/crashtracker.rb +91 -0
  47. data/lib/datadog/profiling/exporter.rb +6 -3
  48. data/lib/datadog/profiling/http_transport.rb +7 -11
  49. data/lib/datadog/profiling/load_native_extension.rb +14 -1
  50. data/lib/datadog/profiling/profiler.rb +9 -2
  51. data/lib/datadog/profiling/stack_recorder.rb +6 -2
  52. data/lib/datadog/profiling.rb +12 -0
  53. data/lib/datadog/tracing/component.rb +5 -1
  54. data/lib/datadog/tracing/configuration/dynamic.rb +39 -1
  55. data/lib/datadog/tracing/configuration/settings.rb +1 -0
  56. data/lib/datadog/tracing/contrib/action_pack/integration.rb +1 -1
  57. data/lib/datadog/tracing/contrib/action_view/integration.rb +1 -1
  58. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +1 -0
  59. data/lib/datadog/tracing/contrib/active_record/integration.rb +11 -1
  60. data/lib/datadog/tracing/contrib/active_support/integration.rb +1 -1
  61. data/lib/datadog/tracing/contrib/configuration/resolver.rb +43 -0
  62. data/lib/datadog/tracing/contrib/grape/endpoint.rb +43 -5
  63. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +1 -1
  64. data/lib/datadog/tracing/correlation.rb +3 -4
  65. data/lib/datadog/tracing/remote.rb +5 -1
  66. data/lib/datadog/tracing/sampling/ext.rb +5 -1
  67. data/lib/datadog/tracing/sampling/matcher.rb +75 -26
  68. data/lib/datadog/tracing/sampling/rule.rb +27 -4
  69. data/lib/datadog/tracing/sampling/rule_sampler.rb +19 -1
  70. data/lib/datadog/tracing/sampling/span/matcher.rb +13 -41
  71. data/lib/datadog/tracing/span.rb +7 -2
  72. data/lib/datadog/tracing/span_link.rb +92 -0
  73. data/lib/datadog/tracing/span_operation.rb +6 -4
  74. data/lib/datadog/tracing/trace_operation.rb +12 -0
  75. data/lib/datadog/tracing/tracer.rb +4 -3
  76. data/lib/datadog/tracing/transport/serializable_trace.rb +3 -1
  77. data/lib/datadog/tracing/utils.rb +16 -0
  78. data/lib/datadog/version.rb +1 -1
  79. metadata +10 -31
  80. data/lib/datadog/core/telemetry/collector.rb +0 -248
  81. data/lib/datadog/core/telemetry/v1/app_event.rb +0 -59
  82. data/lib/datadog/core/telemetry/v1/application.rb +0 -94
  83. data/lib/datadog/core/telemetry/v1/configuration.rb +0 -27
  84. data/lib/datadog/core/telemetry/v1/dependency.rb +0 -45
  85. data/lib/datadog/core/telemetry/v1/host.rb +0 -59
  86. data/lib/datadog/core/telemetry/v1/install_signature.rb +0 -38
  87. data/lib/datadog/core/telemetry/v1/integration.rb +0 -66
  88. data/lib/datadog/core/telemetry/v1/product.rb +0 -36
  89. data/lib/datadog/core/telemetry/v1/telemetry_request.rb +0 -108
  90. data/lib/datadog/core/telemetry/v2/app_client_configuration_change.rb +0 -41
  91. data/lib/datadog/core/telemetry/v2/request.rb +0 -29
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'utils'
4
- require_relative 'metadata/ext'
5
4
  require_relative '../core/logging/ext'
6
5
 
7
6
  module Datadog
@@ -36,11 +35,11 @@ module Datadog
36
35
  version: nil
37
36
  )
38
37
  # Dup and freeze strings so they aren't modified by reference.
39
- @env = Core::Utils::SafeDup.frozen_dup(env || Datadog.configuration.env)
40
- @service = Core::Utils::SafeDup.frozen_dup(service || Datadog.configuration.service)
38
+ @env = env || Datadog.configuration.env
39
+ @service = service || Datadog.configuration.service
41
40
  @span_id = (span_id || 0).to_s
42
41
  @trace_id = trace_id || 0
43
- @version = Core::Utils::SafeDup.frozen_dup(version || Datadog.configuration.version)
42
+ @version = version || Datadog.configuration.version
44
43
  end
45
44
 
46
45
  def to_h
@@ -12,12 +12,16 @@ module Datadog
12
12
  class << self
13
13
  PRODUCT = 'APM_TRACING'
14
14
 
15
+ CAPABILITIES = [
16
+ 1 << 29 # APM_TRACING_SAMPLE_RULES: Dynamic trace sampling rules configuration
17
+ ].freeze
18
+
15
19
  def products
16
20
  [PRODUCT]
17
21
  end
18
22
 
19
23
  def capabilities
20
- [] # No capabilities advertised
24
+ CAPABILITIES
21
25
  end
22
26
 
23
27
  def process_config(config, content)
@@ -40,7 +40,7 @@ module Datadog
40
40
  DEFAULT = '-0'
41
41
  # The sampling rate received in the agent's http response.
42
42
  AGENT_RATE = '-1'
43
- # Sampling rule or sampling rate based on tracer config.
43
+ # Locally configured rule.
44
44
  TRACE_SAMPLING_RULE = '-3'
45
45
  # User directly sets sampling priority via {Tracing.reject!} or {Tracing.keep!},
46
46
  # or by a custom sampler implementation.
@@ -49,6 +49,10 @@ module Datadog
49
49
  ASM = '-5'
50
50
  # Single Span Sampled.
51
51
  SPAN_SAMPLING_RATE = '-8'
52
+ # Dynamically configured rule, explicitly created by the user.
53
+ REMOTE_USER_RULE = '-11'
54
+ # Dynamically configured rule, automatically generated by Datadog.
55
+ REMOTE_DYNAMIC_RULE = '-12'
52
56
  end
53
57
  end
54
58
  end
@@ -6,6 +6,9 @@ module Datadog
6
6
  # Checks if a trace conforms to a matching criteria.
7
7
  # @abstract
8
8
  class Matcher
9
+ # Pattern that matches any string
10
+ MATCH_ALL_PATTERN = '*'
11
+
9
12
  # Returns `true` if the trace should conforms to this rule, `false` otherwise
10
13
  #
11
14
  # @param [TraceOperation] trace
@@ -13,51 +16,97 @@ module Datadog
13
16
  def match?(trace)
14
17
  raise NotImplementedError
15
18
  end
16
- end
17
19
 
18
- # A {Datadog::Sampling::Matcher} that supports matching a trace by
19
- # trace name and/or service name.
20
- class SimpleMatcher < Matcher
21
- # Returns `true` for case equality (===) with any object
20
+ # Converts a glob pattern String to a case-insensitive String matcher object.
21
+ # The match object will only return `true` if it matches the complete String.
22
+ #
23
+ # The following special characters are supported:
24
+ # - `?` matches any single character
25
+ # - `*` matches any substring
26
+ #
27
+ # @param glob [String]
28
+ # @return [#match?(String)]
29
+ def self.glob_to_regex(glob)
30
+ # Optimization for match-all case
31
+ return MATCH_ALL if glob == MATCH_ALL_PATTERN
32
+
33
+ # Ensure no undesired characters are treated as regex.
34
+ glob = Regexp.quote(glob)
35
+
36
+ # Our valid special characters, `?` and `*`, were just escaped
37
+ # by `Regexp.quote` above. We need to unescape them:
38
+ glob.gsub!('\?', '.') # Any single character
39
+ glob.gsub!('\*', '.*') # Any substring
40
+
41
+ # Patterns have to match the whole input string
42
+ glob = "\\A#{glob}\\z"
43
+
44
+ Regexp.new(glob, Regexp::IGNORECASE)
45
+ end
46
+
47
+ # Returns `true` for any input
22
48
  MATCH_ALL = Class.new do
23
- # DEV: A class that implements `#===` is ~20% faster than
24
- # DEV: a `Proc` that always returns `true`.
25
- def ===(other)
49
+ def match?(_other)
26
50
  true
27
51
  end
52
+
53
+ def inspect
54
+ "MATCH_ALL:Matcher('*')"
55
+ end
28
56
  end.new
57
+ end
29
58
 
30
- attr_reader :name, :service
59
+ # A {Datadog::Sampling::Matcher} that supports matching a trace by
60
+ # trace name and/or service name.
61
+ class SimpleMatcher < Matcher
62
+ attr_reader :name, :service, :resource, :tags
31
63
 
32
64
  # @param name [String,Regexp,Proc] Matcher for case equality (===) with the trace name,
33
65
  # defaults to always match
34
66
  # @param service [String,Regexp,Proc] Matcher for case equality (===) with the service name,
35
67
  # defaults to always match
36
- def initialize(name: MATCH_ALL, service: MATCH_ALL)
68
+ # @param resource [String,Regexp,Proc] Matcher for case equality (===) with the resource name,
69
+ # defaults to always match
70
+ def initialize(
71
+ name: MATCH_ALL_PATTERN,
72
+ service: MATCH_ALL_PATTERN,
73
+ resource: MATCH_ALL_PATTERN,
74
+ tags: {}
75
+ )
37
76
  super()
38
- @name = name
39
- @service = service
77
+
78
+ name = Matcher.glob_to_regex(name)
79
+ service = Matcher.glob_to_regex(service)
80
+ resource = Matcher.glob_to_regex(resource)
81
+ tags = tags.transform_values { |matcher| Matcher.glob_to_regex(matcher) }
82
+
83
+ @name = name || Datadog::Tracing::Sampling::Matcher::MATCH_ALL
84
+ @service = service || Datadog::Tracing::Sampling::Matcher::MATCH_ALL
85
+ @resource = resource || Datadog::Tracing::Sampling::Matcher::MATCH_ALL
86
+ @tags = tags
40
87
  end
41
88
 
42
89
  def match?(trace)
43
- name === trace.name && service === trace.service
90
+ @name.match?(trace.name) &&
91
+ @service.match?(trace.service) &&
92
+ @resource.match?(trace.resource) &&
93
+ tags_match?(trace)
44
94
  end
45
- end
46
95
 
47
- # A {Datadog::Tracing::Sampling::Matcher} that allows for arbitrary trace matching
48
- # based on the return value of a provided block.
49
- class ProcMatcher < Matcher
50
- attr_reader :block
96
+ private
51
97
 
52
- # @yield [name, service] Provides trace name and service to the block
53
- # @yieldreturn [Boolean] Whether the trace conforms to this matcher
54
- def initialize(&block)
55
- super()
56
- @block = block
57
- end
98
+ # Match against the trace tags and metrics.
99
+ def tags_match?(trace)
100
+ @tags.all? do |name, matcher|
101
+ tag = trace.get_tag(name)
58
102
 
59
- def match?(trace)
60
- block.call(trace.name, trace.service)
103
+ # Format metrics as strings, to allow for partial number matching (/4.*/ matching '400', '404', etc.).
104
+ # Because metrics are floats, we use the '%g' format specifier to avoid trailing zeros, which
105
+ # can affect exact string matching (e.g. '400' matching '400.0').
106
+ tag = format('%g', tag) if tag.is_a?(Numeric)
107
+
108
+ matcher.match?(tag)
109
+ end
61
110
  end
62
111
  end
63
112
  end
@@ -10,13 +10,18 @@ module Datadog
10
10
  # a specific criteria and what sampling strategy to
11
11
  # apply in case of a positive match.
12
12
  class Rule
13
- attr_reader :matcher, :sampler
13
+ PROVENANCE_LOCAL = :local
14
+ PROVENANCE_REMOTE_USER = :customer
15
+ PROVENANCE_REMOTE_DYNAMIC = :dynamic
16
+
17
+ attr_reader :matcher, :sampler, :provenance
14
18
 
15
19
  # @param [Matcher] matcher A matcher to verify trace conformity against
16
20
  # @param [Sampler] sampler A sampler to be consulted on a positive match
17
- def initialize(matcher, sampler)
21
+ def initialize(matcher, sampler, provenance)
18
22
  @matcher = matcher
19
23
  @sampler = sampler
24
+ @provenance = provenance
20
25
  end
21
26
 
22
27
  # Evaluates if the provided `trace` conforms to the `matcher`.
@@ -51,9 +56,27 @@ module Datadog
51
56
  # @param name [String,Regexp,Proc] Matcher for case equality (===) with the trace name, defaults to always match
52
57
  # @param service [String,Regexp,Proc] Matcher for case equality (===) with the service name,
53
58
  # defaults to always match
59
+ # @param resource [String,Regexp,Proc] Matcher for case equality (===) with the resource name,
60
+ # defaults to always match
54
61
  # @param sample_rate [Float] Sampling rate between +[0,1]+
55
- def initialize(name: SimpleMatcher::MATCH_ALL, service: SimpleMatcher::MATCH_ALL, sample_rate: 1.0)
56
- super(SimpleMatcher.new(name: name, service: service), RateSampler.new(sample_rate))
62
+ def initialize(
63
+ name: SimpleMatcher::MATCH_ALL_PATTERN, service: SimpleMatcher::MATCH_ALL_PATTERN,
64
+ resource: SimpleMatcher::MATCH_ALL_PATTERN, tags: {},
65
+ provenance: Rule::PROVENANCE_LOCAL,
66
+ sample_rate: 1.0
67
+ )
68
+ # We want to allow 0.0 to drop all traces, but {Datadog::Tracing::Sampling::RateSampler}
69
+ # considers 0.0 an invalid rate and falls back to 100% sampling.
70
+ #
71
+ # We address that here by not setting the rate in the constructor,
72
+ # but using the setter method.
73
+ #
74
+ # We don't want to make this change directly to {Datadog::Tracing::Sampling::RateSampler}
75
+ # because it breaks its current contract to existing users.
76
+ sampler = RateSampler.new
77
+ sampler.sample_rate = sample_rate
78
+
79
+ super(SimpleMatcher.new(name: name, service: service, resource: resource, tags: tags), sampler, provenance)
57
80
  end
58
81
  end
59
82
  end
@@ -62,7 +62,15 @@ module Datadog
62
62
  kwargs = {
63
63
  name: rule['name'],
64
64
  service: rule['service'],
65
+ resource: rule['resource'],
66
+ tags: rule['tags'],
65
67
  sample_rate: sample_rate,
68
+ provenance: if (provenance = rule['provenance'])
69
+ # `Rule::PROVENANCE_*` values are symbols, so convert strings to match
70
+ provenance.to_sym
71
+ else
72
+ Rule::PROVENANCE_LOCAL
73
+ end,
66
74
  }
67
75
 
68
76
  kwargs.compact!
@@ -116,7 +124,17 @@ module Datadog
116
124
  rate_limiter.allow?(1).tap do |allowed|
117
125
  set_priority(trace, allowed)
118
126
  set_limiter_metrics(trace, rate_limiter.effective_rate)
119
- trace.set_tag(Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER, Ext::Decision::TRACE_SAMPLING_RULE)
127
+
128
+ provenance = case rule.provenance
129
+ when Rule::PROVENANCE_REMOTE_USER
130
+ Ext::Decision::REMOTE_USER_RULE
131
+ when Rule::PROVENANCE_REMOTE_DYNAMIC
132
+ Ext::Decision::REMOTE_DYNAMIC_RULE
133
+ else
134
+ Ext::Decision::TRACE_SAMPLING_RULE
135
+ end
136
+
137
+ trace.set_tag(Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER, provenance)
120
138
  end
121
139
  rescue StandardError => e
122
140
  Datadog.logger.error(
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../matcher'
4
+
3
5
  module Datadog
4
6
  module Tracing
5
7
  module Sampling
@@ -31,29 +33,19 @@ module Datadog
31
33
  # @param name_pattern [String] a pattern to be matched against {SpanOperation#name}
32
34
  # @param service_pattern [String] a pattern to be matched against {SpanOperation#service}
33
35
  def initialize(name_pattern: MATCH_ALL_PATTERN, service_pattern: MATCH_ALL_PATTERN)
34
- @name = pattern_to_regex(name_pattern)
35
- @service = pattern_to_regex(service_pattern)
36
+ @name = Sampling::Matcher.glob_to_regex(name_pattern)
37
+ @service = Sampling::Matcher.glob_to_regex(service_pattern)
36
38
  end
37
39
 
38
- # {Regexp#match?} was added in Ruby 2.4, and it's measurably
39
- # the least costly way to get a boolean result for a Regexp match.
40
- # @see https://www.ruby-lang.org/en/news/2016/12/25/ruby-2-4-0-released/
41
- if Regexp.method_defined?(:match?)
42
- # Returns `true` if the span conforms to the configured patterns,
43
- # `false` otherwise
44
- #
45
- # @param [SpanOperation] span
46
- # @return [Boolean]
47
- def match?(span)
48
- # Matching is performed at the end of the lifecycle of a Span,
49
- # thus both `name` and `service` are guaranteed to be not `nil`.
50
- @name.match?(span.name) && @service.match?(span.service)
51
- end
52
- else
53
- # DEV: Remove when support for Ruby 2.3 and older is removed.
54
- def match?(span)
55
- @name === span.name && @service === span.service
56
- end
40
+ # Returns `true` if the span conforms to the configured patterns,
41
+ # `false` otherwise
42
+ #
43
+ # @param [SpanOperation] span
44
+ # @return [Boolean]
45
+ def match?(span)
46
+ # Matching is performed at the end of the lifecycle of a Span,
47
+ # thus both `name` and `service` are guaranteed to be not `nil`.
48
+ @name.match?(span.name) && @service.match?(span.service)
57
49
  end
58
50
 
59
51
  def ==(other)
@@ -62,26 +54,6 @@ module Datadog
62
54
  name == other.name &&
63
55
  service == other.service
64
56
  end
65
-
66
- private
67
-
68
- # @param pattern [String]
69
- # @return [Regexp]
70
- def pattern_to_regex(pattern)
71
- # Ensure no undesired characters are treated as regex.
72
- # Our valid special characters, `?` and `*`,
73
- # will be escaped so...
74
- pattern = Regexp.quote(pattern)
75
-
76
- # ...we account for that here:
77
- pattern.gsub!('\?', '.') # Any single character
78
- pattern.gsub!('\*', '.*') # Any substring
79
-
80
- # Patterns have to match the whole input string
81
- pattern = "\\A#{pattern}\\z"
82
-
83
- Regexp.new(pattern)
84
- end
85
57
  end
86
58
  end
87
59
  end
@@ -26,6 +26,7 @@ module Datadog
26
26
  :parent_id,
27
27
  :resource,
28
28
  :service,
29
+ :links,
29
30
  :type,
30
31
  :start_time,
31
32
  :status,
@@ -58,7 +59,8 @@ module Datadog
58
59
  status: 0,
59
60
  type: nil,
60
61
  trace_id: nil,
61
- service_entry: nil
62
+ service_entry: nil,
63
+ links: nil
62
64
  )
63
65
  @name = Core::Utils::SafeDup.frozen_or_dup(name)
64
66
  @service = Core::Utils::SafeDup.frozen_or_dup(service)
@@ -86,6 +88,8 @@ module Datadog
86
88
 
87
89
  @service_entry = service_entry
88
90
 
91
+ @links = links || []
92
+
89
93
  # Mark with the service entry span metric, if applicable
90
94
  set_metric(Metadata::Ext::TAG_TOP_LEVEL, 1.0) if service_entry
91
95
  end
@@ -136,7 +140,8 @@ module Datadog
136
140
  service: @service,
137
141
  span_id: @id,
138
142
  trace_id: @trace_id,
139
- type: @type
143
+ type: @type,
144
+ span_links: @links.map(&:to_hash)
140
145
  }
141
146
 
142
147
  if stopped?
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ # SpanLink represents a causal link between two spans.
6
+ # @public_api
7
+ class SpanLink
8
+ # @!attribute [r] span_id
9
+ # Datadog id for the currently active span.
10
+ # @return [Integer]
11
+ attr_reader :span_id
12
+
13
+ # @!attribute [r] trace_id
14
+ # Datadog id for the currently active trace.
15
+ # @return [Integer]
16
+ attr_reader :trace_id
17
+
18
+ # @!attribute [r] attributes
19
+ # Datadog-specific tags that support richer distributed tracing association.
20
+ # @return [Hash<String,String>]
21
+ attr_reader :attributes
22
+
23
+ # @!attribute [r] trace_flags
24
+ # The W3C "trace-flags" extracted from a distributed context. This field is an 8-bit unsigned integer.
25
+ # @return [Integer]
26
+ # @see https://www.w3.org/TR/trace-context/#trace-flags
27
+ attr_reader :trace_flags
28
+
29
+ # @!attribute [r] trace_state
30
+ # The W3C "tracestate" extracted from a distributed context.
31
+ # This field is a string representing vendor-specific distribution data.
32
+ # The `dd=` entry is removed from `trace_state` as its value is dynamically calculated
33
+ # on every propagation injection.
34
+ # @return [String]
35
+ # @see https://www.w3.org/TR/trace-context/#tracestate-header
36
+ attr_reader :trace_state
37
+
38
+ # @!attribute [r] dropped_attributes
39
+ # The number of attributes that were discarded due to serialization limits.
40
+ # @return [Integer]
41
+ attr_reader :dropped_attributes
42
+
43
+ def initialize(
44
+ digest,
45
+ attributes: nil
46
+ )
47
+ @span_id = digest.span_id
48
+ @trace_id = digest.trace_id
49
+ @trace_flags = if digest.trace_sampling_priority.nil?
50
+ nil
51
+ elsif digest.trace_sampling_priority > 0
52
+ 1
53
+ else
54
+ 0
55
+ end
56
+ @trace_state = digest.trace_state && digest.trace_state.dup
57
+ @dropped_attributes = 0
58
+ @attributes = (attributes && attributes.dup) || {}
59
+ end
60
+
61
+ def to_hash
62
+ h = {
63
+ span_id: @span_id || 0,
64
+ trace_id: Tracing::Utils::TraceId.to_low_order(@trace_id) || 0,
65
+ }
66
+ # Optimization: Hash non empty attributes
67
+ if @trace_id.to_i > Tracing::Utils::EXTERNAL_MAX_ID
68
+ h[:trace_id_high] =
69
+ Tracing::Utils::TraceId.to_high_order(@trace_id)
70
+ end
71
+ unless @attributes&.empty?
72
+ h[:attributes] = {}
73
+ @attributes.each do |k1, v1|
74
+ Tracing::Utils.serialize_attribute(k1, v1).each do |new_k1, value|
75
+ h[:attributes][new_k1] = value.to_s
76
+ end
77
+ end
78
+ end
79
+ h[:dropped_attributes_count] = @dropped_attributes if @dropped_attributes > 0
80
+ h[:tracestate] = @trace_state if @trace_state
81
+ # If traceflags set, the high bit (bit 31) should be set to 1 (uint32).
82
+ # This helps us distinguish between when the sample decision is zero or not set
83
+ h[:flags] = if @trace_flags.nil?
84
+ 0
85
+ else
86
+ @trace_flags | (1 << 31)
87
+ end
88
+ h
89
+ end
90
+ end
91
+ end
92
+ end
@@ -35,9 +35,7 @@ module Datadog
35
35
  :start_time,
36
36
  :trace_id,
37
37
  :type
38
-
39
- attr_accessor \
40
- :status
38
+ attr_accessor :links, :status
41
39
 
42
40
  def initialize(
43
41
  name,
@@ -49,7 +47,8 @@ module Datadog
49
47
  start_time: nil,
50
48
  tags: nil,
51
49
  trace_id: nil,
52
- type: nil
50
+ type: nil,
51
+ links: nil
53
52
  )
54
53
  # Ensure dynamically created strings are UTF-8 encoded.
55
54
  #
@@ -66,6 +65,8 @@ module Datadog
66
65
  @trace_id = trace_id || Tracing::Utils::TraceId.next_id
67
66
 
68
67
  @status = 0
68
+ # stores array of span links
69
+ @links = links || []
69
70
 
70
71
  # start_time and end_time track wall clock. In Ruby, wall clock
71
72
  # has less accuracy than monotonic clock, so if possible we look to only use wall clock
@@ -452,6 +453,7 @@ module Datadog
452
453
  status: @status,
453
454
  type: @type,
454
455
  trace_id: @trace_id,
456
+ links: @links,
455
457
  service_entry: parent.nil? || (service && parent.service != service)
456
458
  )
457
459
  end
@@ -8,6 +8,7 @@ require_relative 'metadata/tagging'
8
8
  require_relative 'sampling/ext'
9
9
  require_relative 'span_operation'
10
10
  require_relative 'trace_digest'
11
+ require_relative 'correlation'
11
12
  require_relative 'trace_segment'
12
13
  require_relative 'utils'
13
14
 
@@ -306,6 +307,17 @@ module Datadog
306
307
  ).freeze
307
308
  end
308
309
 
310
+ def to_correlation
311
+ # Resolve current span ID
312
+ span_id = @active_span && @active_span.id
313
+ span_id ||= @parent_span_id unless finished?
314
+
315
+ Correlation::Identifier.new(
316
+ trace_id: @id,
317
+ span_id: span_id
318
+ )
319
+ end
320
+
309
321
  # Returns a copy of this trace suitable for forks (w/o spans.)
310
322
  # Used for continuation of traces across forks.
311
323
  def fork_clone
@@ -224,9 +224,10 @@ module Datadog
224
224
  # @return [Datadog::Tracing::Correlation::Identifier] correlation object
225
225
  def active_correlation(key = nil)
226
226
  trace = active_trace(key)
227
- Correlation.identifier_from_digest(
228
- trace && trace.to_digest
229
- )
227
+
228
+ return Datadog::Tracing::Correlation::Identifier.new unless trace
229
+
230
+ trace.to_correlation
230
231
  end
231
232
 
232
233
  # Setup a new trace to continue from where another
@@ -58,7 +58,7 @@ module Datadog
58
58
  def to_msgpack(packer = nil)
59
59
  packer ||= MessagePack::Packer.new
60
60
 
61
- number_of_elements_to_write = 10
61
+ number_of_elements_to_write = 11
62
62
 
63
63
  if span.stopped?
64
64
  packer.write_map_header(number_of_elements_to_write + 2) # Set header with how many elements in the map
@@ -93,6 +93,8 @@ module Datadog
93
93
  packer.write(span.meta)
94
94
  packer.write('metrics')
95
95
  packer.write(span.metrics)
96
+ packer.write('span_links')
97
+ packer.write(span.links.map(&:to_hash))
96
98
  packer.write('error')
97
99
  packer.write(span.status)
98
100
  packer
@@ -45,6 +45,22 @@ module Datadog
45
45
  @id_rng = Random.new
46
46
  end
47
47
 
48
+ # Serialize values into Datadog span tags and metrics.
49
+ # Notably, arrays are exploded into many keys, each with
50
+ # a numeric suffix representing the array index, for example:
51
+ # `'foo' => ['a','b']` becomes `'foo.0' => 'a', 'foo.1' => 'b'`
52
+ def self.serialize_attribute(key, value)
53
+ if value.is_a?(Array)
54
+ value.flat_map.with_index do |v, idx|
55
+ serialize_attribute("#{key}.#{idx}", v)
56
+ end
57
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
58
+ [[key, value.to_s]]
59
+ else
60
+ [[key, value]]
61
+ end
62
+ end
63
+
48
64
  private_class_method :id_rng, :reset!
49
65
 
50
66
  # The module handles bitwise operation for trace id
@@ -5,7 +5,7 @@ module Datadog
5
5
  MAJOR = 2
6
6
  MINOR = 0
7
7
  PATCH = 0
8
- PRE = 'beta1'
8
+ PRE = 'rc1'
9
9
  BUILD = nil
10
10
  # PRE and BUILD above are modified for dev gems during gem build GHA workflow
11
11