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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +181 -1
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +1 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +40 -32
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +23 -12
- data/ext/datadog_profiling_native_extension/crashtracker.c +108 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +9 -23
- data/ext/datadog_profiling_native_extension/heap_recorder.c +81 -4
- data/ext/datadog_profiling_native_extension/heap_recorder.h +12 -1
- data/ext/datadog_profiling_native_extension/http_transport.c +1 -94
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.c +86 -0
- data/ext/datadog_profiling_native_extension/libdatadog_helpers.h +4 -0
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +2 -12
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +25 -86
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +3 -5
- data/ext/datadog_profiling_native_extension/stack_recorder.c +161 -62
- data/lib/datadog/appsec/contrib/devise/tracking.rb +8 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -13
- data/lib/datadog/appsec/event.rb +2 -2
- data/lib/datadog/core/configuration/components.rb +2 -1
- data/lib/datadog/core/configuration/option.rb +7 -5
- data/lib/datadog/core/configuration/settings.rb +34 -79
- data/lib/datadog/core/configuration.rb +20 -4
- data/lib/datadog/core/environment/platform.rb +7 -1
- data/lib/datadog/core/remote/client/capabilities.rb +2 -1
- data/lib/datadog/core/remote/client.rb +1 -5
- data/lib/datadog/core/remote/configuration/repository.rb +1 -1
- data/lib/datadog/core/remote/dispatcher.rb +3 -3
- data/lib/datadog/core/remote/transport/http/config.rb +5 -5
- data/lib/datadog/core/telemetry/client.rb +18 -10
- data/lib/datadog/core/telemetry/emitter.rb +9 -13
- data/lib/datadog/core/telemetry/event.rb +247 -57
- data/lib/datadog/core/telemetry/ext.rb +1 -0
- data/lib/datadog/core/telemetry/heartbeat.rb +1 -3
- data/lib/datadog/core/telemetry/http/ext.rb +4 -1
- data/lib/datadog/core/telemetry/http/response.rb +4 -0
- data/lib/datadog/core/telemetry/http/transport.rb +9 -4
- data/lib/datadog/core/telemetry/request.rb +59 -0
- data/lib/datadog/core/utils/base64.rb +22 -0
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +19 -2
- data/lib/datadog/opentelemetry/sdk/trace/span.rb +3 -17
- data/lib/datadog/profiling/collectors/code_provenance.rb +10 -4
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +25 -0
- data/lib/datadog/profiling/component.rb +49 -17
- data/lib/datadog/profiling/crashtracker.rb +91 -0
- data/lib/datadog/profiling/exporter.rb +6 -3
- data/lib/datadog/profiling/http_transport.rb +7 -11
- data/lib/datadog/profiling/load_native_extension.rb +14 -1
- data/lib/datadog/profiling/profiler.rb +9 -2
- data/lib/datadog/profiling/stack_recorder.rb +6 -2
- data/lib/datadog/profiling.rb +12 -0
- data/lib/datadog/tracing/component.rb +5 -1
- data/lib/datadog/tracing/configuration/dynamic.rb +39 -1
- data/lib/datadog/tracing/configuration/settings.rb +1 -0
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/action_view/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +1 -0
- data/lib/datadog/tracing/contrib/active_record/integration.rb +11 -1
- data/lib/datadog/tracing/contrib/active_support/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +43 -0
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +43 -5
- data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +1 -1
- data/lib/datadog/tracing/correlation.rb +3 -4
- data/lib/datadog/tracing/remote.rb +5 -1
- data/lib/datadog/tracing/sampling/ext.rb +5 -1
- data/lib/datadog/tracing/sampling/matcher.rb +75 -26
- data/lib/datadog/tracing/sampling/rule.rb +27 -4
- data/lib/datadog/tracing/sampling/rule_sampler.rb +19 -1
- data/lib/datadog/tracing/sampling/span/matcher.rb +13 -41
- data/lib/datadog/tracing/span.rb +7 -2
- data/lib/datadog/tracing/span_link.rb +92 -0
- data/lib/datadog/tracing/span_operation.rb +6 -4
- data/lib/datadog/tracing/trace_operation.rb +12 -0
- data/lib/datadog/tracing/tracer.rb +4 -3
- data/lib/datadog/tracing/transport/serializable_trace.rb +3 -1
- data/lib/datadog/tracing/utils.rb +16 -0
- data/lib/datadog/version.rb +1 -1
- metadata +10 -31
- data/lib/datadog/core/telemetry/collector.rb +0 -248
- data/lib/datadog/core/telemetry/v1/app_event.rb +0 -59
- data/lib/datadog/core/telemetry/v1/application.rb +0 -94
- data/lib/datadog/core/telemetry/v1/configuration.rb +0 -27
- data/lib/datadog/core/telemetry/v1/dependency.rb +0 -45
- data/lib/datadog/core/telemetry/v1/host.rb +0 -59
- data/lib/datadog/core/telemetry/v1/install_signature.rb +0 -38
- data/lib/datadog/core/telemetry/v1/integration.rb +0 -66
- data/lib/datadog/core/telemetry/v1/product.rb +0 -36
- data/lib/datadog/core/telemetry/v1/telemetry_request.rb +0 -108
- data/lib/datadog/core/telemetry/v2/app_client_configuration_change.rb +0 -41
- 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 =
|
40
|
-
@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 =
|
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
|
-
|
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
|
-
#
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
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
|
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
|
-
|
48
|
-
# based on the return value of a provided block.
|
49
|
-
class ProcMatcher < Matcher
|
50
|
-
attr_reader :block
|
96
|
+
private
|
51
97
|
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
60
|
-
|
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
|
-
|
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(
|
56
|
-
|
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
|
-
|
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 =
|
35
|
-
@service =
|
36
|
+
@name = Sampling::Matcher.glob_to_regex(name_pattern)
|
37
|
+
@service = Sampling::Matcher.glob_to_regex(service_pattern)
|
36
38
|
end
|
37
39
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
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
|
data/lib/datadog/tracing/span.rb
CHANGED
@@ -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
|
-
|
228
|
-
|
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 =
|
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
|