Skip to content

Commit f8f6d29

Browse files
authored
Fix analytics.event override (#2409)
* fix: Explicitly check for true !(case sensitive) when overriding analytics event * Mimics Go's `strconv.ParseBool` & add tests * style: Refactor * Don't set the '_dd1.sr.eausr' on parsing failure * Don't set the '_dd1.sr.eausr' on parsing failure
1 parent 1af839d commit f8f6d29

File tree

2 files changed

+94
-3
lines changed

2 files changed

+94
-3
lines changed

ext/serializer.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,46 @@ void ddtrace_shutdown_span_sampling_limiter(void) {
11501150
zend_hash_destroy(&dd_span_sampling_limiters);
11511151
}
11521152

1153+
// ParseBool returns the boolean value represented by the string.
1154+
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
1155+
// Any other value returns -1.
1156+
static zend_always_inline double strconv_parse_bool(zend_string *str) {
1157+
// See Go's strconv.ParseBool
1158+
// https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/strconv/atob.go;drc=1f137052e4a20dbd302f947b1cf34cdf4b427d65;l=10
1159+
size_t len = ZSTR_LEN(str);
1160+
if (len == 0) {
1161+
return -1;
1162+
}
1163+
1164+
char *s = ZSTR_VAL(str);
1165+
switch (len) {
1166+
case 1:
1167+
switch (s[0]) {
1168+
case '1':
1169+
case 't':
1170+
case 'T':
1171+
return 1;
1172+
case '0':
1173+
case 'f':
1174+
case 'F':
1175+
return 0;
1176+
}
1177+
break;
1178+
case 4:
1179+
if (strcmp(s, "TRUE") == 0 || strcmp(s, "True") == 0 || strcmp(s, "true") == 0) {
1180+
return 1;
1181+
}
1182+
break;
1183+
case 5:
1184+
if (strcmp(s, "FALSE") == 0 || strcmp(s, "False") == 0 || strcmp(s, "false") == 0) {
1185+
return 0;
1186+
}
1187+
break;
1188+
}
1189+
1190+
return -1;
1191+
}
1192+
11531193
void ddtrace_serialize_span_to_array(ddtrace_span_data *span, zval *array) {
11541194
bool is_root_span = span->std.ce == ddtrace_ce_root_span_data;
11551195

@@ -1273,12 +1313,17 @@ void ddtrace_serialize_span_to_array(ddtrace_span_data *span, zval *array) {
12731313
if (analytics_event) {
12741314
zval analytics_event_as_double;
12751315
if (Z_TYPE_P(analytics_event) == IS_STRING) {
1276-
ZVAL_DOUBLE(&analytics_event_as_double, zend_is_true(analytics_event)); // 'true' => 1.0, false => 0.0
1316+
double parsed_analytics_event = strconv_parse_bool(Z_STR_P(analytics_event));
1317+
if (parsed_analytics_event >= 0) {
1318+
ZVAL_DOUBLE(&analytics_event_as_double, parsed_analytics_event);
1319+
zend_array *metrics = ddtrace_property_array(&span->property_metrics);
1320+
zend_hash_str_add_new(metrics, ZEND_STRL("_dd1.sr.eausr"), &analytics_event_as_double);
1321+
}
12771322
} else {
12781323
ZVAL_DOUBLE(&analytics_event_as_double, zval_get_double(analytics_event));
1324+
zend_array *metrics = ddtrace_property_array(&span->property_metrics);
1325+
zend_hash_str_add_new(metrics, ZEND_STRL("_dd1.sr.eausr"), &analytics_event_as_double);
12791326
}
1280-
zend_array *metrics = ddtrace_property_array(&span->property_metrics);
1281-
zend_hash_str_add_new(metrics, ZEND_STRL("_dd1.sr.eausr"), &analytics_event_as_double);
12821327
zend_hash_str_del(meta, ZEND_STRL("analytics.event"));
12831328
}
12841329

tests/OpenTelemetry/Integration/API/TracerTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,52 @@ public function providerSpanKind()
336336
];
337337
}
338338

339+
public function providerAnalyticsEvent()
340+
{
341+
return [
342+
["true", 1],
343+
["TRUE", 1],
344+
["True", 1],
345+
["false", 0],
346+
["False", 0],
347+
["FALSE", 0],
348+
["something-else", null],
349+
[True, 1],
350+
[False, 0],
351+
['t', 1],
352+
['T', 1],
353+
['f', 0],
354+
['F', 0],
355+
['1', 1],
356+
['0', 0],
357+
['fAlse', null],
358+
['trUe', null]
359+
];
360+
}
361+
362+
/**
363+
* @dataProvider providerAnalyticsEvent
364+
*/
365+
public function testReservedAttributesOverridesAnalyticsEvent($analyticsEventValue, $expectedMetricValue)
366+
{
367+
$traces = $this->isolateTracer(function () use ($analyticsEventValue) {
368+
$tracer = self::getTracer();
369+
$span = $tracer->spanBuilder('operation')
370+
->setSpanKind(SpanKind::KIND_SERVER)
371+
->startSpan();
372+
$span->setAttribute('analytics.event', $analyticsEventValue);
373+
$span->end();
374+
});
375+
376+
$span = $traces[0][0];
377+
if ($expectedMetricValue !== null) {
378+
$actualMetricValue = $span['metrics']['_dd1.sr.eausr'];
379+
$this->assertEquals($expectedMetricValue, $actualMetricValue);
380+
} else {
381+
$this->assertArrayNotHasKey('_dd1.sr.eausr', $span['metrics']);
382+
}
383+
}
384+
339385
public function testSpanErrorStatus()
340386
{
341387
$traces = $this->isolateTracer(function () {

0 commit comments

Comments
 (0)