Skip to content

Commit 33e544a

Browse files
committed
merge breadcrumbs
1 parent f4bcdb7 commit 33e544a

File tree

2 files changed

+141
-60
lines changed

2 files changed

+141
-60
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: 51 additions & 57 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
{
@@ -645,119 +646,112 @@ SENTRY_TEST(scope_level)
645646
sentry_close();
646647
}
647648

649+
static sentry_value_t
650+
breadcrumb_ts(const char *message, uint64_t ts)
651+
{
652+
sentry_value_t breadcrumb = sentry_value_new_breadcrumb(NULL, message);
653+
sentry_value_set_by_key(breadcrumb, "timestamp",
654+
sentry__value_new_string_owned(sentry__usec_time_to_iso8601(ts)));
655+
return breadcrumb;
656+
}
657+
648658
SENTRY_TEST(scope_breadcrumbs)
649659
{
650660
SENTRY_TEST_OPTIONS_NEW(options);
661+
sentry_options_set_max_breadcrumbs(options, 5);
651662
sentry_init(options);
652663

653-
// global: ["global1", "global2"]
654-
sentry_add_breadcrumb(sentry_value_new_breadcrumb(NULL, "global1"));
655-
sentry_add_breadcrumb(sentry_value_new_breadcrumb(NULL, "global2"));
664+
// global: ["global1", "global4"]
665+
sentry_add_breadcrumb(breadcrumb_ts("global1", 1));
666+
sentry_add_breadcrumb(breadcrumb_ts("global4", 4));
667+
668+
#define TEST_CHECK_MESSAGE_EQUAL(breadcrumbs, index, message) \
669+
TEST_CHECK_STRING_EQUAL( \
670+
sentry_value_as_string(sentry_value_get_by_key( \
671+
sentry_value_get_by_index(breadcrumbs, index), "message")), \
672+
message)
656673

657674
SENTRY_WITH_SCOPE (global_scope) {
658675
// event: null
659676
sentry_value_t event = sentry_value_new_object();
660677

661-
// event <- global: ["global1", "global2"]
678+
// event <- global: ["global1", "global4"]
662679
sentry__scope_apply_to_event(
663680
global_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
664681

665682
sentry_value_t result = sentry_value_get_by_key(event, "breadcrumbs");
666683
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
667684
TEST_CHECK(sentry_value_get_length(result) == 2);
668-
TEST_CHECK_STRING_EQUAL(
669-
sentry_value_as_string(sentry_value_get_by_key(
670-
sentry_value_get_by_index(result, 0), "message")),
671-
"global1");
672-
TEST_CHECK_STRING_EQUAL(
673-
sentry_value_as_string(sentry_value_get_by_key(
674-
sentry_value_get_by_index(result, 1), "message")),
675-
"global2");
685+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "global1");
686+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "global4");
676687

677688
sentry_value_decref(event);
678689
}
679690

