39 #include <crow/crow.hpp> 40 #include <src/crow_config.hpp> 41 #include <src/crow_utilities.hpp> 42 #include <thirdparty/curl_wrapper/curl_wrapper.hpp> 43 #include <thirdparty/json/json.hpp> 45 using json = nlohmann::json;
49 crow* crow::m_client_that_installed_termination_handler =
nullptr;
53 const bool install_handlers)
54 : m_enabled(not dsn.empty())
59 const std::regex dsn_regex(
"(http[s]?)://([^:]+):([^@]+)@([^/]+)/([0-9]+)");
60 std::smatch pieces_match;
62 if (std::regex_match(dsn, pieces_match, dsn_regex) and pieces_match.size() == 6)
64 const auto scheme = pieces_match.str(1);
65 m_public_key = pieces_match.str(2);
66 m_secret_key = pieces_match.str(3);
67 const auto host = pieces_match.str(4);
68 const auto project_id = pieces_match.str(5);
69 m_store_url = scheme +
"://" + host +
"/api/" + project_id +
"/store/";
73 throw std::invalid_argument(
"DNS " + dsn +
" is invalid");
90 if (existing_termination_handler ==
nullptr)
92 existing_termination_handler = std::set_terminate([]() {});
93 std::set_terminate(&new_termination_handler);
97 m_client_that_installed_termination_handler =
this;
102 : m_enabled(other.m_enabled)
103 , m_public_key(other.m_public_key)
104 , m_secret_key(other.m_secret_key)
105 , m_store_url(other.m_store_url)
106 , m_payload(other.m_payload)
112 if (m_pending_future.valid())
114 m_pending_future.wait();
119 const json& attributes,
120 const bool asynchronous)
122 std::lock_guard<std::mutex> lock(m_payload_mutex);
123 m_payload[
"message"] = message;
127 if (attributes.is_object())
130 auto logger = attributes.find(
"logger");
131 if (logger != attributes.end())
133 m_payload[
"logger"] = *logger;
137 m_payload[
"level"] = attributes.value(
"level",
"error");
140 auto context = attributes.find(
"context");
141 if (context != attributes.end())
147 auto extra = attributes.find(
"extra");
148 if (extra != attributes.end())
150 m_payload[
"extra"] = *extra;
154 enqueue_post(asynchronous);
160 const bool asynchronous,
163 std::stringstream thread_id;
164 thread_id << std::this_thread::get_id();
165 std::lock_guard<std::mutex> lock(m_payload_mutex);
167 {
"value", exception.what()},
169 {
"mechanism", {{
"handled", handled}, {
"description", handled ?
"handled exception" :
"unhandled exception"}}},
171 {
"thread_id", thread_id.str()}});
178 enqueue_post(asynchronous);
182 const json& attributes)
187 {
"message", message},
194 if (attributes.is_object())
197 auto type = attributes.find(
"type");
198 if (type != attributes.end())
200 breadcrumb[
"type"] = *type;
204 auto level = attributes.find(
"level");
205 if (level != attributes.end())
207 breadcrumb[
"level"] = *level;
211 auto category = attributes.find(
"category");
212 if (category != attributes.end())
214 breadcrumb[
"category"] = *category;
218 auto data = attributes.find(
"data");
219 if (data != attributes.end())
221 breadcrumb[
"data"] = *data;
225 std::lock_guard<std::mutex> lock(m_payload_mutex);
226 m_payload[
"breadcrumbs"][
"values"].push_back(std::move(breadcrumb));
231 std::lock_guard<std::mutex> lock(m_pending_future_mutex);
232 if (m_pending_future.valid())
236 return json::parse(m_pending_future.get()).at(
"id");
238 catch (
const json::exception&)
256 std::lock_guard<std::mutex> lock(m_payload_mutex);
257 m_payload[
"user"].update(data);
262 std::lock_guard<std::mutex> lock(m_payload_mutex);
263 m_payload[
"tags"].update(data);
268 std::lock_guard<std::mutex> lock(m_payload_mutex);
269 m_payload[
"request"].update(data);
274 std::lock_guard<std::mutex> lock(m_payload_mutex);
275 m_payload[
"extra"].update(data);
280 if (context.is_object())
282 std::lock_guard<std::mutex> lock(m_payload_mutex);
283 for (
const auto& el : context.items())
285 if (el.key() ==
"user" or el.key() ==
"request" or el.key() ==
"extra" or el.key() ==
"tags")
287 m_payload[el.key()].update(el.value());
291 throw std::runtime_error(
"invalid context");
299 std::lock_guard<std::mutex> lock(m_payload_mutex);
301 m_payload[
"platform"] =
"c";
302 m_payload[
"sdk"][
"name"] =
"crow";
303 m_payload[
"sdk"][
"version"] = NLOHMANN_CROW_VERSION;
306 m_payload[
"contexts"][
"app"][
"build_type"] = NLOHMANN_CROW_CMAKE_BUILD_TYPE;
307 m_payload[
"contexts"][
"app"][
"pointer_size"] = NLOHMANN_CROW_BITS;
310 m_payload[
"contexts"][
"device"][
"arch"] = NLOHMANN_CROW_CMAKE_SYSTEM_PROCESSOR;
311 m_payload[
"contexts"][
"device"][
"name"] = NLOHMANN_CROW_HOSTNAME;
312 m_payload[
"contexts"][
"device"][
"model"] = NLOHMANN_CROW_SYSCTL_HW_MODEL;
313 m_payload[
"contexts"][
"device"][
"memory_size"] = NLOHMANN_CROW_TOTAL_PHYSICAL_MEMORY;
316 m_payload[
"contexts"][
"os"][
"name"] = NLOHMANN_CROW_CMAKE_SYSTEM_NAME;
317 m_payload[
"contexts"][
"os"][
"version"] = NLOHMANN_CROW_OS_RELEASE;
318 if (not std::string(NLOHMANN_CROW_OS_VERSION).empty())
320 m_payload[
"contexts"][
"os"][
"build"] = NLOHMANN_CROW_OS_VERSION;
324 m_payload[
"contexts"][
"os"][
"build"] = NLOHMANN_CROW_CMAKE_SYSTEM_VERSION;
326 if (not std::string(NLOHMANN_CROW_UNAME).empty())
328 m_payload[
"contexts"][
"os"][
"kernel_version"] = NLOHMANN_CROW_UNAME;
330 else if (not std::string(NLOHMANN_CROW_SYSTEMINFO).empty())
332 m_payload[
"contexts"][
"os"][
"kernel_version"] = NLOHMANN_CROW_SYSTEMINFO;
336 m_payload[
"contexts"][
"runtime"][
"name"] = NLOHMANN_CROW_CMAKE_CXX_COMPILER_ID;
337 m_payload[
"contexts"][
"runtime"][
"version"] = NLOHMANN_CROW_CMAKE_CXX_COMPILER_VERSION;
338 m_payload[
"contexts"][
"runtime"][
"detail"] = NLOHMANN_CROW_CXX;
341 const char* user = getenv(
"USER");
344 user = getenv(
"USERNAME");
348 m_payload[
"user"][
"id"] = std::string(user) +
"@" + NLOHMANN_CROW_HOSTNAME;
349 m_payload[
"user"][
"username"] = user;
353 std::string crow::post(json payload)
const 360 std::string security_header =
"X-Sentry-Auth: Sentry sentry_version=5,sentry_client=crow/";
361 security_header += std::string(NLOHMANN_CROW_VERSION) +
",sentry_timestamp=";
363 security_header +=
",sentry_key=" + m_public_key;
364 security_header +=
",sentry_secret=" + m_secret_key;
365 curl.set_header(security_header.c_str());
367 return curl.post(m_store_url, payload);
373 void crow::new_termination_handler()
375 assert(m_client_that_installed_termination_handler !=
nullptr);
377 auto current_ex = std::current_exception();
380 m_client_that_installed_termination_handler->
add_breadcrumb(
"uncaught exception", {{
"type",
"exceptiomn"}, {
"level",
"critical"}});
383 std::rethrow_exception(current_ex);
385 catch (
const std::exception& e)
387 m_client_that_installed_termination_handler->
capture_exception(e,
nullptr,
false,
false);
391 m_client_that_installed_termination_handler->existing_termination_handler();
394 void crow::enqueue_post(
const bool asynchronous)
396 std::lock_guard<std::mutex> lock(m_pending_future_mutex);
399 if (m_pending_future.valid())
401 m_pending_future.wait();
405 m_pending_future = std::async(std::launch::async, [
this] {
return post(m_payload); });
406 if (not asynchronous)
408 m_pending_future.wait();
std::string get_iso8601()
get the current date and time as ISO 8601 string
json get_backtrace(int skip)
void add_user_context(const json &data)
add elements to the "user" context for future events
void add_request_context(const json &data)
add elements to the "request" context for future events
const json & get_context() const
return current context
void merge_context(const json &context)
add context information to payload for future events
void install_handler()
install termination handler to handle uncaught exceptions
crow(const std::string &dsn, const json &context=nullptr, bool install_handlers=true)
create a client
void add_extra_context(const json &data)
add elements to the "extra" context for future events
void capture_exception(const std::exception &exception, const json &context=nullptr, bool asynchronous=true, bool handled=true)
capture an exception
std::string get_last_event_id() const
return the id of the last reported event
namespace for Niels Lohmann
void clear_context()
reset context for future events
std::int64_t get_timestamp()
get the seconds since epoch
void add_breadcrumb(const std::string &message, const json &attributes=nullptr)
add a breadcrumb to the current context
std::string pretty_name(const char *type_id_name, const bool only_module)
return pretty type name
void capture_message(const std::string &message, const json &attributes=nullptr, bool asynchronous=true)
capture a message
void add_tags_context(const json &data)
add elements to the "tags" context for future events
std::string generate_uuid()
generate a UUID4 without dashes