Skip to content

Commit 75927ac

Browse files
committed
merge breadcrumbs
1 parent f627713 commit 75927ac

File tree

2 files changed

+142
-61
lines changed

2 files changed

+142
-61
lines changed

src/sentry_scope.c

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,89 @@ sentry__scope_get_span_or_transaction(void)
301301
}
302302
#endif
303303

304+
static int
305+
cmp_breadcrumb(sentry_value_t a, sentry_value_t b)
306+
{
307+
sentry_value_t timestamp_a = sentry_value_get_by_key(a, "timestamp");
308+
sentry_value_t timestamp_b = sentry_value_get_by_key(b, "timestamp");
309+
if (sentry_value_is_null(timestamp_a)
310+
|| sentry_value_is_null(timestamp_b)) {
311+
return 0;
312+
}
313+
314+
return strcmp(sentry_value_as_string(timestamp_a),
315+
sentry_value_as_string(timestamp_b));
316+
}
317+
318+
static sentry_value_t
319+
merge_breadcrumbs(sentry_value_t list_a, sentry_value_t list_b, size_t max)
320+
{
321+
size_t len_a = sentry_value_get_type(list_a) == SENTRY_VALUE_TYPE_LIST
322+
? sentry_value_get_length(list_a)
323+
: 0;
324+
size_t len_b = sentry_value_get_type(list_b) == SENTRY_VALUE_TYPE_LIST
325+
? sentry_value_get_length(list_b)
326+
: 0;
327+
328+
if (len_a == 0 && len_b == 0) {
329+
return sentry_value_new_null();
330+
} else if (len_a == 0) {
331+
sentry_value_incref(list_b);
332+
return list_b;
333+
} else if (len_b == 0) {
334+
sentry_value_incref(list_a);
335+
return list_a;
336+
}
337+
338+
size_t idx_a = 0;
339+
size_t idx_b = 0;
340+
size_t total = len_a + len_b;
341+
size_t skip = total > max ? total - max : 0;
342+
sentry_value_t result = sentry__value_new_list_with_size(total - skip);
343+
344+
// skip oldest breadcrumbs to fit max
345+
while (idx_a < len_a && idx_b < len_b && idx_a + idx_b < skip) {
346+
sentry_value_t item_a = sentry_value_get_by_index(list_a, idx_a);
347+
sentry_value_t item_b = sentry_value_get_by_index(list_b, idx_b);
348+
349+
if (cmp_breadcrumb(item_a, item_b) <= 0) {
350+
idx_a++;
351+
} else {
352+
idx_b++;
353+
}
354+
}
355+
while (idx_a < len_a && idx_a + idx_b < skip) {
356+
idx_a++;
357+
}
358+
while (idx_b < len_b && idx_a + idx_b < skip) {
359+
idx_b++;
360+
}
361+
362+
// merge the remaining breadcrumbs in timestamp order
363+
while (idx_a < len_a && idx_b < len_b) {
364+
sentry_value_t item_a = sentry_value_get_by_index(list_a, idx_a);
365+
sentry_value_t item_b = sentry_value_get_by_index(list_b, idx_b);
366+
367+
if (cmp_breadcrumb(item_a, item_b) <= 0) {
368+
sentry_value_append(
369+
result, sentry_value_get_by_index_owned(list_a, idx_a++));
370+
} else {
371+
sentry_value_append(
372+
result, sentry_value_get_by_index_owned(list_b, idx_b++));
373+
}
374+
}
375+
while (idx_a < len_a) {
376+
sentry_value_append(
377+
result, sentry_value_get_by_index_owned(list_a, idx_a++));
378+
}
379+
while (idx_b < len_b) {
380+
sentry_value_append(
381+
result, sentry_value_get_by_index_owned(list_b, idx_b++));
382+
}
383+
384+
return result;
385+
}
386+
304387
void
305388
sentry__scope_apply_to_event(const sentry_scope_t *scope,
306389
const sentry_options_t *options, sentry_value_t event,
@@ -396,10 +479,14 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope,
396479
sentry_value_decref(contexts);
397480

398481
if (mode & SENTRY_SCOPE_BREADCRUMBS) {
399-
sentry_value_t l
482+
sentry_value_t event_breadcrumbs
483+
= sentry_value_get_by_key(event, "breadcrumbs");
484+
sentry_value_t scope_breadcrumbs
400485
= sentry__value_ring_buffer_to_list(scope->breadcrumbs);
401-
PLACE_VALUE("breadcrumbs", l);
402-
sentry_value_decref(l);
486+
sentry_value_set_by_key(event, "breadcrumbs",
487+
merge_breadcrumbs(event_breadcrumbs, scope_breadcrumbs,
488+
options->max_breadcrumbs));
489+
sentry_value_decref(scope_breadcrumbs);
403490
}
404491

405492
#if !defined(SENTRY_PLATFORM_NX) && !defined(SENTRY_PLATFORM_PS)

tests/unit/test_scope.c

Lines changed: 52 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "sentry.h"
22
#include "sentry_scope.h"
33
#include "sentry_testsupport.h"
4+
#include "sentry_utils.h"
45

56
SENTRY_TEST(scope_contexts)
67
{
@@ -558,119 +559,112 @@ SENTRY_TEST(scope_level)
558559
sentry_close();
559560
}
560561

562+
static sentry_value_t
563+
breadcrumb_ts(const char *message, uint64_t ts)
564+
{
565+
sentry_value_t breadcrumb = sentry_value_new_breadcrumb(NULL, message);
566+
sentry_value_set_by_key(breadcrumb, "timestamp",
567+
sentry__value_new_string_owned(sentry__usec_time_to_iso8601(ts)));
568+
return breadcrumb;
569+
}
570+
561571
SENTRY_TEST(scope_breadcrumbs)
562572
{
563573
SENTRY_TEST_OPTIONS_NEW(options);
574+
sentry_options_set_max_breadcrumbs(options, 5);
564575
sentry_init(options);
565576

566-
// global: ["global1", "global2"]
567-
sentry_add_breadcrumb(sentry_value_new_breadcrumb(NULL, "global1"));
568-
sentry_add_breadcrumb(sentry_value_new_breadcrumb(NULL, "global2"));
577+
// global: ["global1", "global4"]
578+
sentry_add_breadcrumb(breadcrumb_ts("global1", 1));
579+
sentry_add_breadcrumb(breadcrumb_ts("global4", 4));
580+
581+
#define TEST_CHECK_MESSAGE_EQUAL(breadcrumbs, index, message) \
582+
TEST_CHECK_STRING_EQUAL( \
583+
sentry_value_as_string(sentry_value_get_by_key( \
584+
sentry_value_get_by_index(breadcrumbs, index), "message")), \
585+
message)
569586

570587
SENTRY_WITH_SCOPE (global_scope) {
571588
// event: null
572589
sentry_value_t event = sentry_value_new_object();
573590

574-
// event <- global: ["global1", "global2"]
591+
// event <- global: ["global1", "global4"]
575592
sentry__scope_apply_to_event(
576593
global_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
577594

578595
sentry_value_t result = sentry_value_get_by_key(event, "breadcrumbs");
579596
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
580597
TEST_CHECK(sentry_value_get_length(result) == 2);
581-
TEST_CHECK_STRING_EQUAL(
582-
sentry_value_as_string(sentry_value_get_by_key(
583-
sentry_value_get_by_index(result, 0), "message")),
584-
"global1");
585-
TEST_CHECK_STRING_EQUAL(
586-
sentry_value_as_string(sentry_value_get_by_key(
587-
sentry_value_get_by_index(result, 1), "message")),
588-
"global2");
598+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "global1");
599+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "global4");
589600

590601
sentry_value_decref(event);
591602
}
592603

593604
SENTRY_WITH_SCOPE (global_scope) {
594-
// event: ["event1", "event2"]
605+
// event: ["event3", "event5"]
595606
sentry_value_t event = sentry_value_new_object();
596607
{
597608
sentry_value_t breadcrumbs = sentry_value_new_list();
598-
sentry_value_append(
599-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event1"));
600-
sentry_value_append(
601-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event2"));
609+
sentry_value_append(breadcrumbs, breadcrumb_ts("event3", 3));
610+
sentry_value_append(breadcrumbs, breadcrumb_ts("event5", 5));
602611
sentry_value_set_by_key(event, "breadcrumbs", breadcrumbs);
603612
}
604613

605-
// event <- global: ["event1", "event2"]
614+
// event <- global: ["global1", "event3", "global4", "event5"]
606615
sentry__scope_apply_to_event(
607616
global_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
608617

609618
sentry_value_t result = sentry_value_get_by_key(event, "breadcrumbs");
610619
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
611-
TEST_CHECK(sentry_value_get_length(result) == 2);
612-
TEST_CHECK_STRING_EQUAL(
613-
sentry_value_as_string(sentry_value_get_by_key(
614-
sentry_value_get_by_index(result, 0), "message")),
615-
"event1");
616-
TEST_CHECK_STRING_EQUAL(
617-
sentry_value_as_string(sentry_value_get_by_key(
618-
sentry_value_get_by_index(result, 1), "message")),
619-
"event2");
620+
TEST_CHECK(sentry_value_get_length(result) == 4);
621+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "global1");
622+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "event3");
623+
TEST_CHECK_MESSAGE_EQUAL(result, 2, "global4");
624+
TEST_CHECK_MESSAGE_EQUAL(result, 3, "event5");
620625

621626
sentry_value_decref(event);
622627
}
623628

624629
SENTRY_WITH_SCOPE (global_scope) {
625-
// local: ["local1", "local2"]
630+
// local: ["local2", "local6"]
626631
sentry_scope_t *local_scope = sentry_local_scope_new();
627-
sentry_scope_add_breadcrumb(
628-
local_scope, sentry_value_new_breadcrumb(NULL, "local1"));
629-
sentry_scope_add_breadcrumb(
630-
local_scope, sentry_value_new_breadcrumb(NULL, "local2"));
632+
sentry_scope_add_breadcrumb(local_scope, breadcrumb_ts("local2", 2));
633+
sentry_scope_add_breadcrumb(local_scope, breadcrumb_ts("local6", 6));
631634

632-
// event: ["event1", "event2"]
635+
// event: ["event3", "event5"]
633636
sentry_value_t event = sentry_value_new_object();
634637
{
635638
sentry_value_t breadcrumbs = sentry_value_new_list();
636-
sentry_value_append(
637-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event1"));
638-
sentry_value_append(
639-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event2"));
639+
sentry_value_append(breadcrumbs, breadcrumb_ts("event3", 3));
640+
sentry_value_append(breadcrumbs, breadcrumb_ts("event5", 5));
640641
sentry_value_set_by_key(event, "breadcrumbs", breadcrumbs);
641642
}
642643

643-
// event <- local: ["event1", "event2"]
644+
// event <- local: ["local2", "event3", "event5", "local6"]
644645
sentry__scope_apply_to_event(
645646
local_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
646647

647648
sentry_value_t result = sentry_value_get_by_key(event, "breadcrumbs");
648649
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
649-
TEST_CHECK(sentry_value_get_length(result) == 2);
650-
TEST_CHECK_STRING_EQUAL(
651-
sentry_value_as_string(sentry_value_get_by_key(
652-
sentry_value_get_by_index(result, 0), "message")),
653-
"event1");
654-
TEST_CHECK_STRING_EQUAL(
655-
sentry_value_as_string(sentry_value_get_by_key(
656-
sentry_value_get_by_index(result, 1), "message")),
657-
"event2");
658-
659-
// event <- global: ["event1", "event2"]
650+
TEST_CHECK(sentry_value_get_length(result) == 4);
651+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "local2");
652+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "event3");
653+
TEST_CHECK_MESSAGE_EQUAL(result, 2, "event5");
654+
TEST_CHECK_MESSAGE_EQUAL(result, 3, "local6");
655+
656+
// event <- global: ["local2", "event3", "global4", "event5", "local6"]
660657
sentry__scope_apply_to_event(
661658
global_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
662659

663660
result = sentry_value_get_by_key(event, "breadcrumbs");
664661
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
665-
TEST_CHECK(sentry_value_get_length(result) == 2);
666-
TEST_CHECK_STRING_EQUAL(
667-
sentry_value_as_string(sentry_value_get_by_key(
668-
sentry_value_get_by_index(result, 0), "message")),
669-
"event1");
670-
TEST_CHECK_STRING_EQUAL(
671-
sentry_value_as_string(sentry_value_get_by_key(
672-
sentry_value_get_by_index(result, 0), "message")),
673-
"event1");
662+
TEST_CHECK(sentry_value_get_length(result) == 5);
663+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "local2");
664+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "event3");
665+
TEST_CHECK_MESSAGE_EQUAL(result, 2, "global4");
666+
TEST_CHECK_MESSAGE_EQUAL(result, 3, "event5");
667+
TEST_CHECK_MESSAGE_EQUAL(result, 4, "local6");
674668

675669
sentry_scope_free(local_scope);
676670
sentry_value_decref(event);

0 commit comments

Comments
 (0)