datadog 2.0.0 → 2.2.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 +66 -2
- data/README.md +1 -1
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +19 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +41 -0
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +1 -1
- data/ext/datadog_profiling_native_extension/crashtracker.c +1 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +47 -1
- data/ext/datadog_profiling_native_extension/setup_signal_handler.c +1 -1
- data/ext/datadog_profiling_native_extension/stack_recorder.c +13 -6
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/lib/datadog/appsec/configuration/settings.rb +5 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +0 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
- data/lib/datadog/appsec/extensions.rb +1 -0
- data/lib/datadog/core/configuration/components.rb +6 -3
- data/lib/datadog/core/configuration/ext.rb +1 -0
- data/lib/datadog/core/configuration/option.rb +21 -14
- data/lib/datadog/core/configuration/options.rb +5 -1
- data/lib/datadog/core/configuration/settings.rb +68 -5
- data/lib/datadog/core/configuration.rb +3 -17
- data/lib/datadog/core/deprecations.rb +58 -0
- data/lib/datadog/core/environment/ext.rb +2 -0
- data/lib/datadog/core/environment/yjit.rb +5 -0
- data/lib/datadog/core/runtime/ext.rb +2 -0
- data/lib/datadog/core/runtime/metrics.rb +6 -0
- data/lib/datadog/core/telemetry/component.rb +107 -0
- data/lib/datadog/core/telemetry/event.rb +124 -31
- data/lib/datadog/core/telemetry/ext.rb +2 -0
- data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
- data/lib/datadog/core/telemetry/metric.rb +167 -0
- data/lib/datadog/core/telemetry/metrics_collection.rb +81 -0
- data/lib/datadog/core/telemetry/metrics_manager.rb +81 -0
- data/lib/datadog/core/telemetry/request.rb +1 -1
- data/lib/datadog/core/telemetry/worker.rb +173 -0
- data/lib/datadog/core/utils/only_once_successful.rb +76 -0
- data/lib/datadog/core.rb +2 -19
- data/lib/datadog/opentelemetry/sdk/propagator.rb +5 -10
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +5 -2
- data/lib/datadog/profiling/collectors/code_provenance.rb +18 -5
- data/lib/datadog/profiling/component.rb +18 -1
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +410 -0
- data/lib/datadog/profiling.rb +1 -0
- data/lib/datadog/tracing/configuration/ext.rb +7 -0
- data/lib/datadog/tracing/configuration/settings.rb +52 -3
- data/lib/datadog/tracing/contrib/action_cable/event.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/broadcast.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/perform_action.rb +1 -1
- data/lib/datadog/tracing/contrib/action_cable/events/transmit.rb +1 -1
- data/lib/datadog/tracing/contrib/action_mailer/event.rb +4 -6
- data/lib/datadog/tracing/contrib/action_mailer/events/deliver.rb +9 -4
- data/lib/datadog/tracing/contrib/action_mailer/events/process.rb +3 -2
- data/lib/datadog/tracing/contrib/action_view/events/render_partial.rb +1 -5
- data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
- data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
- data/lib/datadog/tracing/contrib/active_support/cache/event.rb +32 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +156 -0
- data/lib/datadog/tracing/contrib/active_support/cache/events.rb +34 -0
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +45 -41
- data/lib/datadog/tracing/contrib/active_support/cache/patcher.rb +17 -40
- data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +4 -1
- data/lib/datadog/tracing/contrib/active_support/notifications/event.rb +29 -6
- data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +16 -4
- data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +33 -29
- data/lib/datadog/tracing/contrib/analytics.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +5 -0
- data/lib/datadog/tracing/contrib/graphql/patcher.rb +8 -2
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +166 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace_patcher.rb +25 -0
- data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/consumer_group_event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/event.rb +1 -1
- data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_batch.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer/process_message.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/consumer_group/heartbeat.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +3 -3
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +3 -3
- data/lib/datadog/tracing/contrib/racecar/event.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/ext.rb +9 -0
- data/lib/datadog/tracing/contrib/rails/patcher.rb +7 -0
- data/lib/datadog/tracing/contrib/rails/runner.rb +95 -0
- data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
- data/lib/datadog/tracing/distributed/b3_single.rb +3 -1
- data/lib/datadog/tracing/distributed/datadog.rb +2 -2
- data/lib/datadog/tracing/distributed/propagation.rb +39 -4
- data/lib/datadog/tracing/distributed/trace_context.rb +5 -3
- data/lib/datadog/tracing/metadata/ext.rb +1 -0
- data/lib/datadog/tracing/span_operation.rb +3 -2
- data/lib/datadog/tracing/trace_operation.rb +7 -3
- data/lib/datadog/tracing/trace_segment.rb +4 -1
- data/lib/datadog/tracing/tracer.rb +9 -2
- data/lib/datadog/tracing.rb +5 -1
- data/lib/datadog/version.rb +2 -2
- metadata +21 -8
- data/lib/datadog/core/telemetry/client.rb +0 -95
- data/lib/datadog/core/telemetry/heartbeat.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 895fe8f9fdd8391d5c6c86e8d39d0a8e241c85bbce42b35c9744e1f94095853f
|
4
|
+
data.tar.gz: d5a6e88ec35816de59a5d080f66896a4c85656216d2853b46bb1268dfa64df35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fef3c78c7835c47507a1f09d87c2ee84ddcc97303f9b9d7c6b4d601381ac62a47133f3450ddbc888cf54c56353ef4417ff337009a1e60d2ae239fb19093d721b
|
7
|
+
data.tar.gz: 061154162ab97a6e1cdc87f53c18d98fd0eea75b6cb7e71fd1e4e5c4a536ff7722c4e1202258760113ce5a7f0ce3838622e700858103fa97dddc33d0322275cc
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,46 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [2.2.0] - 2024-07-11
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Tracing: Add `Rails` Runner instrumentation ([#2509][])
|
10
|
+
* Tracing: Introduce a new, reworked `GraphQL` tracer to comply with span attributes specification ([#3672][])
|
11
|
+
* Tracing: Enhance `ActiveSupport::Cache` instrumentation with `ActiveSupport::Notifications` subscription ([#3772][])
|
12
|
+
* Profiling: Track unscaled allocation counts in allocation profiler ([#3770][])
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
* Core: Send Telemetry events in batches ([#3749][])
|
17
|
+
* Tracing: Populate spans from `ActiveSupport::Notifications` as early as possible ([#3725][])
|
18
|
+
* Profiling: Upgrade to `libdatadog` 10 ([#3753][])
|
19
|
+
* Profiling: Optimize `CodeProvenance#record_loaded_files` to avoid allocations ([#3757][])
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
* Core: Fix Telemetry events blocking main thread ([#3718][])
|
24
|
+
* Core: Fix deadlock from Telemetry threads ([#3743][])
|
25
|
+
* Tracing: Fix empty log correlation when tracing is disabled ([#3731][])
|
26
|
+
* Tracing: Fix HTTP propagation when missing parent span id ([#3730][])
|
27
|
+
* Tracing: Ensure `_dd.p.tid` tag with fixed size ([#3729][])
|
28
|
+
* OTel: Fix ids encoding/decoding for propagation ([#3709][])
|
29
|
+
* Profiling: Workaround Ruby `Dir` returning incorrect results ([#3720][])
|
30
|
+
* Profiling: Fix `Phusion Passenger` detection when missing from `Gemfile`/`gems.rb` ([#3750][])
|
31
|
+
* Profiling: Fix `rpath` for linking to libdatadog when loading extension ([#3706][])
|
32
|
+
* Profiling: Fix incorrect code provenance due to broken JSON monkey patch ([#3695][])
|
33
|
+
* Profiling: Fix aggregation of actionview template classes ([#3759][], [#3774][])
|
34
|
+
|
35
|
+
## [2.1.0] - 2024-06-10
|
36
|
+
|
37
|
+
### Added
|
38
|
+
|
39
|
+
* Tracing: Configuration by OpenTelemetry environment variables ([#3657][])
|
40
|
+
|
41
|
+
### Fixed
|
42
|
+
|
43
|
+
* Tracing: Improved compatibility with W3C Trace Context propagation ([#3631][])
|
44
|
+
|
5
45
|
## [2.0.0] - 2024-06-06
|
6
46
|
|
7
47
|
### Added
|
@@ -2894,7 +2934,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
2894
2934
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
2895
2935
|
|
2896
2936
|
|
2897
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
2937
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.2.0...master
|
2938
|
+
[2.2.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.1.0...v2.2.0
|
2939
|
+
[2.1.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0...v2.1.0
|
2898
2940
|
[2.0.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.rc1...v2.0.0
|
2899
2941
|
[2.0.0.rc1]: https://github.com/DataDog/dd-trace-rb/compare/v2.0.0.beta2...v2.0.0.rc1
|
2900
2942
|
[1.23.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.22.0...v1.23.0
|
@@ -3890,6 +3932,7 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
3890
3932
|
[#2497]: https://github.com/DataDog/dd-trace-rb/issues/2497
|
3891
3933
|
[#2501]: https://github.com/DataDog/dd-trace-rb/issues/2501
|
3892
3934
|
[#2504]: https://github.com/DataDog/dd-trace-rb/issues/2504
|
3935
|
+
[#2509]: https://github.com/DataDog/dd-trace-rb/issues/2509
|
3893
3936
|
[#2512]: https://github.com/DataDog/dd-trace-rb/issues/2512
|
3894
3937
|
[#2513]: https://github.com/DataDog/dd-trace-rb/issues/2513
|
3895
3938
|
[#2522]: https://github.com/DataDog/dd-trace-rb/issues/2522
|
@@ -4267,9 +4310,30 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4267
4310
|
[#3624]: https://github.com/DataDog/dd-trace-rb/issues/3624
|
4268
4311
|
[#3627]: https://github.com/DataDog/dd-trace-rb/issues/3627
|
4269
4312
|
[#3630]: https://github.com/DataDog/dd-trace-rb/issues/3630
|
4313
|
+
[#3631]: https://github.com/DataDog/dd-trace-rb/issues/3631
|
4270
4314
|
[#3645]: https://github.com/DataDog/dd-trace-rb/issues/3645
|
4271
4315
|
[#3651]: https://github.com/DataDog/dd-trace-rb/issues/3651
|
4316
|
+
[#3657]: https://github.com/DataDog/dd-trace-rb/issues/3657
|
4272
4317
|
[#3664]: https://github.com/DataDog/dd-trace-rb/issues/3664
|
4318
|
+
[#3672]: https://github.com/DataDog/dd-trace-rb/issues/3672
|
4319
|
+
[#3695]: https://github.com/DataDog/dd-trace-rb/issues/3695
|
4320
|
+
[#3706]: https://github.com/DataDog/dd-trace-rb/issues/3706
|
4321
|
+
[#3709]: https://github.com/DataDog/dd-trace-rb/issues/3709
|
4322
|
+
[#3718]: https://github.com/DataDog/dd-trace-rb/issues/3718
|
4323
|
+
[#3720]: https://github.com/DataDog/dd-trace-rb/issues/3720
|
4324
|
+
[#3725]: https://github.com/DataDog/dd-trace-rb/issues/3725
|
4325
|
+
[#3729]: https://github.com/DataDog/dd-trace-rb/issues/3729
|
4326
|
+
[#3730]: https://github.com/DataDog/dd-trace-rb/issues/3730
|
4327
|
+
[#3731]: https://github.com/DataDog/dd-trace-rb/issues/3731
|
4328
|
+
[#3743]: https://github.com/DataDog/dd-trace-rb/issues/3743
|
4329
|
+
[#3749]: https://github.com/DataDog/dd-trace-rb/issues/3749
|
4330
|
+
[#3750]: https://github.com/DataDog/dd-trace-rb/issues/3750
|
4331
|
+
[#3753]: https://github.com/DataDog/dd-trace-rb/issues/3753
|
4332
|
+
[#3757]: https://github.com/DataDog/dd-trace-rb/issues/3757
|
4333
|
+
[#3759]: https://github.com/DataDog/dd-trace-rb/issues/3759
|
4334
|
+
[#3770]: https://github.com/DataDog/dd-trace-rb/issues/3770
|
4335
|
+
[#3772]: https://github.com/DataDog/dd-trace-rb/issues/3772
|
4336
|
+
[#3774]: https://github.com/DataDog/dd-trace-rb/issues/3774
|
4273
4337
|
[@AdrianLC]: https://github.com/AdrianLC
|
4274
4338
|
[@Azure7111]: https://github.com/Azure7111
|
4275
4339
|
[@BabyGroot]: https://github.com/BabyGroot
|
@@ -4421,4 +4485,4 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4421
4485
|
[@y-yagi]: https://github.com/y-yagi
|
4422
4486
|
[@yujideveloper]: https://github.com/yujideveloper
|
4423
4487
|
[@yukimurasawa]: https://github.com/yukimurasawa
|
4424
|
-
[@zachmccormick]: https://github.com/zachmccormick
|
4488
|
+
[@zachmccormick]: https://github.com/zachmccormick
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
## Getting started
|
10
10
|
|
11
|
-
**If you're upgrading from a
|
11
|
+
**If you're upgrading from a 1.x version, check out the [upgrade guide](https://github.com/DataDog/dd-trace-rb/blob/release/docs/UpgradeGuide2.md).**
|
12
12
|
|
13
13
|
For a product overview, installation, and configuration check out our [documentation][public docs].
|
14
14
|
|
@@ -222,6 +222,8 @@ static VALUE _native_with_blocked_sigprof(DDTRACE_UNUSED VALUE self);
|
|
222
222
|
static VALUE rescued_sample_allocation(VALUE tracepoint_data);
|
223
223
|
static void delayed_error(struct cpu_and_wall_time_worker_state *state, const char *error);
|
224
224
|
static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VALUE error_msg);
|
225
|
+
static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self);
|
226
|
+
static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self);
|
225
227
|
|
226
228
|
// Note on sampler global state safety:
|
227
229
|
//
|
@@ -285,7 +287,9 @@ void collectors_cpu_and_wall_time_worker_init(VALUE profiling_module) {
|
|
285
287
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_allocation_count", _native_allocation_count, 0);
|
286
288
|
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_is_running?", _native_is_running, 1);
|
287
289
|
rb_define_singleton_method(testing_module, "_native_current_sigprof_signal_handler", _native_current_sigprof_signal_handler, 0);
|
288
|
-
|
290
|
+
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_hold_signals", _native_hold_signals, 0);
|
291
|
+
rb_define_singleton_method(collectors_cpu_and_wall_time_worker_class, "_native_resume_signals", _native_resume_signals, 0);
|
292
|
+
// TODO: Remove `_native_is_running` from `testing_module` (should be in class) once `prof-correctness` has been updated to not need it
|
289
293
|
rb_define_singleton_method(testing_module, "_native_is_running?", _native_is_running, 1);
|
290
294
|
rb_define_singleton_method(testing_module, "_native_install_testing_signal_handler", _native_install_testing_signal_handler, 0);
|
291
295
|
rb_define_singleton_method(testing_module, "_native_remove_testing_signal_handler", _native_remove_testing_signal_handler, 0);
|
@@ -1159,3 +1163,17 @@ static VALUE _native_delayed_error(DDTRACE_UNUSED VALUE self, VALUE instance, VA
|
|
1159
1163
|
|
1160
1164
|
return Qnil;
|
1161
1165
|
}
|
1166
|
+
|
1167
|
+
// Masks SIGPROF interruptions for the current thread. Please don't use this -- you may end up with incomplete
|
1168
|
+
// profiling data.
|
1169
|
+
static VALUE _native_hold_signals(DDTRACE_UNUSED VALUE self) {
|
1170
|
+
block_sigprof_signal_handler_from_running_in_current_thread();
|
1171
|
+
return Qtrue;
|
1172
|
+
}
|
1173
|
+
|
1174
|
+
// Unmasks SIGPROF interruptions for the current thread. If there's a pending sample, it'll be triggered inside this
|
1175
|
+
// method.
|
1176
|
+
static VALUE _native_resume_signals(DDTRACE_UNUSED VALUE self) {
|
1177
|
+
unblock_sigprof_signal_handler_from_running_in_current_thread();
|
1178
|
+
return Qtrue;
|
1179
|
+
}
|
@@ -34,6 +34,7 @@ static VALUE _native_sample(
|
|
34
34
|
);
|
35
35
|
static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer* buffer, char *frames_omitted_message, int frames_omitted_message_size);
|
36
36
|
static void record_placeholder_stack_in_native_code(sampling_buffer* buffer, VALUE recorder_instance, sample_values values, sample_labels labels);
|
37
|
+
static void maybe_trim_template_random_ids(ddog_CharSlice *name_slice, ddog_CharSlice *filename_slice);
|
37
38
|
|
38
39
|
void collectors_stack_init(VALUE profiling_module) {
|
39
40
|
VALUE collectors_module = rb_define_module_under(profiling_module, "Collectors");
|
@@ -69,6 +70,7 @@ static VALUE _native_sample(
|
|
69
70
|
.cpu_or_wall_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("cpu-samples"), zero)),
|
70
71
|
.wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("wall-time"), zero)),
|
71
72
|
.alloc_samples = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples"), zero)),
|
73
|
+
.alloc_samples_unscaled = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("alloc-samples-unscaled"), zero)),
|
72
74
|
.timeline_wall_time_ns = NUM2UINT(rb_hash_lookup2(metric_values_hash, rb_str_new_cstr("timeline"), zero)),
|
73
75
|
};
|
74
76
|
|
@@ -199,6 +201,8 @@ void sample_thread(
|
|
199
201
|
ddog_CharSlice name_slice = char_slice_from_ruby_string(name);
|
200
202
|
ddog_CharSlice filename_slice = char_slice_from_ruby_string(filename);
|
201
203
|
|
204
|
+
maybe_trim_template_random_ids(&name_slice, &filename_slice);
|
205
|
+
|
202
206
|
bool top_of_the_stack = i == 0;
|
203
207
|
|
204
208
|
// When there's only wall-time in a sample, this means that the thread was not active in the sampled period.
|
@@ -268,6 +272,43 @@ void sample_thread(
|
|
268
272
|
);
|
269
273
|
}
|
270
274
|
|
275
|
+
// Rails's ActionView likes to dynamically generate method names with suffixed hashes/ids, resulting in methods with
|
276
|
+
// names such as:
|
277
|
+
// * "_app_views_layouts_explore_html_haml__2304485752546535910_211320" (__number_number suffix -- two underscores)
|
278
|
+
// * "_app_views_articles_index_html_erb___2022809201779434309_12900" (___number_number suffix -- three underscores)
|
279
|
+
// This makes these stacks not aggregate well, as well as being not-very-useful data.
|
280
|
+
// (Reference:
|
281
|
+
// https://github.com/rails/rails/blob/4fa56814f18fd3da49c83931fa773caa727d8096/actionview/lib/action_view/template.rb#L389
|
282
|
+
// The two vs three underscores happen when @identifier.hash is negative in that method: the "-" gets replaced with
|
283
|
+
// the extra "_".)
|
284
|
+
//
|
285
|
+
// This method trims these suffixes, so that we keep less data + the names correctly aggregate together.
|
286
|
+
static void maybe_trim_template_random_ids(ddog_CharSlice *name_slice, ddog_CharSlice *filename_slice) {
|
287
|
+
// Check filename doesn't end with ".rb"; templates are usually along the lines of .html.erb/.html.haml/...
|
288
|
+
if (filename_slice->len < 3 || memcmp(filename_slice->ptr + filename_slice->len - 3, ".rb", 3) == 0) return;
|
289
|
+
|
290
|
+
int pos = name_slice->len - 1;
|
291
|
+
|
292
|
+
// Let's match on something__number_number:
|
293
|
+
// Find start of id suffix from the end...
|
294
|
+
if (name_slice->ptr[pos] < '0' || name_slice->ptr[pos] > '9') return;
|
295
|
+
|
296
|
+
// ...now match a bunch of numbers and interspersed underscores
|
297
|
+
for (int underscores = 0; pos >= 0 && underscores < 2; pos--) {
|
298
|
+
if (name_slice->ptr[pos] == '_') underscores++;
|
299
|
+
else if (name_slice->ptr[pos] < '0' || name_slice->ptr[pos] > '9') return;
|
300
|
+
}
|
301
|
+
|
302
|
+
// Make sure there's something left before the underscores (hence the <= instead of <) + match the last underscore
|
303
|
+
if (pos <= 0 || name_slice->ptr[pos] != '_') return;
|
304
|
+
|
305
|
+
// Does it have the optional third underscore? If so, remove it as well
|
306
|
+
if (pos > 1 && name_slice->ptr[pos-1] == '_') pos--;
|
307
|
+
|
308
|
+
// If we got here, we matched on our pattern. Let's slice the length of the string to exclude it.
|
309
|
+
name_slice->len = pos;
|
310
|
+
}
|
311
|
+
|
271
312
|
static void maybe_add_placeholder_frames_omitted(VALUE thread, sampling_buffer* buffer, char *frames_omitted_message, int frames_omitted_message_size) {
|
272
313
|
ptrdiff_t frames_omitted = stack_depth_for(thread) - buffer->max_frames;
|
273
314
|
|
@@ -1290,7 +1290,7 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
|
|
1290
1290
|
/* thread: */ current_thread,
|
1291
1291
|
/* stack_from_thread: */ current_thread,
|
1292
1292
|
get_or_create_context_for(current_thread, state),
|
1293
|
-
(sample_values) {.alloc_samples = sample_weight},
|
1293
|
+
(sample_values) {.alloc_samples = sample_weight, .alloc_samples_unscaled = 1},
|
1294
1294
|
INVALID_TIME, // For now we're not collecting timestamps for allocation events, as per profiling team internal discussions
|
1295
1295
|
&ruby_vm_type,
|
1296
1296
|
optional_class_name
|
@@ -57,7 +57,7 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
57
57
|
// "Process.kill('SEGV', Process.pid)" gets run.
|
58
58
|
.create_alt_stack = false,
|
59
59
|
.endpoint = endpoint,
|
60
|
-
.resolve_frames =
|
60
|
+
.resolve_frames = DDOG_PROF_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
|
61
61
|
.timeout_secs = FIX2INT(upload_timeout_seconds),
|
62
62
|
};
|
63
63
|
|
@@ -211,12 +211,14 @@ unless have_type('atomic_int', ['stdatomic.h'])
|
|
211
211
|
skip_building_extension!(Datadog::Profiling::NativeExtensionHelpers::Supported::COMPILER_ATOMIC_MISSING)
|
212
212
|
end
|
213
213
|
|
214
|
-
# See comments on the helper
|
214
|
+
# See comments on the helper methods being used for why we need to additionally set this.
|
215
215
|
# The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of
|
216
216
|
# experimentation. We need to get these special characters across a lot of tools untouched...
|
217
|
-
|
218
|
-
|
219
|
-
|
217
|
+
extra_relative_rpaths = [
|
218
|
+
Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_native_lib_folder,
|
219
|
+
*Datadog::Profiling::NativeExtensionHelpers.libdatadog_folder_relative_to_ruby_extensions_folders,
|
220
|
+
]
|
221
|
+
extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" }
|
220
222
|
Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n")
|
221
223
|
|
222
224
|
# Tag the native extension library with the Ruby version and Ruby platform.
|
@@ -15,7 +15,7 @@ module Datadog
|
|
15
15
|
# The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on debase-ruby_core_source
|
16
16
|
CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?('2.6', '2.7', '3.0.', '3.1.', '3.2.')
|
17
17
|
|
18
|
-
LIBDATADOG_VERSION = '~>
|
18
|
+
LIBDATADOG_VERSION = '~> 10.0.0.1.0'
|
19
19
|
|
20
20
|
def self.fail_install_if_missing_extension?
|
21
21
|
ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == 'true'
|
@@ -67,6 +67,52 @@ module Datadog
|
|
67
67
|
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(profiling_native_lib_folder)).to_s
|
68
68
|
end
|
69
69
|
|
70
|
+
# In https://github.com/DataDog/dd-trace-rb/pull/3582 we got a report of a customer for which the native extension
|
71
|
+
# only got installed into the extensions folder.
|
72
|
+
#
|
73
|
+
# But then this fix was not enough to fully get them moving because then they started to see the issue from
|
74
|
+
# https://github.com/DataDog/dd-trace-rb/issues/2067 / https://github.com/DataDog/dd-trace-rb/pull/2125 :
|
75
|
+
#
|
76
|
+
# > Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling
|
77
|
+
# > native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.2.2_x86_64-linux
|
78
|
+
# > due to libdatadog_profiling.so: cannot open shared object file: No such file or directory
|
79
|
+
#
|
80
|
+
# The problem is that when loading the native extension from the extensions directory, the relative rpath we add
|
81
|
+
# with the #libdatadog_folder_relative_to_native_lib_folder helper above is not correct, we need to add a relative
|
82
|
+
# rpath to the extensions directory.
|
83
|
+
#
|
84
|
+
# So how do we find the full path where the native extension is placed?
|
85
|
+
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/bundler/runtime.rb#L166
|
86
|
+
# `extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + Dir["#{Gem.dir}/bundler/gems/extensions/*/*/*"]`
|
87
|
+
# we get that's in one of two fixed subdirectories of `Gem.dir`
|
88
|
+
# * From https://github.com/ruby/ruby/blob/83f02d42e0a3c39661dc99c049ab9a70ff227d5b/lib/rubygems/basic_specification.rb#L111-L115
|
89
|
+
# we get the structure of the subdirectory (platform/extension_api_version/gem_and_version)
|
90
|
+
#
|
91
|
+
# Thus, `Gem.dir` of `/var/app/current/vendor/bundle/ruby/3.2.0` becomes (for instance)
|
92
|
+
# `/var/app/current/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/datadog-2.0.0/` or
|
93
|
+
# `/var/app/current/vendor/bundle/ruby/3.2.0/bundler/gems/extensions/x86_64-linux/3.2.0/datadog-2.0.0/`
|
94
|
+
#
|
95
|
+
# We then compute the relative path between these folders and the libdatadog folder, and use that as a relative path.
|
96
|
+
def self.libdatadog_folder_relative_to_ruby_extensions_folders(
|
97
|
+
gem_dir: Gem.dir,
|
98
|
+
libdatadog_pkgconfig_folder: Libdatadog.pkgconfig_folder
|
99
|
+
)
|
100
|
+
return unless libdatadog_pkgconfig_folder
|
101
|
+
|
102
|
+
# For the purposes of calculating a folder relative to the other, we don't actually NEED to fill in the
|
103
|
+
# platform, extension_api_version and gem version. We're basically just after how many folders it is deep from
|
104
|
+
# the Gem.dir.
|
105
|
+
expected_ruby_extensions_folders = [
|
106
|
+
"#{gem_dir}/extensions/platform/extension_api_version/datadog_version/",
|
107
|
+
"#{gem_dir}/bundler/gems/extensions/platform/extension_api_version/datadog_version/",
|
108
|
+
]
|
109
|
+
libdatadog_lib_folder = "#{libdatadog_pkgconfig_folder}/../"
|
110
|
+
|
111
|
+
expected_ruby_extensions_folders.map do |folder|
|
112
|
+
Pathname.new(libdatadog_lib_folder).relative_path_from(Pathname.new(folder)).to_s
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
70
116
|
# Used to check if profiler is supported, including user-visible clear messages explaining why their
|
71
117
|
# system may not be supported.
|
72
118
|
module Supported
|
@@ -91,7 +91,7 @@ void remove_sigprof_signal_handler(void) {
|
|
91
91
|
if (sigaction(SIGPROF, &signal_handler_config, NULL) != 0) rb_sys_fail("Failure while removing the signal handler");
|
92
92
|
}
|
93
93
|
|
94
|
-
static void toggle_sigprof_signal_handler_for_current_thread(int action) {
|
94
|
+
static inline void toggle_sigprof_signal_handler_for_current_thread(int action) {
|
95
95
|
sigset_t signals_to_toggle;
|
96
96
|
sigemptyset(&signals_to_toggle);
|
97
97
|
sigaddset(&signals_to_toggle, SIGPROF);
|
@@ -151,21 +151,23 @@ static VALUE error_symbol = Qnil; // :error in Ruby
|
|
151
151
|
#define WALL_TIME_VALUE_ID 2
|
152
152
|
#define ALLOC_SAMPLES_VALUE {.type_ = VALUE_STRING("alloc-samples"), .unit = VALUE_STRING("count")}
|
153
153
|
#define ALLOC_SAMPLES_VALUE_ID 3
|
154
|
+
#define ALLOC_SAMPLES_UNSCALED_VALUE {.type_ = VALUE_STRING("alloc-samples-unscaled"), .unit = VALUE_STRING("count")}
|
155
|
+
#define ALLOC_SAMPLES_UNSCALED_VALUE_ID 4
|
154
156
|
#define HEAP_SAMPLES_VALUE {.type_ = VALUE_STRING("heap-live-samples"), .unit = VALUE_STRING("count")}
|
155
|
-
#define HEAP_SAMPLES_VALUE_ID
|
157
|
+
#define HEAP_SAMPLES_VALUE_ID 5
|
156
158
|
#define HEAP_SIZE_VALUE {.type_ = VALUE_STRING("heap-live-size"), .unit = VALUE_STRING("bytes")}
|
157
|
-
#define HEAP_SIZE_VALUE_ID
|
159
|
+
#define HEAP_SIZE_VALUE_ID 6
|
158
160
|
#define TIMELINE_VALUE {.type_ = VALUE_STRING("timeline"), .unit = VALUE_STRING("nanoseconds")}
|
159
|
-
#define TIMELINE_VALUE_ID
|
161
|
+
#define TIMELINE_VALUE_ID 7
|
160
162
|
|
161
163
|
static const ddog_prof_ValueType all_value_types[] =
|
162
|
-
{CPU_TIME_VALUE, CPU_SAMPLES_VALUE, WALL_TIME_VALUE, ALLOC_SAMPLES_VALUE, HEAP_SAMPLES_VALUE, HEAP_SIZE_VALUE, TIMELINE_VALUE};
|
164
|
+
{CPU_TIME_VALUE, CPU_SAMPLES_VALUE, WALL_TIME_VALUE, ALLOC_SAMPLES_VALUE, ALLOC_SAMPLES_UNSCALED_VALUE, HEAP_SAMPLES_VALUE, HEAP_SIZE_VALUE, TIMELINE_VALUE};
|
163
165
|
|
164
166
|
// This array MUST be kept in sync with all_value_types above and is intended to act as a "hashmap" between VALUE_ID and the position it
|
165
167
|
// occupies on the all_value_types array.
|
166
168
|
// E.g. all_value_types_positions[CPU_TIME_VALUE_ID] => 0, means that CPU_TIME_VALUE was declared at position 0 of all_value_types.
|
167
169
|
static const uint8_t all_value_types_positions[] =
|
168
|
-
{CPU_TIME_VALUE_ID, CPU_SAMPLES_VALUE_ID, WALL_TIME_VALUE_ID, ALLOC_SAMPLES_VALUE_ID, HEAP_SAMPLES_VALUE_ID, HEAP_SIZE_VALUE_ID, TIMELINE_VALUE_ID};
|
170
|
+
{CPU_TIME_VALUE_ID, CPU_SAMPLES_VALUE_ID, WALL_TIME_VALUE_ID, ALLOC_SAMPLES_VALUE_ID, ALLOC_SAMPLES_UNSCALED_VALUE_ID, HEAP_SAMPLES_VALUE_ID, HEAP_SIZE_VALUE_ID, TIMELINE_VALUE_ID};
|
169
171
|
|
170
172
|
#define ALL_VALUE_TYPES_COUNT (sizeof(all_value_types) / sizeof(ddog_prof_ValueType))
|
171
173
|
|
@@ -429,7 +431,7 @@ static VALUE _native_initialize(
|
|
429
431
|
|
430
432
|
uint8_t requested_values_count = ALL_VALUE_TYPES_COUNT -
|
431
433
|
(cpu_time_enabled == Qtrue ? 0 : 1) -
|
432
|
-
(alloc_samples_enabled == Qtrue? 0 :
|
434
|
+
(alloc_samples_enabled == Qtrue? 0 : 2) -
|
433
435
|
(heap_samples_enabled == Qtrue ? 0 : 1) -
|
434
436
|
(heap_size_enabled == Qtrue ? 0 : 1) -
|
435
437
|
(timeline_enabled == Qtrue ? 0 : 1);
|
@@ -464,8 +466,12 @@ static VALUE _native_initialize(
|
|
464
466
|
if (alloc_samples_enabled == Qtrue) {
|
465
467
|
enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) ALLOC_SAMPLES_VALUE;
|
466
468
|
state->position_for[ALLOC_SAMPLES_VALUE_ID] = next_enabled_pos++;
|
469
|
+
|
470
|
+
enabled_value_types[next_enabled_pos] = (ddog_prof_ValueType) ALLOC_SAMPLES_UNSCALED_VALUE;
|
471
|
+
state->position_for[ALLOC_SAMPLES_UNSCALED_VALUE_ID] = next_enabled_pos++;
|
467
472
|
} else {
|
468
473
|
state->position_for[ALLOC_SAMPLES_VALUE_ID] = next_disabled_pos++;
|
474
|
+
state->position_for[ALLOC_SAMPLES_UNSCALED_VALUE_ID] = next_disabled_pos++;
|
469
475
|
}
|
470
476
|
|
471
477
|
if (heap_samples_enabled == Qtrue) {
|
@@ -603,6 +609,7 @@ void record_sample(VALUE recorder_instance, ddog_prof_Slice_Location locations,
|
|
603
609
|
metric_values[position_for[CPU_SAMPLES_VALUE_ID]] = values.cpu_or_wall_samples;
|
604
610
|
metric_values[position_for[WALL_TIME_VALUE_ID]] = values.wall_time_ns;
|
605
611
|
metric_values[position_for[ALLOC_SAMPLES_VALUE_ID]] = values.alloc_samples;
|
612
|
+
metric_values[position_for[ALLOC_SAMPLES_UNSCALED_VALUE_ID]] = values.alloc_samples_unscaled;
|
606
613
|
metric_values[position_for[TIMELINE_VALUE_ID]] = values.timeline_wall_time_ns;
|
607
614
|
|
608
615
|
if (values.alloc_samples != 0) {
|
@@ -6,7 +6,7 @@ require_relative '../diagnostics/environment_logger'
|
|
6
6
|
require_relative '../diagnostics/health'
|
7
7
|
require_relative '../logger'
|
8
8
|
require_relative '../runtime/metrics'
|
9
|
-
require_relative '../telemetry/
|
9
|
+
require_relative '../telemetry/component'
|
10
10
|
require_relative '../workers/runtime_metrics'
|
11
11
|
|
12
12
|
require_relative '../remote/component'
|
@@ -62,9 +62,11 @@ module Datadog
|
|
62
62
|
logger.debug { "Telemetry disabled. Agent network adapter not supported: #{agent_settings.adapter}" }
|
63
63
|
end
|
64
64
|
|
65
|
-
Telemetry::
|
65
|
+
Telemetry::Component.new(
|
66
66
|
enabled: enabled,
|
67
|
+
metrics_enabled: enabled && settings.telemetry.metrics_enabled,
|
67
68
|
heartbeat_interval_seconds: settings.telemetry.heartbeat_interval_seconds,
|
69
|
+
metrics_aggregation_interval_seconds: settings.telemetry.metrics_aggregation_interval_seconds,
|
68
70
|
dependency_collection: settings.telemetry.dependency_collection
|
69
71
|
)
|
70
72
|
end
|
@@ -169,8 +171,9 @@ module Datadog
|
|
169
171
|
unused_statsd = (old_statsd - (old_statsd & new_statsd))
|
170
172
|
unused_statsd.each(&:close)
|
171
173
|
|
172
|
-
telemetry
|
174
|
+
# enqueue closing event before stopping telemetry so it will be send out on shutdown
|
173
175
|
telemetry.emit_closing! unless replacement
|
176
|
+
telemetry.stop!
|
174
177
|
end
|
175
178
|
end
|
176
179
|
end
|
@@ -14,7 +14,7 @@ module Datadog
|
|
14
14
|
# @!attribute [r] precedence_set
|
15
15
|
# When this option was last set, what was the value precedence used?
|
16
16
|
# @return [Precedence::Value]
|
17
|
-
attr_reader :definition, :precedence_set
|
17
|
+
attr_reader :definition, :precedence_set, :resolved_env
|
18
18
|
|
19
19
|
# Option setting precedence.
|
20
20
|
module Precedence
|
@@ -50,6 +50,7 @@ module Datadog
|
|
50
50
|
@context = context
|
51
51
|
@value = nil
|
52
52
|
@is_set = false
|
53
|
+
@resolved_env = nil
|
53
54
|
|
54
55
|
# One value is stored per precedence, to allow unsetting a higher
|
55
56
|
# precedence value and falling back to a lower precedence one.
|
@@ -65,7 +66,7 @@ module Datadog
|
|
65
66
|
#
|
66
67
|
# @param value [Object] the new value to be associated with this option
|
67
68
|
# @param precedence [Precedence] from what precedence order this new value comes from
|
68
|
-
def set(value, precedence: Precedence::PROGRAMMATIC)
|
69
|
+
def set(value, precedence: Precedence::PROGRAMMATIC, resolved_env: nil)
|
69
70
|
# Is there a higher precedence value set?
|
70
71
|
if @precedence_set > precedence
|
71
72
|
# This should be uncommon, as higher precedence values tend to
|
@@ -84,7 +85,7 @@ module Datadog
|
|
84
85
|
return @value
|
85
86
|
end
|
86
87
|
|
87
|
-
internal_set(value, precedence)
|
88
|
+
internal_set(value, precedence, resolved_env)
|
88
89
|
end
|
89
90
|
|
90
91
|
def unset(precedence)
|
@@ -102,7 +103,7 @@ module Datadog
|
|
102
103
|
# Look for value that is set.
|
103
104
|
# The hash `@value_per_precedence` has a custom default value of `UNSET`.
|
104
105
|
if (value = @value_per_precedence[p]) != UNSET
|
105
|
-
internal_set(value, p)
|
106
|
+
internal_set(value, p, nil)
|
106
107
|
return nil
|
107
108
|
end
|
108
109
|
end
|
@@ -260,11 +261,12 @@ module Datadog
|
|
260
261
|
end
|
261
262
|
|
262
263
|
# Directly manipulates the current value and currently set precedence.
|
263
|
-
def internal_set(value, precedence)
|
264
|
+
def internal_set(value, precedence, resolved_env)
|
264
265
|
old_value = @value
|
265
266
|
(@value = context_exec(validate_type(value), old_value, &definition.setter)).tap do |v|
|
266
267
|
@is_set = true
|
267
268
|
@precedence_set = precedence
|
269
|
+
@resolved_env = resolved_env
|
268
270
|
# Store original value to ensure we can always safely call `#internal_set`
|
269
271
|
# when restoring a value from `@value_per_precedence`, and we are only running `definition.setter`
|
270
272
|
# on the original value, not on a valud that has already been processed by `definition.setter`.
|
@@ -284,16 +286,21 @@ module Datadog
|
|
284
286
|
def set_value_from_env_or_default
|
285
287
|
value = nil
|
286
288
|
precedence = nil
|
287
|
-
|
289
|
+
resolved_env = nil
|
288
290
|
|
289
|
-
if definition.env
|
290
|
-
|
291
|
-
|
292
|
-
|
291
|
+
if definition.env
|
292
|
+
Array(definition.env).each do |env|
|
293
|
+
next if ENV[env].nil?
|
294
|
+
|
295
|
+
resolved_env = env
|
296
|
+
value = coerce_env_variable(ENV[env])
|
297
|
+
precedence = Precedence::PROGRAMMATIC
|
298
|
+
break
|
299
|
+
end
|
293
300
|
end
|
294
301
|
|
295
302
|
if value.nil? && definition.deprecated_env && ENV[definition.deprecated_env]
|
296
|
-
|
303
|
+
resolved_env = definition.deprecated_env
|
297
304
|
value = coerce_env_variable(ENV[definition.deprecated_env])
|
298
305
|
precedence = Precedence::PROGRAMMATIC
|
299
306
|
|
@@ -304,11 +311,11 @@ module Datadog
|
|
304
311
|
|
305
312
|
option_value = value.nil? ? default_value : value
|
306
313
|
|
307
|
-
set(option_value, precedence: precedence || Precedence::DEFAULT)
|
314
|
+
set(option_value, precedence: precedence || Precedence::DEFAULT, resolved_env: resolved_env)
|
308
315
|
rescue ArgumentError
|
309
316
|
raise ArgumentError,
|
310
|
-
"Expected environment variable #{
|
311
|
-
"but '#{ENV[
|
317
|
+
"Expected environment variable #{resolved_env} to be a #{@definition.type}, " \
|
318
|
+
"but '#{ENV[resolved_env]}' was provided"
|
312
319
|
end
|
313
320
|
|
314
321
|
# Anchor object that represents a value that is not set.
|
@@ -68,7 +68,7 @@ module Datadog
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def set_option(name, value, precedence: Configuration::Option::Precedence::PROGRAMMATIC)
|
71
|
-
resolve_option(name).set(value, precedence: precedence)
|
71
|
+
resolve_option(name).set(value, precedence: precedence, resolved_env: resolved_env(name))
|
72
72
|
end
|
73
73
|
|
74
74
|
def unset_option(name, precedence: Configuration::Option::Precedence::PROGRAMMATIC)
|
@@ -116,6 +116,10 @@ module Datadog
|
|
116
116
|
options[name] = definition.build(self)
|
117
117
|
end
|
118
118
|
|
119
|
+
def resolved_env(name)
|
120
|
+
return options[name].resolved_env if options.key?(name)
|
121
|
+
end
|
122
|
+
|
119
123
|
def assert_valid_option!(name)
|
120
124
|
raise(InvalidOptionError, "#{self.class.name} doesn't define the option: #{name}") unless option_defined?(name)
|
121
125
|
end
|