Skip to content
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: add traces_sampler #1108

Merged
merged 35 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
60dc201
add traces_sampler option
JoshuaMoelans Dec 20, 2024
dd25b29
update CHANGELOG.md
JoshuaMoelans Dec 20, 2024
9dc332f
introduced `sentry_traces_sampler_function` typedef
JoshuaMoelans Dec 20, 2024
26ff58a
added sampling context
JoshuaMoelans Dec 23, 2024
cc77b52
format
JoshuaMoelans Dec 23, 2024
7344f8a
eof newlines
JoshuaMoelans Dec 24, 2024
78f01c9
feat: pass sampling_context into sampling decision
JoshuaMoelans Jan 3, 2025
49c60eb
remove unnecessary commented include
JoshuaMoelans Jan 3, 2025
2fe7646
add memory freeing/incref-decref
JoshuaMoelans Jan 3, 2025
e08bc8e
change custom sampling context type to sentry_value_t
JoshuaMoelans Jan 7, 2025
2be1836
add decref
JoshuaMoelans Jan 7, 2025
76ca4d2
removed unneeded creation function
JoshuaMoelans Jan 7, 2025
c35fa07
format
JoshuaMoelans Jan 7, 2025
1279675
set codeql runner to ubuntu-22.04
JoshuaMoelans Jan 7, 2025
4e4aa0a
take ownership of custom_sampling_ctx
JoshuaMoelans Jan 9, 2025
372e887
add parent sampling decision
JoshuaMoelans Jan 9, 2025
17177ec
add traces_sampler and parent sampling decision tests
JoshuaMoelans Jan 9, 2025
d43df63
decref
JoshuaMoelans Jan 9, 2025
87efced
change callback arg from struct to parameter list
JoshuaMoelans Jan 9, 2025
f2fe073
format
JoshuaMoelans Jan 9, 2025
a9e2057
mark unused parameters
JoshuaMoelans Jan 10, 2025
a79245c
format
JoshuaMoelans Jan 10, 2025
d172a80
cleanup test + docs
JoshuaMoelans Jan 10, 2025
77e4b23
Merge branch 'master' into joshua/feat/tracesSampler_support
supervacuus Jan 10, 2025
39d9aa3
apply PR feedback
JoshuaMoelans Jan 13, 2025
bfaefe8
format
JoshuaMoelans Jan 13, 2025
e159e12
cleanup example
JoshuaMoelans Jan 13, 2025
a9d7221
add getters for transaction_ctx name + operation
JoshuaMoelans Jan 13, 2025
4610f33
remove (void) on transaction_ctx in example.c
JoshuaMoelans Jan 13, 2025
370b162
add transaction_ctx getter tests
JoshuaMoelans Jan 13, 2025
eb3f9c5
dont expose sampling_context_s in sentry.h
JoshuaMoelans Jan 14, 2025
2936acc
change bool to int
JoshuaMoelans Jan 14, 2025
2175251
cleanup example.c
JoshuaMoelans Jan 14, 2025
72baf78
refactor tx_cxt to tx_ctx convention
JoshuaMoelans Jan 14, 2025
e1d191e
change sampled_int init
JoshuaMoelans Jan 14, 2025
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 @@ -5,6 +5,7 @@
**Features**:

