Skip to content

Commit 46cd9f1

Browse files
committed
add custom-attributes X format string POC
1 parent 15bb9bb commit 46cd9f1

File tree

5 files changed

+113
-38
lines changed

5 files changed

+113
-38
lines changed

examples/example.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,13 @@ main(int argc, char **argv)
519519
sentry_value_set_by_key(attributes, "my.custom.attribute", attr);
520520
sentry_value_set_by_key(attributes, "number.first", attr_2);
521521
sentry_value_set_by_key(attributes, "number.second", attr_3);
522-
sentry_log_debug("logging with custom attributes", attributes);
522+
sentry_log_debug("logging with %d custom attributes", attributes, 3);
523+
sentry_log_debug("logging with %s custom attributes",
524+
sentry_value_new_object(), "no");
525+
// TODO add test that shows we still keep default attributes if
526+
// passed-in value is accidentally not an object
527+
sentry_log_warn("logging with %s custom attributes",
528+
sentry_value_new_null(), "new_null as");
523529
}
524530

525531
if (has_arg(argc, argv, "attachment")) {
@@ -585,11 +591,8 @@ main(int argc, char **argv)
585591
context, "name", sentry_value_new_string("testing-runtime"));
586592
sentry_set_context("runtime", context);
587593

588-
sentry_value_t user = sentry_value_new_object();
589-
sentry_value_new_user(NULL, NULL, NULL, NULL);
590-
sentry_value_set_by_key(user, "id", sentry_value_new_string("42"));
591-
sentry_value_set_by_key(
592-
user, "username", sentry_value_new_string("some_name"));
594+
sentry_value_t user
595+
= sentry_value_new_user("42", "some_name", NULL, NULL);
593596
sentry_set_user(user);
594597

595598
sentry_value_t default_crumb

src/sentry_logs.c

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -678,47 +678,62 @@ construct_log(sentry_level_t level, const char *message, va_list args)
678678
sentry_value_t attributes = sentry_value_new_object();
679679