680691
SENTRY_WITH_SCOPE (global_scope) {
681-
// event: ["event1", "event2"]
692+
// event: ["event3", "event5"]
682693
sentry_value_t event = sentry_value_new_object();
683694
{
684695
sentry_value_t breadcrumbs = sentry_value_new_list();
685-
sentry_value_append(
686-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event1"));
687-
sentry_value_append(
688-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event2"));
696+
sentry_value_append(breadcrumbs, breadcrumb_ts("event3", 3));
697+
sentry_value_append(breadcrumbs, breadcrumb_ts("event5", 5));
689698
sentry_value_set_by_key(event, "breadcrumbs", breadcrumbs);
690699
}
691700

692-
// event <- global: ["event1", "event2"]
701+
// event <- global: ["global1", "event3", "global4", "event5"]
693702
sentry__scope_apply_to_event(
694703
global_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
695704

696705
sentry_value_t result = sentry_value_get_by_key(event, "breadcrumbs");
697706
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
698-
TEST_CHECK(sentry_value_get_length(result) == 2);
699-
TEST_CHECK_STRING_EQUAL(
700-
sentry_value_as_string(sentry_value_get_by_key(
701-
sentry_value_get_by_index(result, 0), "message")),
702-
"event1");
703-
TEST_CHECK_STRING_EQUAL(
704-
sentry_value_as_string(sentry_value_get_by_key(
705-
sentry_value_get_by_index(result, 1), "message")),
706-
"event2");
707+
TEST_CHECK(sentry_value_get_length(result) == 4);
708+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "global1");
709+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "event3");
710+
TEST_CHECK_MESSAGE_EQUAL(result, 2, "global4");
711+
TEST_CHECK_MESSAGE_EQUAL(result, 3, "event5");
707712

708713
sentry_value_decref(event);
709714
}
710715

711716
SENTRY_WITH_SCOPE (global_scope) {
712-
// local: ["local1", "local2"]
717+
// local: ["local2", "local6"]
713718
sentry_scope_t *local_scope = sentry_local_scope_new();
714-
sentry_scope_add_breadcrumb(
715-
local_scope, sentry_value_new_breadcrumb(NULL, "local1"));
716-
sentry_scope_add_breadcrumb(
717-
local_scope, sentry_value_new_breadcrumb(NULL, "local2"));
719+
sentry_scope_add_breadcrumb(local_scope, breadcrumb_ts("local2", 2));
720+
sentry_scope_add_breadcrumb(local_scope, breadcrumb_ts("local6", 6));
718721

719-
// event: ["event1", "event2"]
722+
// event: ["event3", "event5"]
720723
sentry_value_t event = sentry_value_new_object();
721724
{
722725
sentry_value_t breadcrumbs = sentry_value_new_list();
723-
sentry_value_append(
724-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event1"));
725-
sentry_value_append(
726-
breadcrumbs, sentry_value_new_breadcrumb(NULL, "event2"));
726+
sentry_value_append(breadcrumbs, breadcrumb_ts("event3", 3));
727+
sentry_value_append(breadcrumbs, breadcrumb_ts("event5", 5));
727728
sentry_value_set_by_key(event, "breadcrumbs", breadcrumbs);
728729
}
729730

730-
// event <- local: ["event1", "event2"]
731+
// event <- local: ["local2", "event3", "event5", "local6"]
731732
sentry__scope_apply_to_event(
732733
local_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
733734

734735
sentry_value_t result = sentry_value_get_by_key(event, "breadcrumbs");
735736
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
736-
TEST_CHECK(sentry_value_get_length(result) == 2);
737-
TEST_CHECK_STRING_EQUAL(
738-
sentry_value_as_string(sentry_value_get_by_key(
739-
sentry_value_get_by_index(result, 0), "message")),
740-
"event1");
741-
TEST_CHECK_STRING_EQUAL(
742-
sentry_value_as_string(sentry_value_get_by_key(
743-
sentry_value_get_by_index(result, 1), "message")),
744-
"event2");
737+
TEST_CHECK(sentry_value_get_length(result) == 4);
738+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "local2");
739+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "event3");
740+
TEST_CHECK_MESSAGE_EQUAL(result, 2, "event5");
741+
TEST_CHECK_MESSAGE_EQUAL(result, 3, "local6");
745742

746-
// event <- global: ["event1", "event2"]
743+
// event <- global: ["local2", "event3", "global4", "event5", "local6"]
747744
sentry__scope_apply_to_event(
748745
global_scope, options, event, SENTRY_SCOPE_BREADCRUMBS);
749746

750747
result = sentry_value_get_by_key(event, "breadcrumbs");
751748
TEST_CHECK(sentry_value_get_type(result) == SENTRY_VALUE_TYPE_LIST);
752-
TEST_CHECK(sentry_value_get_length(result) == 2);
753-
TEST_CHECK_STRING_EQUAL(
754-
sentry_value_as_string(sentry_value_get_by_key(
755-
sentry_value_get_by_index(result, 0), "message")),
756-
"event1");
757-
TEST_CHECK_STRING_EQUAL(
758-
sentry_value_as_string(sentry_value_get_by_key(
759-
sentry_value_get_by_index(result, 0), "message")),
760-
"event1");
749+
TEST_CHECK(sentry_value_get_length(result) == 5);
750+
TEST_CHECK_MESSAGE_EQUAL(result, 0, "local2");
751+
TEST_CHECK_MESSAGE_EQUAL(result, 1, "event3");
752+
TEST_CHECK_MESSAGE_EQUAL(result, 2, "global4");
753+
TEST_CHECK_MESSAGE_EQUAL(result, 3, "event5");
754+
TEST_CHECK_MESSAGE_EQUAL(result, 4, "local6");
761755

762756
sentry_scope_free(local_scope);
763757
sentry_value_decref(event);

0 commit comments

Comments
 (0)