Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Add custom attributes API for logs. When `logs_with_attributes` is set to `true`, treats the first `varg` passed into `sentry_logs_X(message,...)` as a `sentry_value_t` object of attributes. ([#1435](https://github.com/getsentry/sentry-native/pull/1435))
- Add runtime API to query user consent requirement. ([#1443](https://github.com/getsentry/sentry-native/pull/1443))
- Add logs flush on `sentry_flush()`. ([#1434](https://github.com/getsentry/sentry-native/pull/1434))
- Add global attributes API. ([#1450](https://github.com/getsentry/sentry-native/pull/1450))

## 0.12.1

Expand Down
23 changes: 23 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -1858,6 +1858,29 @@ SENTRY_API void sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key,
SENTRY_API void sentry_remove_extra(const char *key);
SENTRY_API void sentry_remove_extra_n(const char *key, size_t key_len);

/**
* Sets attributes created with `sentry_value_new_attribute` to be applied to
* all:
* - logs
*
* TODO
* - apply to logs (but only if log doesn't already have the attribute(s))
* - expand attributes to allow string[], int[], double[]
*/
SENTRY_API void sentry_set_attribute(const char *key, sentry_value_t attribute);
SENTRY_API void sentry_set_attribute_n(
const char *key, size_t key_len, sentry_value_t attribute);
SENTRY_API void sentry_remove_attribute(const char *key);
SENTRY_API void sentry_remove_attribute_n(const char *key, size_t key_len);
SENTRY_API void sentry_scope_set_attribute(
sentry_scope_t *scope, const char *key, sentry_value_t attribute);
SENTRY_API void sentry_scope_set_attribute_n(sentry_scope_t *scope,
const char *key, size_t key_len, sentry_value_t attribute);
SENTRY_API void sentry_scope_remove_attribute(
sentry_scope_t *scope, const char *key);
SENTRY_API void sentry_scope_remove_attribute_n(
sentry_scope_t *scope, const char *key, size_t key_len);

/**
* Sets a context object.
*/
Expand Down
33 changes: 33 additions & 0 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,39 @@ sentry_remove_extra_n(const char *key, size_t key_len)
}
}

void
sentry_set_attribute(const char *key, sentry_value_t attribute)
{
SENTRY_WITH_SCOPE_MUT (scope) {
sentry_scope_set_attribute(scope, key, attribute);
}
}

void
sentry_set_attribute_n(
const char *key, size_t key_len, sentry_value_t attribute)
{
SENTRY_WITH_SCOPE_MUT (scope) {
sentry_scope_set_attribute_n(scope, key, key_len, attribute);
}
}

void
sentry_remove_attribute(const char *key)
{
SENTRY_WITH_SCOPE_MUT (scope) {
sentry_scope_remove_attribute(scope, key);
}
}

void
sentry_remove_attribute_n(const char *key, size_t key_len)
{
SENTRY_WITH_SCOPE_MUT (scope) {
sentry_scope_remove_attribute_n(scope, key, key_len);
}
}

void
sentry_set_context(const char *key, sentry_value_t value)
{
Expand Down
5 changes: 4 additions & 1 deletion src/sentry_logs.c
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,10 @@ static sentry_value_t
construct_log(sentry_level_t level, const char *message, va_list args)
{
sentry_value_t log = sentry_value_new_object();
sentry_value_t attributes = sentry_value_new_object();
sentry_value_t attributes = sentry_value_new_null();
SENTRY_WITH_SCOPE (scope) {
attributes = sentry__value_clone(scope->attributes);
}

SENTRY_WITH_OPTIONS (options) {
// Extract custom attributes if the option is enabled
Expand Down
34 changes: 34 additions & 0 deletions src/sentry_scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ init_scope(sentry_scope_t *scope)
scope->user = sentry_value_new_null();
scope->tags = sentry_value_new_object();
scope->extra = sentry_value_new_object();
scope->attributes = sentry_value_new_object();
scope->contexts = sentry_value_new_object();
scope->propagation_context = sentry_value_new_object();
scope->breadcrumbs = sentry__ringbuffer_new(SENTRY_BREADCRUMBS_MAX);
Expand Down Expand Up @@ -110,6 +111,7 @@ cleanup_scope(sentry_scope_t *scope)
sentry_value_decref(scope->user);
sentry_value_decref(scope->tags);
sentry_value_decref(scope->extra);
sentry_value_decref(scope->attributes);
sentry_value_decref(scope->contexts);
sentry_value_decref(scope->propagation_context);
sentry__ringbuffer_free(scope->breadcrumbs);
Expand Down Expand Up @@ -588,6 +590,38 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len,
sentry_value_set_by_key_n(scope->extra, key, key_len, value);
}

void
sentry_scope_set_attribute(
sentry_scope_t *scope, const char *key, sentry_value_t attribute)
{
sentry_scope_set_attribute_n(scope, key, strlen(key), attribute);
}

void
sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key,
size_t key_len, sentry_value_t attribute)
{
if (sentry_value_is_null(sentry_value_get_by_key(attribute, "value"))
|| sentry_value_is_null(sentry_value_get_by_key(attribute, "type"))) {
SENTRY_DEBUG("Cannot set attribute with missing 'value' or 'type'");
return;
}
sentry_value_set_by_key_n(scope->attributes, key, key_len, attribute);
}

void
sentry_scope_remove_attribute(sentry_scope_t *scope, const char *key)
{
sentry_value_remove_by_key(scope->attributes, key);
}

void
sentry_scope_remove_attribute_n(
sentry_scope_t *scope, const char *key, size_t key_len)
{
sentry_value_remove_by_key_n(scope->attributes, key, key_len);
}

void
sentry_scope_set_context(
sentry_scope_t *scope, const char *key, sentry_value_t value)
Expand Down
1 change: 1 addition & 0 deletions src/sentry_scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct sentry_scope_s {
sentry_value_t user;
sentry_value_t tags;
sentry_value_t extra;
sentry_value_t attributes;
sentry_value_t contexts;
sentry_value_t propagation_context;
sentry_ringbuffer_t *breadcrumbs;
Expand Down
233 changes: 233 additions & 0 deletions tests/unit/test_scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -602,3 +602,236 @@ SENTRY_TEST(scope_breadcrumbs)

sentry_close();
}

SENTRY_TEST(scope_global_attributes)
{
SENTRY_TEST_OPTIONS_NEW(options);
sentry_init(options);

// Test setting a valid attribute on the global scope
sentry_value_t valid_attr = sentry_value_new_attribute(
sentry_value_new_string("test_value"), NULL);
sentry_set_attribute("valid_key", valid_attr);

SENTRY_WITH_SCOPE (scope) {
sentry_value_t attributes = scope->attributes;
sentry_value_t retrieved_attr
= sentry_value_get_by_key(attributes, "valid_key");

// Check that the attribute was set
TEST_CHECK(!sentry_value_is_null(retrieved_attr));
TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key(
retrieved_attr, "type")),
"string");
TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key(
retrieved_attr, "value")),
"test_value");
}

// Test that invalid attributes (missing 'value' or 'type') are not set
sentry_value_t invalid_attr_no_value = sentry_value_new_object();
sentry_value_set_by_key(
invalid_attr_no_value, "type", sentry_value_new_string("string"));
// Missing 'value' field
sentry_set_attribute("invalid_no_value", invalid_attr_no_value);

SENTRY_WITH_SCOPE (scope) {
sentry_value_t attributes = scope->attributes;
sentry_value_t retrieved_attr
= sentry_value_get_by_key(attributes, "invalid_no_value");

// Check that the attribute was NOT set
TEST_CHECK(sentry_value_is_null(retrieved_attr));
}
sentry_value_decref(invalid_attr_no_value);

// Test invalid attribute missing 'type'
sentry_value_t invalid_attr_no_type = sentry_value_new_object();
sentry_value_set_by_key(
invalid_attr_no_type, "value", sentry_value_new_string("some_value"));
// Missing 'type' field
sentry_set_attribute("invalid_no_type", invalid_attr_no_type);

SENTRY_WITH_SCOPE (scope) {
sentry_value_t attributes = scope->attributes;
sentry_value_t retrieved_attr
= sentry_value_get_by_key(attributes, "invalid_no_type");

// Check that the attribute was NOT set
TEST_CHECK(sentry_value_is_null(retrieved_attr));
}
sentry_value_decref(invalid_attr_no_type);

// Test removing an attribute
sentry_remove_attribute("valid_key");

SENTRY_WITH_SCOPE (scope) {
sentry_value_t attributes = scope->attributes;
sentry_value_t retrieved_attr
= sentry_value_get_by_key(attributes, "valid_key");

// Check that the attribute was removed
TEST_CHECK(sentry_value_is_null(retrieved_attr));
}

// Test setting attribute with _n variant
sentry_value_t attr_n
= sentry_value_new_attribute(sentry_value_new_int32(42), "percent");
sentry_set_attribute_n("key_n", 5, attr_n);

SENTRY_WITH_SCOPE (scope) {
sentry_value_t attributes = scope->attributes;
sentry_value_t retrieved_attr
= sentry_value_get_by_key(attributes, "key_n");

// Check that the attribute was set
TEST_CHECK(!sentry_value_is_null(retrieved_attr));
TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key(
retrieved_attr, "type")),
"integer");
TEST_CHECK(sentry_value_as_int32(
sentry_value_get_by_key(retrieved_attr, "value"))
== 42);
TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key(
retrieved_attr, "unit")),
"percent");
}

