25#include <simgear/debug/LogCallback.hxx>
26#include <simgear/debug/logstream.hxx>
27#include <simgear/debug/ErrorReportingCallback.hxx>
28#include <simgear/debug/Reporting.hxx>
30#include <simgear/misc/sg_path.hxx>
31#include <simgear/props/props.hxx>
32#include <simgear/structure/commands.hxx>
33#include <simgear/structure/exception.hxx>
34#include <simgear/io/iostreams/sgstream.hxx>
40#include <flightgearBuildId.h>
48 for (
auto c : prefixes) {
57 "PNG lib warning : iCCP: known incorrect sRGB profile",
58 "PNG lib warning : iCCP: profile 'ICC Profile': 1000000h: invalid rendering intent",
59 "osgDB ac3d reader: detected surface with less than 3",
60 "osgDB ac3d reader: detected line with less than 2",
61 "Detected particle system using segment(s) with less than 2 vertices"
65 "position is invalid, NaNs",
67 "couldn't find shader",
72 "metar data incomplete",
73 "metar temperature data",
74 "metar pressure data"};
77#if defined(HAVE_SENTRY) && !defined(BUILDING_TESTSUITE)
79static bool static_sentryEnabled =
false;
80static std::string static_sentryUUID;
88void sentryTraceSimgearThrow(
const std::string& msg,
const std::string& origin,
const sg_location& loc)
90 if (!static_sentryEnabled)
97 sentry_value_t exc = sentry_value_new_object();
98 sentry_value_set_by_key(exc,
"type", sentry_value_new_string(
"Exception"));
100 std::string message = msg;
101 sentry_value_t info = sentry_value_new_object();
102 if (!origin.empty()) {
103 sentry_value_set_by_key(info,
"origin", sentry_value_new_string(origin.c_str()));
107 const auto ls = loc.asString();
108 sentry_value_set_by_key(info,
"location", sentry_value_new_string(ls.c_str()));
111 sentry_set_context(
"what", info);
112 sentry_value_set_by_key(exc,
"value", sentry_value_new_string(message.c_str()));
114 sentry_value_t
event = sentry_value_new_event();
115 sentry_value_set_by_key(event,
"exception", exc);
117 sentry_event_value_add_stacktrace(event,
nullptr, 0);
118 sentry_capture_event(event);
121class SentryLogCallback :
public simgear::LogCallback
124 SentryLogCallback() : simgear::LogCallback(SG_ALL, SG_WARN)
128 bool doProcessEntry(
const simgear::LogEntry& e)
override
132 const auto op = e.originalPriority;
133 if ((op != SG_WARN) && (op != SG_ALERT)) {
141 if (e.message == _lastLoggedMessage) {
146 if (_lastLoggedCount > 0) {
148 _lastLoggedCount = 0;
151 _lastLoggedMessage = e.message;
157 std::string _lastLoggedMessage;
158 int _lastLoggedCount = 0;
161const auto missingShaderPrefix = std::string{
"Missing shader"};
165bool isNewMissingShader(
const std::string& path)
167 auto it = std::find(anon_missingShaderList.begin(), anon_missingShaderList.end(), path);
168 if (it != anon_missingShaderList.end()) {
172 anon_missingShaderList.push_back(path);
176void sentrySimgearReportCallback(
const std::string& msg,
const std::string& more,
bool isFatal)
180 using simgear::strutils::starts_with;
181 if (starts_with(msg, missingShaderPrefix)) {
182 if (!isNewMissingShader(more)) {
187 sentry_value_t exc = sentry_value_new_object();
189 sentry_value_set_by_key(exc,
"type", sentry_value_new_string(
"Fatal Error"));
191 sentry_value_set_by_key(exc,
"type", sentry_value_new_string(
"Exception"));
194 sentry_value_set_by_key(exc,
"value", sentry_value_new_string(msg.c_str()));
196 sentry_value_t
event = sentry_value_new_event();
197 sentry_value_set_by_key(event,
"exception", exc);
199 sentry_event_value_add_stacktrace(event,
nullptr, 0);
200 sentry_capture_event(event);
203void sentryReportBadAlloc()
205 if (simgear::ReportBadAllocGuard::isSet()) {
206 sentry_value_t sentryMessage = sentry_value_new_object();
207 sentry_value_set_by_key(sentryMessage,
"type", sentry_value_new_string(
"Fatal Error"));
208 sentry_value_set_by_key(sentryMessage,
"formatted", sentry_value_new_string(
"bad allocation"));
210 sentry_value_t
event = sentry_value_new_event();
211 sentry_value_set_by_key(event,
"message", sentryMessage);
213 sentry_event_value_add_stacktrace(event,
nullptr, 0);
214 sentry_capture_event(event);
217 throw std::bad_alloc();
225bool sentryReportCommand(
const SGPropertyNode* args, SGPropertyNode* root)
227 if (!static_sentryEnabled) {
228 SG_LOG(SG_GENERAL, SG_WARN,
"Sentry.io not enabled at startup");
232 sentry_value_t exc = sentry_value_new_object();
233 sentry_value_set_by_key(exc,
"type", sentry_value_new_string(
"Report"));
235 const auto message = args->getStringValue(
"message");
236 sentry_value_set_by_key(exc,
"value", sentry_value_new_string(message.c_str()));
238 sentry_value_t
event = sentry_value_new_event();
239 sentry_value_set_by_key(event,
"exception", exc);
241 sentry_event_value_add_stacktrace(event,
nullptr, 0);
243 sentry_capture_event(event);
248bool sentrySendError(
const SGPropertyNode* args, SGPropertyNode* root)
250 if (!static_sentryEnabled) {
251 SG_LOG(SG_GENERAL, SG_WARN,
"Sentry.io not enabled at startup");
256 throw sg_io_exception(
"Invalid flurlbe", sg_location(
"/Some/dummy/path/bar.txt", 100, 200));
257 }
catch (sg_exception& e) {
258 SG_LOG(SG_GENERAL, SG_WARN,
"caught dummy exception");
266 if (!static_sentryUUID.empty()) {
267 return static_sentryUUID;
270 const auto uuidPath =
fgHomePath() /
"sentry_uuid.txt";
271 if (!uuidPath.exists()) {
275 sg_ifstream f(uuidPath);
276 std::getline(f, static_sentryUUID);
277 return static_sentryUUID;
282 sentry_options_t *
options = sentry_options_new();
286 sentry_options_set_dsn(
options, SENTRY_API_KEY);
288 if (strcmp(FG_BUILD_TYPE,
"Dev") == 0) {
289 sentry_options_set_release(
options,
"flightgear-dev@" REVISION);
290 }
else if (strcmp(FG_BUILD_TYPE,
"Nightly") == 0) {
291 sentry_options_set_release(
options,
"flightgear-nightly@" BUILD_DATE);
293 sentry_options_set_release(
options,
"flightgear@" FLIGHTGEAR_VERSION);
296 sentry_options_set_dist(
options, REVISION);
299 if (strcmp(FG_BUILD_TYPE,
"Release")) {
300 sentry_options_set_debug(
options, 1);
304#if defined(SG_WINDOWS)
305 const auto homePathString = dataPath.wstr();
306 sentry_options_set_database_pathw(
options, homePathString.c_str());
308 const auto logPath = (
fgHomePath() /
"fgfs.log").wstr();
309 sentry_options_add_attachmentw(
options, logPath.c_str());
311 const auto homePathString = dataPath.utf8Str();
312 sentry_options_set_database_path(
options, homePathString.c_str());
314 const auto logPath = (
fgHomePath() /
"fgfs.log").utf8Str();
315 sentry_options_add_attachment(
options, logPath.c_str());
318 const auto uuidPath =
fgHomePath() /
"sentry_uuid.txt";
319 bool generateUuid =
true;
321 if (uuidPath.exists()) {
325 if ( static_sentryUUID.length() >= 36) {
326 generateUuid =
false;
333 sentry_uuid_t su = sentry_uuid_new_v4();
335 sentry_uuid_as_string(&su, bytes);
338 static_sentryUUID = std::string{bytes};
340 sg_ofstream f(uuidPath);
341 f << static_sentryUUID << std::endl;
344 if (sentry_init(
options) == 0) {
345 static_sentryEnabled =
true;
346 sentry_value_t user = sentry_value_new_object();
347 sentry_value_t userUuidV = sentry_value_new_string(static_sentryUUID.c_str());
348 sentry_value_set_by_key(user,
"id", userUuidV);
349 sentry_set_user(user);
351 sglog().addCallback(
new SentryLogCallback);
352 setThrowCallback(sentryTraceSimgearThrow);
353 simgear::setErrorReportCallback(sentrySimgearReportCallback);
355 std::set_new_handler(sentryReportBadAlloc);
357 SG_LOG(SG_GENERAL, SG_WARN,
"Failed to init Sentry reporting");
358 static_sentryEnabled =
false;
364 if (!static_sentryEnabled)
368 if (!
fgGetBool(
"/sim/startup/sentry-crash-reporting-enabled",
true)) {
369 SG_LOG(SG_GENERAL, SG_INFO,
"Disabling Sentry.io reporting");
371 static_sentryEnabled =
false;
380 fgSetString(
"/sim/crashreport/sentry-user-id", static_sentryUUID);
385 if (static_sentryEnabled) {
387 static_sentryEnabled =
false;
393 return static_sentryEnabled;
398 if (!static_sentryEnabled)
401 sentry_value_t crumb = sentry_value_new_breadcrumb(
"default", msg.c_str());
402 sentry_value_set_by_key(crumb,
"level", sentry_value_new_string(level.c_str()));
403 sentry_add_breadcrumb(crumb);
408 if (!static_sentryEnabled)
414 sentry_set_tag(tag, value);
419 if (tag.empty() || value.empty())
422 if (!static_sentryEnabled)
425 sentry_remove_tag(tag.c_str());
426 sentry_set_tag(tag.c_str(), value.c_str());
431 if (!static_sentryEnabled)
434 sentry_value_t exc = sentry_value_new_object();
435 sentry_value_set_by_key(exc,
"type", sentry_value_new_string(
"Exception"));
436 sentry_value_set_by_key(exc,
"value", sentry_value_new_string(msg.c_str()));
438 sentry_value_t stackData = sentry_value_new_list();
439 for (
const auto& nasalFrame : stack) {
440 sentry_value_append(stackData, sentry_value_new_string(nasalFrame.c_str()));
442 sentry_value_set_by_key(exc,
"stack", stackData);
445 sentry_value_t
event = sentry_value_new_event();
446 sentry_value_set_by_key(event,
"exception", exc);
451 sentry_event_value_add_stacktrace(event,
nullptr, 0);
453 sentry_capture_event(event);
460 if (!static_sentryEnabled)
463 sentry_value_t exc = sentry_value_new_object();
464 sentry_value_set_by_key(exc,
"type", sentry_value_new_string(
"Exception"));
467 sentry_value_t info = sentry_value_new_object();
468 if (!location.empty()) {
469 sentry_value_set_by_key(info,
"location", sentry_value_new_string(location.c_str()));
471 sentry_set_context(
"what", info);
473 sentry_value_set_by_key(exc,
"value", sentry_value_new_string(msg.c_str()));
475 sentry_value_t
event = sentry_value_new_event();
476 sentry_value_set_by_key(event,
"exception", exc);
479 sentry_event_value_add_stacktrace(event,
nullptr, 0);
480 sentry_capture_event(event);
485 if (!static_sentryEnabled)
488 sentry_value_t sentryMessage = sentry_value_new_object();
489 sentry_value_set_by_key(sentryMessage,
"type", sentry_value_new_string(
"Fatal Error"));
491 sentry_value_t info = sentry_value_new_object();
493 sentry_value_set_by_key(info,
"more", sentry_value_new_string(more.c_str()));
496 sentry_set_context(
"what", info);
497 sentry_value_set_by_key(sentryMessage,
"formatted", sentry_value_new_string(msg.c_str()));
499 sentry_value_t
event = sentry_value_new_event();
500 sentry_value_set_by_key(event,
"message", sentryMessage);
502 sentry_event_value_add_stacktrace(event,
nullptr, 0);
503 sentry_capture_event(event);
506void sentryReportUserError(
const std::string& aggregate,
const std::string& parameter,
const std::string& details)
508 if (!static_sentryEnabled)
511 sentry_value_t sentryMessage = sentry_value_new_object();
512 sentry_value_set_by_key(sentryMessage,
"type", sentry_value_new_string(
"Error"));
514 sentry_value_t info = sentry_value_new_object();
515 sentry_value_set_by_key(info,
"details", sentry_value_new_string(details.c_str()));
517 sentry_set_context(
"what", info);
520 if (!parameter.empty()) {
521 m +=
":" + parameter;
524 sentry_value_t
event = sentry_value_new_event();
525 sentry_value_set_by_key(event,
"message", sentry_value_new_string(m.c_str()));
527 sentry_capture_event(event);
601 if (tag.empty() || value.empty())
bool options(int, char **)
SGCommandMgr * get_commands()
std::vector< std::string > string_list
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
void sentryReportUserError(const std::string &, const std::string &, const std::string &)
void updateSentryTag(const std::string &, const std::string &)
void addSentryBreadcrumb(const std::string &, const std::string &)
std::string sentryUserId()
retrive the anonymous user ID (a UUID) for this installation.
void addSentryTag(const char *, const char *)
void sentryReportException(const std::string &, const std::string &)
void sentryReportNasalError(const std::string &, const string_list &)
void sentryReportFatalError(const std::string &, const std::string &)
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
auto OSG_messageWhitelist
bool doesStringMatchPrefixes(const std::string &s, const std::initializer_list< const char * > &prefixes)
auto exception_messageWhitelist