void processAndLogNewSpawnException(SpawnException &e, const Options &options, ResourceLocator &resourceLocator, RandomGenerator &randomGenerator) { ErrorRenderer renderer(resourceLocator); string errorId = randomGenerator.generateHexString(4); string appMessage = e.getErrorPage(); char filename[PATH_MAX]; stringstream stream; e.set("ERROR_ID", errorId); if (appMessage.empty()) { appMessage = "none"; } try { int fd = -1; FdGuard guard(fd, true); string errorPage; errorPage = renderer.renderWithDetails(appMessage, options, &e); #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) snprintf(filename, PATH_MAX, "%s/passenger-error-XXXXXX.html", getSystemTempDir()); fd = mkstemps(filename, sizeof(".html") - 1); #else snprintf(filename, PATH_MAX, "%s/passenger-error.XXXXXX", getSystemTempDir()); fd = mkstemp(filename); #endif if (fd == -1) { int e = errno; throw SystemException("Cannot generate a temporary filename", e); } writeExact(fd, errorPage); } catch (const SystemException &e2) { filename[0] = '\0'; P_ERROR("Cannot render an error page: " << e2.what() << "\n" << e2.backtrace()); } stream << "Could not spawn process for application " << options.appRoot << ": " << e.what() << "\n" << " Error ID: " << errorId << "\n"; if (filename[0] != '\0') { stream << " Error details saved to: " << filename << "\n"; } stream << " Message from application: " << appMessage << "\n"; P_ERROR(stream.str()); }
void processAndLogNewSpawnException(SpawnException &e, const Options &options, const SpawningKit::ConfigPtr &config) { TRACE_POINT(); UnionStation::TransactionPtr transaction; ErrorRenderer renderer(*config->resourceLocator); string appMessage = e.getErrorPage(); string errorId; char filename[PATH_MAX]; stringstream stream; if (options.analytics && config->unionStationCore != NULL) { try { UPDATE_TRACE_POINT(); transaction = config->unionStationCore->newTransaction( options.getAppGroupName(), "exceptions", options.unionStationKey); errorId = transaction->getTxnId(); } catch (const tracable_exception &e2) { transaction.reset(); P_WARN("Cannot log to Union Station: " << e2.what() << "\n Backtrace:\n" << e2.backtrace()); } } UPDATE_TRACE_POINT(); if (appMessage.empty()) { appMessage = "none"; } if (errorId.empty()) { errorId = config->randomGenerator->generateHexString(4); } e.set("error_id", errorId); try { int fd = -1; string errorPage; UPDATE_TRACE_POINT(); errorPage = renderer.renderWithDetails(appMessage, options, &e); #if (defined(__linux__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 11))) || defined(__APPLE__) || defined(__FreeBSD__) snprintf(filename, PATH_MAX, "%s/passenger-error-XXXXXX.html", getSystemTempDir()); fd = mkstemps(filename, sizeof(".html") - 1); #else snprintf(filename, PATH_MAX, "%s/passenger-error.XXXXXX", getSystemTempDir()); fd = mkstemp(filename); #endif FdGuard guard(fd, NULL, 0, true); if (fd == -1) { int e = errno; throw SystemException("Cannot generate a temporary filename", e); } UPDATE_TRACE_POINT(); writeExact(fd, errorPage); } catch (const SystemException &e2) { filename[0] = '\0'; P_ERROR("Cannot render an error page: " << e2.what() << "\n" << e2.backtrace()); } if (transaction != NULL) { try { UPDATE_TRACE_POINT(); transaction->message("Context: spawning"); transaction->message("Message: " + jsonString(e.what())); transaction->message("App message: " + jsonString(appMessage)); const char *kind; switch (e.getErrorKind()) { case SpawnException::PRELOADER_STARTUP_ERROR: kind = "PRELOADER_STARTUP_ERROR"; break; case SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR: kind = "PRELOADER_STARTUP_PROTOCOL_ERROR"; break; case SpawnException::PRELOADER_STARTUP_TIMEOUT: kind = "PRELOADER_STARTUP_TIMEOUT"; break; case SpawnException::PRELOADER_STARTUP_EXPLAINABLE_ERROR: kind = "PRELOADER_STARTUP_EXPLAINABLE_ERROR"; break; case SpawnException::APP_STARTUP_ERROR: kind = "APP_STARTUP_ERROR"; break; case SpawnException::APP_STARTUP_PROTOCOL_ERROR: kind = "APP_STARTUP_PROTOCOL_ERROR"; break; case SpawnException::APP_STARTUP_TIMEOUT: kind = "APP_STARTUP_TIMEOUT"; break; case SpawnException::APP_STARTUP_EXPLAINABLE_ERROR: kind = "APP_STARTUP_EXPLAINABLE_ERROR"; break; default: kind = "UNDEFINED_ERROR"; break; } transaction->message(string("Kind: ") + kind); Json::Value details; const map<string, string> &annotations = e.getAnnotations(); map<string, string>::const_iterator it, end = annotations.end(); for (it = annotations.begin(); it != end; it++) { details[it->first] = it->second; } // This information is not very useful. Union Station // already collects system metrics. details.removeMember("system_metrics"); // Don't include environment variables because they may // contain sensitive information. details.removeMember("envvars"); transaction->message("Details: " + stringifyJson(details)); } catch (const tracable_exception &e2) { P_WARN("Cannot log to Union Station: " << e2.what() << "\n Backtrace:\n" << e2.backtrace()); } } UPDATE_TRACE_POINT(); stream << "Could not spawn process for application " << options.appRoot << ": " << e.what() << "\n" << " Error ID: " << errorId << "\n"; if (filename[0] != '\0') { stream << " Error details saved to: " << filename << "\n"; } stream << " Message from application: " << appMessage << "\n"; P_ERROR(stream.str()); if (config->agentsOptions != NULL) { HookScriptOptions hOptions; hOptions.name = "spawn_failed"; hOptions.spec = config->agentsOptions->get("hook_spawn_failed", false); hOptions.agentsOptions = config->agentsOptions; hOptions.environment.push_back(make_pair("PASSENGER_APP_ROOT", options.appRoot)); hOptions.environment.push_back(make_pair("PASSENGER_APP_GROUP_NAME", options.getAppGroupName())); hOptions.environment.push_back(make_pair("PASSENGER_ERROR_MESSAGE", e.what())); hOptions.environment.push_back(make_pair("PASSENGER_ERROR_ID", errorId)); hOptions.environment.push_back(make_pair("PASSENGER_APP_ERROR_MESSAGE", appMessage)); oxt::thread(boost::bind(runHookScripts, hOptions), "Hook: spawn_failed", 256 * 1024); } }