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
40 changes: 40 additions & 0 deletions examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,33 @@ discarding_before_transaction_callback(sentry_value_t tx, void *user_data)
return tx;
}

static sentry_value_t
before_send_log_callback(sentry_value_t log, void *user_data)
{
(void)user_data;
sentry_value_t attribute = sentry_value_new_object();
sentry_value_set_by_key(
attribute, "value", sentry_value_new_string("little"));
sentry_value_set_by_key(
attribute, "type", sentry_value_new_string("string"));
sentry_value_set_by_key(sentry_value_get_by_key(log, "attributes"),
"coffeepot.size", attribute);
return log;
}

static sentry_value_t
discarding_before_send_log_callback(sentry_value_t log, void *user_data)
{
(void)user_data;
if (sentry_value_is_null(
sentry_value_get_by_key(sentry_value_get_by_key(log, "attributes"),
"sentry.message.template"))) {
sentry_value_decref(log);
return sentry_value_new_null();
}
return log;
}

static void
print_envelope(sentry_envelope_t *envelope, void *unused_state)
{
Expand Down Expand Up @@ -374,6 +401,16 @@ main(int argc, char **argv)
options, discarding_before_transaction_callback, NULL);
}

if (has_arg(argc, argv, "before-send-log")) {
sentry_options_set_before_send_log(
options, before_send_log_callback, NULL);
}

if (has_arg(argc, argv, "discarding-before-send-log")) {
sentry_options_set_before_send_log(
options, discarding_before_send_log_callback, NULL);
}

if (has_arg(argc, argv, "traces-sampler")) {
sentry_options_set_traces_sampler(options, traces_sampler_callback);
}
Expand Down Expand Up @@ -424,6 +461,9 @@ main(int argc, char **argv)

