Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

**Features**:

- Add reusable, user-owned scopes. `sentry_scope_new` creates a scope that `sentry_capture_event_with_scope` applies without consuming, so you can configure it once and reuse it across many captures instead of building a new local scope each time. `sentry_scope_clone` copies a scope, and `sentry_scope_free` releases it. ([#1855](https://github.com/getsentry/sentry-native/pull/1855))

**Fixes**:

- Apply the propagation context to events that already have contexts set, so that events captured with a local scope or with event-level contexts keep their trace. ([#1843](https://github.com/getsentry/sentry-native/pull/1843))
Expand Down
37 changes: 35 additions & 2 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -2118,9 +2118,39 @@ typedef struct sentry_scope_s sentry_scope_t;

/**
* Creates a local scope.
*
* A local scope is a one-shot scope: the capture function it is passed to (such
* as `sentry_capture_event_with_scope`) takes ownership and frees it. To create
* a scope whose lifetime you manage and can reuse across captures, use
* `sentry_scope_new` instead.
*/
SENTRY_API sentry_scope_t *sentry_local_scope_new(void);

/**
* Creates a user-owned scope.
*
* Unlike a local scope, a user-owned scope is applied but not freed by capture
* functions such as `sentry_capture_event_with_scope`, so the same scope can be
* mutated and reused across many captures. You must release it yourself with
* `sentry_scope_free`.
*/
SENTRY_API sentry_scope_t *sentry_scope_new(void);

/**
* Frees a scope created via `sentry_scope_new`, `sentry_scope_clone`, or
* `sentry_local_scope_new`.
*/
SENTRY_API void sentry_scope_free(sentry_scope_t *scope);

/**
* Creates a copy of a scope.
*
* Top-level fields are copied while nested values are shared by reference. The
* returned scope is user-owned regardless of the source's ownership. Release it
* with `sentry_scope_free`.
*/
SENTRY_API sentry_scope_t *sentry_scope_clone(const sentry_scope_t *scope);

/**
* Sends a sentry event.
*
Expand All @@ -2131,9 +2161,12 @@ SENTRY_API sentry_scope_t *sentry_local_scope_new(void);
SENTRY_API sentry_uuid_t sentry_capture_event(sentry_value_t event);

/**
* Sends a sentry event with a local scope.
* Sends a sentry event with a scope.
*
* Takes ownership of `scope`.
* If `scope` is a local scope (`sentry_local_scope_new`), this takes ownership
* of it and frees it. If `scope` is user-owned (`sentry_scope_new` or
* `sentry_scope_clone`), it is applied but not freed, so it can be reused; free
* it yourself with `sentry_scope_free`.
*/
SENTRY_API sentry_uuid_t sentry_capture_event_with_scope(
sentry_value_t event, sentry_scope_t *scope);
Expand Down
4 changes: 3 additions & 1 deletion src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,9 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event,
sentry_scope_mode_t mode = SENTRY_SCOPE_BREADCRUMBS;
sentry__scope_apply_to_event(local_scope, options, event, mode);
sentry__attachments_extend(&all_attachments, local_scope->attachments);
sentry__scope_free(local_scope);
if (local_scope->one_shot) {
sentry_scope_free(local_scope);
}
}

SENTRY_WITH_SCOPE (scope) {
Expand Down
19 changes: 19 additions & 0 deletions src/sentry_ringbuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@ sentry__ringbuffer_free(sentry_ringbuffer_t *rb)
sentry_free(rb);
}

sentry_ringbuffer_t *
sentry__ringbuffer_clone(const sentry_ringbuffer_t *rb)
{
if (!rb) {
return NULL;
}

sentry_ringbuffer_t *clone = SENTRY_MAKE(sentry_ringbuffer_t);
if (!clone) {
return NULL;
}

clone->list = sentry__value_clone(rb->list);
clone->max_size = rb->max_size;
clone->start_idx = rb->start_idx;

return clone;
}

int
sentry__ringbuffer_append(sentry_ringbuffer_t *rb, sentry_value_t value)
{
Expand Down
7 changes: 7 additions & 0 deletions src/sentry_ringbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ sentry_ringbuffer_t *sentry__ringbuffer_new(size_t max_size);
*/
void sentry__ringbuffer_free(sentry_ringbuffer_t *rb);

/**
* Create a copy of a ringbuffer with the same contents and ring state.
* The contained values are shared by reference, like `sentry__value_clone`.
* Returns a pointer to the ringbuffer on success, NULL on failure.
*/
sentry_ringbuffer_t *sentry__ringbuffer_clone(const sentry_ringbuffer_t *rb);

/**
* Append a sentry_value_t to the ringbuffer.
* If the ringbuffer is full, the oldest value will be overwritten.
Expand Down
57 changes: 55 additions & 2 deletions src/sentry_scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ init_scope(sentry_scope_t *scope)
scope->transaction_object = NULL;
scope->span = NULL;
scope->trace_managed = true;
scope->one_shot = false;
}

static sentry_scope_t *
Expand Down Expand Up @@ -170,7 +171,7 @@ sentry__scope_flush_unlock(void)
}

sentry_scope_t *
sentry_local_scope_new(void)
sentry_scope_new(void)
{
sentry_scope_t *scope = SENTRY_MAKE(sentry_scope_t);
if (!scope) {
Expand All @@ -182,7 +183,7 @@ sentry_local_scope_new(void)
}

void
sentry__scope_free(sentry_scope_t *scope)
sentry_scope_free(sentry_scope_t *scope)
{
if (!scope) {
return;
Expand All @@ -192,6 +193,58 @@ sentry__scope_free(sentry_scope_t *scope)
sentry_free(scope);
}

sentry_scope_t *
sentry_local_scope_new(void)
{
sentry_scope_t *scope = sentry_scope_new();
if (scope) {
scope->one_shot = true;
}
return scope;
}

sentry_scope_t *
sentry_scope_clone(const sentry_scope_t *scope)
{
if (!scope) {
return NULL;
}

sentry_scope_t *clone = SENTRY_MAKE(sentry_scope_t);
if (!clone) {
return NULL;
}

clone->release = sentry__string_clone(scope->release);
clone->environment = sentry__string_clone(scope->environment);
clone->transaction = sentry__string_clone(scope->transaction);
clone->fingerprint = sentry__value_clone(scope->fingerprint);
clone->user = sentry__value_clone(scope->user);
clone->tags = sentry__value_clone(scope->tags);
clone->extra = sentry__value_clone(scope->extra);
clone->attributes = sentry__value_clone(scope->attributes);
clone->contexts = sentry__value_clone(scope->contexts);
clone->propagation_context
= sentry__value_clone(scope->propagation_context);
clone->breadcrumbs = sentry__ringbuffer_clone(scope->breadcrumbs);
clone->dynamic_sampling_context
= sentry__value_clone(scope->dynamic_sampling_context);
if (sentry_value_is_frozen(scope->dynamic_sampling_context)) {
sentry_value_freeze(clone->dynamic_sampling_context);
}
clone->level = scope->level;
clone->client_sdk = sentry__value_clone(scope->client_sdk);
sentry__attachments_extend(&clone->attachments, scope->attachments);

clone->transaction_object = scope->transaction_object;
sentry__transaction_incref(clone->transaction_object);
clone->span = scope->span;
sentry__span_incref(clone->span);
clone->trace_managed = scope->trace_managed;

return clone;
}

void
sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming)
{
Expand Down
9 changes: 4 additions & 5 deletions src/sentry_scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ struct sentry_scope_s {
sentry_transaction_t *transaction_object;
sentry_span_t *span;
bool trace_managed;

// Whether this scope is single-use. A capture function frees a one-shot
// scope after applying it.
bool one_shot;
};

/**
Expand Down Expand Up @@ -79,11 +83,6 @@ void sentry__scope_cleanup(void);
*/
void sentry__scope_flush_unlock(void);

/**
* Deallocates a (local) scope.
*/
void sentry__scope_free(sentry_scope_t *scope);

/**
* This will merge the requested data which is in the given `scope` to the given
* `event`.
Expand Down
Loading
Loading