Skip to content

Commit 079aacc

Browse files
feat: implement global attributes API (#1450)
* initial API * implement + test * add global attributes to logs * fix memleak in test * update CHANGELOG.md * fix memleak * initialize attributes * add list-type attribute support * cleanup * add explicit enum cases * move scope-based attributes into non-public API * cleanup * add attribute list test cases * update CHANGELOG.md * rookie mistake (lint)
1 parent 634e8a3 commit 079aacc

File tree

11 files changed

+467
-18
lines changed

11 files changed

+467
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- 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))
88
- Add runtime API to query user consent requirement. ([#1443](https://github.com/getsentry/sentry-native/pull/1443))
99
- Add logs flush on `sentry_flush()`. ([#1434](https://github.com/getsentry/sentry-native/pull/1434))
10+
- Add global attributes API. These are added to all `sentry_log_X` calls. ([#1450](https://github.com/getsentry/sentry-native/pull/1450))
1011

1112
## 0.12.1
1213

include/sentry.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,12 @@ SENTRY_API sentry_value_t sentry_value_new_user_n(const char *id, size_t id_len,
326326

327327
/**
328328
* Creates a new attribute object.
329-
* value` is required, `unit` is optional.
329+
* value is required, unit is optional.
330330
*
331-
*'value' must be a bool, int, double or string (not null, list, object)
331+
* value must be a bool, int, double or string `sentry_value_t`
332+
* OR a list of bool, int, double or string (with all items being the same type)
332333
*
333-
* Moves ownership of `value` into the object. The caller does not
334+
* Moves ownership of value into the object. The caller does not
334335
* have to call `sentry_value_decref` on it.
335336
*/
336337
SENTRY_API sentry_value_t sentry_value_new_attribute(
@@ -1858,6 +1859,17 @@ SENTRY_API void sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key,
18581859
SENTRY_API void sentry_remove_extra(const char *key);
18591860
SENTRY_API void sentry_remove_extra_n(const char *key, size_t key_len);
18601861

1862+
/**
1863+
* Sets attributes created with `sentry_value_new_attribute` to be applied to
1864+
* all:
1865+
* - logs
1866+
*/
1867+
SENTRY_API void sentry_set_attribute(const char *key, sentry_value_t attribute);
1868+
SENTRY_API void sentry_set_attribute_n(
1869+
const char *key, size_t key_len, sentry_value_t attribute);
1870+
SENTRY_API void sentry_remove_attribute(const char *key);
1871+
SENTRY_API void sentry_remove_attribute_n(const char *key, size_t key_len);
1872+
18611873
/**
18621874
* Sets a context object.
18631875
*/

src/sentry_core.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,39 @@ sentry_remove_extra_n(const char *key, size_t key_len)
941941
}
942942
}
943943

944+
void
945+
sentry_set_attribute(const char *key, sentry_value_t attribute)
946+
{
947+
SENTRY_WITH_SCOPE_MUT (scope) {
948+
sentry__scope_set_attribute(scope, key, attribute);
949+
}
950+
}
951+
952+
void
953+
sentry_set_attribute_n(
954+
const char *key, size_t key_len, sentry_value_t attribute)
955+
{
956+
SENTRY_WITH_SCOPE_MUT (scope) {
957+
sentry__scope_set_attribute_n(scope, key, key_len, attribute);
958+
}
959+
}
960+
961+
void
962+
sentry_remove_attribute(const char *key)
963+
{
964+
SENTRY_WITH_SCOPE_MUT (scope) {
965+
sentry__scope_remove_attribute(scope, key);
966+
}
967+
}
968+
969+
void
970+
sentry_remove_attribute_n(const char *key, size_t key_len)
971+
{
972+
SENTRY_WITH_SCOPE_MUT (scope) {
973+
sentry__scope_remove_attribute_n(scope, key, key_len);
974+
}
975+
}
976+
944977
void
945978
sentry_set_context(const char *key, sentry_value_t value)
946979
{

src/sentry_logs.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,10 @@ static sentry_value_t
671671
construct_log(sentry_level_t level, const char *message, va_list args)
672672
{
673673
sentry_value_t log = sentry_value_new_object();
674-
sentry_value_t attributes = sentry_value_new_object();
674+
sentry_value_t attributes = sentry_value_new_null();
675+
SENTRY_WITH_SCOPE (scope) {
676+
attributes = sentry__value_clone(scope->attributes);
677+
}
675678

676679
SENTRY_WITH_OPTIONS (options) {
677680
// Extract custom attributes if the option is enabled

src/sentry_scope.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ init_scope(sentry_scope_t *scope)
7474
scope->user = sentry_value_new_null();
7575
scope->tags = sentry_value_new_object();
7676
scope->extra = sentry_value_new_object();
77+
scope->attributes = sentry_value_new_object();
7778
scope->contexts = sentry_value_new_object();
7879
scope->propagation_context = sentry_value_new_object();
7980
scope->breadcrumbs = sentry__ringbuffer_new(SENTRY_BREADCRUMBS_MAX);
@@ -110,6 +111,7 @@ cleanup_scope(sentry_scope_t *scope)
110111
sentry_value_decref(scope->user);
111112
sentry_value_decref(scope->tags);
112113
sentry_value_decref(scope->extra);
114+
sentry_value_decref(scope->attributes);
113115
sentry_value_decref(scope->contexts);
114116
sentry_value_decref(scope->propagation_context);
115117
sentry__ringbuffer_free(scope->breadcrumbs);
@@ -588,6 +590,38 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len,
588590
sentry_value_set_by_key_n(scope->extra, key, key_len, value);
589591
}
590592

593+
void
594+
sentry__scope_set_attribute(
595+
sentry_scope_t *scope, const char *key, sentry_value_t attribute)
596+
{
597+
sentry__scope_set_attribute_n(scope, key, strlen(key), attribute);
598+
}
599+
600+
void
601+
sentry__scope_set_attribute_n(sentry_scope_t *scope, const char *key,
602+
size_t key_len, sentry_value_t attribute)
603+
{
604+
if (sentry_value_is_null(sentry_value_get_by_key(attribute, "value"))
605+
|| sentry_value_is_null(sentry_value_get_by_key(attribute, "type"))) {
606+
SENTRY_DEBUG("Cannot set attribute with missing 'value' or 'type'");
607+
return;
608+
}
609+
sentry_value_set_by_key_n(scope->attributes, key, key_len, attribute);
610+
}
611+
612+
void
613+
sentry__scope_remove_attribute(sentry_scope_t *scope, const char *key)
614+
{
615+
sentry_value_remove_by_key(scope->attributes, key);
616+
}
617+
618+
void
619+
sentry__scope_remove_attribute_n(
620+
sentry_scope_t *scope, const char *key, size_t key_len)
621+
{
622+
sentry_value_remove_by_key_n(scope->attributes, key, key_len);
623+
}
624+
591625
void
592626
sentry_scope_set_context(
593627
sentry_scope_t *scope, const char *key, sentry_value_t value)

src/sentry_scope.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ struct sentry_scope_s {
1717
sentry_value_t user;
1818
sentry_value_t tags;
1919
sentry_value_t extra;
20+
sentry_value_t attributes;
2021
sentry_value_t contexts;
2122
sentry_value_t propagation_context;
2223
sentry_ringbuffer_t *breadcrumbs;
@@ -96,6 +97,21 @@ void sentry__scope_set_fingerprint_va(
9697
void sentry__scope_set_fingerprint_nva(sentry_scope_t *scope,
9798
const char *fingerprint, size_t fingerprint_len, va_list va);
9899

100+
/**
101+
* Internal scope-based attribute functions.
102+
* For now, these are only used by the non-scope API functions that operate
103+
* on the global scope.
104+
* Once we have attributes for events or scope-based logs/metrics/spans APIs
105+
* these can become part of the public API too.
106+
*/
107+
void sentry__scope_set_attribute(
108+
sentry_scope_t *scope, const char *key, sentry_value_t attribute);
109+
void sentry__scope_set_attribute_n(sentry_scope_t *scope, const char *key,
110+
size_t key_len, sentry_value_t attribute);
111+
void sentry__scope_remove_attribute(sentry_scope_t *scope, const char *key);
112+
void sentry__scope_remove_attribute_n(
113+
sentry_scope_t *scope, const char *key, size_t key_len);
114+
99115
/**
100116
* These are convenience macros to automatically lock/unlock the global scope
101117
* inside a code block.

src/sentry_value.c

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -509,33 +509,67 @@ sentry_value_new_user(const char *id, const char *username, const char *email,
509509
ip_address, ip_address ? strlen(ip_address) : 0);
510510
}
511511

512-
sentry_value_t
513-
sentry_value_new_attribute_n(
514-
sentry_value_t value, const char *unit, size_t unit_len)
512+
/**
513+
* Converts a sentry_value_t attribute to its type string representation.
514+
* For lists, checks the first element to determine the array type.
515+
* Returns NULL for unsupported types (NULL, OBJECT).
516+
* https://develop.sentry.dev/sdk/telemetry/spans/span-protocol/#attribute-object-properties
517+
*/
518+
static const char *
519+
attribute_value_type_to_str(sentry_value_t value)
515520
{
516-
char *type;
517521
switch (sentry_value_get_type(value)) {
518522
case SENTRY_VALUE_TYPE_BOOL:
519-
type = "boolean";
520-
break;
523+
return "boolean";
521524
case SENTRY_VALUE_TYPE_INT32:
522525
case SENTRY_VALUE_TYPE_INT64:
523526
case SENTRY_VALUE_TYPE_UINT64:
524-
type = "integer";
525-
break;
527+
return "integer";
526528
case SENTRY_VALUE_TYPE_DOUBLE:
527-
type = "double";
528-
break;
529+
return "double";
529530
case SENTRY_VALUE_TYPE_STRING:
530-
type = "string";
531-
break;
531+
return "string";
532+
case SENTRY_VALUE_TYPE_LIST: {
533+
sentry_value_t first_item = sentry_value_get_by_index(value, 0);
534+
if (sentry_value_is_null(first_item)) {
535+
return NULL;
536+
}
537+
// Determine type based on first element
538+
switch (sentry_value_get_type(first_item)) {
539+
case SENTRY_VALUE_TYPE_BOOL:
540+
return "boolean[]";
541+
case SENTRY_VALUE_TYPE_INT32:
542+
case SENTRY_VALUE_TYPE_INT64:
543+
case SENTRY_VALUE_TYPE_UINT64:
544+
return "integer[]";
545+
case SENTRY_VALUE_TYPE_DOUBLE:
546+
return "double[]";
547+
case SENTRY_VALUE_TYPE_STRING:
548+
return "string[]";
549+
case SENTRY_VALUE_TYPE_NULL:
550+
case SENTRY_VALUE_TYPE_OBJECT:
551+
case SENTRY_VALUE_TYPE_LIST:
552+
default:
553+
return NULL;
554+
}
555+
}
532556
case SENTRY_VALUE_TYPE_NULL:
533-
case SENTRY_VALUE_TYPE_LIST:
534557
case SENTRY_VALUE_TYPE_OBJECT:
535558
default:
559+
return NULL;
560+
}
561+
}
562+
563+
sentry_value_t
564+
sentry_value_new_attribute_n(
565+
sentry_value_t value, const char *unit, size_t unit_len)
566+
{
567+
const char *type = attribute_value_type_to_str(value);
568+
if (!type) {
536569
sentry_value_decref(value);
537570
return sentry_value_new_null();
538571
}
572+
539573
sentry_value_t attribute = sentry_value_new_object();
540574

541575
sentry_value_set_by_key(

0 commit comments

Comments
 (0)