// TODO incorporate into test
if (sentry_options_get_enable_logs(options)) {
if (has_arg(argc, argv, "capture-log")) {
sentry_log_debug("I'm a log message!");
}
if (has_arg(argc, argv, "logs-timer")) {
for (int i = 0; i < 10; i++) {
sentry_log_info("Informational log nr.%d", i);
Expand Down
17 changes: 17 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,23 @@ SENTRY_EXPERIMENTAL_API void sentry_log_warn(const char *message, ...);
SENTRY_EXPERIMENTAL_API void sentry_log_error(const char *message, ...);
SENTRY_EXPERIMENTAL_API void sentry_log_fatal(const char *message, ...);

/**
* Type of the `before_send_log` callback.
*
* The callback takes ownership of the `log`, and should usually return
* that same log. In case the log should be discarded, the
* callback needs to call `sentry_value_decref` on the provided log, and
* return a `sentry_value_new_null()` instead.
*/
typedef sentry_value_t (*sentry_before_send_log_function_t)(
sentry_value_t log, void *user_data);

/**
* Sets the `before_send_log` callback.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_before_send_log(
sentry_options_t *opts, sentry_before_send_log_function_t func, void *data);

#ifdef SENTRY_PLATFORM_LINUX

/**
Expand Down
30 changes: 25 additions & 5 deletions src/sentry_logs.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,12 +328,13 @@ skip_length(const char *fmt_ptr)
return fmt_ptr;
}

static void
// returns how many parameters were added to the attributes object
static int
populate_message_parameters(
sentry_value_t attributes, const char *message, va_list args)
{
if (!message || sentry_value_is_null(attributes)) {
return;
return 0;
}

const char *fmt_ptr = message;
Expand Down Expand Up @@ -375,6 +376,7 @@ populate_message_parameters(
}

va_end(args_copy);
return param_index;
}

static void
Expand Down Expand Up @@ -494,18 +496,21 @@ construct_log(sentry_level_t level, const char *message, va_list args)
"string", "sentry.sdk.name");
add_attribute(attributes, sentry_value_new_string(sentry_sdk_version()),
"string", "sentry.sdk.version");
add_attribute(attributes, sentry_value_new_string(message), "string",
"sentry.message.template");

// Parse variadic arguments and add them to attributes
populate_message_parameters(attributes, message, args_copy_3);
if (populate_message_parameters(attributes, message, args_copy_3)) {
// only add message template if we have parameters
add_attribute(attributes, sentry_value_new_string(message), "string",
"sentry.message.template");
}
va_end(args_copy_3);

sentry_value_set_by_key(log, "attributes", attributes);

return log;
}

// TODO change to int return
void
sentry__logs_log(sentry_level_t level, const char *message, va_list args)
{
Expand All @@ -514,9 +519,24 @@ sentry__logs_log(sentry_level_t level, const char *message, va_list args)
if (options->enable_logs)
enable_logs = true;
}
int discarded = false;
if (enable_logs) {
// create log from message
sentry_value_t log = construct_log(level, message, args);
SENTRY_WITH_OPTIONS (options) {
if (options->before_send_log_func) {
log = options->before_send_log_func(
log, options->before_send_log_data);
if (sentry_value_is_null(log)) {
SENTRY_DEBUG(
"log was discarded by the `before_send_log` hook");
discarded = true;
}
}
}
if (discarded) {
return;
}
if (!enqueue_log_single(log)) {
sentry_value_decref(log);
}
Expand Down
8 changes: 8 additions & 0 deletions src/sentry_options.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ sentry_options_set_before_transaction(
opts->before_transaction_data = user_data;
}

void
sentry_options_set_before_send_log(sentry_options_t *opts,
sentry_before_send_log_function_t func, void *user_data)
{
opts->before_send_log_func = func;
opts->before_send_log_data = user_data;
}

void
sentry_options_set_dsn_n(
sentry_options_t *opts, const char *raw_dsn, size_t raw_dsn_len)
Expand Down
2 changes: 2 additions & 0 deletions src/sentry_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ struct sentry_options_s {
void *on_crash_data;
sentry_transaction_function_t before_transaction_func;
void *before_transaction_data;
sentry_before_send_log_function_t before_send_log_func;
void *before_send_log_data;

/* Experimentally exposed */
double traces_sample_rate;
Expand Down
63 changes: 63 additions & 0 deletions tests/test_integration_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -1382,3 +1382,66 @@ def test_logs_threaded(cmake, httpserver):
total_count += envelope.items[0].headers["item_count"]
print(f"Total amount of captured logs: {total_count}")
assert total_count >= 100


def test_before_send_log(cmake, httpserver):
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"})

httpserver.expect_oneshot_request(
"/api/123456/envelope/",
headers={"x-sentry-auth": auth_header},
).respond_with_data("OK")
env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver), SENTRY_RELEASE="🤮🚀")

run(
tmp_path,
"sentry_example",
["log", "enable-logs", "capture-log", "before-send-log"],
check=True,
env=env,
)

assert len(httpserver.log) == 1
req = httpserver.log[0][0]
body = req.get_data()

envelope = Envelope.deserialize(body)

# Show what the envelope looks like if the test fails.
envelope.print_verbose()

# Extract the log item
(log_item,) = envelope.items

assert log_item.headers["type"] == "log"
payload = log_item.payload.json

# Get the first log item from the logs payload
log_entry = payload["items"][0]
attributes = log_entry["attributes"]

# Check that the before_send_log callback added the expected attribute
assert "coffeepot.size" in attributes
assert attributes["coffeepot.size"]["value"] == "little"
assert attributes["coffeepot.size"]["type"] == "string"


def test_before_send_log_discard(cmake, httpserver):
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"})

httpserver.expect_oneshot_request(
"/api/123456/envelope/",
headers={"x-sentry-auth": auth_header},
).respond_with_data("OK")
env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver), SENTRY_RELEASE="🤮🚀")

run(
tmp_path,
"sentry_example",
["log", "enable-logs", "capture-log", "discarding-before-send-log"],
check=True,
env=env,
)

# log should have been discarded
assert len(httpserver.log) == 0
Loading