- Add option to set debug log level. ([#1107](https://github.com/getsentry/sentry-native/pull/1107))
- Add `traces_sampler` ([#1108](https://github.com/getsentry/sentry-native/pull/1108))
- Provide support for C++17 compilers when using the `crashpad` backend. ([#1110](https://github.com/getsentry/sentry-native/pull/1110), [crashpad#116](https://github.com/getsentry/crashpad/pull/116), [mini_chromium#1](https://github.com/getsentry/mini_chromium/pull/1))

## 0.7.17
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ The example currently supports the following commands:
- `stack-overflow`: Provokes a stack-overflow.
- `http-proxy`: Uses a localhost `HTTP` proxy on port 8080.
- `socks5-proxy`: Uses a localhost `SOCKS5` proxy on port 1080.
- `capture-transaction`: Captures a transaction.
JoshuaMoelans marked this conversation as resolved.
Show resolved Hide resolved
- `traces-sampler`: Installs a traces sampler callback function when used alongside `capture-transaction`.

Only on Windows using crashpad with its WER handler module:

Expand Down
36 changes: 35 additions & 1 deletion examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@
# define sleep_s(SECONDS) sleep(SECONDS)
#endif

static double
traces_sampler_callback(const sentry_transaction_context_t *transaction_ctx,
sentry_value_t custom_sampling_ctx, const int *parent_sampled)
{
if (parent_sampled != NULL) {
if (*parent_sampled) {
return 0.8; // high sample rate for children of sampled transactions
}
return 0; // parent is not sampled
}
if (strcmp(sentry_transaction_context_get_name(transaction_ctx),
"little.teapot")
== 0) {
if (strcmp(sentry_transaction_context_get_operation(transaction_ctx),
"Short and stout here is my handle and here is my spout")
== 0) {
if (sentry_value_as_int32(
sentry_value_get_by_key(custom_sampling_ctx, "b"))
== 42) {
return 1;
}
}
}
return 0;
}

static sentry_value_t
before_send_callback(sentry_value_t event, void *hint, void *closure)
{
Expand Down Expand Up @@ -232,6 +258,10 @@ main(int argc, char **argv)
options, discarding_on_crash_callback, NULL);
}

if (has_arg(argc, argv, "traces-sampler")) {
sentry_options_set_traces_sampler(options, traces_sampler_callback);
}

if (has_arg(argc, argv, "override-sdk-name")) {
sentry_options_set_sdk_name(options, "sentry.native.android.flutter");
}
Expand Down Expand Up @@ -403,8 +433,12 @@ main(int argc, char **argv)
if (has_arg(argc, argv, "unsample-tx")) {
sentry_transaction_context_set_sampled(tx_ctx, 0);
}

sentry_value_t custom_sampling_ctx = sentry_value_new_object();
sentry_value_set_by_key(
custom_sampling_ctx, "b", sentry_value_new_int32(42));
sentry_transaction_t *tx
= sentry_transaction_start(tx_ctx, sentry_value_new_null());
= sentry_transaction_start(tx_ctx, custom_sampling_ctx);

sentry_transaction_set_data(
tx, "url", sentry_value_new_string("https://example.com"));
Expand Down
62 changes: 43 additions & 19 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,26 @@ SENTRY_EXPERIMENTAL_API void sentry_options_set_traces_sample_rate(
SENTRY_EXPERIMENTAL_API double sentry_options_get_traces_sample_rate(
sentry_options_t *opts);

/**
* A sentry Transaction Context.
*
* See Transaction Interface under
* https://develop.sentry.dev/sdk/performance/#new-span-and-transaction-classes
*/
struct sentry_transaction_context_s;
typedef struct sentry_transaction_context_s sentry_transaction_context_t;
typedef double (*sentry_traces_sampler_function)(
const sentry_transaction_context_t *transaction_ctx,
sentry_value_t custom_sampling_ctx, const int *parent_sampled);

/**
* Sets the traces sampler callback. Should be a function that returns a double
* and takes in a sentry_transaction_context_t pointer, a sentry_value_t for
* a custom sampling context and a int pointer for the parent sampled flag.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_traces_sampler(
sentry_options_t *opts, sentry_traces_sampler_function callback);

#ifdef SENTRY_PLATFORM_LINUX

/**
Expand Down Expand Up @@ -1590,15 +1610,6 @@ SENTRY_EXPERIMENTAL_API void sentry_end_session_with_status(

/* -- Performance Monitoring/Tracing APIs -- */

/**
* A sentry Transaction Context.
*
* See Transaction Interface under
* https://develop.sentry.dev/sdk/performance/#new-span-and-transaction-classes
*/
struct sentry_transaction_context_s;
typedef struct sentry_transaction_context_s sentry_transaction_context_t;

/**
* A sentry Transaction.
*
Expand Down Expand Up @@ -1649,9 +1660,14 @@ sentry_transaction_context_new_n(const char *name, size_t name_len,
* setting a name on it.
*/
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name(
sentry_transaction_context_t *tx_cxt, const char *name);
sentry_transaction_context_t *tx_ctx, const char *name);
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name_n(
sentry_transaction_context_t *tx_cxt, const char *name, size_t name_len);
sentry_transaction_context_t *tx_ctx, const char *name, size_t name_len);
/**
* Gets the `name` of a Transaction Context.
*/
SENTRY_EXPERIMENTAL_API const char *sentry_transaction_context_get_name(
const sentry_transaction_context_t *tx_ctx);

/**
* Sets the `operation` on a Transaction Context, which will be used in the
Expand All @@ -1664,10 +1680,15 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name_n(
* setting an operation on it.
*/
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_operation(
sentry_transaction_context_t *tx_cxt, const char *operation);
sentry_transaction_context_t *tx_ctx, const char *operation);
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_operation_n(
sentry_transaction_context_t *tx_cxt, const char *operation,
sentry_transaction_context_t *tx_ctx, const char *operation,
size_t operation_len);
/**
* Gets the `operation` of a Transaction Context.
*/
SENTRY_EXPERIMENTAL_API const char *sentry_transaction_context_get_operation(
const sentry_transaction_context_t *tx_ctx);

/**
* Sets the `sampled` field on a Transaction Context, which will be used in the
Expand All @@ -1681,7 +1702,7 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_operation_n(
* setting `sampled` on it.
*/
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_sampled(
sentry_transaction_context_t *tx_cxt, int sampled);
sentry_transaction_context_t *tx_ctx, int sampled);

/**
* Removes the `sampled` field on a Transaction Context, which will be used in
Expand All @@ -1693,7 +1714,7 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_sampled(
* removing `sampled`.
*/
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_remove_sampled(
sentry_transaction_context_t *tx_cxt);
sentry_transaction_context_t *tx_ctx);

/**
* Update the Transaction Context with the given HTTP header key/value pair.
Expand All @@ -1704,9 +1725,9 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_remove_sampled(
* upstream service.
*/
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header(
sentry_transaction_context_t *tx_cxt, const char *key, const char *value);
sentry_transaction_context_t *tx_ctx, const char *key, const char *value);
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header_n(
sentry_transaction_context_t *tx_cxt, const char *key, size_t key_len,
sentry_transaction_context_t *tx_ctx, const char *key, size_t key_len,
const char *value, size_t value_len);

