Skip to content

Commit cff2034

Browse files
bwoebiPROFeNoM
andauthored
Add DDTrace\try_drop_span() API (#3136)
* Add DDTrace\try_drop_span() API Signed-off-by: Bob Weinand <[email protected]> * tests: Add testSilencedSpansAreDropped for Symfony CLI Tests --------- Signed-off-by: Bob Weinand <[email protected]> Co-authored-by: Alexandre Choura <[email protected]>
1 parent 6a524da commit cff2034

20 files changed

Lines changed: 701 additions & 26 deletions

ext/ddtrace.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,8 @@ PHP_METHOD(DDTrace_SpanData, getStartTime) {
12361236
PHP_METHOD(DDTrace_SpanData, getLink) {
12371237
ddtrace_span_data *span = OBJ_SPANDATA(Z_OBJ_P(ZEND_THIS));
12381238

1239+
span->flags |= DDTRACE_SPAN_FLAG_NOT_DROPPABLE;
1240+
12391241
zval fci_zv;
12401242
object_init_ex(&fci_zv, ddtrace_ce_span_link);
12411243
ddtrace_span_link *link = (ddtrace_span_link *)Z_OBJ_P(&fci_zv);
@@ -2858,6 +2860,41 @@ PHP_FUNCTION(DDTrace_update_span_duration) {
28582860
RETURN_NULL();
28592861
}
28602862

2863+
/* {{{ proto string DDTrace\try_drop_span() */
2864+
PHP_FUNCTION(DDTrace_try_drop_span) {
2865+
zval *spanzv = NULL;
2866+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &spanzv, ddtrace_ce_span_data) != SUCCESS) {
2867+
RETURN_FALSE;
2868+
}
2869+
2870+
ddtrace_span_data *span = OBJ_SPANDATA(Z_OBJ_P(spanzv));
2871+
2872+
if (span->flags & DDTRACE_SPAN_FLAG_NOT_DROPPABLE) {
2873+
RETURN_FALSE;
2874+
}
2875+
2876+
if (span->duration == DDTRACE_DROPPED_SPAN || span->duration == DDTRACE_SILENTLY_DROPPED_SPAN) {
2877+
RETURN_TRUE;
2878+
}
2879+
2880+
ddtrace_span_stack *active_stack = DDTRACE_G(active_stack);
2881+
if (span->active_child_spans) {
2882+
RETURN_FALSE;
2883+
}
2884+
2885+
bool on_active_stack = active_stack == span->stack;
2886+
if (!on_active_stack) {
2887+
GC_ADDREF(&active_stack->std);
2888+
}
2889+
ddtrace_drop_span(span);
2890+
if (!on_active_stack) {
2891+
ddtrace_switch_span_stack(active_stack);
2892+
GC_DELREF(&active_stack->std);
2893+
}
2894+
2895+
RETURN_TRUE;
2896+
}
2897+
28612898
/* {{{ proto string DDTrace\active_stack() */
28622899
PHP_FUNCTION(DDTrace_active_stack) {
28632900
if (zend_parse_parameters_none() == FAILURE) {

ext/ddtrace.stub.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ public function __construct(\Throwable $exception, array $attributes = []) {}
7272
class SpanLink implements \JsonSerializable {
7373
/**
7474
* @var string $traceId A 32-character, lower-case hexadecimal encoded string of the linked trace ID. This field
75-
* shouldn't be directly assigned an id from SpanData. Use the SpanData::getLinks() method instead.
75+
* shouldn't be directly assigned an id from SpanData. Use the SpanData::getLink() method instead.
7676
*/
7777
public string $traceId;
7878

7979
/**
8080
* @var string $spanId A 16-character, lower-case hexadecimal encoded string of the linked span ID. This field
81-
* shouldn't be directly assigned an id from SpanData. Use the SpanData::getLinks() method instead.
81+
* shouldn't be directly assigned an id from SpanData. Use the SpanData::getLink() method instead.
8282
*/
8383
public string $spanId;
8484

@@ -575,6 +575,18 @@ function update_span_duration(SpanData $span, float $finishTime = 0): false|null
575575
*/
576576
function start_trace_span(float $startTime = 0): RootSpanData {}
577577

578+
579+
/**
580+
* Attempts to drop a span without breaking the trace.
581+
* No metrics will be collected for that span, if successfully dropped.
582+
* This means, the span is not dropped if any of the following were true before calling this function:
583+
* - the span has a non-dropped child span
584+
* - generate_distributed_tracing_headers() was called while this span was active
585+
* - a span link to this span was generated
586+
* @return bool Whether the span was successfully dropped.
587+
*/
588+
function try_drop_span(SpanData $span): bool {}
589+
578590
/**
579591
* Get the active stack
580592
*

ext/ddtrace_arginfo.h

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: f7e8f6a8052abd0404a3e01fa6b52d27024e6f1b */
2+
* Stub hash: 21dd48a5e3e602ba3be010a325d2cb8b703907c7 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_trace_method, 0, 3, _IS_BOOL, 0)
55
ZEND_ARG_TYPE_INFO(0, className, IS_STRING, 0)
@@ -65,6 +65,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_DDTrace_start_trace_span, 0, 0, D
6565
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, startTime, IS_DOUBLE, 0, "0")
6666
ZEND_END_ARG_INFO()
6767

68+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_try_drop_span, 0, 1, _IS_BOOL, 0)
69+
ZEND_ARG_OBJ_INFO(0, span, DDTrace\\SpanData, 0)
70+
ZEND_END_ARG_INFO()
71+
6872
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_DDTrace_active_stack, 0, 0, DDTrace\\SpanStack, 1)
6973
ZEND_END_ARG_INFO()
7074

