#include "sentry_session.h" #include "sentry_alloc.h" #include "sentry_database.h" #include "sentry_envelope.h" #include "sentry_json.h" #include "sentry_options.h" #include "sentry_scope.h" #include "sentry_string.h" #include "sentry_utils.h" #include "sentry_value.h" #include #include static const char * status_as_string(sentry_session_status_t status) { switch (status) { case SENTRY_SESSION_STATUS_OK: return "ok"; case SENTRY_SESSION_STATUS_CRASHED: return "crashed"; case SENTRY_SESSION_STATUS_ABNORMAL: return "abnormal"; case SENTRY_SESSION_STATUS_EXITED: return "exited"; default: assert(!"invalid session status"); return "invalid"; } } static sentry_session_status_t status_from_string(const char *status) { if (sentry__string_eq(status, "ok")) { return SENTRY_SESSION_STATUS_OK; } else if (sentry__string_eq(status, "exited")) { return SENTRY_SESSION_STATUS_EXITED; } else if (sentry__string_eq(status, "crashed")) { return SENTRY_SESSION_STATUS_CRASHED; } else if (sentry__string_eq(status, "abnormal")) { return SENTRY_SESSION_STATUS_ABNORMAL; } else { return SENTRY_SESSION_STATUS_OK; } } sentry_session_t * sentry__session_new(void) { char *release = NULL; char *environment = NULL; SENTRY_WITH_OPTIONS (options) { release = sentry__string_clone(sentry_options_get_release(options)); environment = sentry__string_clone(sentry_options_get_environment(options)); } if (!release) { sentry_free(environment); return NULL; } sentry_session_t *rv = SENTRY_MAKE(sentry_session_t); if (!rv) { sentry_free(release); sentry_free(environment); return NULL; } rv->release = release; rv->environment = environment; rv->session_id = sentry_uuid_new_v4(); rv->distinct_id = sentry_value_new_null(); rv->status = SENTRY_SESSION_STATUS_OK; rv->init = true; rv->errors = 0; rv->started_ms = sentry__msec_time(); rv->duration_ms = (uint64_t)-1; return rv; } void sentry__session_free(sentry_session_t *session) { if (!session) { return; } sentry_value_decref(session->distinct_id); sentry_free(session->release); sentry_free(session->environment); sentry_free(session); } void sentry__session_to_json( const sentry_session_t *session, sentry_jsonwriter_t *jw) { sentry__jsonwriter_write_object_start(jw); if (session->init) { sentry__jsonwriter_write_key(jw, "init"); sentry__jsonwriter_write_bool(jw, true); } sentry__jsonwriter_write_key(jw, "sid"); sentry__jsonwriter_write_uuid(jw, &session->session_id); sentry__jsonwriter_write_key(jw, "status"); sentry__jsonwriter_write_str(jw, status_as_string(session->status)); if (!sentry_value_is_null(session->distinct_id)) { char *did = sentry__value_stringify(session->distinct_id); if (did) { sentry__jsonwriter_write_key(jw, "did"); sentry__jsonwriter_write_str(jw, did); sentry_free(did); } } sentry__jsonwriter_write_key(jw, "errors"); sentry__jsonwriter_write_int32(jw, (int32_t)session->errors); sentry__jsonwriter_write_key(jw, "started"); sentry__jsonwriter_write_msec_timestamp(jw, session->started_ms); // if there is a duration stored on the struct (that happens after // reading back from disk) we use that, otherwise we calculate the // difference to the start time. sentry__jsonwriter_write_key(jw, "duration"); double duration; if (session->duration_ms != (uint64_t)-1) { duration = (double)session->duration_ms / 1000.0; } else { duration = (double)(sentry__msec_time() - session->started_ms) / 1000.0; } sentry__jsonwriter_write_double(jw, duration); sentry__jsonwriter_write_key(jw, "attrs"); sentry__jsonwriter_write_object_start(jw); sentry__jsonwriter_write_key(jw, "release"); sentry__jsonwriter_write_str(jw, session->release); sentry__jsonwriter_write_key(jw, "environment"); sentry__jsonwriter_write_str(jw, session->environment); sentry__jsonwriter_write_object_end(jw); sentry__jsonwriter_write_object_end(jw); } sentry_session_t * sentry__session_from_json(const char *buf, size_t buflen) { sentry_value_t value = sentry__value_from_json(buf, buflen); if (sentry_value_is_null(value)) { return NULL; } sentry_value_t attrs = sentry_value_get_by_key(value, "attrs"); if (sentry_value_is_null(attrs)) { return NULL; } char *release = sentry__string_clone( sentry_value_as_string(sentry_value_get_by_key(attrs, "release"))); if (!release) { return NULL; } sentry_session_t *rv = SENTRY_MAKE(sentry_session_t); if (!rv) { sentry_free(release); return NULL; } rv->session_id = sentry__value_as_uuid(sentry_value_get_by_key(value, "sid")); rv->distinct_id = sentry_value_get_by_key_owned(value, "did"); rv->release = release; rv->environment = sentry__string_clone( sentry_value_as_string(sentry_value_get_by_key(attrs, "environment"))); const char *status = sentry_value_as_string(sentry_value_get_by_key(value, "status")); rv->status = status_from_string(status); rv->init = sentry_value_is_true(sentry_value_get_by_key(value, "init")); rv->errors = (int64_t)sentry_value_as_int32( sentry_value_get_by_key(value, "errors")); rv->started_ms = sentry__iso8601_to_msec( sentry_value_as_string(sentry_value_get_by_key(value, "started"))); double duration = sentry_value_as_double(sentry_value_get_by_key(value, "duration")); rv->duration_ms = (uint64_t)(duration * 1000); sentry_value_decref(value); return rv; } sentry_session_t * sentry__session_from_path(const sentry_path_t *path) { size_t buf_len; char *buf = sentry__path_read_to_buffer(path, &buf_len); if (!buf) { return NULL; } sentry_session_t *rv = sentry__session_from_json(buf, buf_len); sentry_free(buf); return rv; } void sentry_start_session(void) { sentry_end_session(); SENTRY_WITH_SCOPE (scope) { sentry_options_t *options = sentry__options_lock(); if (options) { options->session = sentry__session_new(); if (options->session) { sentry__session_sync_user(options->session, scope->user); sentry__run_write_session(options->run, options->session); } } sentry__options_unlock(); } } void sentry__record_errors_on_current_session(uint32_t error_count) { sentry_options_t *options = sentry__options_lock(); if (options && options->session) { options->session->errors += error_count; } sentry__options_unlock(); } static sentry_session_t * sentry__end_session_internal(void) { sentry_session_t *session = NULL; sentry_options_t *options = sentry__options_lock(); if (options) { session = options->session; options->session = NULL; sentry__run_clear_session(options->run); } sentry__options_unlock(); if (session && session->status == SENTRY_SESSION_STATUS_OK) { session->status = SENTRY_SESSION_STATUS_EXITED; } return session; } sentry_session_t * sentry__end_current_session_with_status(sentry_session_status_t status) { sentry_session_t *session = sentry__end_session_internal(); if (session) { session->status = status; } return session; } static void sentry__capture_session(sentry_session_t *session) { sentry_envelope_t *envelope = sentry__envelope_new(); sentry__envelope_add_session(envelope, session); SENTRY_WITH_OPTIONS (options) { sentry__capture_envelope(options->transport, envelope); } } void sentry_end_session(void) { sentry_session_t *session = sentry__end_session_internal(); if (!session) { return; } sentry__capture_session(session); sentry__session_free(session); } void sentry_end_session_with_status(sentry_session_status_t status) { sentry_session_t *session = sentry__end_current_session_with_status(status); if (!session) { return; } sentry__capture_session(session); sentry__session_free(session); } void sentry__session_sync_user(sentry_session_t *session, sentry_value_t user) { sentry_value_t did = sentry_value_get_by_key(user, "id"); if (sentry_value_is_null(did)) { did = sentry_value_get_by_key(user, "email"); } if (sentry_value_is_null(did)) { did = sentry_value_get_by_key(user, "username"); } sentry_value_decref(session->distinct_id); sentry_value_incref(did); session->distinct_id = did; }