-
-
Notifications
You must be signed in to change notification settings - Fork 171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(tracing): Defer some transaction validation and allow creation of internal spans #633
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1124,10 +1124,32 @@ sentry_value_new_stacktrace(void **ips, size_t len) | |
return stacktrace; | ||
} | ||
|
||
sentry_value_t | ||
sentry__value_new_span(sentry_value_t parent, const char *operation) | ||
{ | ||
sentry_value_t span = sentry_value_new_object(); | ||
|
||
sentry_transaction_context_set_operation(span, operation); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this should now be renamed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think what we can do is define one common implementation for setting operations on spans/transactions/transaction contexts, but i'm thinking that the final API will most likely include a i think having a setter regardless can be useful in case one is only able to figure out what a transaction might be after a transaction context is created, or a transaction is started. it's not an immutable field until the transaction is finished. regarding the naming and reusing
this might make sense for other SDKs where class/struct inheritance does exist, and transactions do indeed get |
||
sentry_value_set_by_key(span, "status", sentry_value_new_string("ok")); | ||
|
||
if (!sentry_value_is_null(parent)) { | ||
sentry_value_set_by_key(span, "trace_id", | ||
sentry_value_get_by_key_owned(parent, "trace_id")); | ||
sentry_value_set_by_key(span, "parent_span_id", | ||
sentry_value_get_by_key_owned(parent, "span_id")); | ||
sentry_value_set_by_key( | ||
span, "sampled", sentry_value_get_by_key_owned(parent, "sampled")); | ||
} | ||
|
||
return span; | ||
} | ||
|
||
sentry_value_t | ||
sentry_value_new_transaction_context(const char *name, const char *operation) | ||
{ | ||
sentry_value_t transaction_context = sentry_value_new_object(); | ||
sentry_value_t transaction_context | ||
= sentry__value_new_span(sentry_value_new_null(), operation); | ||
relaxolotl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sentry_transaction_context_set_name(transaction_context, name); | ||
|
||
sentry_uuid_t trace_id = sentry_uuid_new_v4(); | ||
sentry_value_set_by_key(transaction_context, "trace_id", | ||
|
@@ -1138,7 +1160,6 @@ sentry_value_new_transaction_context(const char *name, const char *operation) | |
transaction_context, "span_id", sentry__value_new_span_uuid(&span_id)); | ||
|
||
sentry_transaction_context_set_name(transaction_context, name); | ||
sentry_transaction_context_set_operation(transaction_context, operation); | ||
|
||
return transaction_context; | ||
} | ||
|
@@ -1147,14 +1168,8 @@ void | |
sentry_transaction_context_set_name( | ||
sentry_value_t transaction_context, const char *name) | ||
{ | ||
sentry_value_t sv_name = sentry_value_new_string(name); | ||
// TODO: Consider doing this checking right before sending or flushing | ||
// the transaction. | ||
if (sentry_value_is_null(sv_name) || sentry__string_eq(name, "")) { | ||
sentry_value_decref(sv_name); | ||
sv_name = sentry_value_new_string("<unlabeled transaction>"); | ||
} | ||
sentry_value_set_by_key(transaction_context, "name", sv_name); | ||
sentry_value_set_by_key( | ||
transaction_context, "transaction", sentry_value_new_string(name)); | ||
} | ||
|
||
void | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -61,6 +61,13 @@ sentry_value_t sentry__value_new_list_with_size(size_t size); | |||||||
*/ | ||||||||
sentry_value_t sentry__value_new_object_with_size(size_t size); | ||||||||
|
||||||||
/** | ||||||||
* Constructs a new Span. | ||||||||
* | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as noted in #631 (comment), i think it would be fine to not mention places where parameters are borrowed, as that's the assumed default. |
||||||||
*/ | ||||||||
sentry_value_t sentry__value_new_span( | ||||||||
sentry_value_t parent, const char *operation); | ||||||||
|
||||||||
/** | ||||||||
* This will parse the Value into a UUID, or return a `nil` UUID on error. | ||||||||
* See also `sentry_uuid_from_string`. | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,12 @@ | |
#include "sentry_tracing.h" | ||
#include "sentry_uuid.h" | ||
|
||
#define IS_NULL(Src, Field) \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe these helpers make sense in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is too generic name. I'd rather have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seeing as there's already another helper macro in the tests right below, can i defer any relocations or changes to the macros to #637? |
||
sentry_value_is_null(sentry_value_get_by_key(Src, Field)) | ||
#define CHECK_STRING_PROPERTY(Src, Field, Expected) \ | ||
TEST_CHECK_STRING_EQUAL( \ | ||
sentry_value_as_string(sentry_value_get_by_key(Src, Field)), Expected) | ||
|
||
SENTRY_TEST(basic_tracing_context) | ||
{ | ||
sentry_value_t span = sentry_value_new_object(); | ||
|
@@ -38,46 +44,31 @@ SENTRY_TEST(basic_transaction) | |
{ | ||
sentry_value_t tx_cxt = sentry_value_new_transaction_context(NULL, NULL); | ||
TEST_CHECK(!sentry_value_is_null(tx_cxt)); | ||
const char *tx_name | ||
= sentry_value_as_string(sentry_value_get_by_key(tx_cxt, "name")); | ||
TEST_CHECK_STRING_EQUAL(tx_name, "<unlabeled transaction>"); | ||
const char *tx_op | ||
= sentry_value_as_string(sentry_value_get_by_key(tx_cxt, "op")); | ||
TEST_CHECK_STRING_EQUAL(tx_op, ""); | ||
TEST_CHECK( | ||
!sentry_value_is_null(sentry_value_get_by_key(tx_cxt, "trace_id"))); | ||
TEST_CHECK( | ||
!sentry_value_is_null(sentry_value_get_by_key(tx_cxt, "span_id"))); | ||
CHECK_STRING_PROPERTY(tx_cxt, "transaction", ""); | ||
CHECK_STRING_PROPERTY(tx_cxt, "op", ""); | ||
TEST_CHECK(!IS_NULL(tx_cxt, "trace_id")); | ||
TEST_CHECK(!IS_NULL(tx_cxt, "span_id")); | ||
|
||
sentry_value_decref(tx_cxt); | ||
tx_cxt = sentry_value_new_transaction_context("", ""); | ||
TEST_CHECK(!sentry_value_is_null(tx_cxt)); | ||
tx_name = sentry_value_as_string(sentry_value_get_by_key(tx_cxt, "name")); | ||
TEST_CHECK_STRING_EQUAL(tx_name, "<unlabeled transaction>"); | ||
TEST_CHECK_STRING_EQUAL(tx_op, ""); | ||
TEST_CHECK( | ||
!sentry_value_is_null(sentry_value_get_by_key(tx_cxt, "trace_id"))); | ||
TEST_CHECK( | ||
!sentry_value_is_null(sentry_value_get_by_key(tx_cxt, "span_id"))); | ||
CHECK_STRING_PROPERTY(tx_cxt, "transaction", ""); | ||
CHECK_STRING_PROPERTY(tx_cxt, "op", ""); | ||
TEST_CHECK(!IS_NULL(tx_cxt, "trace_id")); | ||
TEST_CHECK(!IS_NULL(tx_cxt, "span_id")); | ||
|
||
sentry_value_decref(tx_cxt); | ||
tx_cxt = sentry_value_new_transaction_context("honk.beep", "beepbeep"); | ||
tx_name = sentry_value_as_string(sentry_value_get_by_key(tx_cxt, "name")); | ||
TEST_CHECK_STRING_EQUAL(tx_name, "honk.beep"); | ||
tx_op = sentry_value_as_string(sentry_value_get_by_key(tx_cxt, "op")); | ||
TEST_CHECK_STRING_EQUAL(tx_op, "beepbeep"); | ||
TEST_CHECK( | ||
!sentry_value_is_null(sentry_value_get_by_key(tx_cxt, "trace_id"))); | ||
TEST_CHECK( | ||
!sentry_value_is_null(sentry_value_get_by_key(tx_cxt, "span_id"))); | ||
CHECK_STRING_PROPERTY(tx_cxt, "transaction", "honk.beep"); | ||
CHECK_STRING_PROPERTY(tx_cxt, "op", "beepbeep"); | ||
TEST_CHECK(!IS_NULL(tx_cxt, "trace_id")); | ||
TEST_CHECK(!IS_NULL(tx_cxt, "span_id")); | ||
|
||
sentry_transaction_context_set_name(tx_cxt, ""); | ||
tx_name = sentry_value_as_string(sentry_value_get_by_key(tx_cxt, "name")); | ||
TEST_CHECK_STRING_EQUAL(tx_name, "<unlabeled transaction>"); | ||
CHECK_STRING_PROPERTY(tx_cxt, "transaction", ""); | ||
|
||
sentry_transaction_context_set_operation(tx_cxt, ""); | ||
tx_op = sentry_value_as_string(sentry_value_get_by_key(tx_cxt, "op")); | ||
TEST_CHECK_STRING_EQUAL(tx_op, ""); | ||
CHECK_STRING_PROPERTY(tx_cxt, "op", ""); | ||
|
||
sentry_transaction_context_set_sampled(tx_cxt, 1); | ||
TEST_CHECK( | ||
|
@@ -86,24 +77,65 @@ SENTRY_TEST(basic_transaction) | |
sentry_value_decref(tx_cxt); | ||
} | ||
|
||
static void | ||
check_backfilled_name(sentry_envelope_t *envelope, void *data) | ||
{ | ||
uint64_t *called = data; | ||
*called += 1; | ||
|
||
sentry_value_t tx = sentry_envelope_get_transaction(envelope); | ||
TEST_CHECK(!sentry_value_is_null(tx)); | ||
CHECK_STRING_PROPERTY(tx, "transaction", "<unlabeled transaction>"); | ||
|
||
sentry_envelope_free(envelope); | ||
} | ||
|
||
SENTRY_TEST(transaction_name_backfill_on_finish) | ||
{ | ||
uint64_t called = 0; | ||
|
||
sentry_options_t *options = sentry_options_new(); | ||
sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); | ||
|
||
sentry_transport_t *transport = sentry_transport_new(check_backfilled_name); | ||
sentry_transport_set_state(transport, &called); | ||
sentry_options_set_transport(options, transport); | ||
|
||
sentry_options_set_traces_sample_rate(options, 1.0); | ||
sentry_init(options); | ||
|
||
sentry_value_t tx_cxt = sentry_value_new_transaction_context(NULL, NULL); | ||
sentry_value_t tx = sentry_transaction_start(tx_cxt); | ||
sentry_uuid_t event_id = sentry_transaction_finish(tx); | ||
TEST_CHECK(!sentry_uuid_is_nil(&event_id)); | ||
|
||
tx_cxt = sentry_value_new_transaction_context("", ""); | ||
tx = sentry_transaction_start(tx_cxt); | ||
event_id = sentry_transaction_finish(tx); | ||
TEST_CHECK(!sentry_uuid_is_nil(&event_id)); | ||
|
||
sentry_close(); | ||
TEST_CHECK_INT_EQUAL(called, 2); | ||
} | ||
|
||
static void | ||
send_transaction_envelope_test_basic(sentry_envelope_t *envelope, void *data) | ||
{ | ||
uint64_t *called = data; | ||
*called += 1; | ||
|
||
sentry_value_t transaction = sentry_envelope_get_transaction(envelope); | ||
TEST_CHECK(!sentry_value_is_null(transaction)); | ||
const char *event_id = sentry_value_as_string( | ||
sentry_value_get_by_key(transaction, "event_id")); | ||
sentry_value_t tx = sentry_envelope_get_transaction(envelope); | ||
TEST_CHECK(!sentry_value_is_null(tx)); | ||
const char *event_id | ||
= sentry_value_as_string(sentry_value_get_by_key(tx, "event_id")); | ||
TEST_CHECK_STRING_EQUAL(event_id, "4c035723-8638-4c3a-923f-2ab9d08b4018"); | ||
|
||
if (*called == 1) { | ||
const char *type = sentry_value_as_string( | ||
sentry_value_get_by_key(transaction, "type")); | ||
const char *type | ||
= sentry_value_as_string(sentry_value_get_by_key(tx, "type")); | ||
TEST_CHECK_STRING_EQUAL(type, "transaction"); | ||
const char *name = sentry_value_as_string( | ||
sentry_value_get_by_key(transaction, "transaction")); | ||
sentry_value_get_by_key(tx, "transaction")); | ||
TEST_CHECK_STRING_EQUAL(name, "honk"); | ||
} | ||
|
||
|
@@ -126,25 +158,25 @@ SENTRY_TEST(basic_function_transport_transaction) | |
sentry_options_set_require_user_consent(options, true); | ||
sentry_init(options); | ||
|
||
sentry_value_t transaction = sentry_value_new_transaction_context( | ||
sentry_value_t tx_cxt = sentry_value_new_transaction_context( | ||
"How could you", "Don't capture this."); | ||
transaction = sentry_transaction_start(transaction); | ||
sentry_uuid_t event_id = sentry_transaction_finish(transaction); | ||
sentry_value_t tx = sentry_transaction_start(tx_cxt); | ||
sentry_uuid_t event_id = sentry_transaction_finish(tx); | ||
// TODO: `sentry_capture_event` acts as if the event was sent if user | ||
// consent was not given | ||
TEST_CHECK(!sentry_uuid_is_nil(&event_id)); | ||
sentry_user_consent_give(); | ||
|
||
transaction = sentry_value_new_transaction_context("honk", "beep"); | ||
transaction = sentry_transaction_start(transaction); | ||
event_id = sentry_transaction_finish(transaction); | ||
tx_cxt = sentry_value_new_transaction_context("honk", "beep"); | ||
tx = sentry_transaction_start(tx_cxt); | ||
event_id = sentry_transaction_finish(tx); | ||
TEST_CHECK(!sentry_uuid_is_nil(&event_id)); | ||
|
||
sentry_user_consent_revoke(); | ||
transaction = sentry_value_new_transaction_context( | ||
tx_cxt = sentry_value_new_transaction_context( | ||
"How could you again", "Don't capture this either."); | ||
transaction = sentry_transaction_start(transaction); | ||
event_id = sentry_transaction_finish(transaction); | ||
tx = sentry_transaction_start(tx_cxt); | ||
event_id = sentry_transaction_finish(tx); | ||
// TODO: `sentry_capture_event` acts as if the event was sent if user | ||
// consent was not given | ||
TEST_CHECK(!sentry_uuid_is_nil(&event_id)); | ||
|
@@ -171,10 +203,10 @@ SENTRY_TEST(transport_sampling_transactions) | |
|
||
uint64_t sent_transactions = 0; | ||
for (int i = 0; i < 100; i++) { | ||
sentry_value_t transaction | ||
sentry_value_t tx_cxt | ||
= sentry_value_new_transaction_context("honk", "beep"); | ||
transaction = sentry_transaction_start(transaction); | ||
sentry_uuid_t event_id = sentry_transaction_finish(transaction); | ||
sentry_value_t tx = sentry_transaction_start(tx_cxt); | ||
sentry_uuid_t event_id = sentry_transaction_finish(tx); | ||
if (!sentry_uuid_is_nil(&event_id)) { | ||
sent_transactions += 1; | ||
} | ||
|
@@ -214,14 +246,17 @@ SENTRY_TEST(transactions_skip_before_send) | |
sentry_options_set_before_send(options, before_send, &called_beforesend); | ||
sentry_init(options); | ||
|
||
sentry_value_t transaction | ||
sentry_value_t tx_cxt | ||
= sentry_value_new_transaction_context("honk", "beep"); | ||
transaction = sentry_transaction_start(transaction); | ||
sentry_uuid_t event_id = sentry_transaction_finish(transaction); | ||
sentry_value_t tx = sentry_transaction_start(tx_cxt); | ||
sentry_uuid_t event_id = sentry_transaction_finish(tx); | ||
TEST_CHECK(!sentry_uuid_is_nil(&event_id)); | ||
|
||
sentry_close(); | ||
|
||
TEST_CHECK_INT_EQUAL(called_transport, 1); | ||
TEST_CHECK_INT_EQUAL(called_beforesend, 0); | ||
} | ||
|
||
#undef IS_NULL | ||
#undef CHECK_STRING_PROPERTY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you're thinking of aiming for here. If it is purely about the internal representation of the tx_ctx object in the tx object then I'm not sure what would improve. Also the current public API seems fine to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
being able to invoke some sort of
sentry_value_merge_objects(tx, tx_cxt)
wheretx_cxt
wins out would be much more preferable to us manually enumerating over everytx_cxt
field, checking for its existence, and stuffing the value intotx
if it's present intx_cxt
. that's what this means by "just merging tx_cxt into tx". the function just wants a union of those two objects.