datadog 2.41.0 → 2.42.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -2
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +12 -24
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +110 -61
- data/ext/datadog_profiling_native_extension/collectors_stack.c +11 -3
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +141 -103
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +7 -3
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -10
- data/ext/datadog_profiling_native_extension/extconf.rb +52 -93
- data/ext/datadog_profiling_native_extension/heap_recorder.c +338 -255
- data/ext/datadog_profiling_native_extension/heap_recorder.h +46 -31
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -24
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +509 -440
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +15 -3
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -79
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +0 -7
- data/ext/datadog_profiling_native_extension/stack_recorder.c +93 -61
- data/ext/datadog_profiling_native_extension/stack_recorder.h +12 -4
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -10
- data/ext/libdatadog_api/di.c +10 -0
- data/ext/libdatadog_api/extconf.rb +3 -0
- data/ext/libdatadog_api/init.c +2 -0
- data/ext/libdatadog_api/otel_thread_context.c +232 -0
- data/ext/libdatadog_api/otel_thread_context.h +5 -0
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/assets/blocked.html +1 -108
- data/lib/datadog/core/configuration/components.rb +1 -0
- data/lib/datadog/core/crashtracking/component.rb +5 -1
- data/lib/datadog/data_streams/pathway_context.rb +20 -22
- data/lib/datadog/data_streams/processor.rb +31 -0
- data/lib/datadog/di/instrumenter.rb +41 -1
- data/lib/datadog/di/logger.rb +2 -2
- data/lib/datadog/di/probe.rb +9 -1
- data/lib/datadog/di/probe_notification_builder.rb +1 -0
- data/lib/datadog/di/remote.rb +3 -3
- data/lib/datadog/open_feature/evaluation_engine.rb +29 -3
- data/lib/datadog/open_feature/exposures/event.rb +10 -3
- data/lib/datadog/open_feature/ext.rb +19 -0
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +236 -80
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +179 -68
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +24 -21
- data/lib/datadog/open_feature/native_evaluator.rb +33 -6
- data/lib/datadog/open_feature/noop_evaluator.rb +5 -0
- data/lib/datadog/open_feature/provider.rb +11 -2
- data/lib/datadog/opentelemetry/sdk/propagator.rb +1 -1
- data/lib/datadog/opentelemetry/trace.rb +3 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +0 -4
- data/lib/datadog/profiling/component.rb +8 -16
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
- data/lib/datadog/tracing/distributed/baggage.rb +0 -1
- data/lib/datadog/tracing/distributed/datadog.rb +3 -3
- data/lib/datadog/tracing/distributed/propagation.rb +3 -0
- data/lib/datadog/tracing/distributed/trace_context.rb +14 -271
- data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
- data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
- data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
- data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
- data/lib/datadog/tracing/otel_thread_context.rb +30 -0
- data/lib/datadog/tracing/remote.rb +195 -27
- data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
- data/lib/datadog/tracing/trace_digest.rb +22 -4
- data/lib/datadog/tracing/trace_operation.rb +22 -10
- data/lib/datadog/tracing/tracer.rb +5 -5
- data/lib/datadog/version.rb +1 -1
- metadata +14 -8
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -69
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/thread.h>
|
|
3
|
+
|
|
4
|
+
#include "datadog_ruby_common.h"
|
|
5
|
+
#include "otel_thread_context.h"
|
|
6
|
+
|
|
7
|
+
#ifdef __linux__
|
|
8
|
+
#include <datadog/otel-thread-ctx.h>
|
|
9
|
+
extern __thread const uint8_t *otel_thread_ctx_v1;
|
|
10
|
+
|
|
11
|
+
static void otel_ctx_handle_free(void *handle) {
|
|
12
|
+
ddog_otel_thread_ctx_free((ddog_ThreadContextHandle *) handle);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static const rb_data_type_t otel_ctx_handle_t = {
|
|
16
|
+
.wrap_struct_name = "Datadog::Tracing::OTelThreadContext handle",
|
|
17
|
+
.function = {.dfree = otel_ctx_handle_free},
|
|
18
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
19
|
+
};
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
static ID fiber_context_slot;
|
|
23
|
+
|
|
24
|
+
DDTRACE_UNUSED static bool otel_context_enabled = false;
|
|
25
|
+
|
|
26
|
+
static VALUE native_set(VALUE _self, VALUE trace_id, VALUE span_id, VALUE local_root_span_id);
|
|
27
|
+
static VALUE native_supported_p(VALUE _self);
|
|
28
|
+
static VALUE native_enable(VALUE _self);
|
|
29
|
+
static VALUE native_read(VALUE _self);
|
|
30
|
+
|
|
31
|
+
void otel_thread_context_init(VALUE tracing_module) {
|
|
32
|
+
fiber_context_slot = rb_intern("__dd_otel_fiber_context");
|
|
33
|
+
|
|
34
|
+
VALUE otel_thread_context_module = rb_define_module_under(tracing_module, "OTelThreadContext");
|
|
35
|
+
|
|
36
|
+
rb_define_singleton_method(otel_thread_context_module, "_native_enable", native_enable, 0);
|
|
37
|
+
rb_define_singleton_method(otel_thread_context_module, "_native_set", native_set, 3);
|
|
38
|
+
rb_define_singleton_method(otel_thread_context_module, "_native_supported?", native_supported_p, 0);
|
|
39
|
+
|
|
40
|
+
VALUE testing_module = rb_define_module_under(otel_thread_context_module, "Testing");
|
|
41
|
+
rb_define_singleton_method(testing_module, "_native_read", native_read, 0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#ifdef __linux__
|
|
45
|
+
static ddog_ThreadContextHandle *get_fiber_handle_for(VALUE thread) {
|
|
46
|
+
VALUE obj = rb_thread_local_aref(thread, fiber_context_slot);
|
|
47
|
+
|
|
48
|
+
if (!rb_typeddata_is_kind_of(obj, &otel_ctx_handle_t)) return NULL;
|
|
49
|
+
|
|
50
|
+
return RTYPEDDATA_DATA(obj);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static ddog_ThreadContextHandle *get_current_fiber_handle(void) {
|
|
54
|
+
return get_fiber_handle_for(rb_thread_current());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static void store_current_fiber_handle(ddog_ThreadContextHandle *handle) {
|
|
58
|
+
VALUE obj = TypedData_Wrap_Struct(rb_cObject, &otel_ctx_handle_t, handle);
|
|
59
|
+
rb_thread_local_aset(rb_thread_current(), fiber_context_slot, obj);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#ifdef RUBY_INTERNAL_THREAD_EVENT_EXITED
|
|
63
|
+
static void on_thread_exited(
|
|
64
|
+
DDTRACE_UNUSED rb_event_flag_t event,
|
|
65
|
+
DDTRACE_UNUSED const rb_internal_thread_event_data_t *event_data,
|
|
66
|
+
DDTRACE_UNUSED void *user_data
|
|
67
|
+
) {
|
|
68
|
+
ddog_otel_thread_ctx_detach();
|
|
69
|
+
}
|
|
70
|
+
#else
|
|
71
|
+
static void on_thread_end(
|
|
72
|
+
DDTRACE_UNUSED rb_event_flag_t evflag,
|
|
73
|
+
DDTRACE_UNUSED VALUE data,
|
|
74
|
+
DDTRACE_UNUSED VALUE self,
|
|
75
|
+
DDTRACE_UNUSED ID mid,
|
|
76
|
+
DDTRACE_UNUSED VALUE klass
|
|
77
|
+
) {
|
|
78
|
+
ddog_otel_thread_ctx_detach();
|
|
79
|
+
}
|
|
80
|
+
#endif
|
|
81
|
+
|
|
82
|
+
#ifdef HAVE_RUBY_THREAD_STORAGE_API
|
|
83
|
+
// This function is declared in internal/thread.h.
|
|
84
|
+
// It returns 0 when the current thread sits in a blocking region.
|
|
85
|
+
int ruby_thread_has_gvl_p(void);
|
|
86
|
+
|
|
87
|
+
static bool in_blocking_region(void) {
|
|
88
|
+
#ifdef HAVE_RUBY_THREAD_HAS_GVL_P
|
|
89
|
+
return ruby_thread_has_gvl_p() == 0;
|
|
90
|
+
#else
|
|
91
|
+
return false;
|
|
92
|
+
#endif
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Under M:N a Ruby thread can migrate between native threads,
|
|
96
|
+
// so we need to detach the context when the Ruby thread gets suspended.
|
|
97
|
+
static void on_thread_suspended(
|
|
98
|
+
DDTRACE_UNUSED rb_event_flag_t event,
|
|
99
|
+
DDTRACE_UNUSED const rb_internal_thread_event_data_t *event_data,
|
|
100
|
+
DDTRACE_UNUSED void *user_data
|
|
101
|
+
) {
|
|
102
|
+
// Ruby pins threads that release GVL to call into a native extension,
|
|
103
|
+
// so we know the thread will not migrate.
|
|
104
|
+
if (in_blocking_region()) return;
|
|
105
|
+
|
|
106
|
+
ddog_otel_thread_ctx_detach();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static void on_thread_resumed(
|
|
110
|
+
DDTRACE_UNUSED rb_event_flag_t event,
|
|
111
|
+
const rb_internal_thread_event_data_t *event_data,
|
|
112
|
+
DDTRACE_UNUSED void *user_data
|
|
113
|
+
) {
|
|
114
|
+
ddog_ThreadContextHandle *handle = get_fiber_handle_for(event_data->thread);
|
|
115
|
+
|
|
116
|
+
if (handle != NULL) ddog_otel_thread_ctx_attach(handle);
|
|
117
|
+
}
|
|
118
|
+
#endif
|
|
119
|
+
|
|
120
|
+
static void on_fiber_switch(
|
|
121
|
+
DDTRACE_UNUSED rb_event_flag_t evflag,
|
|
122
|
+
DDTRACE_UNUSED VALUE data,
|
|
123
|
+
DDTRACE_UNUSED VALUE self,
|
|
124
|
+
DDTRACE_UNUSED ID mid,
|
|
125
|
+
DDTRACE_UNUSED VALUE klass
|
|
126
|
+
) {
|
|
127
|
+
ddog_ThreadContextHandle *handle = get_current_fiber_handle();
|
|
128
|
+
|
|
129
|
+
if (handle != NULL) {
|
|
130
|
+
ddog_otel_thread_ctx_attach(handle);
|
|
131
|
+
} else {
|
|
132
|
+
ddog_otel_thread_ctx_detach();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
#endif
|
|
136
|
+
|
|
137
|
+
static VALUE native_enable(DDTRACE_UNUSED VALUE _self) {
|
|
138
|
+
#ifdef __linux__
|
|
139
|
+
if (otel_context_enabled) return Qtrue;
|
|
140
|
+
otel_context_enabled = true;
|
|
141
|
+
|
|
142
|
+
#ifdef RUBY_INTERNAL_THREAD_EVENT_EXITED
|
|
143
|
+
rb_internal_thread_add_event_hook(on_thread_exited, RUBY_INTERNAL_THREAD_EVENT_EXITED, NULL);
|
|
144
|
+
#else
|
|
145
|
+
rb_add_event_hook(on_thread_end, RUBY_EVENT_THREAD_END, Qnil);
|
|
146
|
+
#endif
|
|
147
|
+
|
|
148
|
+
// These hooks only matter under the M:N thread scheduler (Ruby 3.3+),
|
|
149
|
+
// where a Ruby thread can migrate between native threads.
|
|
150
|
+
#ifdef HAVE_RUBY_THREAD_STORAGE_API
|
|
151
|
+
rb_internal_thread_add_event_hook(on_thread_suspended, RUBY_INTERNAL_THREAD_EVENT_SUSPENDED, NULL);
|
|
152
|
+
rb_internal_thread_add_event_hook(on_thread_resumed, RUBY_INTERNAL_THREAD_EVENT_RESUMED, NULL);
|
|
153
|
+
#endif
|
|
154
|
+
|
|
155
|
+
rb_add_event_hook(on_fiber_switch, RUBY_EVENT_FIBER_SWITCH, Qnil);
|
|
156
|
+
|
|
157
|
+
return Qtrue;
|
|
158
|
+
#else
|
|
159
|
+
return Qfalse;
|
|
160
|
+
#endif
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static VALUE native_supported_p(DDTRACE_UNUSED VALUE _self) {
|
|
164
|
+
#ifdef __linux__
|
|
165
|
+
return Qtrue;
|
|
166
|
+
#else
|
|
167
|
+
return Qfalse;
|
|
168
|
+
#endif
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static VALUE native_set(
|
|
172
|
+
DDTRACE_UNUSED VALUE _self,
|
|
173
|
+
DDTRACE_UNUSED VALUE trace_id,
|
|
174
|
+
DDTRACE_UNUSED VALUE span_id,
|
|
175
|
+
DDTRACE_UNUSED VALUE local_root_span_id
|
|
176
|
+
) {
|
|
177
|
+
#ifdef __linux__
|
|
178
|
+
if (!otel_context_enabled) return Qfalse;
|
|
179
|
+
|
|
180
|
+
uint8_t trace_id_bytes[16];
|
|
181
|
+
uint8_t span_id_bytes[8];
|
|
182
|
+
uint8_t local_root_span_id_bytes[8];
|
|
183
|
+
|
|
184
|
+
const int BIG_ENDIAN_PACK_FLAGS = INTEGER_PACK_MSWORD_FIRST | INTEGER_PACK_BIG_ENDIAN;
|
|
185
|
+
rb_integer_pack(trace_id, trace_id_bytes, sizeof(trace_id_bytes), 1, 0, BIG_ENDIAN_PACK_FLAGS);
|
|
186
|
+
rb_integer_pack(span_id, span_id_bytes, sizeof(span_id_bytes), 1, 0, BIG_ENDIAN_PACK_FLAGS);
|
|
187
|
+
rb_integer_pack(local_root_span_id, local_root_span_id_bytes, sizeof(local_root_span_id_bytes), 1, 0, BIG_ENDIAN_PACK_FLAGS);
|
|
188
|
+
|
|
189
|
+
ddog_ThreadContextHandle *handle = get_current_fiber_handle();
|
|
190
|
+
|
|
191
|
+
if (handle == NULL) {
|
|
192
|
+
handle = ddog_otel_thread_ctx_new(&trace_id_bytes, &span_id_bytes, 0, &local_root_span_id_bytes);
|
|
193
|
+
store_current_fiber_handle(handle);
|
|
194
|
+
ddog_otel_thread_ctx_attach(handle);
|
|
195
|
+
} else {
|
|
196
|
+
// FIXME: add a method to libdatadog to update the context record before attaching it
|
|
197
|
+
ddog_otel_thread_ctx_attach(handle);
|
|
198
|
+
ddog_otel_thread_ctx_update(&trace_id_bytes, &span_id_bytes, 0, &local_root_span_id_bytes);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return Qtrue;
|
|
202
|
+
#else
|
|
203
|
+
return Qfalse;
|
|
204
|
+
#endif
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static VALUE native_read(DDTRACE_UNUSED VALUE _self) {
|
|
208
|
+
#ifdef __linux__
|
|
209
|
+
const uint8_t *raw = otel_thread_ctx_v1;
|
|
210
|
+
if (!raw) return Qnil;
|
|
211
|
+
|
|
212
|
+
uint16_t attrs_data_size = (uint16_t) raw[26] | ((uint16_t) raw[27] << 8);
|
|
213
|
+
|
|
214
|
+
if (attrs_data_size > ddog_MAX_ATTRS_DATA_SIZE) {
|
|
215
|
+
raise_error(
|
|
216
|
+
rb_eRuntimeError,
|
|
217
|
+
"Invalid OTel thread context record: attrs_data_size (%d) exceeds maximum (%d)",
|
|
218
|
+
attrs_data_size, ddog_MAX_ATTRS_DATA_SIZE
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
VALUE result = rb_hash_new();
|
|
223
|
+
rb_hash_aset(result, ID2SYM(rb_intern("trace_id")), rb_str_new((const char *) raw, 16));
|
|
224
|
+
rb_hash_aset(result, ID2SYM(rb_intern("span_id")), rb_str_new((const char *) (raw + 16), 8));
|
|
225
|
+
rb_hash_aset(result, ID2SYM(rb_intern("valid")), rb_str_new((const char *) (raw + 24), 1));
|
|
226
|
+
rb_hash_aset(result, ID2SYM(rb_intern("attrs")), rb_str_new((const char *) (raw + 28), attrs_data_size));
|
|
227
|
+
|
|
228
|
+
return result;
|
|
229
|
+
#else
|
|
230
|
+
return Qnil;
|
|
231
|
+
#endif
|
|
232
|
+
}
|
|
@@ -10,7 +10,7 @@ module Datadog
|
|
|
10
10
|
module LibdatadogExtconfHelpers
|
|
11
11
|
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
|
12
12
|
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
|
13
|
-
LIBDATADOG_VERSION = "~> 40.0.0.
|
|
13
|
+
LIBDATADOG_VERSION = "~> 40.0.0.2.0"
|
|
14
14
|
|
|
15
15
|
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
|
16
16
|
# libdatadog are moved after the extension gets compiled.
|
|
@@ -1,108 +1 @@
|
|
|
1
|
-
|
|
2
|
-
<!DOCTYPE html>
|
|
3
|
-
<html lang="en">
|
|
4
|
-
|
|
5
|
-
<head>
|
|
6
|
-
<meta charset="UTF-8">
|
|
7
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
8
|
-
<title>You've been blocked</title>
|
|
9
|
-
<style>
|
|
10
|
-
a,
|
|
11
|
-
body,
|
|
12
|
-
div,
|
|
13
|
-
html,
|
|
14
|
-
span {
|
|
15
|
-
margin: 0;
|
|
16
|
-
padding: 0;
|
|
17
|
-
border: 0;
|
|
18
|
-
font-size: 100%;
|
|
19
|
-
font: inherit;
|
|
20
|
-
vertical-align: baseline
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
body {
|
|
24
|
-
background: -webkit-radial-gradient(26% 19%, circle, #fff, #f4f7f9);
|
|
25
|
-
background: radial-gradient(circle at 26% 19%, #fff, #f4f7f9);
|
|
26
|
-
display: -webkit-box;
|
|
27
|
-
display: -ms-flexbox;
|
|
28
|
-
display: flex;
|
|
29
|
-
-webkit-box-pack: center;
|
|
30
|
-
-ms-flex-pack: center;
|
|
31
|
-
justify-content: center;
|
|
32
|
-
-webkit-box-align: center;
|
|
33
|
-
-ms-flex-align: center;
|
|
34
|
-
align-items: center;
|
|
35
|
-
-ms-flex-line-pack: center;
|
|
36
|
-
align-content: center;
|
|
37
|
-
width: 100%;
|
|
38
|
-
min-height: 100vh;
|
|
39
|
-
line-height: 1;
|
|
40
|
-
flex-direction: column
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
p {
|
|
44
|
-
display: block
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
main {
|
|
49
|
-
text-align: center;
|
|
50
|
-
flex: 1;
|
|
51
|
-
display: -webkit-box;
|
|
52
|
-
display: -ms-flexbox;
|
|
53
|
-
display: flex;
|
|
54
|
-
-webkit-box-pack: center;
|
|
55
|
-
-ms-flex-pack: center;
|
|
56
|
-
justify-content: center;
|
|
57
|
-
-webkit-box-align: center;
|
|
58
|
-
-ms-flex-align: center;
|
|
59
|
-
align-items: center;
|
|
60
|
-
-ms-flex-line-pack: center;
|
|
61
|
-
align-content: center;
|
|
62
|
-
flex-direction: column
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
p {
|
|
66
|
-
font-size: 18px;
|
|
67
|
-
line-height: normal;
|
|
68
|
-
color: #646464;
|
|
69
|
-
font-family: sans-serif;
|
|
70
|
-
font-weight: 400
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
a {
|
|
74
|
-
color: #4842b7
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
footer {
|
|
78
|
-
width: 100%;
|
|
79
|
-
text-align: center
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
footer p {
|
|
83
|
-
font-size: 16px
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.security-response-id {
|
|
87
|
-
font-size:14px;
|
|
88
|
-
color:#999;
|
|
89
|
-
margin-top:20px;
|
|
90
|
-
font-family:monospace
|
|
91
|
-
}
|
|
92
|
-
</style>
|
|
93
|
-
</head>
|
|
94
|
-
|
|
95
|
-
<body>
|
|
96
|
-
<main>
|
|
97
|
-
<p>Sorry, you cannot access this page. Please contact the customer service team.</p>
|
|
98
|
-
<p class="security-response-id">Security Response ID: [security_response_id]</p>
|
|
99
|
-
</main>
|
|
100
|
-
<footer>
|
|
101
|
-
<p>Security provided by <a
|
|
102
|
-
href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/"
|
|
103
|
-
target="_blank"
|
|
104
|
-
rel="noopener noreferrer">Datadog</a></p>
|
|
105
|
-
</footer>
|
|
106
|
-
</body>
|
|
107
|
-
|
|
108
|
-
</html>
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>You've been blocked</title><style>a,body,div,html,span{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{background:-webkit-radial-gradient(26% 19%,circle,#fff,#f4f7f9);background:radial-gradient(circle at 26% 19%,#fff,#f4f7f9);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;width:100%;min-height:100vh;line-height:1;flex-direction:column}p{display:block}main{text-align:center;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;flex-direction:column}p{font-size:18px;line-height:normal;color:#646464;font-family:sans-serif;font-weight:400}a{color:#4842b7}footer{width:100%;text-align:center}footer p{font-size:16px}.security-response-id{font-size:14px;color:#999;margin-top:20px;font-family:monospace}</style></head><body><main><p>Sorry, you cannot access this page. Please contact the customer service team.</p><p class="security-response-id">Security Response ID: [security_response_id]</p></main><footer><p>Security provided by <a href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/" target="_blank" rel="noopener noreferrer">Datadog</a></p></footer></body></html>
|
|
@@ -44,7 +44,11 @@ module Datadog
|
|
|
44
44
|
# Reports unhandled exceptions to the crash tracker if available and appropriate.
|
|
45
45
|
# This is called from the at_exit hook to report unhandled exceptions.
|
|
46
46
|
def self.report_unhandled_exception(exception)
|
|
47
|
-
return unless exception &&
|
|
47
|
+
return unless exception &&
|
|
48
|
+
!exception.is_a?(SystemExit) &&
|
|
49
|
+
!exception.is_a?(SignalException) &&
|
|
50
|
+
!exception.is_a?(NoMemoryError) &&
|
|
51
|
+
!exception.is_a?(SystemStackError)
|
|
48
52
|
|
|
49
53
|
begin
|
|
50
54
|
crashtracker = Datadog.send(:components, allow_initialization: false)&.crashtracker
|
|
@@ -98,40 +98,38 @@ module Datadog
|
|
|
98
98
|
end
|
|
99
99
|
private_class_method :decode
|
|
100
100
|
|
|
101
|
-
# Encode
|
|
101
|
+
# Encode a signed 64-bit integer using ZigZag + LEB128 variable-length encoding.
|
|
102
102
|
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
103
|
+
# dd-trace-go's DSM pathway encoder (internal/datastreams/propagator.go, via
|
|
104
|
+
# sketches-go's ddsketch/encoding package) ZigZag-maps the signed value before
|
|
105
|
+
# applying plain unsigned LEB128 (DWARF5 section 7.6:
|
|
106
|
+
# https://dwarfstd.org/doc/DWARF5.pdf#page=301). This implementation previously
|
|
107
|
+
# skipped the ZigZag step, which silently decoded to the wrong timestamp on any
|
|
108
|
+
# dd-trace-go consumer -- see https://github.com/DataDog/dd-trace-go/issues/5163.
|
|
106
109
|
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
110
|
+
# ZigZag maps signed values to unsigned ones so small-magnitude negative numbers
|
|
111
|
+
# still encode to few bytes: 0, -1, 1, -2, 2, ... -> 0, 1, 2, 3, 4, ...
|
|
109
112
|
#
|
|
110
|
-
# @param value [Integer]
|
|
113
|
+
# @param value [Integer] Signed integer value to encode
|
|
111
114
|
# @return [String] Binary string of encoded bytes
|
|
112
115
|
def encode_var_int_64(value)
|
|
116
|
+
zigzag = (value << 1) ^ (value >> 63)
|
|
113
117
|
bytes = []
|
|
114
|
-
while
|
|
115
|
-
bytes << ((
|
|
116
|
-
|
|
118
|
+
while zigzag >= 0x80
|
|
119
|
+
bytes << ((zigzag & 0x7F) | 0x80)
|
|
120
|
+
zigzag >>= 7
|
|
117
121
|
end
|
|
118
|
-
bytes <<
|
|
122
|
+
bytes << zigzag
|
|
119
123
|
bytes.pack("C*")
|
|
120
124
|
end
|
|
121
125
|
|
|
122
|
-
# Decode
|
|
126
|
+
# Decode a signed 64-bit integer encoded with ZigZag + LEB128 from an IO stream.
|
|
123
127
|
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
# https://dwarfstd.org/doc/DWARF5.pdf#page=301
|
|
127
|
-
#
|
|
128
|
-
# VarInt format: Each byte uses 7 bits for data, 1 bit for continuation
|
|
129
|
-
# - High bit set = more bytes follow
|
|
130
|
-
# - High bit clear = final byte
|
|
131
|
-
# - Data bits accumulated in little-endian order
|
|
128
|
+
# See #encode_var_int_64 for why the ZigZag un-map is required to match
|
|
129
|
+
# dd-trace-go's wire format.
|
|
132
130
|
#
|
|
133
131
|
# @param io [StringIO] IO stream to read from
|
|
134
|
-
# @return [Integer, nil] Decoded
|
|
132
|
+
# @return [Integer, nil] Decoded signed integer, or nil if malformed
|
|
135
133
|
def self.decode_varint(io)
|
|
136
134
|
value = 0
|
|
137
135
|
shift = 0
|
|
@@ -143,7 +141,7 @@ module Datadog
|
|
|
143
141
|
value |= (byte & 0x7F) << shift
|
|
144
142
|
|
|
145
143
|
# If high bit is clear, we're done
|
|
146
|
-
return value unless (byte & 0x80).nonzero?
|
|
144
|
+
return (value >> 1) ^ -(value & 1) unless (byte & 0x80).nonzero?
|
|
147
145
|
|
|
148
146
|
shift += 7
|
|
149
147
|
|
|
@@ -62,10 +62,27 @@ module Datadog
|
|
|
62
62
|
|
|
63
63
|
super()
|
|
64
64
|
self.loop_base_interval = interval
|
|
65
|
+
# Without this, a preload-then-fork deployment model (e.g. Puma cluster mode with
|
|
66
|
+
# preload_app!, or Sidekiq Enterprise's sidekiqswarm with SIDEKIQ_PRELOAD_APP) leaves
|
|
67
|
+
# every forked child with a dead flush thread: Ruby threads don't survive fork, and the
|
|
68
|
+
# default FORK_POLICY_STOP means #perform would otherwise just mark the worker stopped
|
|
69
|
+
# instead of restarting it. See #restart_flush_thread.
|
|
70
|
+
self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_RESTART
|
|
65
71
|
|
|
66
72
|
perform
|
|
67
73
|
end
|
|
68
74
|
|
|
75
|
+
# Restarts the flush thread if a fork has been detected since this processor was created.
|
|
76
|
+
# Called by Components#after_fork. Safe to call even when no fork occurred: #perform (via
|
|
77
|
+
# Workers::Async::Thread) only restarts the worker when +forked?+ is true. Deliberately not
|
|
78
|
+
# named +after_fork+: Workers::Async::Thread already defines a protected +after_fork+
|
|
79
|
+
# template method that #perform's internal restart path (+restart_after_fork+) calls on
|
|
80
|
+
# every restart -- overriding it here would recurse.
|
|
81
|
+
def restart_flush_thread
|
|
82
|
+
discard_inherited_state_after_fork! if forked?
|
|
83
|
+
perform
|
|
84
|
+
end
|
|
85
|
+
|
|
69
86
|
# Track Kafka produce offset for lag monitoring
|
|
70
87
|
# @param topic [String] The Kafka topic name
|
|
71
88
|
# @param partition [Integer] The partition number
|
|
@@ -302,6 +319,20 @@ module Datadog
|
|
|
302
319
|
PathwayContext.decode_b64(encoded_ctx)
|
|
303
320
|
end
|
|
304
321
|
|
|
322
|
+
# A fork copies whatever was buffered in the parent (unflushed events, aggregated
|
|
323
|
+
# buckets, consumer stats) into the child. If the parent process is still alive and
|
|
324
|
+
# flushing (e.g. a supervisor process that stays up after forking workers), letting
|
|
325
|
+
# the child flush that same inherited data as well would double-report it to the
|
|
326
|
+
# agent. Discard it instead: losing a few seconds of pre-fork stats is preferable to
|
|
327
|
+
# corrupting pipeline throughput/lag numbers with duplicates.
|
|
328
|
+
def discard_inherited_state_after_fork!
|
|
329
|
+
@event_buffer.pop
|
|
330
|
+
@stats_mutex.synchronize do
|
|
331
|
+
@buckets.clear
|
|
332
|
+
@consumer_stats.clear
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
305
336
|
def flush_stats
|
|
306
337
|
stats_buckets = @stats_mutex.synchronize do
|
|
307
338
|
return if @buckets.empty? && @consumer_stats.empty?
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../core/rate_limiter"
|
|
3
4
|
require_relative "../core/utils/time"
|
|
4
5
|
require_relative "../ruby_version"
|
|
5
6
|
require_relative "fatal_exceptions"
|
|
@@ -69,12 +70,22 @@ module Datadog
|
|
|
69
70
|
#
|
|
70
71
|
# @api private
|
|
71
72
|
class Instrumenter
|
|
73
|
+
# Maximum number of capturing-probe snapshots admitted per second across the
|
|
74
|
+
# whole process.
|
|
75
|
+
GLOBAL_SNAPSHOT_RATE_LIMIT = 20
|
|
76
|
+
|
|
77
|
+
# Maximum number of non-capturing probe emissions admitted per second across
|
|
78
|
+
# the whole process.
|
|
79
|
+
GLOBAL_LOG_RATE_LIMIT = 5000
|
|
80
|
+
|
|
72
81
|
def initialize(settings, serializer, logger, code_tracker: nil, telemetry: nil)
|
|
73
82
|
@settings = settings
|
|
74
83
|
@serializer = serializer
|
|
75
84
|
@logger = logger
|
|
76
85
|
@telemetry = telemetry
|
|
77
86
|
@code_tracker = code_tracker
|
|
87
|
+
@global_snapshot_rate_limiter = Datadog::Core::TokenBucket.new(GLOBAL_SNAPSHOT_RATE_LIMIT)
|
|
88
|
+
@global_log_rate_limiter = Datadog::Core::TokenBucket.new(GLOBAL_LOG_RATE_LIMIT)
|
|
78
89
|
|
|
79
90
|
@lock = Mutex.new
|
|
80
91
|
end
|
|
@@ -91,12 +102,31 @@ module Datadog
|
|
|
91
102
|
# Component#start! assigns the now-current tracker here.
|
|
92
103
|
attr_writer :code_tracker
|
|
93
104
|
|
|
105
|
+
# Process-wide snapshot rate limiter.
|
|
106
|
+
# @return [Datadog::Core::TokenBucket]
|
|
107
|
+
attr_reader :global_snapshot_rate_limiter
|
|
108
|
+
# Process-wide log rate limiter.
|
|
109
|
+
# @return [Datadog::Core::TokenBucket]
|
|
110
|
+
attr_reader :global_log_rate_limiter
|
|
111
|
+
|
|
94
112
|
def capture_expression_evaluator
|
|
95
113
|
@capture_expression_evaluator ||= CaptureExpressionEvaluator.new(
|
|
96
114
|
settings: settings, serializer: serializer, logger: logger, telemetry: telemetry,
|
|
97
115
|
)
|
|
98
116
|
end
|
|
99
117
|
|
|
118
|
+
# Returns the process-wide rate limiter for the probe.
|
|
119
|
+
#
|
|
120
|
+
# @param probe [Probe] the probe whose invocation is being rate limited
|
|
121
|
+
# @return [Datadog::Core::TokenBucket] the shared limiter for the probe's category
|
|
122
|
+
def probe_global_rate_limiter(probe)
|
|
123
|
+
if probe.capturing?
|
|
124
|
+
global_snapshot_rate_limiter
|
|
125
|
+
else
|
|
126
|
+
global_log_rate_limiter
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
100
130
|
# This is a substitute for Thread::Backtrace::Location
|
|
101
131
|
# which does not have a public constructor.
|
|
102
132
|
# Used for the fabricated stack frame for the method itself
|
|
@@ -529,7 +559,12 @@ module Datadog
|
|
|
529
559
|
end
|
|
530
560
|
|
|
531
561
|
rate_limiter = probe.rate_limiter
|
|
532
|
-
|
|
562
|
+
admitted = continue && (rate_limiter.nil? || rate_limiter.allow?)
|
|
563
|
+
if admitted && !probe_global_rate_limiter(probe).allow?
|
|
564
|
+
admitted = false
|
|
565
|
+
logger.trace { "di: #{probe.type} probe #{probe.id}: skipping due to global rate limit" }
|
|
566
|
+
end
|
|
567
|
+
if admitted
|
|
533
568
|
# Arguments may be mutated by the method, therefore
|
|
534
569
|
# they need to be serialized prior to method invocation.
|
|
535
570
|
serialized_entry_args = if probe.capture_snapshot?
|
|
@@ -794,6 +829,11 @@ module Datadog
|
|
|
794
829
|
# and check that it is in fact set.
|
|
795
830
|
return if probe.rate_limiter && !probe.rate_limiter.allow?
|
|
796
831
|
|
|
832
|
+
unless probe_global_rate_limiter(probe).allow?
|
|
833
|
+
logger.trace { "di: #{probe.type} probe #{probe.id}: skipping due to global rate limit" }
|
|
834
|
+
return
|
|
835
|
+
end
|
|
836
|
+
|
|
797
837
|
# The context creation is relatively expensive and we don't
|
|
798
838
|
# want to run it if the callback won't be executed due to the
|
|
799
839
|
# rate limit.
|
data/lib/datadog/di/logger.rb
CHANGED
data/lib/datadog/di/probe.rb
CHANGED
|
@@ -97,7 +97,7 @@ module Datadog
|
|
|
97
97
|
@evaluate_at = evaluate_at
|
|
98
98
|
@condition = condition
|
|
99
99
|
|
|
100
|
-
@rate_limit = rate_limit || (
|
|
100
|
+
@rate_limit = rate_limit || (capturing? ? 1 : 5000)
|
|
101
101
|
@rate_limiter = Datadog::Core::TokenBucket.new(@rate_limit)
|
|
102
102
|
|
|
103
103
|
# At most one report per second.
|
|
@@ -166,6 +166,14 @@ module Datadog
|
|
|
166
166
|
!@capture_expressions.empty?
|
|
167
167
|
end
|
|
168
168
|
|
|
169
|
+
# Whether this probe captures user data, either as a full snapshot
|
|
170
|
+
# or via capture expressions.
|
|
171
|
+
#
|
|
172
|
+
# @return [Boolean]
|
|
173
|
+
def capturing?
|
|
174
|
+
capture_snapshot? || capture_expressions?
|
|
175
|
+
end
|
|
176
|
+
|
|
169
177
|
def evaluate_at_entry?
|
|
170
178
|
evaluate_at == :entry
|
|
171
179
|
end
|
|
@@ -379,6 +379,7 @@ module Datadog
|
|
|
379
379
|
thread_id: nil,
|
|
380
380
|
version: 2,
|
|
381
381
|
},
|
|
382
|
+
runtime_id: Core::Environment::Identity.id,
|
|
382
383
|
# TODO add tests that the trace/span id is correctly propagated
|
|
383
384
|
"dd.trace_id": active_trace&.id&.to_s,
|
|
384
385
|
"dd.span_id": active_span&.id&.to_s,
|
data/lib/datadog/di/remote.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Datadog
|
|
|
22
22
|
# when DI is explicitly disabled, or the runtime cannot run DI
|
|
23
23
|
# (JRuby, Ruby 2.5), that block — including this bit — is skipped.
|
|
24
24
|
# The enable signal itself is delivered in APM_TRACING payloads and
|
|
25
|
-
# routed here by Tracing::Remote.
|
|
25
|
+
# routed here by Tracing::Remote.merge_and_apply_configs.
|
|
26
26
|
CAPABILITIES = [
|
|
27
27
|
1 << 38, # APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION: Implicit DI enablement
|
|
28
28
|
].freeze
|
|
@@ -37,7 +37,7 @@ module Datadog
|
|
|
37
37
|
|
|
38
38
|
# Entry point for the RC-driven DI enable/disable path.
|
|
39
39
|
#
|
|
40
|
-
# Invoked from {Datadog::Tracing::Remote.
|
|
40
|
+
# Invoked from {Datadog::Tracing::Remote.merge_and_apply_configs} when an
|
|
41
41
|
# APM_TRACING payload carries `dynamic_instrumentation_enabled`. Runs
|
|
42
42
|
# on the remote-config thread; never raises.
|
|
43
43
|
#
|
|
@@ -45,7 +45,7 @@ module Datadog
|
|
|
45
45
|
# false to stop it. The `DD_DYNAMIC_INSTRUMENTATION_ENABLED=false`
|
|
46
46
|
# env var blocks an enable here (see {.explicitly_disabled?}).
|
|
47
47
|
# @param repository [Datadog::Core::Remote::Configuration::Repository, nil]
|
|
48
|
-
# the RC repository, passed by {Datadog::Tracing::Remote.
|
|
48
|
+
# the RC repository, passed by {Datadog::Tracing::Remote.apply_lib_config}
|
|
49
49
|
# so that a stopped->started transition can reconcile against probes that
|
|
50
50
|
# were delivered in an earlier poll. nil when called outside the RC
|
|
51
51
|
# dispatch path (e.g. unit tests), in which case no reconcile happens.
|