/**
Expand Down Expand Up @@ -1735,14 +1756,17 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header_n(
* Takes ownership of `transaction_context`. A Transaction Context cannot be
* modified or re-used after it is used to start a Transaction.
*
* Takes ownership of `custom_sampling_ctx`. A Sampling Context cannot be
* modified or re-used after it is used to start a Transaction.
*
* The returned value is not thread-safe. Users are expected to ensure that
* appropriate locking mechanisms are implemented over the Transaction if it
* needs to be mutated across threads. Methods operating on the Transaction will
* mention what kind of expectations they carry if they need to mutate or access
* the object in a thread-safe way.
*/
SENTRY_EXPERIMENTAL_API sentry_transaction_t *sentry_transaction_start(
sentry_transaction_context_t *tx_cxt, sentry_value_t sampling_ctx);
sentry_transaction_context_t *tx_ctx, sentry_value_t custom_sampling_ctx);
/**
* Also starts a transaction like the regular `sentry_transaction_start`
* function, but has an additional timestamp parameter to let the user provide
Expand All @@ -1751,7 +1775,7 @@ SENTRY_EXPERIMENTAL_API sentry_transaction_t *sentry_transaction_start(
* The timestamp should be provided in microseconds since the Unix epoch.
*/
SENTRY_EXPERIMENTAL_API sentry_transaction_t *sentry_transaction_start_ts(
sentry_transaction_context_t *tx_cxt, sentry_value_t sampling_ctx,
sentry_transaction_context_t *tx_ctx, sentry_value_t custom_sampling_ctx,
uint64_t timestamp);