680680
SENTRY_WITH_OPTIONS (options) {
681+
// Extract custom attributes if the option is enabled
681682
if (sentry_options_get_logs_with_attributes(options)) {
682-
sentry_value_set_by_key(
683-
log, "body", sentry_value_new_string(message));
684683
va_list args_copy;
685684
va_copy(args_copy, args);
686685
sentry_value_t custom_attributes
687686
= va_arg(args_copy, sentry_value_t);
688687
va_end(args_copy);
689-
// TODO find out if this is legal
690-
sentry_value_decref(attributes);
691-
attributes = custom_attributes;
692-
} else {
693-
va_list args_copy_1, args_copy_2, args_copy_3;
694-
va_copy(args_copy_1, args);
695-
va_copy(args_copy_2, args);
696-
va_copy(args_copy_3, args);
697-
int len = vsnprintf(NULL, 0, message, args_copy_1) + 1;
698-
va_end(args_copy_1);
699-
size_t size = (size_t)len;
700-
char *fmt_message = sentry_malloc(size);
701-
if (!fmt_message) {
702-
va_end(args_copy_2);
703-
va_end(args_copy_3);
704-
return sentry_value_new_null();
688+
// TODO is this enough to check whether this is a valid
689+
// sentry_value_t object?
690+
if (sentry_value_get_type(custom_attributes)
691+
== SENTRY_VALUE_TYPE_OBJECT) {
692+
SENTRY_DEBUG("Discarded custom attributes on log: non-object "
693+
"sentry_value_t passed in");
694+
sentry_value_decref(attributes);
695+
attributes = custom_attributes;
705696
}
697+
}
706698

707-
vsnprintf(fmt_message, size, message, args_copy_2);
708-
va_end(args_copy_2);
699+
// Format the message with remaining args (or all args if not using
700+
// custom attributes)
701+
va_list args_copy_1, args_copy_2, args_copy_3;
702+
va_copy(args_copy_1, args);
703+
va_copy(args_copy_2, args);
704+
va_copy(args_copy_3, args);
709705

710-
sentry_value_set_by_key(
711-
log, "body", sentry_value_new_string(fmt_message));
712-
sentry_free(fmt_message);
713-
714-
// Parse variadic arguments and add them to attributes
715-
if (populate_message_parameters(attributes, message, args_copy_3)) {
716-
// only add message template if we have parameters
717-
add_attribute(attributes, sentry_value_new_string(message),
718-
"string", "sentry.message.template");
719-
}
706+
// Skip the first argument (attributes) if using custom attributes
707+
if (sentry_options_get_logs_with_attributes(options)) {
708+
va_arg(args_copy_1, sentry_value_t);
709+
va_arg(args_copy_2, sentry_value_t);
710+
va_arg(args_copy_3, sentry_value_t);
711+
}
712+
713+
int len = vsnprintf(NULL, 0, message, args_copy_1) + 1;
714+
va_end(args_copy_1);
715+
size_t size = (size_t)len;
716+
char *fmt_message = sentry_malloc(size);
717+
if (!fmt_message) {
718+
va_end(args_copy_2);
720719
va_end(args_copy_3);
720+
return sentry_value_new_null();
721+
}
722+
723+
vsnprintf(fmt_message, size, message, args_copy_2);
724+
va_end(args_copy_2);
725+
726+
sentry_value_set_by_key(
727+
log, "body", sentry_value_new_string(fmt_message));
728+
sentry_free(fmt_message);
729+
730+
// Parse variadic arguments and add them to attributes
731+
if (populate_message_parameters(attributes, message, args_copy_3)) {
732+
// only add message template if we have parameters
733+
add_attribute(attributes, sentry_value_new_string(message),
734+
"string", "sentry.message.template");
721735
}
736+
va_end(args_copy_3);
722737
}
723738

724739
sentry_value_set_by_key(

src/sentry_options.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ struct sentry_options_s {
6565
void *traces_sampler_data;
6666
size_t max_spans;
6767
bool enable_logs;
68-
// no longer parses log messages as format strings, and takes the first varg
69-
// as the attributes sentry_value_t object
68+
// takes the first varg as a `sentry_value_t` object containing attributes
69+
// if no custom attributes are to be passed, use `sentry_value_new_object()`
7070
bool logs_with_attributes;
7171

7272
/* everything from here on down are options which are stored here but

tests/unit/test_logs.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,59 @@ SENTRY_TEST(logs_param_types)
287287
uint64_t g = 0xDEADBEEFDEADBEEF;
288288
test_param_conversion_types("%u %d %f %c %s %p %x", a, b, c, d, e, f, g);
289289
}
290+
291+
SENTRY_TEST(logs_custom_attributes_with_format_strings)
292+
{
293+
transport_validation_data_t validation_data = { 0, false };
294+
295+
SENTRY_TEST_OPTIONS_NEW(options);
296+
sentry_options_set_dsn(options, "https://[email protected]/42");
297+
sentry_options_set_enable_logs(options, true);
298+
sentry_options_set_logs_with_attributes(options, true);
299+
300+
sentry_transport_t *transport
301+
= sentry_transport_new(validate_logs_envelope);
302+
sentry_transport_set_state(transport, &validation_data);
303+
sentry_options_set_transport(options, transport);
304+
305+
sentry_init(options);
306+
sentry__logs_wait_for_thread_startup();
307+
308+
// Test 1: Custom attributes with format string
309+
sentry_value_t attributes1 = sentry_value_new_object();
310+
sentry_value_t attr1 = sentry_value_new_attribute(
311+
"string", sentry_value_new_string("custom_value"), NULL);
312+
sentry_value_set_by_key(attributes1, "my.custom.attribute", attr1);
313+
TEST_CHECK_INT_EQUAL(sentry_log_info("User %s logged in with code %d",
314+
attributes1, "Alice", 200),
315+
0);
316+
317+
// Test 2: Null attributes with format string (should still work)
318+
TEST_CHECK_INT_EQUAL(sentry_log_warn("No custom attrs: %s has %d items",
319+
sentry_value_new_null(), "cart", 5),
320+
0);
321+
322+
// Test 3: Custom attributes with no format parameters
323+
sentry_value_t attributes2 = sentry_value_new_object();
324+
sentry_value_t attr2 = sentry_value_new_attribute(
325+
"integer", sentry_value_new_int32(42), NULL);
326+
sentry_value_set_by_key(attributes2, "special.number", attr2);
327+
TEST_CHECK_INT_EQUAL(
328+
sentry_log_error("Simple message with custom attrs", attributes2), 0);
329+
330+
// Test 4: Custom attributes with multiple format types
331+
sentry_value_t attributes3 = sentry_value_new_object();
332+
sentry_value_t attr3 = sentry_value_new_attribute(
333+
"string", sentry_value_new_string("tracking"), NULL);
334+
sentry_value_set_by_key(attributes3, "event.type", attr3);
335+
TEST_CHECK_INT_EQUAL(
336+
sentry_log_debug("Processing item %d of %d (%.1f%% complete)",
337+
attributes3, 3, 10, 30.0),
338+
0);
339+
340+
sentry_close();
341+
342+
// Validate that logs were sent
343+
TEST_CHECK(!validation_data.has_validation_error);
344+
TEST_CHECK_INT_EQUAL(validation_data.called_count, 1);
345+
}

tests/unit/tests.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ XX(iso_time)
8787
XX(lazy_attachments)
8888
XX(logger_enable_disable_functionality)
8989
XX(logger_level)
90+
XX(logs_custom_attributes_with_format_strings)
9091
XX(logs_disabled_by_default)
9192
XX(logs_param_conversion)
9293
XX(logs_param_types)

0 commit comments

Comments
 (0)