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 more Event Payload convenience methods #517

Merged
merged 5 commits into from
Apr 19, 2021
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
21 changes: 7 additions & 14 deletions examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,14 @@ main(int argc, char **argv)
sentry_capture_event(event);
}
if (has_arg(argc, argv, "capture-exception")) {
// TODO: Create a convenience API to create a new exception object,
// and to attach a stacktrace to the exception.
// See also https://github.com/getsentry/sentry-native/issues/235
sentry_value_t exc = sentry_value_new_exception(
"ParseIntError", "invalid digit found in string");
if (has_arg(argc, argv, "add-stacktrace")) {
sentry_value_t stacktrace = sentry_value_new_stacktrace(NULL, 0);
sentry_value_set_by_key(exc, "stacktrace", stacktrace);
}
sentry_value_t event = sentry_value_new_event();
sentry_value_t exception = sentry_value_new_object();
// for example:
sentry_value_set_by_key(
exception, "type", sentry_value_new_string("ParseIntError"));
sentry_value_set_by_key(exception, "value",
sentry_value_new_string("invalid digit found in string"));
sentry_value_t exceptions = sentry_value_new_list();
sentry_value_append(exceptions, exception);
sentry_value_t values = sentry_value_new_object();
sentry_value_set_by_key(values, "values", exceptions);
sentry_value_set_by_key(event, "exception", values);
sentry_event_add_exception(event, exc);

sentry_capture_event(event);
}
Expand Down
87 changes: 79 additions & 8 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,26 +338,92 @@ typedef enum sentry_level_e {
} sentry_level_t;

/**
* Creates a new empty event value.
* Creates a new empty Event value.
*
* See https://docs.sentry.io/platforms/native/enriching-events/ for how to
* further work with events, and https://develop.sentry.dev/sdk/event-payloads/
* for a detailed overview of the possible properties of an Event.
*/
SENTRY_API sentry_value_t sentry_value_new_event(void);

/**
* Creates a new message event value.
* Creates a new Message Event value.
*
* See https://develop.sentry.dev/sdk/event-payloads/message/
*
* `logger` can be NULL to omit the logger value.
*/
SENTRY_API sentry_value_t sentry_value_new_message_event(
sentry_level_t level, const char *logger, const char *text);

/**
* Creates a new breadcrumb with a specific type and message.
* Creates a new Breadcrumb with a specific type and message.
*
* See https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/
*
* Either parameter can be NULL in which case no such attributes is created.
*/
SENTRY_API sentry_value_t sentry_value_new_breadcrumb(
const char *type, const char *message);

/**
* Creates a new Exception value.
*
* This is intended for capturing language-level exception, such as from a
* try-catch block. `type` and `value` here refer to the exception class and
* a possible description.
*
* See https://develop.sentry.dev/sdk/event-payloads/exception/
*
* The returned value needs to be attached to an event via
* `sentry_event_add_exception`.
*/
SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_exception(
const char *type, const char *value);
Swatinem marked this conversation as resolved.
Show resolved Hide resolved

/**
* Creates a new Thread value.
*
* See https://develop.sentry.dev/sdk/event-payloads/threads/
*
* The returned value needs to be attached to an event via
* `sentry_event_add_thread`.
*
* `name` can be NULL.
*/
SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread(
uint64_t id, const char *name);

/**
* Creates a new Stack Trace conforming to the Stack Trace Interface.
*
* See https://develop.sentry.dev/sdk/event-payloads/stacktrace/
*
* The returned object needs to be attached to either an exception
* event, or a thread object.
*
* If `ips` is NULL the current stack trace is captured, otherwise `len`
* stack trace instruction pointers are attached to the event.
*/
SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_stacktrace(
void **ips, size_t len);

/**
* Adds an Exception to an Event value.
*
* This takes ownership of the `exception`.
*/
SENTRY_EXPERIMENTAL_API void sentry_event_add_exception(
sentry_value_t event, sentry_value_t exception);

/**
* Adds a Thread to an Event value.
*
* This takes ownership of the `thread`.
*/
SENTRY_EXPERIMENTAL_API void sentry_event_add_thread(
sentry_value_t event, sentry_value_t thread);

/* -- Experimental APIs -- */