/**
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ sentry_target_sources_cwd(sentry
sentry_random.h
sentry_ratelimiter.c
sentry_ratelimiter.h
sentry_sampling_context.h
sentry_scope.c
sentry_scope.h
sentry_session.c
Expand Down
70 changes: 45 additions & 25 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,18 +448,40 @@ sentry__capture_event(sentry_value_t event)
}

bool
sentry__should_send_transaction(sentry_value_t tx_cxt)
sentry__should_send_transaction(
sentry_value_t tx_ctx, sentry_sampling_context_t *sampling_ctx)
{
sentry_value_t context_setting = sentry_value_get_by_key(tx_cxt, "sampled");
if (!sentry_value_is_null(context_setting)) {
return sentry_value_is_true(context_setting);
}
sentry_value_t context_setting = sentry_value_get_by_key(tx_ctx, "sampled");
bool sampled = sentry_value_is_null(context_setting)
? false
: sentry_value_is_true(context_setting);
sampling_ctx->parent_sampled
= sentry_value_is_null(context_setting) ? NULL : &sampled;

int parent_sampled_int; // bool to int conversion
JoshuaMoelans marked this conversation as resolved.
Show resolved Hide resolved
if (sampling_ctx->parent_sampled != NULL) {
parent_sampled_int = *sampling_ctx->parent_sampled ? 1 : 0;
}
bool send = false;
SENTRY_WITH_OPTIONS (options) {
send = sentry__roll_dice(options->traces_sample_rate);
// TODO(tracing): Run through traces sampler function if rate is
// unavailable.
if (options->traces_sampler) {
const double result
= ((sentry_traces_sampler_function)options->traces_sampler)(
sampling_ctx->transaction_context,
sampling_ctx->custom_sampling_context,
sampling_ctx->parent_sampled == NULL ? NULL
: &parent_sampled_int);
send = sentry__roll_dice(result);
} else {
if (sampling_ctx->parent_sampled != NULL) {
send = *sampling_ctx->parent_sampled;
} else {
send = sentry__roll_dice(options->traces_sample_rate);
}
}
}
if (sampling_ctx->parent_sampled != NULL) {
sampling_ctx->parent_sampled = NULL;
}
return send;
}
Expand Down Expand Up @@ -840,32 +862,28 @@ sentry_set_level(sentry_level_t level)
}

sentry_transaction_t *
sentry_transaction_start(
sentry_transaction_context_t *opaque_tx_cxt, sentry_value_t sampling_ctx)
sentry_transaction_start(sentry_transaction_context_t *opaque_tx_ctx,
sentry_value_t custom_sampling_ctx)
{
return sentry_transaction_start_ts(
opaque_tx_cxt, sampling_ctx, sentry__usec_time());
opaque_tx_ctx, custom_sampling_ctx, sentry__usec_time());
}

sentry_transaction_t *
sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_cxt,
sentry_value_t sampling_ctx, uint64_t timestamp)
sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx,
sentry_value_t custom_sampling_ctx, uint64_t timestamp)
{
// Just free this immediately until we implement proper support for
// traces_sampler.
sentry_value_decref(sampling_ctx);

if (!opaque_tx_cxt) {
if (!opaque_tx_ctx) {
return NULL;
}

sentry_value_t tx_cxt = opaque_tx_cxt->inner;
sentry_value_t tx_ctx = opaque_tx_ctx->inner;

// If the parent span ID is some empty-ish value, just remove it
sentry_value_t parent_span
= sentry_value_get_by_key(tx_cxt, "parent_span_id");
= sentry_value_get_by_key(tx_ctx, "parent_span_id");
if (sentry_value_get_length(parent_span) < 1) {
sentry_value_remove_by_key(tx_cxt, "parent_span_id");
sentry_value_remove_by_key(tx_ctx, "parent_span_id");
}

// The ending timestamp is stripped to avoid misleading ourselves later
Expand All @@ -874,17 +892,19 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_cxt,
sentry_value_t tx = sentry_value_new_event();
sentry_value_remove_by_key(tx, "timestamp");

sentry__value_merge_objects(tx, tx_cxt);

bool should_sample = sentry__should_send_transaction(tx_cxt);
sentry__value_merge_objects(tx, tx_ctx);
sentry_sampling_context_t sampling_ctx
= { opaque_tx_ctx, custom_sampling_ctx, NULL };
bool should_sample = sentry__should_send_transaction(tx_ctx, &sampling_ctx);
sentry_value_set_by_key(
tx, "sampled", sentry_value_new_bool(should_sample));
sentry_value_decref(custom_sampling_ctx);

sentry_value_set_by_key(tx, "start_timestamp",
sentry__value_new_string_owned(
sentry__usec_time_to_iso8601(timestamp)));

sentry__transaction_context_free(opaque_tx_cxt);
sentry__transaction_context_free(opaque_tx_ctx);
return sentry__transaction_new(tx);
}

Expand Down
4 changes: 3 additions & 1 deletion src/sentry_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "sentry_boot.h"
#include "sentry_logger.h"
#include "sentry_sampling_context.h"

#define SENTRY_BREADCRUMBS_MAX 100
#define SENTRY_SPANS_MAX 1000
Expand Down Expand Up @@ -117,7 +118,8 @@ void sentry__options_unlock(void);
// these for now are only needed outside of core for tests
#ifdef SENTRY_UNITTEST
bool sentry__roll_dice(double probability);
bool sentry__should_send_transaction(sentry_value_t tx_cxt);
bool sentry__should_send_transaction(
sentry_value_t tx_ctx, sentry_sampling_context_t *sampling_ctx);
#endif

#endif
7 changes: 7 additions & 0 deletions src/sentry_options.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,13 @@ sentry_options_get_traces_sample_rate(sentry_options_t *opts)
return opts->traces_sample_rate;
}

void
sentry_options_set_traces_sampler(
sentry_options_t *opts, sentry_traces_sampler_function callback)
{
opts->traces_sampler = callback;
}

void
sentry_options_set_backend(sentry_options_t *opts, sentry_backend_t *backend)
{
Expand Down
1 change: 1 addition & 0 deletions src/sentry_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ typedef struct sentry_options_s {

/* Experimentally exposed */
double traces_sample_rate;
sentry_traces_sampler_function traces_sampler;
size_t max_spans;

/* everything from here on down are options which are stored here but
Expand Down
13 changes: 13 additions & 0 deletions src/sentry_sampling_context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// sentry_sampling_context.h
#ifndef SENTRY_SAMPLING_CONTEXT_H_INCLUDED
#define SENTRY_SAMPLING_CONTEXT_H_INCLUDED

#include "sentry_tracing.h"

typedef struct sentry_sampling_context_s {
sentry_transaction_context_t *transaction_context;
sentry_value_t custom_sampling_context;
bool *parent_sampled;
} sentry_sampling_context_t;

#endif // SENTRY_SAMPLING_CONTEXT_H_INCLUDED
Loading
Loading