@@ -318,6 +322,7 @@ ZEND_FUNCTION(DDTrace_start_span);
318322
ZEND_FUNCTION(DDTrace_close_span);
319323
ZEND_FUNCTION(DDTrace_update_span_duration);
320324
ZEND_FUNCTION(DDTrace_start_trace_span);
325+
ZEND_FUNCTION(DDTrace_try_drop_span);
321326
ZEND_FUNCTION(DDTrace_active_stack);
322327
ZEND_FUNCTION(DDTrace_create_stack);
323328
ZEND_FUNCTION(DDTrace_switch_stack);
@@ -402,6 +407,7 @@ static const zend_function_entry ext_functions[] = {
402407
ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "close_span"), zif_DDTrace_close_span, arginfo_DDTrace_close_span, 0, NULL, NULL)
403408
ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "update_span_duration"), zif_DDTrace_update_span_duration, arginfo_DDTrace_update_span_duration, 0, NULL, NULL)
404409
ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "start_trace_span"), zif_DDTrace_start_trace_span, arginfo_DDTrace_start_trace_span, 0, NULL, NULL)
410+
ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "try_drop_span"), zif_DDTrace_try_drop_span, arginfo_DDTrace_try_drop_span, 0, NULL, NULL)
405411
ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "active_stack"), zif_DDTrace_active_stack, arginfo_DDTrace_active_stack, 0, NULL, NULL)
406412
ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "create_stack"), zif_DDTrace_create_stack, arginfo_DDTrace_create_stack, 0, NULL, NULL)
407413
ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "switch_stack"), zif_DDTrace_switch_stack, arginfo_DDTrace_switch_stack, 0, NULL, NULL)
@@ -520,9 +526,7 @@ static zend_class_entry *register_class_DDTrace_SpanEvent(zend_class_entry *clas
520526

521527
zval property_name_default_value;
522528
ZVAL_UNDEF(&property_name_default_value);
523-
zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
524-
zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
525-
zend_string_release(property_name_name);
529+
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
526530

527531
zval property_attributes_default_value;
528532
ZVAL_UNDEF(&property_attributes_default_value);
@@ -628,15 +632,11 @@ static zend_class_entry *register_class_DDTrace_SpanData(void)
628632

629633
zval property_name_default_value;
630634
ZVAL_EMPTY_STRING(&property_name_default_value);
631-
zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
632-
zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
633-
zend_string_release(property_name_name);
635+
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
634636

635637
zval property_resource_default_value;
636638
ZVAL_EMPTY_STRING(&property_resource_default_value);
637-
zend_string *property_resource_name = zend_string_init("resource", sizeof("resource") - 1, 1);
638-
zend_declare_typed_property(class_entry, property_resource_name, &property_resource_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
639-
zend_string_release(property_resource_name);
639+
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_RESOURCE), &property_resource_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
640640

641641
zval property_service_default_value;
642642
ZVAL_EMPTY_STRING(&property_service_default_value);
@@ -664,9 +664,7 @@ static zend_class_entry *register_class_DDTrace_SpanData(void)
664664

665665
zval property_type_default_value;
666666
ZVAL_EMPTY_STRING(&property_type_default_value);
667-
zend_string *property_type_name = zend_string_init("type", sizeof("type") - 1, 1);
668-
zend_declare_typed_property(class_entry, property_type_name, &property_type_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
669-
zend_string_release(property_type_name);
667+
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_TYPE), &property_type_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL));
670668

671669
zval property_meta_default_value;
672670
ZVAL_EMPTY_ARRAY(&property_meta_default_value);
@@ -713,10 +711,8 @@ static zend_class_entry *register_class_DDTrace_SpanData(void)
713711

