From 5e7cc99668af227cde9929e6419b2847eacf4ca0 Mon Sep 17 00:00:00 2001 From: Luc Vieillescazes Date: Tue, 9 Apr 2024 17:12:06 +0200 Subject: [PATCH] Reapply "Send `spans_created` telemetry metric (#2577)" This reverts commit 8e6c72bb07d9cf5b5fd45db148932f583390dda0. --- Makefile | 3 + components-rs/ddtrace.h | 8 + components-rs/telemetry.h | 202 +++++++-------- components-rs/telemetry.rs | 36 +++ ext/ddtrace.c | 18 ++ ext/ddtrace.h | 2 + ext/ddtrace.stub.php | 24 ++ ext/ddtrace_arginfo.h | 11 +- ext/span.c | 5 + ext/span.h | 4 + ext/telemetry.c | 48 ++++ ext/telemetry.h | 4 +- src/DDTrace/OpenTelemetry/Span.php | 4 + src/DDTrace/OpenTracer/Tracer.php | 16 +- src/DDTrace/OpenTracer1/Tracer.php | 16 +- tests/Common/CLITestCase.php | 13 +- tests/Common/TracerTestTrait.php | 18 +- .../Custom/OpenTelemetry/composer.json | 6 + .../Frameworks/Custom/OpenTelemetry/index.php | 16 ++ .../Custom/OpenTracing/composer.json | 6 + tests/Frameworks/Custom/OpenTracing/index.php | 16 ++ .../Custom/Autoloaded/InstrumentationTest.php | 27 +- .../Integration/InternalTelemetryTest.php | 60 +++++ tests/OpenTracing/InternalTelemetryTest.php | 53 ++++ tests/ext/telemetry/metrics.phpt | 237 ++++++++++++++++++ tests/phpunit.xml | 1 + 26 files changed, 741 insertions(+), 113 deletions(-) create mode 100644 tests/Frameworks/Custom/OpenTelemetry/composer.json create mode 100644 tests/Frameworks/Custom/OpenTelemetry/index.php create mode 100644 tests/Frameworks/Custom/OpenTracing/composer.json create mode 100644 tests/Frameworks/Custom/OpenTracing/index.php create mode 100644 tests/OpenTelemetry/Integration/InternalTelemetryTest.php create mode 100644 tests/OpenTracing/InternalTelemetryTest.php create mode 100644 tests/ext/telemetry/metrics.phpt diff --git a/Makefile b/Makefile index 85665222cf..35eb00b7f8 100644 --- a/Makefile +++ b/Makefile @@ -1121,6 +1121,7 @@ benchmarks_opcache: benchmarks_run_dependencies call_benchmarks_opcache test_opentelemetry_1: global_test_run_dependencies rm -f tests/.scenarios.lock/opentelemetry1/composer.lock $(MAKE) test_scenario_opentelemetry1 + $(call run_composer_with_retry,tests/Frameworks/Custom/OpenTelemetry,) $(eval TEST_EXTRA_ENV=$(shell [ $(PHP_MAJOR_MINOR) -ge 81 ] && echo "OTEL_PHP_FIBERS_ENABLED=1" || echo '') DD_TRACE_OTEL_ENABLED=1 DD_TRACE_GENERATE_ROOT_SPAN=0) $(call run_tests,--testsuite=opentelemetry1 $(TESTS)) $(eval TEST_EXTRA_ENV=) @@ -1135,7 +1136,9 @@ test_opentracing_beta6: global_test_run_dependencies test_opentracing_10: global_test_run_dependencies $(MAKE) test_scenario_opentracing10 + $(call run_composer_with_retry,tests/Frameworks/Custom/OpenTracing,) $(call run_tests,tests/OpenTracer1Unit) + $(call run_tests,tests/OpenTracing) test_integrations: $(TEST_INTEGRATIONS_$(PHP_MAJOR_MINOR)) test_web: $(TEST_WEB_$(PHP_MAJOR_MINOR)) diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index f6b82f542d..7981ea96ea 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -171,6 +171,14 @@ ddog_MaybeError ddog_sidecar_telemetry_buffer_flush(ddog_SidecarTransport **tran const ddog_QueueId *queue_id, struct ddog_SidecarActionsBuffer *buffer); +void ddog_sidecar_telemetry_register_metric_buffer(struct ddog_SidecarActionsBuffer *buffer, + ddog_CharSlice metric_name); + +void ddog_sidecar_telemetry_add_span_metric_point_buffer(struct ddog_SidecarActionsBuffer *buffer, + ddog_CharSlice metric_name, + double metric_value, + ddog_CharSlice integration_name); + ddog_MaybeError ddog_sidecar_connect_php(ddog_SidecarTransport **connection, const char *error_path, ddog_CharSlice log_level, diff --git a/components-rs/telemetry.h b/components-rs/telemetry.h index f58c0c2c47..f3261e8822 100644 --- a/components-rs/telemetry.h +++ b/components-rs/telemetry.h @@ -17,33 +17,33 @@ void ddog_MaybeError_drop(ddog_MaybeError); * # Safety * * builder should be a non null pointer to a null pointer to a builder */ -ddog_MaybeError ddog_builder_instantiate(struct ddog_TelemetryWorkerBuilder **out_builder, - ddog_CharSlice service_name, - ddog_CharSlice language_name, - ddog_CharSlice language_version, - ddog_CharSlice tracer_version); +ddog_MaybeError ddog_telemetry_builder_instantiate(struct ddog_TelemetryWorkerBuilder **out_builder, + ddog_CharSlice service_name, + ddog_CharSlice language_name, + ddog_CharSlice language_version, + ddog_CharSlice tracer_version); /** * # Safety * * builder should be a non null pointer to a null pointer to a builder */ -ddog_MaybeError ddog_builder_instantiate_with_hostname(struct ddog_TelemetryWorkerBuilder **out_builder, - ddog_CharSlice hostname, - ddog_CharSlice service_name, - ddog_CharSlice language_name, - ddog_CharSlice language_version, - ddog_CharSlice tracer_version); +ddog_MaybeError ddog_telemetry_builder_instantiate_with_hostname(struct ddog_TelemetryWorkerBuilder **out_builder, + ddog_CharSlice hostname, + ddog_CharSlice service_name, + ddog_CharSlice language_name, + ddog_CharSlice language_version, + ddog_CharSlice tracer_version); -ddog_MaybeError ddog_builder_with_native_deps(struct ddog_TelemetryWorkerBuilder *builder, - bool include_native_deps); +ddog_MaybeError ddog_telemetry_builder_with_native_deps(struct ddog_TelemetryWorkerBuilder *builder, + bool include_native_deps); -ddog_MaybeError ddog_builder_with_rust_shared_lib_deps(struct ddog_TelemetryWorkerBuilder *builder, - bool include_rust_shared_lib_deps); +ddog_MaybeError ddog_telemetry_builder_with_rust_shared_lib_deps(struct ddog_TelemetryWorkerBuilder *builder, + bool include_rust_shared_lib_deps); -ddog_MaybeError ddog_builder_with_config(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice name, - ddog_CharSlice value, - enum ddog_ConfigurationOrigin origin); +ddog_MaybeError ddog_telemetry_builder_with_config(struct ddog_TelemetryWorkerBuilder *builder, + ddog_CharSlice name, + ddog_CharSlice value, + enum ddog_ConfigurationOrigin origin); /** * Builds the telemetry worker and return a handle to it @@ -51,8 +51,8 @@ ddog_MaybeError ddog_builder_with_config(struct ddog_TelemetryWorkerBuilder *bui * # Safety * * handle should be a non null pointer to a null pointer */ -ddog_MaybeError ddog_builder_run(struct ddog_TelemetryWorkerBuilder *builder, - struct ddog_TelemetryWorkerHandle **out_handle); +ddog_MaybeError ddog_telemetry_builder_run(struct ddog_TelemetryWorkerBuilder *builder, + struct ddog_TelemetryWorkerHandle **out_handle); /** * Builds the telemetry worker and return a handle to it. The worker will only process and send @@ -62,41 +62,41 @@ ddog_MaybeError ddog_builder_run(struct ddog_TelemetryWorkerBuilder *builder, * # Safety * * handle should be a non null pointer to a null pointer */ -ddog_MaybeError ddog_builder_run_metric_logs(struct ddog_TelemetryWorkerBuilder *builder, - struct ddog_TelemetryWorkerHandle **out_handle); +ddog_MaybeError ddog_telemetry_builder_run_metric_logs(struct ddog_TelemetryWorkerBuilder *builder, + struct ddog_TelemetryWorkerHandle **out_handle); -ddog_MaybeError ddog_builder_with_str_application_service_version(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_application_service_version(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_application_env(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_application_env(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_application_runtime_name(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_application_runtime_name(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_application_runtime_version(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_application_runtime_version(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); + +ddog_MaybeError ddog_telemetry_builder_with_str_application_runtime_patches(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_application_runtime_patches(struct ddog_TelemetryWorkerBuilder *builder, +ddog_MaybeError ddog_telemetry_builder_with_str_host_container_id(struct ddog_TelemetryWorkerBuilder *telemetry_builder, ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_host_container_id(struct ddog_TelemetryWorkerBuilder *builder, +ddog_MaybeError ddog_telemetry_builder_with_str_host_os(struct ddog_TelemetryWorkerBuilder *telemetry_builder, ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_host_os(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_host_kernel_name(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_host_kernel_name(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_host_kernel_release(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_host_kernel_release(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_host_kernel_version(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_str_host_kernel_version(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); - -ddog_MaybeError ddog_builder_with_str_runtime_id(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_runtime_id(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice param); /** * Sets a property from it's string value. @@ -127,9 +127,9 @@ ddog_MaybeError ddog_builder_with_str_runtime_id(struct ddog_TelemetryWorkerBuil * * */ -ddog_MaybeError ddog_builder_with_property_str(struct ddog_TelemetryWorkerBuilder *builder, - enum ddog_TelemetryWorkerBuilderStrProperty property, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_property_str(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + enum ddog_TelemetryWorkerBuilderStrProperty property, + ddog_CharSlice param); /** * Sets a property from it's string value. @@ -160,12 +160,12 @@ ddog_MaybeError ddog_builder_with_property_str(struct ddog_TelemetryWorkerBuilde * * */ -ddog_MaybeError ddog_builder_with_str_named_property(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice property, - ddog_CharSlice param); +ddog_MaybeError ddog_telemetry_builder_with_str_named_property(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice property, + ddog_CharSlice param); -ddog_MaybeError ddog_builder_with_bool_config_telemetry_debug_logging_enabled(struct ddog_TelemetryWorkerBuilder *builder, - bool param); +ddog_MaybeError ddog_telemetry_builder_with_bool_config_telemetry_debug_logging_enabled(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + bool param); /** * Sets a property from it's string value. @@ -176,9 +176,9 @@ ddog_MaybeError ddog_builder_with_bool_config_telemetry_debug_logging_enabled(st * * */ -ddog_MaybeError ddog_builder_with_property_bool(struct ddog_TelemetryWorkerBuilder *builder, - enum ddog_TelemetryWorkerBuilderBoolProperty property, - bool param); +ddog_MaybeError ddog_telemetry_builder_with_property_bool(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + enum ddog_TelemetryWorkerBuilderBoolProperty property, + bool param); /** * Sets a property from it's string value. @@ -189,12 +189,12 @@ ddog_MaybeError ddog_builder_with_property_bool(struct ddog_TelemetryWorkerBuild * * */ -ddog_MaybeError ddog_builder_with_bool_named_property(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice property, - bool param); +ddog_MaybeError ddog_telemetry_builder_with_bool_named_property(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice property, + bool param); -ddog_MaybeError ddog_builder_with_endpoint_config_endpoint(struct ddog_TelemetryWorkerBuilder *builder, - const struct ddog_Endpoint *param); +ddog_MaybeError ddog_telemetry_builder_with_endpoint_config_endpoint(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + const struct ddog_Endpoint *param); /** * Sets a property from it's string value. @@ -205,9 +205,9 @@ ddog_MaybeError ddog_builder_with_endpoint_config_endpoint(struct ddog_Telemetry * * */ -ddog_MaybeError ddog_builder_with_property_endpoint(struct ddog_TelemetryWorkerBuilder *builder, - enum ddog_TelemetryWorkerBuilderEndpointProperty property, - const struct ddog_Endpoint *param); +ddog_MaybeError ddog_telemetry_builder_with_property_endpoint(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + enum ddog_TelemetryWorkerBuilderEndpointProperty property, + const struct ddog_Endpoint *param); /** * Sets a property from it's string value. @@ -218,20 +218,20 @@ ddog_MaybeError ddog_builder_with_property_endpoint(struct ddog_TelemetryWorkerB * * */ -ddog_MaybeError ddog_builder_with_endpoint_named_property(struct ddog_TelemetryWorkerBuilder *builder, - ddog_CharSlice property, - const struct ddog_Endpoint *param); +ddog_MaybeError ddog_telemetry_builder_with_endpoint_named_property(struct ddog_TelemetryWorkerBuilder *telemetry_builder, + ddog_CharSlice property, + const struct ddog_Endpoint *param); -ddog_MaybeError ddog_handle_add_dependency(const struct ddog_TelemetryWorkerHandle *handle, - ddog_CharSlice dependency_name, - ddog_CharSlice dependency_version); +ddog_MaybeError ddog_telemetry_handle_add_dependency(const struct ddog_TelemetryWorkerHandle *handle, + ddog_CharSlice dependency_name, + ddog_CharSlice dependency_version); -ddog_MaybeError ddog_handle_add_integration(const struct ddog_TelemetryWorkerHandle *handle, - ddog_CharSlice dependency_name, - ddog_CharSlice dependency_version, - bool enabled, - struct ddog_Option_Bool compatible, - struct ddog_Option_Bool auto_enabled); +ddog_MaybeError ddog_telemetry_handle_add_integration(const struct ddog_TelemetryWorkerHandle *handle, + ddog_CharSlice dependency_name, + ddog_CharSlice dependency_version, + bool enabled, + struct ddog_Option_Bool compatible, + struct ddog_Option_Bool auto_enabled); /** * * indentifier: identifies a logging location uniquely. This can for instance be the template @@ -239,52 +239,52 @@ ddog_MaybeError ddog_handle_add_integration(const struct ddog_TelemetryWorkerHan * * stack_trace: stack trace associated with the log. If no stack trace is available, an empty * string should be passed */ -ddog_MaybeError ddog_handle_add_log(const struct ddog_TelemetryWorkerHandle *handle, - ddog_CharSlice indentifier, - ddog_CharSlice message, - enum ddog_LogLevel level, - ddog_CharSlice stack_trace); +ddog_MaybeError ddog_telemetry_handle_add_log(const struct ddog_TelemetryWorkerHandle *handle, + ddog_CharSlice indentifier, + ddog_CharSlice message, + enum ddog_LogLevel level, + ddog_CharSlice stack_trace); -ddog_MaybeError ddog_handle_start(const struct ddog_TelemetryWorkerHandle *handle); +ddog_MaybeError ddog_telemetry_handle_start(const struct ddog_TelemetryWorkerHandle *handle); -struct ddog_TelemetryWorkerHandle *ddog_handle_clone(const struct ddog_TelemetryWorkerHandle *handle); +struct ddog_TelemetryWorkerHandle *ddog_telemetry_handle_clone(const struct ddog_TelemetryWorkerHandle *handle); -ddog_MaybeError ddog_handle_stop(const struct ddog_TelemetryWorkerHandle *handle); +ddog_MaybeError ddog_telemetry_handle_stop(const struct ddog_TelemetryWorkerHandle *handle); /** * * compatible: should be false if the metric is language specific, true otherwise */ -struct ddog_ContextKey ddog_handle_register_metric_context(const struct ddog_TelemetryWorkerHandle *handle, - ddog_CharSlice name, - enum ddog_MetricType metric_type, - struct ddog_Vec_Tag tags, - bool common, - enum ddog_MetricNamespace namespace_); - -ddog_MaybeError ddog_handle_add_point(const struct ddog_TelemetryWorkerHandle *handle, - const struct ddog_ContextKey *context_key, - double value); - -ddog_MaybeError ddog_handle_add_point_with_tags(const struct ddog_TelemetryWorkerHandle *handle, +struct ddog_ContextKey ddog_telemetry_handle_register_metric_context(const struct ddog_TelemetryWorkerHandle *handle, + ddog_CharSlice name, + enum ddog_MetricType metric_type, + struct ddog_Vec_Tag tags, + bool common, + enum ddog_MetricNamespace namespace_); + +ddog_MaybeError ddog_telemetry_handle_add_point(const struct ddog_TelemetryWorkerHandle *handle, const struct ddog_ContextKey *context_key, - double value, - struct ddog_Vec_Tag extra_tags); + double value); + +ddog_MaybeError ddog_telemetry_handle_add_point_with_tags(const struct ddog_TelemetryWorkerHandle *handle, + const struct ddog_ContextKey *context_key, + double value, + struct ddog_Vec_Tag extra_tags); /** * This function takes ownership of the handle. It should not be used after calling it */ -void ddog_handle_wait_for_shutdown(struct ddog_TelemetryWorkerHandle *handle); +void ddog_telemetry_handle_wait_for_shutdown(struct ddog_TelemetryWorkerHandle *handle); /** * This function takes ownership of the handle. It should not be used after calling it */ -void ddog_handle_wait_for_shutdown_ms(struct ddog_TelemetryWorkerHandle *handle, - uint64_t wait_for_ms); +void ddog_telemetry_handle_wait_for_shutdown_ms(struct ddog_TelemetryWorkerHandle *handle, + uint64_t wait_for_ms); /** * Drops the handle without waiting for shutdown. The worker will continue running in the * background until it exits by itself */ -void ddog_handle_drop(struct ddog_TelemetryWorkerHandle *handle); +void ddog_telemetry_handle_drop(struct ddog_TelemetryWorkerHandle *handle); #endif /* DDOG_TELEMETRY_H */ diff --git a/components-rs/telemetry.rs b/components-rs/telemetry.rs index 3ca54b1e3a..cf6bbb36a4 100644 --- a/components-rs/telemetry.rs +++ b/components-rs/telemetry.rs @@ -2,8 +2,10 @@ use datadog_sidecar::interface::blocking::SidecarTransport; use datadog_sidecar::interface::{blocking, InstanceId, QueueId, SidecarAction}; use ddcommon_ffi::slice::AsBytes; use ddcommon_ffi::CharSlice; +use ddcommon::tag::Tag; use ddtelemetry::data; use ddtelemetry::data::{Dependency, Integration}; +use ddtelemetry::metrics::MetricContext; use ddtelemetry::worker::TelemetryActions; use ddtelemetry_ffi::{try_c, MaybeError}; use std::error::Error; @@ -127,3 +129,37 @@ pub extern "C" fn ddog_sidecar_telemetry_buffer_flush( MaybeError::None } + +#[no_mangle] +pub unsafe extern "C" fn ddog_sidecar_telemetry_register_metric_buffer( + buffer: &mut SidecarActionsBuffer, + metric_name: CharSlice, +) { + + buffer.buffer.push(SidecarAction::RegisterTelemetryMetric(MetricContext { + name: metric_name.to_utf8_lossy().into_owned(), + namespace: data::metrics::MetricNamespace::Tracers, + metric_type: data::metrics::MetricType::Count, + tags: Vec::default(), + common: false, + })); +} + +#[no_mangle] +pub unsafe extern "C" fn ddog_sidecar_telemetry_add_span_metric_point_buffer( + buffer: &mut SidecarActionsBuffer, + metric_name: CharSlice, + metric_value: f64, + integration_name: CharSlice, +) { + let mut tags: Vec = Vec::default(); + if integration_name.len() > 0 { + tags.push(Tag::new("integration_name", integration_name.to_utf8_lossy().into_owned()).unwrap()) + } + + buffer.buffer.push(SidecarAction::AddTelemetryMetricPoint(( + metric_name.to_utf8_lossy().into_owned(), + metric_value, + tags, + ))); +} diff --git a/ext/ddtrace.c b/ext/ddtrace.c index e9a522c335..1e249325f4 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -419,6 +419,7 @@ static void ddtrace_activate(void) { zai_hook_rinit(); zai_interceptor_activate(); zai_uhook_rinit(); + ddtrace_telemetry_rinit(); zend_hash_init(&DDTRACE_G(traced_spans), 8, unused, NULL, 0); zend_hash_init(&DDTRACE_G(tracestate_unknown_dd_keys), 8, unused, NULL, 0); @@ -1402,6 +1403,8 @@ static PHP_RSHUTDOWN_FUNCTION(ddtrace) { } dd_finalize_telemetry(); + ddtrace_telemetry_rshutdown(); + if (DDTRACE_G(last_flushed_root_service_name)) { zend_string_release(DDTRACE_G(last_flushed_root_service_name)); DDTRACE_G(last_flushed_root_service_name) = NULL; @@ -1955,6 +1958,21 @@ PHP_FUNCTION(DDTrace_Testing_trigger_error) { } } +PHP_FUNCTION(DDTrace_Internal_add_span_flag) { + zend_object *span; + zend_long flag; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJ_OF_CLASS_EX(span, ddtrace_ce_span_data, 0, 1) + Z_PARAM_LONG(flag) + ZEND_PARSE_PARAMETERS_END(); + + ddtrace_span_data *span_data = OBJ_SPANDATA(span); + span_data->flags |= (uint8_t)flag; + + RETURN_NULL(); +} + PHP_FUNCTION(ddtrace_init) { if (DDTRACE_G(request_init_hook_loaded) == 1) { RETURN_FALSE; diff --git a/ext/ddtrace.h b/ext/ddtrace.h index c836a70c35..52223a3308 100644 --- a/ext/ddtrace.h +++ b/ext/ddtrace.h @@ -109,6 +109,8 @@ ZEND_BEGIN_MODULE_GLOBALS(ddtrace) zend_string *last_flushed_root_service_name; zend_string *last_flushed_root_env_name; + HashTable telemetry_spans_created_per_integration; + HashTable uhook_active_hooks; HashTable uhook_closure_hooks; ZEND_END_MODULE_GLOBALS(ddtrace) diff --git a/ext/ddtrace.stub.php b/ext/ddtrace.stub.php index c937593422..e57db272aa 100644 --- a/ext/ddtrace.stub.php +++ b/ext/ddtrace.stub.php @@ -669,6 +669,30 @@ function set_blocking_function(\DDTrace\RootSpanData $span, callable $blockingFu function trigger_error(string $message, int $errorType): void {} } +namespace DDTrace\Internal { + /** + * @var int + * @cvalue DDTRACE_SPAN_FLAG_OPENTELEMETRY + */ + const SPAN_FLAG_OPENTELEMETRY = UNKNOWN; + + /** + * @var int + * @cvalue DDTRACE_SPAN_FLAG_OPENTRACING + */ + const SPAN_FLAG_OPENTRACING = UNKNOWN; + + /** + * Adds a flag to a span. + * + * @internal + * + * @param \DDTrace\SpanData $span the span to flag + * @param int $flag the flag to add to the span + */ + function add_span_flag(\DDTrace\SpanData $span, int $flag): void {} +} + namespace { /** diff --git a/ext/ddtrace_arginfo.h b/ext/ddtrace_arginfo.h index 798b44f8c9..9ad58cc0eb 100644 --- a/ext/ddtrace_arginfo.h +++ b/ext/ddtrace_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7fec613751ec101cba339b6dfa3a72fbaf38074f */ + * Stub hash: 1271064f2b0fcdc3d20e2f3d6c780607b300c5f9 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_trace_method, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, className, IS_STRING, 0) @@ -165,6 +165,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_Testing_trigger_error, 0 ZEND_ARG_TYPE_INFO(0, errorType, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_Internal_add_span_flag, 0, 2, IS_VOID, 0) + ZEND_ARG_OBJ_INFO(0, span, DDTrace\\SpanData, 0) + ZEND_ARG_TYPE_INFO(0, flag, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dd_trace_env_config, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, envName, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -326,6 +331,7 @@ ZEND_FUNCTION(DDTrace_UserRequest_notify_start); ZEND_FUNCTION(DDTrace_UserRequest_notify_commit); ZEND_FUNCTION(DDTrace_UserRequest_set_blocking_function); ZEND_FUNCTION(DDTrace_Testing_trigger_error); +ZEND_FUNCTION(DDTrace_Internal_add_span_flag); ZEND_FUNCTION(dd_trace_env_config); ZEND_FUNCTION(dd_trace_disable_in_request); ZEND_FUNCTION(dd_trace_reset); @@ -409,6 +415,7 @@ static const zend_function_entry ext_functions[] = { ZEND_NS_FALIAS("DDTrace\\UserRequest", notify_commit, DDTrace_UserRequest_notify_commit, arginfo_DDTrace_UserRequest_notify_commit) ZEND_NS_FALIAS("DDTrace\\UserRequest", set_blocking_function, DDTrace_UserRequest_set_blocking_function, arginfo_DDTrace_UserRequest_set_blocking_function) ZEND_NS_FALIAS("DDTrace\\Testing", trigger_error, DDTrace_Testing_trigger_error, arginfo_DDTrace_Testing_trigger_error) + ZEND_NS_FALIAS("DDTrace\\Internal", add_span_flag, DDTrace_Internal_add_span_flag, arginfo_DDTrace_Internal_add_span_flag) ZEND_FE(dd_trace_env_config, arginfo_dd_trace_env_config) ZEND_FE(dd_trace_disable_in_request, arginfo_dd_trace_disable_in_request) ZEND_FE(dd_trace_reset, arginfo_dd_trace_reset) @@ -479,6 +486,8 @@ static void register_ddtrace_symbols(int module_number) REGISTER_LONG_CONSTANT("DDTrace\\DBM_PROPAGATION_DISABLED", DD_TRACE_DBM_PROPAGATION_DISABLED, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("DDTrace\\DBM_PROPAGATION_SERVICE", DD_TRACE_DBM_PROPAGATION_SERVICE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("DDTrace\\DBM_PROPAGATION_FULL", DD_TRACE_DBM_PROPAGATION_FULL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DDTrace\\Internal\\SPAN_FLAG_OPENTELEMETRY", DDTRACE_SPAN_FLAG_OPENTELEMETRY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("DDTrace\\Internal\\SPAN_FLAG_OPENTRACING", DDTRACE_SPAN_FLAG_OPENTRACING, CONST_PERSISTENT); REGISTER_STRING_CONSTANT("DD_TRACE_VERSION", PHP_DDTRACE_VERSION, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("DD_TRACE_PRIORITY_SAMPLING_AUTO_KEEP", PRIORITY_SAMPLING_AUTO_KEEP, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("DD_TRACE_PRIORITY_SAMPLING_AUTO_REJECT", PRIORITY_SAMPLING_AUTO_REJECT, CONST_PERSISTENT); diff --git a/ext/span.c b/ext/span.c index 1507e9706f..e9616130fc 100644 --- a/ext/span.c +++ b/ext/span.c @@ -12,6 +12,7 @@ #include #include "random.h" #include "serializer.h" +#include "telemetry.h" #include "ext/standard/php_string.h" #include #include "user_request.h" @@ -553,6 +554,10 @@ void ddtrace_close_span(ddtrace_span_data *span) { ddtrace_switch_span_stack(span->stack); } + // Telemetry: increment the spans_created counter + // Must be done at closing because we need to read the "component" span's meta which is not available at creation + ddtrace_telemetry_inc_spans_created(span); + ddtrace_close_stack_userland_spans_until(span); ddtrace_close_top_span_without_stack_swap(span); diff --git a/ext/span.h b/ext/span.h index a5ff7a78c0..496814f75d 100644 --- a/ext/span.h +++ b/ext/span.h @@ -13,6 +13,9 @@ #define DDTRACE_DROPPED_SPAN (-1ull) #define DDTRACE_SILENTLY_DROPPED_SPAN (-2ull) +#define DDTRACE_SPAN_FLAG_OPENTELEMETRY (1 << 0) +#define DDTRACE_SPAN_FLAG_OPENTRACING (1 << 1) + struct ddtrace_span_stack; enum ddtrace_span_dataype { @@ -72,6 +75,7 @@ struct ddtrace_span_data { uint64_t start; uint64_t duration_start; uint64_t duration; + uint8_t flags; enum ddtrace_span_dataype type : 8; bool notify_user_req_end; struct ddtrace_span_data *next; diff --git a/ext/telemetry.c b/ext/telemetry.c index 7fa80aac5c..3a2bcb49ae 100644 --- a/ext/telemetry.c +++ b/ext/telemetry.c @@ -25,6 +25,14 @@ void ddtrace_telemetry_first_init(void) { dd_composer_hook_id = zai_hook_install((zai_str)ZAI_STR_EMPTY, (zai_str)ZAI_STR_EMPTY, dd_check_for_composer_autoloader, NULL, ZAI_HOOK_AUX_UNUSED, 0); } +void ddtrace_telemetry_rinit(void) { + zend_hash_init(&DDTRACE_G(telemetry_spans_created_per_integration), 8, unused, NULL, 0); +} + +void ddtrace_telemetry_rshutdown(void) { + zend_hash_destroy(&DDTRACE_G(telemetry_spans_created_per_integration)); +} + void ddtrace_telemetry_finalize(void) { if (!ddtrace_sidecar || !get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { return; @@ -69,6 +77,16 @@ void ddtrace_telemetry_finalize(void) { ddog_sidecar_telemetry_addIntegration_buffer(buffer, integration_name, DDOG_CHARSLICE_C(""), false); } } + + // Telemetry metrics + ddog_CharSlice metric_name = DDOG_CHARSLICE_C("spans_created"); + ddog_sidecar_telemetry_register_metric_buffer(buffer, metric_name); + zend_string *integration_name; + zval *metric_value; + ZEND_HASH_FOREACH_STR_KEY_VAL(&DDTRACE_G(telemetry_spans_created_per_integration), integration_name, metric_value) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, metric_name, Z_DVAL_P(metric_value), dd_zend_string_to_CharSlice(integration_name)); + } ZEND_HASH_FOREACH_END(); + ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(telemetry_queue_id), buffer); ddog_CharSlice service_name = DDOG_CHARSLICE_C_BARE("unnamed-php-service"); @@ -98,3 +116,33 @@ void ddtrace_telemetry_notify_integration(const char *name, size_t name_len) { DDOG_CHARSLICE_C(""), true); } } + +void ddtrace_telemetry_inc_spans_created(ddtrace_span_data *span) { + zval *component = NULL; + if (Z_TYPE(span->property_meta) == IS_ARRAY) { + component = zend_hash_str_find(Z_ARRVAL(span->property_meta), ZEND_STRL("component")); + } + + zend_string *integration = NULL; + if (component && Z_TYPE_P(component) == IS_STRING) { + integration = zend_string_copy(Z_STR_P(component)); + } else if (span->flags & DDTRACE_SPAN_FLAG_OPENTELEMETRY) { + integration = zend_string_init(ZEND_STRL("otel"), 0); + } else if (span->flags & DDTRACE_SPAN_FLAG_OPENTRACING) { + integration = zend_string_init(ZEND_STRL("opentracing"), 0); + } else { + // Fallback value when the span has not been created by an integration, nor OpenTelemetry/OpenTracing (i.e. \DDTrace\span_start()) + integration = zend_string_init(ZEND_STRL("datadog"), 0); + } + + zval *current = zend_hash_find(&DDTRACE_G(telemetry_spans_created_per_integration), integration); + if (current) { + ++Z_DVAL_P(current); + } else { + zval counter; + ZVAL_DOUBLE(&counter, 1.0); + zend_hash_add(&DDTRACE_G(telemetry_spans_created_per_integration), integration, &counter); + } + + zend_string_release(integration); +} diff --git a/ext/telemetry.h b/ext/telemetry.h index 69ec068285..fbc012770e 100644 --- a/ext/telemetry.h +++ b/ext/telemetry.h @@ -4,8 +4,10 @@ #include void ddtrace_telemetry_first_init(void); +void ddtrace_telemetry_rinit(void); +void ddtrace_telemetry_rshutdown(void); ddog_TelemetryWorkerHandle *ddtrace_build_telemetry_handle(void); void ddtrace_telemetry_notify_integration(const char *name, size_t name_len); void ddtrace_telemetry_finalize(void); - +void ddtrace_telemetry_inc_spans_created(ddtrace_span_data *span); #endif // DDTRACE_TELEMETRY_H diff --git a/src/DDTrace/OpenTelemetry/Span.php b/src/DDTrace/OpenTelemetry/Span.php index 9991eff3f8..084e8a91fd 100644 --- a/src/DDTrace/OpenTelemetry/Span.php +++ b/src/DDTrace/OpenTelemetry/Span.php @@ -22,6 +22,7 @@ use function DDTrace\close_span; use function DDTrace\switch_stack; +use function DDTrace\Internal\add_span_flag; final class Span extends API\Span implements ReadWriteSpanInterface { @@ -143,6 +144,9 @@ public static function startSpan( $resourceAttributes = $resource->getAttributes()->toArray(); self::_setAttributes($span, $resourceAttributes); + // Mark the span as created by OpenTelemetry + add_span_flag($span, \DDTrace\Internal\SPAN_FLAG_OPENTELEMETRY); + $OTelSpan = new self( $span, $context, diff --git a/src/DDTrace/OpenTracer/Tracer.php b/src/DDTrace/OpenTracer/Tracer.php index 0894e3d2cf..81925908d4 100644 --- a/src/DDTrace/OpenTracer/Tracer.php +++ b/src/DDTrace/OpenTracer/Tracer.php @@ -10,6 +10,8 @@ use OpenTracing\ScopeManager as OTScopeManager; use OpenTracing\SpanContext as OTSpanContext; use OpenTracing\Tracer as OTTracer; +use function DDTrace\active_span; +use function DDTrace\Internal\add_span_flag; final class Tracer implements OTTracer { @@ -82,9 +84,14 @@ public function startSpan($operationName, $options = []) if ($options instanceof \OpenTracing\StartSpanOptions) { $options = self::deconstructStartSpanOptions($options); } - return new Span( + $span = new Span( $this->tracer->startSpan($operationName, $options) ); + + // Mark the span as created by OpenTracing + add_span_flag(active_span(), \DDTrace\Internal\SPAN_FLAG_OPENTRACING); + + return $span; } /** @@ -95,9 +102,14 @@ public function startActiveSpan($operationName, $options = []) if ($options instanceof \OpenTracing\StartSpanOptions) { $options = self::deconstructStartSpanOptions($options); } - return new Scope( + $scope = new Scope( $this->tracer->startActiveSpan($operationName, $options) ); + + // Mark the span as created by OpenTracing + add_span_flag(active_span(), \DDTrace\Internal\SPAN_FLAG_OPENTRACING); + + return $scope; } /** diff --git a/src/DDTrace/OpenTracer1/Tracer.php b/src/DDTrace/OpenTracer1/Tracer.php index c175dc48e4..ab32464b1c 100644 --- a/src/DDTrace/OpenTracer1/Tracer.php +++ b/src/DDTrace/OpenTracer1/Tracer.php @@ -12,6 +12,8 @@ use OpenTracing\Span as OTSpan; use OpenTracing\SpanContext as OTSpanContext; use OpenTracing\Tracer as OTTracer; +use function DDTrace\active_span; +use function DDTrace\Internal\add_span_flag; final class Tracer implements OTTracer { @@ -81,9 +83,14 @@ public function startSpan(string $operationName, $options = []): OTSpan if ($options instanceof \OpenTracing\StartSpanOptions) { $options = self::deconstructStartSpanOptions($options); } - return new Span( + $span = new Span( $this->tracer->startSpan($operationName, $options) ); + + // Mark the span as created by OpenTracing + add_span_flag(active_span(), \DDTrace\Internal\SPAN_FLAG_OPENTRACING); + + return $span; } /** @@ -94,9 +101,14 @@ public function startActiveSpan(string $operationName, $options = []): OTScope if ($options instanceof \OpenTracing\StartSpanOptions) { $options = self::deconstructStartSpanOptions($options); } - return new Scope( + $scope = new Scope( $this->tracer->startActiveSpan($operationName, $options) ); + + // Mark the span as created by OpenTracing + add_span_flag(active_span(), \DDTrace\Internal\SPAN_FLAG_OPENTRACING); + + return $scope; } /** diff --git a/tests/Common/CLITestCase.php b/tests/Common/CLITestCase.php index 8d62e61e30..cfa7585ec7 100644 --- a/tests/Common/CLITestCase.php +++ b/tests/Common/CLITestCase.php @@ -72,6 +72,18 @@ public function getTracesFromCommand($arguments = '', $overrideEnvs = []) * @return array | null */ public function getAgentRequestFromCommand($arguments = '', $overrideEnvs = []) + { + $this->executeCommand($arguments, $overrideEnvs); + return $this->retrieveDumpedTraceData()[0] ?? []; + } + + /** + * Run a command from the CLI. + * + * @param string $arguments + * @param array $overrideEnvs + */ + public function executeCommand($arguments = '', $overrideEnvs = []) { $envs = (string) new EnvSerializer(array_merge([], static::getEnvs(), $overrideEnvs)); $inis = (string) new IniSerializer(static::getInis()); @@ -79,7 +91,6 @@ public function getAgentRequestFromCommand($arguments = '', $overrideEnvs = []) $arguments = escapeshellarg($arguments); $commandToExecute = "$envs " . PHP_BINARY . " $inis $script $arguments"; `$commandToExecute`; - return $this->retrieveDumpedTraceData()[0] ?? []; } /** diff --git a/tests/Common/TracerTestTrait.php b/tests/Common/TracerTestTrait.php index 55945e93ca..ebaa241758 100644 --- a/tests/Common/TracerTestTrait.php +++ b/tests/Common/TracerTestTrait.php @@ -463,8 +463,14 @@ public function parseMultipleRequestsFromDumpedData() /** * Returns the raw response body, if any, or null otherwise. */ - public function retrieveDumpedData() + public function retrieveDumpedData(callable $until = null, $throw = false) { + if (!$until) { + $until = function ($request) { + return (strpos($request["uri"] ?? "", "/telemetry/") !== 0); + }; + } + $allResponses = []; // When tests run with the background sender enabled, there might be some delay between when a trace is flushed @@ -488,12 +494,18 @@ public function retrieveDumpedData() $loaded = json_decode($response, true); array_push($allResponses, ...$loaded); foreach ($loaded as $request) { - if (strpos($request["uri"] ?? "", "/telemetry/") !== 0) { - break 2; + if ($until($request)) { + return $allResponses; } } + \usleep(1000); } } + + if ($throw) { + throw new \LogicException('The expected request was not found'); + } + return $allResponses; } diff --git a/tests/Frameworks/Custom/OpenTelemetry/composer.json b/tests/Frameworks/Custom/OpenTelemetry/composer.json new file mode 100644 index 0000000000..c5811adc60 --- /dev/null +++ b/tests/Frameworks/Custom/OpenTelemetry/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "php": ">=7.4", + "open-telemetry/sdk": "^1.0" + } +} diff --git a/tests/Frameworks/Custom/OpenTelemetry/index.php b/tests/Frameworks/Custom/OpenTelemetry/index.php new file mode 100644 index 0000000000..10d3a610c3 --- /dev/null +++ b/tests/Frameworks/Custom/OpenTelemetry/index.php @@ -0,0 +1,16 @@ +getTracer('foo'); + $span = $tracer->spanBuilder('barbar') + ->startSpan() + ; + $span->end(); +} finally { + \dd_trace_internal_fn("finalize_telemetry"); +} diff --git a/tests/Frameworks/Custom/OpenTracing/composer.json b/tests/Frameworks/Custom/OpenTracing/composer.json new file mode 100644 index 0000000000..a0a010e19e --- /dev/null +++ b/tests/Frameworks/Custom/OpenTracing/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "php": ">=7.1", + "opentracing/opentracing": "^1.0" + } +} diff --git a/tests/Frameworks/Custom/OpenTracing/index.php b/tests/Frameworks/Custom/OpenTracing/index.php new file mode 100644 index 0000000000..9b2228f38b --- /dev/null +++ b/tests/Frameworks/Custom/OpenTracing/index.php @@ -0,0 +1,16 @@ +startActiveSpan('web.request'); + $span = $scope->getSpan(); + $span->setTag('service.name', 'service_name'); + $span->setTag('resource.name', 'resource_name'); + $span->setTag('span.type', 'web'); + $span->setTag('http.method', $_SERVER['REQUEST_METHOD']); + $span->finish(); +} finally { + \dd_trace_internal_fn("finalize_telemetry"); +} diff --git a/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php b/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php index 35aae49a36..d803e1d97a 100644 --- a/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php +++ b/tests/Integrations/Custom/Autoloaded/InstrumentationTest.php @@ -53,8 +53,15 @@ public function testInstrumentation() $this->fail("Go no response from request-dumper"); } - $this->assertCount(3, $response); + $this->assertCount(4, $response); $payloads = $this->readTelemetryPayloads($response); + + $isMetric = function (array $payload) { + return 'generate-metrics' === $payload['request_type']; + }; + $metrics = array_values(array_filter($payloads, $isMetric)); + $payloads = array_values(array_filter($payloads, function($p) use ($isMetric) { return !$isMetric($p); })); + $this->assertEquals("app-started", $payloads[0]["request_type"]); $this->assertContains([ "name" => "agent_host", @@ -70,6 +77,12 @@ public function testInstrumentation() })); // Not asserting app-closing, this is not expected to happen until shutdown + $this->assertCount(1, $metrics); + $this->assertEquals("generate-metrics", $metrics[0]["request_type"]); + $this->assertEquals("tracers", $metrics[0]["payload"]["series"][0]["namespace"]); + $this->assertEquals("spans_created", $metrics[0]["payload"]["series"][0]["metric"]); + $this->assertEquals(["integration_name:datadog"], $metrics[0]["payload"]["series"][0]["tags"]); + $this->call(GetSpec::create("autoloaded", "/pdo")); $response = $this->retrieveDumpedData(); @@ -77,8 +90,12 @@ public function testInstrumentation() $this->fail("Go no response from request-dumper"); } - $this->assertCount(3, $response); + $this->assertCount(4, $response); $payloads = $this->readTelemetryPayloads($response); + + $metrics = array_values(array_filter($payloads, $isMetric)); + $payloads = array_values(array_filter($payloads, function($p) use ($isMetric) { return !$isMetric($p); })); + $this->assertEquals("app-started", $payloads[0]["request_type"]); $this->assertEquals("app-dependencies-loaded", $payloads[1]["request_type"]); $this->assertEquals("app-integrations-change", $payloads[2]["request_type"]); @@ -105,5 +122,11 @@ public function testInstrumentation() 'auto_enabled' => null, ] ], $payloads[2]["payload"]["integrations"]); + + $this->assertCount(1, $metrics); + $this->assertEquals("generate-metrics", $metrics[0]["request_type"]); + $this->assertEquals("tracers", $metrics[0]["payload"]["series"][0]["namespace"]); + $this->assertEquals("spans_created", $metrics[0]["payload"]["series"][0]["metric"]); + $this->assertEquals(["integration_name:pdo"], $metrics[0]["payload"]["series"][0]["tags"]); } } diff --git a/tests/OpenTelemetry/Integration/InternalTelemetryTest.php b/tests/OpenTelemetry/Integration/InternalTelemetryTest.php new file mode 100644 index 0000000000..5faa34874d --- /dev/null +++ b/tests/OpenTelemetry/Integration/InternalTelemetryTest.php @@ -0,0 +1,60 @@ + 1, + ]); + } + + private function readTelemetryPayloads($response) + { + $telemetryPayloads = []; + foreach ($response as $request) { + if (strpos($request["uri"], "/telemetry/") === 0) { + $json = json_decode($request["body"], true); + $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; + foreach ($batch as $json) { + $telemetryPayloads[] = $json; + } + } + } + return $telemetryPayloads; + } + + public function testInternalMetricWithOpenTelemetry() + { + $this->resetRequestDumper(); + + $this->executeCommand(); + + $requests = $this->retrieveDumpedData(function ($request) { + return (strpos($request["uri"] ?? "", "/telemetry/") === 0) + && (strpos($request["body"] ?? "", "generate-metrics") !== false) + ; + }, true); + + $payloads = $this->readTelemetryPayloads($requests); + $isMetric = function (array $payload) { + return 'generate-metrics' === $payload['request_type']; + }; + $metrics = array_values(array_filter($payloads, $isMetric)); + + $this->assertCount(1, $metrics); + $this->assertEquals("generate-metrics", $metrics[0]["request_type"]); + $this->assertEquals("tracers", $metrics[0]["payload"]["series"][0]["namespace"]); + $this->assertEquals("spans_created", $metrics[0]["payload"]["series"][0]["metric"]); + $this->assertEquals(["integration_name:otel"], $metrics[0]["payload"]["series"][0]["tags"]); + } +} diff --git a/tests/OpenTracing/InternalTelemetryTest.php b/tests/OpenTracing/InternalTelemetryTest.php new file mode 100644 index 0000000000..62689acbae --- /dev/null +++ b/tests/OpenTracing/InternalTelemetryTest.php @@ -0,0 +1,53 @@ +resetRequestDumper(); + + $this->executeCommand(); + + $requests = $this->retrieveDumpedData(function ($request) { + return (strpos($request["uri"] ?? "", "/telemetry/") === 0) + && (strpos($request["body"] ?? "", "generate-metrics") !== false) + ; + }, true); + + $payloads = $this->readTelemetryPayloads($requests); + $isMetric = function (array $payload) { + return 'generate-metrics' === $payload['request_type']; + }; + $metrics = array_values(array_filter($payloads, $isMetric)); + + $this->assertCount(1, $metrics); + $this->assertEquals("generate-metrics", $metrics[0]["request_type"]); + $this->assertEquals("tracers", $metrics[0]["payload"]["series"][0]["namespace"]); + $this->assertEquals("spans_created", $metrics[0]["payload"]["series"][0]["metric"]); + $this->assertEquals(["integration_name:opentracing"], $metrics[0]["payload"]["series"][0]["tags"]); + } +} diff --git a/tests/ext/telemetry/metrics.phpt b/tests/ext/telemetry/metrics.phpt new file mode 100644 index 0000000000..ec30c523b1 --- /dev/null +++ b/tests/ext/telemetry/metrics.phpt @@ -0,0 +1,237 @@ +--TEST-- +Signal integration telemetry +--SKIPIF-- + +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +_DD_LOAD_TEST_INTEGRATIONS=1 +DD_INSTRUMENTATION_TELEMETRY_ENABLED=1 +--INI-- +datadog.trace.agent_url="file://{PWD}/integration-telemetry.out" +ddtrace.request_init_hook="{PWD}/../sandbox/deferred_loading_helper.php" +zend.assertions=1 +--FILE-- +meta['component'] = $args[0]; + }); + + dd_trace_method("Test", "create_span_with_flag", function(\DDTrace\SpanData $span, array $args) { + \DDTrace\Internal\add_span_flag($span, $args[0]); + }); + + return Integration::LOADED; + } + } +} + +namespace +{ + class Test + { + // Must be called to trigger the call of TestSandboxedIntegration::init() + public static function public_static_method() + { + echo __METHOD__."\n"; + } + + public static function create_span_from_anomymous_source() + { + echo __METHOD__."\n"; + } + + public static function create_span_from_named_integration($integrationName) + { + echo __METHOD__."({$integrationName})\n"; + } + + public static function create_span_with_flag() + { + echo __METHOD__."\n"; + } + } + + // Don't create a span + Test::public_static_method(); + + Test::create_span_from_anomymous_source(); + Test::create_span_from_anomymous_source(); + + Test::create_span_from_named_integration('testintegration'); + Test::create_span_from_named_integration('testintegration'); + Test::create_span_from_named_integration('testintegration'); + Test::create_span_from_named_integration('foo'); + + Test::create_span_with_flag(\DDTRACE\Internal\SPAN_FLAG_OPENTELEMETRY); + + dd_trace_internal_fn("finalize_telemetry"); + + for ($i = 0; $i < 100; ++$i) { + usleep(100000); + if (file_exists(__DIR__ . '/integration-telemetry.out')) { + foreach (file(__DIR__ . '/integration-telemetry.out') as $l) { + if ($l) { + $json = json_decode($l, true); + $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; + foreach ($batch as $json) { + if ($json["request_type"] == "generate-metrics") { + $series = []; + foreach ($json['payload']['series'] as $serie) { + $key = $serie['namespace'].$serie['metric'].implode(',', $serie['tags']); + $series[$key] = $serie; + }; + ksort($series); + var_dump(array_values($series)); + + break 3; + } + } + } + } + } + } +} + +?> +--EXPECTF-- +Test::public_static_method +Test::create_span_from_anomymous_source +Test::create_span_from_anomymous_source +Test::create_span_from_named_integration(testintegration) +Test::create_span_from_named_integration(testintegration) +Test::create_span_from_named_integration(testintegration) +Test::create_span_from_named_integration(foo) +Test::create_span_with_flag +array(4) { + [0]=> + array(7) { + ["namespace"]=> + string(7) "tracers" + ["metric"]=> + string(13) "spans_created" + ["points"]=> + array(1) { + [0]=> + array(2) { + [0]=> + int(%d) + [1]=> + float(2) + } + } + ["tags"]=> + array(1) { + [0]=> + string(24) "integration_name:datadog" + } + ["common"]=> + bool(false) + ["type"]=> + string(5) "count" + ["interval"]=> + int(10) + } + [1]=> + array(7) { + ["namespace"]=> + string(7) "tracers" + ["metric"]=> + string(13) "spans_created" + ["points"]=> + array(1) { + [0]=> + array(2) { + [0]=> + int(%d) + [1]=> + float(1) + } + } + ["tags"]=> + array(1) { + [0]=> + string(20) "integration_name:foo" + } + ["common"]=> + bool(false) + ["type"]=> + string(5) "count" + ["interval"]=> + int(10) + } + [2]=> + array(7) { + ["namespace"]=> + string(7) "tracers" + ["metric"]=> + string(13) "spans_created" + ["points"]=> + array(1) { + [0]=> + array(2) { + [0]=> + int(%d) + [1]=> + float(1) + } + } + ["tags"]=> + array(1) { + [0]=> + string(21) "integration_name:otel" + } + ["common"]=> + bool(false) + ["type"]=> + string(5) "count" + ["interval"]=> + int(10) + } + [3]=> + array(7) { + ["namespace"]=> + string(7) "tracers" + ["metric"]=> + string(13) "spans_created" + ["points"]=> + array(1) { + [0]=> + array(2) { + [0]=> + int(%d) + [1]=> + float(3) + } + } + ["tags"]=> + array(1) { + [0]=> + string(32) "integration_name:testintegration" + } + ["common"]=> + bool(false) + ["type"]=> + string(5) "count" + ["interval"]=> + int(10) + } +} +--CLEAN-- +./OpenTelemetry/Integration/Logs ./OpenTelemetry/Integration/SDK ./OpenTelemetry/Integration/InteroperabilityTest.php + ./OpenTelemetry/Integration/InternalTelemetryTest.php ./Integrations/Slim/V3_12