/**
Expand All @@ -371,10 +437,15 @@ SENTRY_EXPERIMENTAL_API char *sentry_value_to_msgpack(
sentry_value_t value, size_t *size_out);

/**
* Adds a stacktrace to an event.
* Adds a stack trace to an event.
*
* The stack trace is added as part of a new thread object.
* This function is **deprecated** in favor of using
* `sentry_value_new_stacktrace` in combination with `sentry_value_new_thread`
* and `sentry_event_add_thread`.
*
* If `ips` is NULL the current stacktrace is captured, otherwise `len`
* stacktrace instruction pointers are attached to the event.
* If `ips` is NULL the current stack trace is captured, otherwise `len`
* stack trace instruction pointers are attached to the event.
*/
SENTRY_EXPERIMENTAL_API void sentry_event_value_add_stacktrace(
sentry_value_t event, void **ips, size_t len);
Expand All @@ -398,7 +469,7 @@ typedef struct sentry_ucontext_s {
*
* If the address is given in `addr` the stack is unwound form there.
* Otherwise (NULL is passed) the current instruction pointer is used as
* start address. The stacktrace is written to `stacktrace_out` with upt o
* start address. The stack trace is written to `stacktrace_out` with up to
* `max_len` frames being written. The actual number of unwound stackframes
* is returned.
*/
Expand All @@ -408,7 +479,7 @@ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack(
/**
* Unwinds the stack from the given context.
*
* The stacktrace is written to `stacktrace_out` with upt o `max_len` frames
* The stack trace is written to `stacktrace_out` with up to `max_len` frames
* being written. The actual number of unwound stackframes is returned.
*/
SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack_from_ucontext(
Expand Down
29 changes: 6 additions & 23 deletions src/backends/sentry_backend_inproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,9 @@ make_signal_event(
sentry_value_set_by_key(
event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL));

sentry_value_t exc = sentry_value_new_object();
sentry_value_set_by_key(exc, "type",
sentry_value_new_string(
sig_slot ? sig_slot->signame : "UNKNOWN_SIGNAL"));
sentry_value_set_by_key(exc, "value",
sentry_value_new_string(
sig_slot ? sig_slot->sigdesc : "UnknownSignal"));
sentry_value_t exc = sentry_value_new_exception(
sig_slot ? sig_slot->signame : "UNKNOWN_SIGNAL",
sig_slot ? sig_slot->sigdesc : "UnknownSignal");

sentry_value_t mechanism = sentry_value_new_object();
sentry_value_set_by_key(exc, "mechanism", mechanism);
Expand Down Expand Up @@ -217,25 +213,12 @@ make_signal_event(
}
SENTRY_TRACEF("captured backtrace with %lu frames", frame_count);

sentry_value_t frames = sentry__value_new_list_with_size(frame_count);
for (size_t i = 0; i < frame_count; i++) {
sentry_value_t frame = sentry_value_new_object();
sentry_value_set_by_key(frame, "instruction_addr",
sentry__value_new_addr(
(uint64_t)(size_t)backtrace[frame_count - i - 1]));
sentry_value_append(frames, frame);
}

sentry_value_t stacktrace = sentry_value_new_object();
sentry_value_set_by_key(stacktrace, "frames", frames);
sentry_value_t stacktrace
= sentry_value_new_stacktrace(&backtrace[0], frame_count);

sentry_value_set_by_key(exc, "stacktrace", stacktrace);

sentry_value_t exceptions = sentry_value_new_object();
sentry_value_t values = sentry_value_new_list();
sentry_value_set_by_key(exceptions, "values", values);
sentry_value_append(values, exc);
sentry_value_set_by_key(event, "exception", exceptions);
sentry_event_add_exception(event, exc);

return event;
}
Expand Down
92 changes: 83 additions & 9 deletions src/sentry_value.c
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,8 @@ sentry_value_new_event(void)
sentry__value_new_string_owned(
sentry__msec_time_to_iso8601(sentry__msec_time())));

sentry_value_set_by_key(rv, "platform", sentry_value_new_string("native"));

return rv;
}

Expand Down Expand Up @@ -1036,8 +1038,38 @@ sentry_value_new_breadcrumb(const char *type, const char *message)
return rv;
}