714712
zval property_parent_default_value;
715713
ZVAL_UNDEF(&property_parent_default_value);
716-
zend_string *property_parent_name = zend_string_init("parent", sizeof("parent") - 1, 1);
717714
zend_string *property_parent_class_DDTrace_SpanData = zend_string_init("DDTrace\\SpanData", sizeof("DDTrace\\SpanData")-1, 1);
718-
zend_declare_typed_property(class_entry, property_parent_name, &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanData, 0, MAY_BE_NULL));
719-
zend_string_release(property_parent_name);
715+
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PARENT), &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanData, 0, MAY_BE_NULL));
720716

721717
zval property_stack_default_value;
722718
ZVAL_UNDEF(&property_stack_default_value);
@@ -831,10 +827,8 @@ static zend_class_entry *register_class_DDTrace_SpanStack(void)
831827

832828
zval property_parent_default_value;
833829
ZVAL_UNDEF(&property_parent_default_value);
834-
zend_string *property_parent_name = zend_string_init("parent", sizeof("parent") - 1, 1);
835830
zend_string *property_parent_class_DDTrace_SpanStack = zend_string_init("DDTrace\\SpanStack", sizeof("DDTrace\\SpanStack")-1, 1);
836-
zend_declare_typed_property(class_entry, property_parent_name, &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanStack, 0, MAY_BE_NULL));
837-
zend_string_release(property_parent_name);
831+
zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PARENT), &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanStack, 0, MAY_BE_NULL));
838832

839833
zval property_active_default_value;
840834
ZVAL_NULL(&property_active_default_value);