sentry_close();
}

SENTRY_TEST(scope_local_attributes)
{
SENTRY_TEST_OPTIONS_NEW(options);
sentry_init(options);

// global:
// {"all":"global","scope":"global","global":"global"}
sentry_set_attribute("all",
sentry_value_new_attribute(sentry_value_new_string("global"), NULL));
sentry_set_attribute("global",
sentry_value_new_attribute(sentry_value_new_string("global"), NULL));
sentry_set_attribute("scope",
sentry_value_new_attribute(sentry_value_new_string("global"), NULL));

SENTRY_WITH_SCOPE (global_scope) {
sentry_value_t attributes = global_scope->attributes;

// Verify global attributes are set
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(attributes, "all"), "value")),
"global");
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(attributes, "global"), "value")),
"global");
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(attributes, "scope"), "value")),
"global");
}

SENTRY_WITH_SCOPE (global_scope) {
// local:
// {"all":"local","scope":"local","local":"local"}
sentry_scope_t *local_scope = sentry_local_scope_new();
sentry_scope_set_attribute(local_scope, "all",
sentry_value_new_attribute(sentry_value_new_string("local"), NULL));
sentry_scope_set_attribute(local_scope, "local",
sentry_value_new_attribute(sentry_value_new_string("local"), NULL));
sentry_scope_set_attribute(local_scope, "scope",
sentry_value_new_attribute(sentry_value_new_string("local"), NULL));

sentry_value_t local_attributes = local_scope->attributes;

// Verify local attributes are set
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(local_attributes, "all"), "value")),
"local");
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(local_attributes, "local"), "value")),
"local");
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(local_attributes, "scope"), "value")),
"local");