void
sentry_event_value_add_stacktrace(sentry_value_t event, void **ips, size_t len)
sentry_value_t
sentry_value_new_exception(const char *type, const char *value)
{
sentry_value_t exc = sentry_value_new_object();
sentry_value_set_by_key(exc, "type", sentry_value_new_string(type));
Swatinem marked this conversation as resolved.
Show resolved Hide resolved
sentry_value_set_by_key(exc, "value", sentry_value_new_string(value));
return exc;
}

sentry_value_t
sentry_value_new_thread(uint64_t id, const char *name)
{
sentry_value_t thread = sentry_value_new_object();

// NOTE: values end up as JSON, which has no support for `u64`.
char buf[20 + 1];
size_t written
= (size_t)snprintf(buf, sizeof(buf), "%llu", (unsigned long long)id);
if (written < sizeof(buf)) {
buf[written] = '\0';
Swatinem marked this conversation as resolved.
Show resolved Hide resolved
sentry_value_set_by_key(thread, "id", sentry_value_new_string(buf));
}

if (name) {
sentry_value_set_by_key(thread, "name", sentry_value_new_string(name));
}

return thread;
}

sentry_value_t
sentry_value_new_stacktrace(void **ips, size_t len)
{
void *walked_backtrace[256];

Expand All @@ -1058,14 +1090,56 @@ sentry_event_value_add_stacktrace(sentry_value_t event, void **ips, size_t len)
sentry_value_t stacktrace = sentry_value_new_object();
sentry_value_set_by_key(stacktrace, "frames", frames);

sentry_value_t thread = sentry_value_new_object();
sentry_value_set_by_key(thread, "stacktrace", stacktrace);
return stacktrace;
}

static sentry_value_t
sentry__get_or_insert_values_list(sentry_value_t parent, const char *key)
{
sentry_value_t obj = sentry_value_get_by_key(parent, key);
if (sentry_value_is_null(obj)) {
obj = sentry_value_new_object();
sentry_value_set_by_key(parent, key, obj);
}

sentry_value_type_t type = sentry_value_get_type(obj);
sentry_value_t values = sentry_value_new_null();
if (type == SENTRY_VALUE_TYPE_OBJECT) {
values = sentry_value_get_by_key(obj, "values");
if (sentry_value_is_null(values)) {
values = sentry_value_new_list();
sentry_value_set_by_key(obj, "values", values);
}
} else if (type == SENTRY_VALUE_TYPE_LIST) {
values = obj;
}

sentry_value_t values = sentry_value_new_list();
sentry_value_append(values, thread);
return values;
}

void
sentry_event_add_exception(sentry_value_t event, sentry_value_t exception)
{
sentry_value_t exceptions
= sentry__get_or_insert_values_list(event, "exception");
sentry_value_append(exceptions, exception);
}

sentry_value_t threads = sentry_value_new_object();
sentry_value_set_by_key(threads, "values", values);
void
sentry_event_add_thread(sentry_value_t event, sentry_value_t thread)
{
sentry_value_t threads
= sentry__get_or_insert_values_list(event, "threads");
sentry_value_append(threads, thread);
}

void
sentry_event_value_add_stacktrace(sentry_value_t event, void **ips, size_t len)
{
sentry_value_t stacktrace = sentry_value_new_stacktrace(ips, len);

sentry_value_t thread = sentry_value_new_object();
sentry_value_set_by_key(thread, "stacktrace", stacktrace);

sentry_value_set_by_key(event, "threads", threads);
sentry_event_add_thread(event, thread);
}
3 changes: 1 addition & 2 deletions tests/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,7 @@ def assert_exception(envelope):
"type": "ParseIntError",
"value": "invalid digit found in string",
}
expected = {"exception": {"values": [exception]}}
assert matches(event, expected)
assert matches(event["exception"]["values"][0], exception)
assert_timestamp(event["timestamp"])


Expand Down
3 changes: 2 additions & 1 deletion tests/test_integration_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def test_exception_and_session_http(cmake, httpserver):
run(
tmp_path,
"sentry_example",
["log", "start-session", "capture-exception"],
["log", "start-session", "capture-exception", "add-stacktrace"],
check=True,
env=env,
)
Expand All @@ -129,6 +129,7 @@ def test_exception_and_session_http(cmake, httpserver):
envelope = Envelope.deserialize(output)

assert_exception(envelope)
assert_stacktrace(envelope, inside_exception=True)
assert_session(envelope, {"init": True, "status": "ok", "errors": 1})

output = httpserver.log[1][0].get_data()
Expand Down