ext/handlers_http.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ static inline void ddtrace_inject_distributed_headers_config(zend_array *array,
202202
zend_string *tracestate = DDTRACE_G(tracestate);
203203
zend_array *baggage = &DDTRACE_G(baggage);
204204
if (root) {
205+
SPANDATA(DDTRACE_G(active_stack)->active)->flags |= DDTRACE_SPAN_FLAG_NOT_DROPPABLE;
206+
205207
if (Z_TYPE(root->property_origin) == IS_STRING && Z_STRLEN(root->property_origin)) {
206208
origin = Z_STR(root->property_origin);
207209
} else {

ext/span.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ static void dd_drop_span_nodestroy(ddtrace_span_data *span, bool silent) {
4848
ddtrace_root_span_data *root = ROOTSPANDATA(&span->std);
4949
LOG(SPAN_TRACE, "Dropping root span: trace_id=%s, span_id=%" PRIu64, Z_STRVAL(root->property_trace_id), span->span_id);
5050
} else {
51+
if (span->parent) {
52+
--SPANDATA(span->parent)->active_child_spans;
53+
}
5154
LOG(SPAN_TRACE, "Dropping span: trace_id=%s, span_id=%" PRIu64, Z_STRVAL(span->root->property_trace_id), span->span_id);
5255
}
5356
}
@@ -289,6 +292,8 @@ ddtrace_span_data *ddtrace_open_span(enum ddtrace_span_dataype type) {
289292

290293
ddtrace_set_root_span_properties(root);
291294
} else {
295+
++parent_span->active_child_spans;
296+
292297
// do not copy the parent, it was active span before, just transfer that reference
293298
ZVAL_OBJ(&span->property_parent, &parent_span->std);
294299
ddtrace_inherit_span_properties(span, parent_span);
@@ -898,7 +903,10 @@ void ddtrace_close_top_span_without_stack_swap(ddtrace_span_data *span) {
898903
stack->active = span->parent;
899904
// The top span is always referenced by the span stack
900905
if (stack->active) {
901-
GC_ADDREF(&stack->active->std);
906+
ddtrace_span_data *parent = SPANDATA(stack->active);
907+
GC_ADDREF(&parent->std);
908+
parent->flags |= DDTRACE_SPAN_FLAG_NOT_DROPPABLE;
909+
--parent->active_child_spans;
902910
} else {
903911
ZVAL_NULL(&stack->property_active);
904912
}

ext/span.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#define DDTRACE_SPAN_FLAG_OPENTELEMETRY (1 << 0)
1919
#define DDTRACE_SPAN_FLAG_OPENTRACING (1 << 1)
20+
#define DDTRACE_SPAN_FLAG_NOT_DROPPABLE (1 << 2)
2021

2122
struct ddtrace_span_stack;
2223

@@ -88,6 +89,7 @@ struct ddtrace_span_data {
8889
uint8_t flags;
8990
enum ddtrace_span_dataype type : 8;
9091
bool notify_user_req_end;
92+
uint32_t active_child_spans;
9193
struct ddtrace_span_data *next;
9294
struct ddtrace_root_span_data *root;
9395

src/DDTrace/Integrations/Symfony/SymfonyIntegration.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,23 @@ function (HookData $hook) use ($controllerName, $integration) {
572572
\DDTrace\trace_method('Symfony\Component\Templating\DelegatingEngine', 'render', $traceRender);
573573
\DDTrace\trace_method('Symfony\Component\Templating\PhpEngine', 'render', $traceRender);
574574
\DDTrace\trace_method('Twig\Environment', 'render', $traceRender);
575+
576+
/* Silence ExecIntegration spans to stty. These are going to fail intentionally,
577+
* and always executed within symfony requests. This is pure noise which we hereby silence.
578+
*/
579+
foreach (['Symfony\Component\Console\Terminal::hasSttyAvailable', 'Symfony\Component\Console\Helper\QuestionHelper::isInteractiveInput'] as $method) {
580+
\DDTrace\install_hook($method, function (HookData $hook) {
581+
$hook->data = false;
582+
\DDTrace\active_stack()->spanCreationObservers[] = function (SpanData $span) use ($hook) {
583+
if ($hook->data) {
584+
return false;
585+
}
586+
\DDTrace\try_drop_span($span);
587+
};
588+
}, function (HookData $hook) {
589+
$hook->data = true;
590+
});
591+
}
575592
}
576593

577594
/**

tests/Common/TracerTestTrait.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,10 @@ public function inWebServer($fn, $rootPath, $envs = [], $inis = [], &$curlInfo =
227227
/**
228228
* This method executes a single script with the provided configuration.
229229
*/
230-
public function inCli($scriptPath, $customEnvs = [], $customInis = [], $arguments = '', $withOutput = false, $until = null, $throw = true)
230+
public function inCli($scriptPath, $customEnvs = [], $customInis = [], $arguments = '', $withOutput = false, $until = null, $throw = true, $checkTty = false)
231231
{
232232
$this->resetRequestDumper();
233-
$output = $this->executeCli($scriptPath, $customEnvs, $customInis, $arguments, $withOutput);
233+
$output = $this->executeCli($scriptPath, $customEnvs, $customInis, $arguments, $withOutput, false, false, $checkTty);
234234
usleep(100000); // Add a slight delay to give the request-replayer time to handle and store all requests.
235235
$out = [$this->parseTracesFromDumpedData($until, $throw)];
236236
if ($withOutput) {
@@ -239,7 +239,7 @@ public function inCli($scriptPath, $customEnvs = [], $customInis = [], $argument
239239
return $out;
240240
}
241241

242-
public function executeCli($scriptPath, $customEnvs = [], $customInis = [], $arguments = '', $withOutput = false, $skipSyncFlush = false, $withExitCode = false)
242+
public function executeCli($scriptPath, $customEnvs = [], $customInis = [], $arguments = '', $withOutput = false, $skipSyncFlush = false, $withExitCode = false, $checkTty = false)
243243
{
244244
$envs = (string) new EnvSerializer(array_merge(
245245
[
@@ -277,7 +277,11 @@ public function executeCli($scriptPath, $customEnvs = [], $customInis = [], $arg
277277
} elseif (\is_array($arguments)) {
278278
$arguments = implode(' ', array_map('escapeshellarg', $arguments));
279279
}
280-
$commandToExecute = "$envs " . PHP_BINARY . " $inis $script $arguments";
280+
if ($checkTty && !posix_isatty(STDOUT)) {
281+
$commandToExecute = "script -q -c \"$envs " . PHP_BINARY . " $inis $script $arguments\" /dev/null";
282+
} else {
283+
$commandToExecute = "$envs " . PHP_BINARY . " $inis $script $arguments";
284+
}
281285
$output = [];
282286
$exitCode = 0;
283287
$createHook = \DDTrace\install_hook('DDTrace\Integrations\Exec\ExecIntegration::createSpan', function (HookData $hook) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace App\Command;
4+
5+
use Symfony\Component\Console\Attribute\AsCommand;
6+
use Symfony\Component\Console\Command\Command;
7+
use Symfony\Component\Console\Input\InputInterface;
8+
use Symfony\Component\Console\Output\OutputInterface;
9+
use Symfony\Component\Console\Terminal;
10+
11+
#[AsCommand(
12+
name: 'app:stty'
13+
)]
14+
class SttyCommand extends Command
15+
{
16+
protected function execute(InputInterface $input, OutputInterface $output): int
17+
{
18+
$terminal = new Terminal();
19+
$terminal->hasSttyAvailable();
20+
21+
return Command::SUCCESS;
22+
}
23+
}

tests/Frameworks/Symfony/Version_4_4/src/Command/SttyCommand.php

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)