datadog 2.22.0 → 2.23.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 +59 -2
- data/ext/LIBDATADOG_DEVELOPMENT.md +1 -58
- data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
- data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
- data/ext/libdatadog_api/feature_flags.c +554 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/init.c +2 -0
- data/ext/libdatadog_api/library_config.c +12 -11
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
- data/lib/datadog/appsec/api_security/sampler.rb +7 -4
- data/lib/datadog/appsec/assets/blocked.html +8 -0
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- data/lib/datadog/appsec/assets.rb +1 -1
- data/lib/datadog/appsec/remote.rb +4 -0
- data/lib/datadog/appsec/response.rb +18 -4
- data/lib/datadog/core/configuration/components.rb +30 -3
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +14 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +330 -301
- data/lib/datadog/core/ddsketch.rb +0 -2
- data/lib/datadog/core/environment/ext.rb +6 -0
- data/lib/datadog/core/environment/process.rb +79 -0
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/remote/client/capabilities.rb +7 -0
- data/lib/datadog/core/remote/transport/config.rb +2 -10
- data/lib/datadog/core/remote/transport/http/config.rb +9 -9
- data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
- data/lib/datadog/core/remote/transport/http.rb +2 -0
- data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
- data/lib/datadog/core/remote/worker.rb +25 -37
- data/lib/datadog/core/tag_builder.rb +0 -4
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +7 -3
- data/lib/datadog/core/telemetry/event/app_started.rb +52 -49
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
- data/lib/datadog/core/telemetry/logger.rb +2 -2
- data/lib/datadog/core/telemetry/logging.rb +2 -8
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
- data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
- data/lib/datadog/core/transport/http/client.rb +69 -0
- data/lib/datadog/core/utils/array.rb +29 -0
- data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
- data/lib/datadog/core/utils/network.rb +3 -1
- data/lib/datadog/core/utils/only_once_successful.rb +6 -2
- data/lib/datadog/core/utils.rb +2 -0
- data/lib/datadog/data_streams/configuration/settings.rb +49 -0
- data/lib/datadog/data_streams/configuration.rb +11 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +509 -0
- data/lib/datadog/data_streams/transport/http/api.rb +33 -0
- data/lib/datadog/data_streams/transport/http/client.rb +21 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
- data/lib/datadog/data_streams/transport/http.rb +41 -0
- data/lib/datadog/data_streams/transport/stats.rb +60 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/component.rb +0 -16
- data/lib/datadog/di/el/evaluator.rb +1 -1
- data/lib/datadog/di/error.rb +4 -0
- data/lib/datadog/di/instrumenter.rb +76 -30
- data/lib/datadog/di/probe.rb +20 -0
- data/lib/datadog/di/probe_manager.rb +10 -2
- data/lib/datadog/di/probe_notification_builder.rb +62 -23
- data/lib/datadog/di/proc_responder.rb +32 -0
- data/lib/datadog/di/transport/diagnostics.rb +2 -2
- data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
- data/lib/datadog/di/transport/http/input.rb +2 -4
- data/lib/datadog/di/transport/http.rb +6 -2
- data/lib/datadog/di/transport/input.rb +64 -4
- data/lib/datadog/open_feature/component.rb +60 -0
- data/lib/datadog/open_feature/configuration.rb +27 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +14 -0
- data/lib/datadog/open_feature/native_evaluator.rb +38 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +141 -0
- data/lib/datadog/open_feature/remote.rb +74 -0
- data/lib/datadog/open_feature/resolution_details.rb +35 -0
- data/lib/datadog/open_feature/transport.rb +72 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
- data/lib/datadog/opentelemetry/metrics.rb +110 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
- data/lib/datadog/opentelemetry.rb +3 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +4 -0
- data/lib/datadog/profiling/tag_builder.rb +36 -3
- data/lib/datadog/profiling.rb +1 -2
- data/lib/datadog/single_step_instrument.rb +1 -1
- data/lib/datadog/tracing/configuration/ext.rb +9 -0
- data/lib/datadog/tracing/configuration/settings.rb +74 -0
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +1 -1
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
- data/lib/datadog/tracing/transport/traces.rb +3 -5
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +2 -0
- metadata +78 -15
- data/lib/datadog/core/remote/transport/http/client.rb +0 -49
- data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
- data/lib/datadog/di/transport/http/client.rb +0 -47
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
#include "feature_flags.h"
|
|
2
|
+
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include <datadog/ffe.h>
|
|
5
|
+
#include <datadog/common.h>
|
|
6
|
+
|
|
7
|
+
#include "datadog_ruby_common.h"
|
|
8
|
+
|
|
9
|
+
#include <stdint.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
|
|
13
|
+
// Forward declarations
|
|
14
|
+
static VALUE configuration_new(VALUE klass, VALUE json_str);
|
|
15
|
+
static void configuration_free(void *ptr);
|
|
16
|
+
static VALUE configuration_get_assignment(
|
|
17
|
+
VALUE self, VALUE flag_key, VALUE expected_type, VALUE context);
|
|
18
|
+
|
|
19
|
+
static void resolution_details_free(void *ptr);
|
|
20
|
+
static VALUE resolution_details_get_raw_value(VALUE self);
|
|
21
|
+
static VALUE resolution_details_get_flag_type(VALUE self);
|
|
22
|
+
static VALUE resolution_details_get_variant(VALUE self);
|
|
23
|
+
static VALUE resolution_details_get_allocation_key(VALUE self);
|
|
24
|
+
static VALUE resolution_details_get_reason(VALUE self);
|
|
25
|
+
static VALUE resolution_details_get_error_code(VALUE self);
|
|
26
|
+
static VALUE resolution_details_get_error_message(VALUE self);
|
|
27
|
+
static VALUE resolution_details_get_do_log(VALUE self);
|
|
28
|
+
static VALUE resolution_details_get_flag_metadata(VALUE self);
|
|
29
|
+
|
|
30
|
+
static const rb_data_type_t configuration_data_type = {
|
|
31
|
+
.wrap_struct_name = "Datadog::Core::FeatureFlags::Configuration",
|
|
32
|
+
.function = {
|
|
33
|
+
.dmark = NULL,
|
|
34
|
+
.dfree = configuration_free,
|
|
35
|
+
.dsize = NULL,
|
|
36
|
+
},
|
|
37
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
static const rb_data_type_t resolution_details_typed_data = {
|
|
41
|
+
.wrap_struct_name = "Datadog::Core::FeatureFlags::ResolutionDetails",
|
|
42
|
+
.function = {
|
|
43
|
+
.dmark = NULL,
|
|
44
|
+
.dfree = resolution_details_free,
|
|
45
|
+
.dsize = NULL,
|
|
46
|
+
},
|
|
47
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Cached values to use in function later in the code.
|
|
51
|
+
static VALUE feature_flags_error_class = Qnil;
|
|
52
|
+
static VALUE resolution_details_class = Qnil;
|
|
53
|
+
static ID id_boolean;
|
|
54
|
+
static ID id_string;
|
|
55
|
+
static ID id_number;
|
|
56
|
+
static ID id_object;
|
|
57
|
+
static ID id_any;
|
|
58
|
+
static ID id_integer;
|
|
59
|
+
static ID id_float;
|
|
60
|
+
|
|
61
|
+
// SAFETY: The returned borrowed string points directly to Ruby's
|
|
62
|
+
// internal string buffer.
|
|
63
|
+
//
|
|
64
|
+
// This is safe as long as the GVL is held preventing garbage
|
|
65
|
+
// collection. It is held automatically when C extension is called.
|
|
66
|
+
// Note that calling into any Ruby code (rb_funcall, or even
|
|
67
|
+
// rb_hash_lookup) may release GVL or run GC, so are unsafe.
|
|
68
|
+
static inline ddog_ffe_BorrowedStr borrow_str(VALUE str) {
|
|
69
|
+
ENFORCE_TYPE(str, T_STRING);
|
|
70
|
+
return (ddog_ffe_BorrowedStr){
|
|
71
|
+
.ptr = (const uint8_t*)RSTRING_PTR(str),
|
|
72
|
+
.len = RSTRING_LEN(str)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Create a new Ruby string from borrowed string. Returns nil if the
|
|
77
|
+
// borrowed string pointer is NULL.
|
|
78
|
+
static inline VALUE str_from_borrow(ddog_ffe_BorrowedStr str) {
|
|
79
|
+
if (str.ptr == NULL) {
|
|
80
|
+
return Qnil;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return rb_str_new((const char *)str.ptr, str.len);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void feature_flags_init(VALUE core_module) {
|
|
87
|
+
VALUE feature_flags_module = rb_define_module_under(core_module, "FeatureFlags");
|
|
88
|
+
|
|
89
|
+
rb_gc_register_address(&feature_flags_error_class);
|
|
90
|
+
feature_flags_error_class = rb_define_class_under(feature_flags_module, "Error", rb_eStandardError);
|
|
91
|
+
|
|
92
|
+
VALUE configuration_class = rb_define_class_under(feature_flags_module, "Configuration", rb_cObject);
|
|
93
|
+
rb_undef_alloc_func(configuration_class);
|
|
94
|
+
rb_define_singleton_method(configuration_class, "new", configuration_new, 1);
|
|
95
|
+
rb_define_method(configuration_class, "get_assignment", configuration_get_assignment, 3);
|
|
96
|
+
|
|
97
|
+
rb_gc_register_address(&resolution_details_class);
|
|
98
|
+
resolution_details_class = rb_define_class_under(feature_flags_module, "ResolutionDetails", rb_cObject);
|
|
99
|
+
rb_undef_alloc_func(resolution_details_class);
|
|
100
|
+
rb_define_method(resolution_details_class, "raw_value", resolution_details_get_raw_value, 0);
|
|
101
|
+
rb_define_method(resolution_details_class, "flag_type", resolution_details_get_flag_type, 0);
|
|
102
|
+
rb_define_method(resolution_details_class, "variant", resolution_details_get_variant, 0);
|
|
103
|
+
rb_define_method(resolution_details_class, "allocation_key", resolution_details_get_allocation_key, 0);
|
|
104
|
+
rb_define_method(resolution_details_class, "reason", resolution_details_get_reason, 0);
|
|
105
|
+
rb_define_method(resolution_details_class, "error_code", resolution_details_get_error_code, 0);
|
|
106
|
+
rb_define_method(resolution_details_class, "error_message", resolution_details_get_error_message, 0);
|
|
107
|
+
rb_define_method(resolution_details_class, "log?", resolution_details_get_do_log, 0);
|
|
108
|
+
rb_define_method(resolution_details_class, "flag_metadata", resolution_details_get_flag_metadata, 0);
|
|
109
|
+
|
|
110
|
+
// Cache symbol IDs for expected types
|
|
111
|
+
id_boolean = rb_intern_const("boolean");
|
|
112
|
+
id_string = rb_intern_const("string");
|
|
113
|
+
id_number = rb_intern_const("number");
|
|
114
|
+
id_object = rb_intern_const("object");
|
|
115
|
+
id_any = rb_intern_const("any");
|
|
116
|
+
id_integer = rb_intern_const("integer");
|
|
117
|
+
id_float = rb_intern_const("float");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/*
|
|
121
|
+
* call-seq:
|
|
122
|
+
* Configuration.new(json_str) -> Configuration
|
|
123
|
+
*
|
|
124
|
+
* Creates a new Configuration from a JSON string.
|
|
125
|
+
*
|
|
126
|
+
* @param json_str [String] The JSON configuration string
|
|
127
|
+
* @return [Configuration] The configuration instance
|
|
128
|
+
* @raise [Datadog::Core::FeatureFlags::Error] If the JSON is invalid
|
|
129
|
+
*/
|
|
130
|
+
static VALUE configuration_new(VALUE klass, VALUE json_str) {
|
|
131
|
+
struct ddog_ffe_Result_HandleConfiguration result = ddog_ffe_configuration_new(borrow_str(json_str));
|
|
132
|
+
if (result.tag == DDOG_FFE_RESULT_HANDLE_CONFIGURATION_ERR_HANDLE_CONFIGURATION) {
|
|
133
|
+
rb_raise(feature_flags_error_class, "Failed to create configuration from JSON: %"PRIsVALUE, get_error_details_and_drop(&result.err));
|
|
134
|
+
}
|
|
135
|
+
return TypedData_Wrap_Struct(klass, &configuration_data_type, result.ok);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static void configuration_free(void *ptr) {
|
|
139
|
+
ddog_ffe_Handle_Configuration config = (ddog_ffe_Handle_Configuration)ptr;
|
|
140
|
+
ddog_ffe_configuration_drop(&config);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static ddog_ffe_ExpectedFlagType expected_type_from_value(VALUE expected_type) {
|
|
144
|
+
ENFORCE_TYPE(expected_type, T_SYMBOL);
|
|
145
|
+
|
|
146
|
+
const ID id = rb_sym2id(expected_type);
|
|
147
|
+
if (id == id_boolean) {
|
|
148
|
+
return DDOG_FFE_EXPECTED_FLAG_TYPE_BOOLEAN;
|
|
149
|
+
} else if (id == id_string) {
|
|
150
|
+
return DDOG_FFE_EXPECTED_FLAG_TYPE_STRING;
|
|
151
|
+
} else if (id == id_number) {
|
|
152
|
+
return DDOG_FFE_EXPECTED_FLAG_TYPE_NUMBER;
|
|
153
|
+
} else if (id == id_object) {
|
|
154
|
+
return DDOG_FFE_EXPECTED_FLAG_TYPE_OBJECT;
|
|
155
|
+
} else if (id == id_any) {
|
|
156
|
+
return DDOG_FFE_EXPECTED_FLAG_TYPE_ANY;
|
|
157
|
+
} else if (id == id_integer) {
|
|
158
|
+
return DDOG_FFE_EXPECTED_FLAG_TYPE_INTEGER;
|
|
159
|
+
} else if (id == id_float) {
|
|
160
|
+
return DDOG_FFE_EXPECTED_FLAG_TYPE_FLOAT;
|
|
161
|
+
} else {
|
|
162
|
+
rb_raise(feature_flags_error_class, "Internal: Unexpected flag type: %"PRIsVALUE, expected_type);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Structure to hold state during hash iteration for building evaluation context
|
|
167
|
+
struct evaluation_context_builder {
|
|
168
|
+
VALUE hash;
|
|
169
|
+
const char *targeting_key;
|
|
170
|
+
ddog_ffe_AttributePair *attrs;
|
|
171
|
+
long attr_count;
|
|
172
|
+
long attr_capacity;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Callback function for rb_hash_foreach to process each key-value pair
|
|
176
|
+
static int evaluation_context_foreach_callback(VALUE key, VALUE value, VALUE arg) {
|
|
177
|
+
struct evaluation_context_builder *builder = (struct evaluation_context_builder *)arg;
|
|
178
|
+
|
|
179
|
+
ENFORCE_TYPE(key, T_STRING);
|
|
180
|
+
const char *name = RSTRING_PTR(key);
|
|
181
|
+
|
|
182
|
+
// Extract targeting_key separately if present.
|
|
183
|
+
//
|
|
184
|
+
// If targeting_key has wrong type, we will skip it and attempt
|
|
185
|
+
// evaluation without it. If targeting_key turns out to be required,
|
|
186
|
+
// the error will be reported by
|
|
187
|
+
// ddog_ffe_configuration_get_assignment function.
|
|
188
|
+
if (strcmp(name, "targeting_key") == 0 && TYPE(value) == T_STRING) {
|
|
189
|
+
builder->targeting_key = RSTRING_PTR(value);
|
|
190
|
+
return ST_CONTINUE;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Skip nil values
|
|
194
|
+
if (value == Qnil) {
|
|
195
|
+
return ST_CONTINUE;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Ensure we don't exceed capacity.
|
|
199
|
+
if (builder->attr_count >= builder->attr_capacity) {
|
|
200
|
+
// This should never happen because evaluation_context_from_hash()
|
|
201
|
+
// pre-allocates attr_capacity equal to iterated Hash size.
|
|
202
|
+
rb_raise(feature_flags_error_class, "Internal: Attribute count exceeded capacity");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
ddog_ffe_AttributePair *attr = &builder->attrs[builder->attr_count];
|
|
206
|
+
|
|
207
|
+
switch (TYPE(value)) {
|
|
208
|
+
case T_STRING:
|
|
209
|
+
attr->name = name;
|
|
210
|
+
attr->value.tag = DDOG_FFE_ATTRIBUTE_VALUE_STRING;
|
|
211
|
+
attr->value.string = RSTRING_PTR(value);
|
|
212
|
+
break;
|
|
213
|
+
case T_FIXNUM:
|
|
214
|
+
case T_FLOAT:
|
|
215
|
+
attr->name = name;
|
|
216
|
+
attr->value.tag = DDOG_FFE_ATTRIBUTE_VALUE_NUMBER;
|
|
217
|
+
attr->value.number = NUM2DBL(value);
|
|
218
|
+
break;
|
|
219
|
+
case T_TRUE:
|
|
220
|
+
attr->name = name;
|
|
221
|
+
attr->value.tag = DDOG_FFE_ATTRIBUTE_VALUE_BOOLEAN;
|
|
222
|
+
attr->value.boolean = true;
|
|
223
|
+
break;
|
|
224
|
+
case T_FALSE:
|
|
225
|
+
attr->name = name;
|
|
226
|
+
attr->value.tag = DDOG_FFE_ATTRIBUTE_VALUE_BOOLEAN;
|
|
227
|
+
attr->value.boolean = false;
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
// Skip unsupported attribute types.
|
|
231
|
+
return ST_CONTINUE;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
builder->attr_count += 1;
|
|
235
|
+
return ST_CONTINUE;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
static VALUE protected_context_build(VALUE p) {
|
|
240
|
+
struct evaluation_context_builder *builder = (struct evaluation_context_builder *)p;
|
|
241
|
+
|
|
242
|
+
rb_hash_foreach(builder->hash, evaluation_context_foreach_callback, p);
|
|
243
|
+
|
|
244
|
+
return Qnil;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// The hash should contain attributes for feature flag evaluation. The
|
|
248
|
+
// special key "targeting_key" (if present) is extracted separately as
|
|
249
|
+
// it has special meaning in the libdatadog API. All other key-value
|
|
250
|
+
// pairs become attributes.
|
|
251
|
+
//
|
|
252
|
+
// Note: all strings are copied into the new EvaluationContext, so
|
|
253
|
+
// there are no safety concerns regarding string lifetimes. However,
|
|
254
|
+
// the caller is responsible for the returned EvaluationContext and
|
|
255
|
+
// must free it when no longer in use.
|
|
256
|
+
static ddog_ffe_Handle_EvaluationContext evaluation_context_from_hash(VALUE hash) {
|
|
257
|
+
ENFORCE_TYPE(hash, T_HASH);
|
|
258
|
+
|
|
259
|
+
// Initialize builder with pre-allocated attribute array
|
|
260
|
+
struct evaluation_context_builder builder = {
|
|
261
|
+
.hash = hash,
|
|
262
|
+
.targeting_key = NULL,
|
|
263
|
+
.attrs = ruby_xcalloc(RHASH_SIZE(hash), sizeof(ddog_ffe_AttributePair)),
|
|
264
|
+
.attr_count = 0,
|
|
265
|
+
.attr_capacity = RHASH_SIZE(hash)
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
int state = 0;
|
|
269
|
+
rb_protect(protected_context_build, (VALUE)&builder, &state);
|
|
270
|
+
|
|
271
|
+
// If an exception occurred, clean up and re-raise
|
|
272
|
+
if (state != 0) {
|
|
273
|
+
ruby_xfree(builder.attrs);
|
|
274
|
+
rb_jump_tag(state);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
ddog_ffe_Handle_EvaluationContext context = ddog_ffe_evaluation_context_new(
|
|
278
|
+
builder.targeting_key,
|
|
279
|
+
builder.attrs,
|
|
280
|
+
builder.attr_count
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
ruby_xfree(builder.attrs);
|
|
284
|
+
|
|
285
|
+
return context;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/*
|
|
289
|
+
* call-seq:
|
|
290
|
+
* configuration.get_assignment(flag_key, expected_type, context) -> ResolutionDetails
|
|
291
|
+
*
|
|
292
|
+
* Get assignment for a feature flag.
|
|
293
|
+
*
|
|
294
|
+
* @param flag_key [String] The key of the feature flag
|
|
295
|
+
* @param expected_type [Symbol] Expected type (:boolean, :string, :number, :object, :any, :integer, :float)
|
|
296
|
+
* @param context [Hash] Evaluation context with targeting_key and other attributes
|
|
297
|
+
* @return [ResolutionDetails] The resolution details
|
|
298
|
+
*/
|
|
299
|
+
static VALUE configuration_get_assignment(VALUE self, VALUE flag_key, VALUE expected_type, VALUE context_hash) {
|
|
300
|
+
ENFORCE_TYPED_DATA(self, &configuration_data_type);
|
|
301
|
+
ENFORCE_TYPE(flag_key, T_STRING);
|
|
302
|
+
ENFORCE_TYPE(expected_type, T_SYMBOL);
|
|
303
|
+
ENFORCE_TYPE(context_hash, T_HASH);
|
|
304
|
+
|
|
305
|
+
const ddog_ffe_Handle_Configuration config =
|
|
306
|
+
(ddog_ffe_Handle_Configuration)rb_check_typeddata(self, &configuration_data_type);
|
|
307
|
+
const ddog_ffe_ExpectedFlagType expected_ty = expected_type_from_value(expected_type);
|
|
308
|
+
ddog_ffe_Handle_EvaluationContext context = evaluation_context_from_hash(context_hash);
|
|
309
|
+
|
|
310
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details = ddog_ffe_get_assignment(
|
|
311
|
+
config,
|
|
312
|
+
RSTRING_PTR(flag_key),
|
|
313
|
+
expected_ty,
|
|
314
|
+
context
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
ddog_ffe_evaluation_context_drop(&context);
|
|
318
|
+
|
|
319
|
+
return TypedData_Wrap_Struct(resolution_details_class, &resolution_details_typed_data, resolution_details);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
static void resolution_details_free(void *ptr) {
|
|
323
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details = (ddog_ffe_Handle_ResolutionDetails)ptr;
|
|
324
|
+
ddog_ffe_assignment_drop(&resolution_details);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/*
|
|
328
|
+
* call-seq:
|
|
329
|
+
* resolution_details.raw_value() -> Object
|
|
330
|
+
*
|
|
331
|
+
* Get the raw resolved value from libdatadog.
|
|
332
|
+
*
|
|
333
|
+
* The value can be any type depending on the feature flag (String, Integer, Float, Boolean, or nil).
|
|
334
|
+
* For object types, returns the raw JSON string without parsing.
|
|
335
|
+
*/
|
|
336
|
+
static VALUE resolution_details_get_raw_value(VALUE self) {
|
|
337
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
338
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
339
|
+
|
|
340
|
+
struct ddog_ffe_VariantValue value = ddog_ffe_assignment_get_value(resolution_details);
|
|
341
|
+
|
|
342
|
+
switch (value.tag) {
|
|
343
|
+
case DDOG_FFE_VARIANT_VALUE_STRING:
|
|
344
|
+
return str_from_borrow(value.string);
|
|
345
|
+
case DDOG_FFE_VARIANT_VALUE_INTEGER:
|
|
346
|
+
return LONG2NUM(value.integer);
|
|
347
|
+
case DDOG_FFE_VARIANT_VALUE_FLOAT:
|
|
348
|
+
return rb_float_new(value.float_);
|
|
349
|
+
case DDOG_FFE_VARIANT_VALUE_BOOLEAN:
|
|
350
|
+
return value.boolean ? Qtrue : Qfalse;
|
|
351
|
+
case DDOG_FFE_VARIANT_VALUE_OBJECT:
|
|
352
|
+
return str_from_borrow(value.string);
|
|
353
|
+
case DDOG_FFE_VARIANT_VALUE_NONE:
|
|
354
|
+
return Qnil;
|
|
355
|
+
default:
|
|
356
|
+
// This should never happen as we checked for all possible tag values.
|
|
357
|
+
rb_raise(feature_flags_error_class, "Internal: Unexpected ResolutionDetails value tag");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/*
|
|
362
|
+
* call-seq:
|
|
363
|
+
* resolution_details.flag_type() -> Symbol or nil
|
|
364
|
+
*
|
|
365
|
+
* Get the type of the flag value.
|
|
366
|
+
*
|
|
367
|
+
* @return [Symbol, nil] One of: :string, :integer, :float, :boolean, :object, nil
|
|
368
|
+
*/
|
|
369
|
+
static VALUE resolution_details_get_flag_type(VALUE self) {
|
|
370
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
371
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
372
|
+
|
|
373
|
+
struct ddog_ffe_VariantValue value = ddog_ffe_assignment_get_value(resolution_details);
|
|
374
|
+
|
|
375
|
+
switch (value.tag) {
|
|
376
|
+
case DDOG_FFE_VARIANT_VALUE_STRING:
|
|
377
|
+
return ID2SYM(id_string);
|
|
378
|
+
case DDOG_FFE_VARIANT_VALUE_INTEGER:
|
|
379
|
+
return ID2SYM(id_integer);
|
|
380
|
+
case DDOG_FFE_VARIANT_VALUE_FLOAT:
|
|
381
|
+
return ID2SYM(id_float);
|
|
382
|
+
case DDOG_FFE_VARIANT_VALUE_BOOLEAN:
|
|
383
|
+
return ID2SYM(id_boolean);
|
|
384
|
+
case DDOG_FFE_VARIANT_VALUE_OBJECT:
|
|
385
|
+
return ID2SYM(id_object);
|
|
386
|
+
case DDOG_FFE_VARIANT_VALUE_NONE:
|
|
387
|
+
return Qnil;
|
|
388
|
+
default:
|
|
389
|
+
// This should never happen as we checked for all possible tag values.
|
|
390
|
+
rb_raise(feature_flags_error_class, "Internal: Unexpected ResolutionDetails value tag");
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/*
|
|
395
|
+
* call-seq:
|
|
396
|
+
* resolution_details.variant() -> String or nil
|
|
397
|
+
*
|
|
398
|
+
* Get the variant identifier.
|
|
399
|
+
*
|
|
400
|
+
* @return [String, nil] The variant identifier or nil
|
|
401
|
+
*/
|
|
402
|
+
static VALUE resolution_details_get_variant(VALUE self) {
|
|
403
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
404
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
405
|
+
struct ddog_ffe_BorrowedStr variant = ddog_ffe_assignment_get_variant(resolution_details);
|
|
406
|
+
return str_from_borrow(variant);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/*
|
|
410
|
+
* call-seq:
|
|
411
|
+
* resolution_details.allocation_key() -> String or nil
|
|
412
|
+
*
|
|
413
|
+
* Get the allocation key.
|
|
414
|
+
*
|
|
415
|
+
* @return [String, nil] The allocation key or nil
|
|
416
|
+
*/
|
|
417
|
+
static VALUE resolution_details_get_allocation_key(VALUE self) {
|
|
418
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
419
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
420
|
+
struct ddog_ffe_BorrowedStr allocation_key = ddog_ffe_assignment_get_allocation_key(resolution_details);
|
|
421
|
+
return str_from_borrow(allocation_key);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/*
|
|
425
|
+
* call-seq:
|
|
426
|
+
* resolution_details.reason() -> String
|
|
427
|
+
*
|
|
428
|
+
* Get the reason for the resolution.
|
|
429
|
+
*
|
|
430
|
+
* @return [String] One of: "STATIC", "DEFAULT", "TARGETING_MATCH", "SPLIT", "DISABLED", "ERROR", "UNKNOWN"
|
|
431
|
+
*/
|
|
432
|
+
static VALUE resolution_details_get_reason(VALUE self) {
|
|
433
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
434
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
435
|
+
|
|
436
|
+
enum ddog_ffe_Reason reason = ddog_ffe_assignment_get_reason(resolution_details);
|
|
437
|
+
|
|
438
|
+
switch (reason) {
|
|
439
|
+
case DDOG_FFE_REASON_STATIC:
|
|
440
|
+
return rb_str_new_lit("STATIC");
|
|
441
|
+
case DDOG_FFE_REASON_DEFAULT:
|
|
442
|
+
return rb_str_new_lit("DEFAULT");
|
|
443
|
+
case DDOG_FFE_REASON_TARGETING_MATCH:
|
|
444
|
+
return rb_str_new_lit("TARGETING_MATCH");
|
|
445
|
+
case DDOG_FFE_REASON_SPLIT:
|
|
446
|
+
return rb_str_new_lit("SPLIT");
|
|
447
|
+
case DDOG_FFE_REASON_DISABLED:
|
|
448
|
+
return rb_str_new_lit("DISABLED");
|
|
449
|
+
case DDOG_FFE_REASON_ERROR:
|
|
450
|
+
return rb_str_new_lit("ERROR");
|
|
451
|
+
default:
|
|
452
|
+
return rb_str_new_lit("UNKNOWN");
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/*
|
|
457
|
+
* call-seq:
|
|
458
|
+
* resolution_details.error_code() -> String or nil
|
|
459
|
+
*
|
|
460
|
+
* Get the error code if there was an error.
|
|
461
|
+
*
|
|
462
|
+
* @return [String, nil] Error code or nil if no error
|
|
463
|
+
*/
|
|
464
|
+
static VALUE resolution_details_get_error_code(VALUE self) {
|
|
465
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
466
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
467
|
+
|
|
468
|
+
enum ddog_ffe_ErrorCode error_code = ddog_ffe_assignment_get_error_code(resolution_details);
|
|
469
|
+
|
|
470
|
+
switch (error_code) {
|
|
471
|
+
case DDOG_FFE_ERROR_CODE_OK:
|
|
472
|
+
return Qnil;
|
|
473
|
+
case DDOG_FFE_ERROR_CODE_TYPE_MISMATCH:
|
|
474
|
+
return rb_str_new_lit("TYPE_MISMATCH");
|
|
475
|
+
case DDOG_FFE_ERROR_CODE_PARSE_ERROR:
|
|
476
|
+
return rb_str_new_lit("PARSE_ERROR");
|
|
477
|
+
case DDOG_FFE_ERROR_CODE_FLAG_NOT_FOUND:
|
|
478
|
+
return rb_str_new_lit("FLAG_NOT_FOUND");
|
|
479
|
+
case DDOG_FFE_ERROR_CODE_TARGETING_KEY_MISSING:
|
|
480
|
+
return rb_str_new_lit("TARGETING_KEY_MISSING");
|
|
481
|
+
case DDOG_FFE_ERROR_CODE_INVALID_CONTEXT:
|
|
482
|
+
return rb_str_new_lit("INVALID_CONTEXT");
|
|
483
|
+
case DDOG_FFE_ERROR_CODE_PROVIDER_NOT_READY:
|
|
484
|
+
return rb_str_new_lit("PROVIDER_NOT_READY");
|
|
485
|
+
case DDOG_FFE_ERROR_CODE_GENERAL:
|
|
486
|
+
default:
|
|
487
|
+
return rb_str_new_lit("GENERAL");
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/*
|
|
492
|
+
* call-seq:
|
|
493
|
+
* resolution_details.error_message() -> String or nil
|
|
494
|
+
*
|
|
495
|
+
* Get the error message if there was an error.
|
|
496
|
+
*
|
|
497
|
+
* @return [String, nil] Error message or nil
|
|
498
|
+
*/
|
|
499
|
+
static VALUE resolution_details_get_error_message(VALUE self) {
|
|
500
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
501
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
502
|
+
struct ddog_ffe_BorrowedStr error_message = ddog_ffe_assignment_get_error_message(resolution_details);
|
|
503
|
+
return str_from_borrow(error_message);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/*
|
|
507
|
+
* call-seq:
|
|
508
|
+
* resolution_details.log?() -> Boolean
|
|
509
|
+
*
|
|
510
|
+
* Check if this resolution should be logged.
|
|
511
|
+
*
|
|
512
|
+
* @return [Boolean] True if should be logged
|
|
513
|
+
*/
|
|
514
|
+
static VALUE resolution_details_get_do_log(VALUE self) {
|
|
515
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
516
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
517
|
+
return ddog_ffe_assignment_get_do_log(resolution_details) ? Qtrue : Qfalse;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/*
|
|
521
|
+
* call-seq:
|
|
522
|
+
* resolution_details.flag_metadata() -> Hash
|
|
523
|
+
*
|
|
524
|
+
* Get the flag metadata.
|
|
525
|
+
*
|
|
526
|
+
* @return [Hash{String => String}] The flag metadata as a hash
|
|
527
|
+
*/
|
|
528
|
+
static VALUE resolution_details_get_flag_metadata(VALUE self) {
|
|
529
|
+
ddog_ffe_Handle_ResolutionDetails resolution_details =
|
|
530
|
+
(ddog_ffe_Handle_ResolutionDetails)rb_check_typeddata(self, &resolution_details_typed_data);
|
|
531
|
+
(void)resolution_details;
|
|
532
|
+
|
|
533
|
+
VALUE hash = rb_hash_new();
|
|
534
|
+
|
|
535
|
+
// TODO(FFL-1450): datadog-ffe-ffi-1.0.1 has a memory corruption bug
|
|
536
|
+
// when returning flag metadata. Therefore, this section is
|
|
537
|
+
// currently commented out. We'll uncommented it when the bug is
|
|
538
|
+
// fixed in libdatadog.
|
|
539
|
+
//
|
|
540
|
+
// This is not a blocker as flag_metadata should be empty for now
|
|
541
|
+
// until we decide to add more fields to it.
|
|
542
|
+
//
|
|
543
|
+
// struct ddog_ffe_ArrayMap_BorrowedStr metadata =
|
|
544
|
+
// ddog_ffe_assignnment_get_flag_metadata(resolution_details);
|
|
545
|
+
//
|
|
546
|
+
// for (size_t i = 0; i < metadata.count; i++) {
|
|
547
|
+
// ddog_ffe_KeyValue_BorrowedStr kv = metadata.elements[i];
|
|
548
|
+
// VALUE key = str_from_borrow(kv.key);
|
|
549
|
+
// VALUE value = str_from_borrow(kv.value);
|
|
550
|
+
// rb_hash_aset(hash, key, value);
|
|
551
|
+
// }
|
|
552
|
+
|
|
553
|
+
return hash;
|
|
554
|
+
}
|
data/ext/libdatadog_api/init.c
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include "crashtracker.h"
|
|
5
5
|
#include "process_discovery.h"
|
|
6
6
|
#include "library_config.h"
|
|
7
|
+
#include "feature_flags.h"
|
|
7
8
|
|
|
8
9
|
void ddsketch_init(VALUE core_module);
|
|
9
10
|
|
|
@@ -15,4 +16,5 @@ void DDTRACE_EXPORT Init_libdatadog_api(void) {
|
|
|
15
16
|
process_discovery_init(core_module);
|
|
16
17
|
library_config_init(core_module);
|
|
17
18
|
ddsketch_init(core_module);
|
|
19
|
+
feature_flags_init(core_module);
|
|
18
20
|
}
|
|
@@ -99,27 +99,28 @@ static VALUE _native_configurator_get(VALUE self) {
|
|
|
99
99
|
ddog_Configurator *configurator;
|
|
100
100
|
TypedData_Get_Struct(self, ddog_Configurator, &configurator_typed_data, configurator);
|
|
101
101
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
// So we cannot reference it with &config_logged_result
|
|
105
|
-
// We are doing this in case one of the ruby API raises an exception before the end of this function,
|
|
106
|
-
// so the allocated memory will still be freed
|
|
107
|
-
ddog_LibraryConfigLoggedResult *configurator_logged_result = ruby_xcalloc(1, sizeof(ddog_LibraryConfigLoggedResult));
|
|
108
|
-
*configurator_logged_result = ddog_library_configurator_get(configurator);
|
|
109
|
-
VALUE config_logged_result_rb = TypedData_Wrap_Struct(config_logged_result_class, &config_logged_result_typed_data, configurator_logged_result);
|
|
102
|
+
// We don't allocate memory here so if there is an error, we don't need to manage the memory
|
|
103
|
+
ddog_LibraryConfigLoggedResult before_error_result = ddog_library_configurator_get(configurator);
|
|
110
104
|
|
|
111
|
-
if (
|
|
112
|
-
ddog_Error err =
|
|
105
|
+
if (before_error_result.tag == DDOG_LIBRARY_CONFIG_LOGGED_RESULT_ERR) {
|
|
106
|
+
ddog_Error err = before_error_result.err;
|
|
113
107
|
VALUE message = get_error_details_and_drop(&err);
|
|
114
108
|
if (is_config_loaded()) {
|
|
115
109
|
log_warning(message);
|
|
116
110
|
} else {
|
|
117
111
|
log_warning_without_config(message);
|
|
118
112
|
}
|
|
119
|
-
RB_GC_GUARD(config_logged_result_rb);
|
|
120
113
|
return rb_hash_new();
|
|
121
114
|
}
|
|
122
115
|
|
|
116
|
+
// Wrapping config_logged_result into a Ruby object enables the Ruby GC to manage its memory
|
|
117
|
+
// We need to allocate memory for config_logged_result because once it is out of scope, it will be freed (at the end of this function)
|
|
118
|
+
// We are doing this in case one of the ruby API raises an exception before the end of this function,
|
|
119
|
+
// so the allocated memory will still be freed
|
|
120
|
+
ddog_LibraryConfigLoggedResult *configurator_logged_result = ruby_xcalloc(1, sizeof(ddog_LibraryConfigLoggedResult));
|
|
121
|
+
*configurator_logged_result = before_error_result;
|
|
122
|
+
VALUE config_logged_result_rb = TypedData_Wrap_Struct(config_logged_result_class, &config_logged_result_typed_data, configurator_logged_result);
|
|
123
|
+
|
|
123
124
|
VALUE logs = Qnil;
|
|
124
125
|
if (configurator_logged_result->ok.logs.length > 0) {
|
|
125
126
|
logs = rb_utf8_str_new_cstr(configurator_logged_result->ok.logs.ptr);
|
|
@@ -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 = '~>
|
|
13
|
+
LIBDATADOG_VERSION = '~> 24.0.1.1.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,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../tracing/contrib/rack/route_inference'
|
|
4
|
+
|
|
3
5
|
module Datadog
|
|
4
6
|
module AppSec
|
|
5
7
|
module APISecurity
|
|
@@ -8,7 +10,8 @@ module Datadog
|
|
|
8
10
|
SINATRA_ROUTE_KEY = 'sinatra.route'
|
|
9
11
|
SINATRA_ROUTE_SEPARATOR = ' '
|
|
10
12
|
GRAPE_ROUTE_KEY = 'grape.routing_args'
|
|
11
|
-
|
|
13
|
+
RAILS_ROUTE_URI_PATTERN_KEY = 'action_dispatch.route_uri_pattern'
|
|
14
|
+
RAILS_ROUTE_KEY = 'action_dispatch.route' # Rails 8.1.1+
|
|
12
15
|
RAILS_ROUTES_KEY = 'action_dispatch.routes'
|
|
13
16
|
RAILS_PATH_PARAMS_KEY = 'action_dispatch.request.path_parameters'
|
|
14
17
|
RAILS_FORMAT_SUFFIX = '(.:format)'
|
|
@@ -35,6 +38,9 @@ module Datadog
|
|
|
35
38
|
# Rails > 7.1 (fast path)
|
|
36
39
|
# uses `action_dispatch.route_uri_pattern` with a string like
|
|
37
40
|
# "/users/:id(.:format)"
|
|
41
|
+
# Rails > 8.1.1 (fast path)
|
|
42
|
+
# uses `action_dispatch.route` to store the ActionDispatch::Journey::Route
|
|
43
|
+
# that matched when the request was routed
|
|
38
44
|
#
|
|
39
45
|
# WARNING: This method works only *after* the request has been routed.
|
|
40
46
|
#
|
|
@@ -50,11 +56,18 @@ module Datadog
|
|
|
50
56
|
pattern = request.env[SINATRA_ROUTE_KEY].split(SINATRA_ROUTE_SEPARATOR, 2)[1]
|
|
51
57
|
"#{request.script_name}#{pattern}"
|
|
52
58
|
elsif request.env.key?(RAILS_ROUTE_KEY)
|
|
53
|
-
request.env[RAILS_ROUTE_KEY].delete_suffix(RAILS_FORMAT_SUFFIX)
|
|
59
|
+
request.env[RAILS_ROUTE_KEY].path.spec.to_s.delete_suffix(RAILS_FORMAT_SUFFIX)
|
|
60
|
+
elsif request.env.key?(RAILS_ROUTE_URI_PATTERN_KEY)
|
|
61
|
+
request.env[RAILS_ROUTE_URI_PATTERN_KEY].delete_suffix(RAILS_FORMAT_SUFFIX)
|
|
54
62
|
elsif request.env.key?(RAILS_ROUTES_KEY) && !request.env.fetch(RAILS_PATH_PARAMS_KEY, {}).empty?
|
|
55
|
-
# NOTE: Rails
|
|
56
|
-
#
|
|
57
|
-
|
|
63
|
+
# NOTE: In Rails < 7.1 this `request` argument will be a Rack::Request,
|
|
64
|
+
# it does not have all the methods that ActionDispatch::Request has.
|
|
65
|
+
# Before trying to use the router to recognize the route, we need to
|
|
66
|
+
# create a new ActionDispatch::Request from the request env
|
|
67
|
+
#
|
|
68
|
+
# NOTE: Rails mutates HEAD request by changing the method to GET
|
|
69
|
+
# and uses it for route recognition to check if the route is defined
|
|
70
|
+
request = request.env[RAILS_ROUTES_KEY].request_class.new(request.env)
|
|
58
71
|
|
|
59
72
|
pattern = request.env[RAILS_ROUTES_KEY].router
|
|
60
73
|
.recognize(request) { |route, _| break route.path.spec.to_s }
|
|
@@ -66,8 +79,12 @@ module Datadog
|
|
|
66
79
|
# to generic request path
|
|
67
80
|
(pattern || request.path).delete_suffix(RAILS_FORMAT_SUFFIX)
|
|
68
81
|
else
|
|
69
|
-
request.
|
|
82
|
+
Tracing::Contrib::Rack::RouteInference.read_or_infer(request.env)
|
|
70
83
|
end
|
|
84
|
+
rescue => e
|
|
85
|
+
AppSec.telemetry&.report(e, description: 'AppSec: Could not extract route pattern')
|
|
86
|
+
|
|
87
|
+
nil
|
|
71
88
|
end
|
|
72
89
|
end
|
|
73
90
|
end
|