// Verify global scope still has its own attributes
sentry_value_t global_attributes = global_scope->attributes;
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(global_attributes, "all"), "value")),
"global");
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(
sentry_value_get_by_key(global_attributes, "global"), "value")),
"global");

sentry__scope_free(local_scope);
}

// Test removing attributes from global scope
sentry_remove_attribute("all");

SENTRY_WITH_SCOPE (scope) {
sentry_value_t attributes = scope->attributes;
TEST_CHECK(
sentry_value_is_null(sentry_value_get_by_key(attributes, "all")));
// Other attributes should still exist
TEST_CHECK(!sentry_value_is_null(
sentry_value_get_by_key(attributes, "global")));
TEST_CHECK(!sentry_value_is_null(
sentry_value_get_by_key(attributes, "scope")));
}

// Test _n variants with local scope
SENTRY_WITH_SCOPE (global_scope) {
sentry_scope_t *local_scope = sentry_local_scope_new();
sentry_scope_set_attribute_n(local_scope, "test_key", 8,
sentry_value_new_attribute(sentry_value_new_int32(100), "percent"));

sentry_value_t local_attributes = local_scope->attributes;
sentry_value_t attr
= sentry_value_get_by_key(local_attributes, "test_key");

TEST_CHECK(!sentry_value_is_null(attr));
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(attr, "type")),
"integer");
TEST_CHECK(sentry_value_as_int32(sentry_value_get_by_key(attr, "value"))
== 100);
TEST_CHECK_STRING_EQUAL(
sentry_value_as_string(sentry_value_get_by_key(attr, "unit")),
"percent");

// Remove using _n variant
sentry_scope_remove_attribute_n(local_scope, "test_key", 8);
TEST_CHECK(sentry_value_is_null(
sentry_value_get_by_key(local_attributes, "test_key")));

sentry__scope_free(local_scope);
}

// Test that invalid attributes are not set on local scope
SENTRY_WITH_SCOPE (global_scope) {
sentry_scope_t *local_scope = sentry_local_scope_new();

// Try to set invalid attribute (missing 'value')
sentry_value_t invalid_attr = sentry_value_new_object();
sentry_value_set_by_key(
invalid_attr, "type", sentry_value_new_string("string"));
sentry_scope_set_attribute(local_scope, "invalid", invalid_attr);

sentry_value_t local_attributes = local_scope->attributes;
TEST_CHECK(sentry_value_is_null(
sentry_value_get_by_key(local_attributes, "invalid")));
sentry_value_decref(invalid_attr);

sentry__scope_free(local_scope);
}

sentry_close();
}
3 changes: 3 additions & 0 deletions tests/unit/test_uninit.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ SENTRY_TEST(uninitialized)
sentry_remove_tag("foo");
sentry_set_extra("foo", sentry_value_new_null());
sentry_remove_extra("foo");
sentry_set_attribute("foo",
sentry_value_new_attribute(sentry_value_new_string("bar"), NULL));
sentry_remove_attribute("foo");
sentry_set_context("foo", sentry_value_new_object());
sentry_remove_context("foo");
sentry_set_fingerprint("foo", "bar", NULL);
Expand Down
Loading
Loading