Line data Source code
1 : /****************************************************************************/ 2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo 3 : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others. 4 : // This program and the accompanying materials are made available under the 5 : // terms of the Eclipse Public License 2.0 which is available at 6 : // https://www.eclipse.org/legal/epl-2.0/ 7 : // This Source Code may also be made available under the following Secondary 8 : // Licenses when the conditions for such availability set forth in the Eclipse 9 : // Public License 2.0 are satisfied: GNU General Public License, version 2 10 : // or later which is available at 11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html 12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later 13 : /****************************************************************************/ 14 : /// @file MsgHandler.cpp 15 : /// @author Daniel Krajzewicz 16 : /// @author Michael Behrisch 17 : /// @author Mirko Barthauer 18 : /// @date Tue, 17 Jun 2003 19 : /// 20 : // Retrieves messages about the process and gives them further to output 21 : /****************************************************************************/ 22 : #include <config.h> 23 : 24 : #include <string> 25 : #include <cassert> 26 : #include <vector> 27 : #include <algorithm> 28 : #include <iostream> 29 : #include <chrono> 30 : #ifdef WIN32 31 : #define NOMINMAX 32 : #include <windows.h> 33 : #undef NOMINMAX 34 : #else 35 : #include <unistd.h> 36 : #endif 37 : #include <utils/options/OptionsCont.h> 38 : #include <utils/iodevices/OutputDevice.h> 39 : #include <utils/common/UtilExceptions.h> 40 : #include "MsgHandler.h" 41 : 42 : 43 : // =========================================================================== 44 : // static member variables 45 : // =========================================================================== 46 : MsgHandler::Factory MsgHandler::myFactory = nullptr; 47 : MsgHandler* MsgHandler::myDebugInstance = nullptr; 48 : MsgHandler* MsgHandler::myGLDebugInstance = nullptr; 49 : MsgHandler* MsgHandler::myErrorInstance = nullptr; 50 : MsgHandler* MsgHandler::myWarningInstance = nullptr; 51 : MsgHandler* MsgHandler::myMessageInstance = nullptr; 52 : bool MsgHandler::myAmProcessingProcess = false; 53 : bool MsgHandler::myWriteDebugMessages = false; 54 : bool MsgHandler::myWriteDebugGLMessages = false; 55 : bool MsgHandler::myWriteTimestamps = false; 56 : bool MsgHandler::myWriteProcessId = false; 57 : std::string MsgHandler::myErrorPrefix = "Error: "; 58 : std::string MsgHandler::myWarningPrefix = "Warning: "; 59 : 60 : 61 : // =========================================================================== 62 : // method definitions 63 : // =========================================================================== 64 : 65 : MsgHandler* 66 1232997 : MsgHandler::getMessageInstance() { 67 1232997 : if (myMessageInstance == nullptr) { 68 71989 : if (myFactory == nullptr) { 69 32346 : myMessageInstance = new MsgHandler(MsgType::MT_MESSAGE); 70 : } else { 71 39643 : myMessageInstance = myFactory(MsgType::MT_MESSAGE); 72 : } 73 : } 74 1232997 : return myMessageInstance; 75 : } 76 : 77 : 78 : MsgHandler* 79 779997 : MsgHandler::getWarningInstance() { 80 779997 : if (myWarningInstance == nullptr) { 81 110379 : if (myFactory == nullptr) { 82 59893 : myWarningInstance = new MsgHandler(MsgType::MT_WARNING); 83 : } else { 84 50486 : myWarningInstance = myFactory(MsgType::MT_WARNING); 85 : } 86 : } 87 779997 : return myWarningInstance; 88 : } 89 : 90 : 91 : MsgHandler* 92 1467256 : MsgHandler::getErrorInstance() { 93 1467256 : if (myErrorInstance == nullptr) { 94 110366 : myErrorInstance = new MsgHandler(MsgType::MT_ERROR); 95 : } 96 1467256 : return myErrorInstance; 97 : } 98 : 99 : 100 : MsgHandler* 101 0 : MsgHandler::getDebugInstance() { 102 0 : if (myDebugInstance == nullptr) { 103 0 : myDebugInstance = new MsgHandler(MsgType::MT_DEBUG); 104 : } 105 0 : return myDebugInstance; 106 : } 107 : 108 : 109 : MsgHandler* 110 0 : MsgHandler::getGLDebugInstance() { 111 0 : if (myGLDebugInstance == nullptr) { 112 0 : myGLDebugInstance = new MsgHandler(MsgType::MT_GLDEBUG); 113 : } 114 0 : return myGLDebugInstance; 115 : } 116 : 117 : 118 : void 119 0 : MsgHandler::enableDebugMessages(bool enable) { 120 0 : myWriteDebugMessages = enable; 121 0 : } 122 : 123 : void 124 0 : MsgHandler::enableDebugGLMessages(bool enable) { 125 0 : myWriteDebugGLMessages = enable; 126 0 : } 127 : 128 : 129 : std::string 130 0 : MsgHandler::insertLineBreaks(std::string msg, int lineWidth) { 131 : // TODO: check what FXFont::getTextWidth can do 132 : //int textWidth = getApp()->getNormalFont()->getTextWidth 133 0 : if ((int)msg.size() <= lineWidth) { 134 : return msg; 135 : } 136 : size_t pos = 0; 137 0 : size_t nextLineBreak = msg.find('\n'); 138 0 : size_t spaceAfterLine = msg.find(' ', lineWidth); 139 0 : while (spaceAfterLine != std::string::npos) { 140 0 : if (nextLineBreak == std::string::npos || nextLineBreak > spaceAfterLine) { 141 : msg = msg.replace(spaceAfterLine, 1, "\n"); 142 0 : pos = spaceAfterLine + 1; 143 : } else { 144 0 : pos = nextLineBreak + 1; 145 : } 146 0 : spaceAfterLine = msg.find(' ', pos + lineWidth); 147 0 : nextLineBreak = msg.find('\n', pos); 148 : } 149 : return msg; 150 : } 151 : 152 : 153 : void 154 1222425 : MsgHandler::inform(std::string msg, bool addType) { 155 1222425 : if (addType && !myInitialMessages.empty() && myInitialMessages.size() < 5) { 156 91 : myInitialMessages.push_back(msg); 157 : } 158 : // beautify progress output 159 1222425 : if (myAmProcessingProcess) { 160 744 : myAmProcessingProcess = false; 161 1488 : MsgHandler::getMessageInstance()->inform(""); 162 : } 163 2444850 : msg = build(msg, addType); 164 : // inform all receivers 165 1652484 : for (auto i : myRetrievers) { 166 430059 : i->inform(msg); 167 : } 168 : // set the information that something occurred 169 1222425 : myWasInformed = true; 170 1222425 : } 171 : 172 : 173 : void 174 116451 : MsgHandler::beginProcessMsg(std::string msg, bool addType) { 175 232902 : msg = build(msg, addType); 176 : // inform all other receivers 177 145844 : for (auto i : myRetrievers) { 178 29393 : i->inform(msg, ' '); 179 29393 : myAmProcessingProcess = true; 180 : } 181 : // set the information that something occurred 182 116451 : myWasInformed = true; 183 116451 : } 184 : 185 : 186 : void 187 115049 : MsgHandler::endProcessMsg2(bool success, long duration) { 188 115049 : if (success) { 189 113620 : if (duration > -1) { 190 206336 : endProcessMsg(TLF("done (%ms).", toString(duration))); 191 : } else { 192 20904 : endProcessMsg(TL("done.")); 193 : } 194 : } else { 195 2858 : endProcessMsg(TL("failed.")); 196 : } 197 115049 : } 198 : 199 : 200 : void 201 115632 : MsgHandler::endProcessMsg(std::string msg) { 202 : // inform all other receivers 203 144839 : for (auto i : myRetrievers) { 204 29207 : i->inform(msg); 205 : } 206 : // set the information that something occurred 207 115632 : myWasInformed = true; 208 115632 : myAmProcessingProcess = false; 209 115632 : } 210 : 211 : 212 : void 213 325002 : MsgHandler::clear(bool resetInformed) { 214 325002 : if (myAggregationThreshold >= 0) { 215 11477 : for (const auto& i : myAggregationCount) { 216 2052 : if (i.second > myAggregationThreshold) { 217 892 : inform(toString(i.second) + " total messages of type: " + i.first); 218 : } 219 : } 220 : } 221 : myAggregationCount.clear(); 222 325002 : if (!resetInformed && myInitialMessages.size() > 1) { 223 3 : const bool wasInformed = myWasInformed; 224 15 : for (const std::string& msg : myInitialMessages) { 225 24 : inform(msg, false); 226 : } 227 : myInitialMessages.clear(); 228 3 : myWasInformed = wasInformed; 229 : } 230 325002 : if (resetInformed) { 231 322021 : myWasInformed = false; 232 : } 233 325002 : } 234 : 235 : 236 : void 237 363909 : MsgHandler::addRetriever(OutputDevice* retriever) { 238 363909 : if (!isRetriever(retriever)) { 239 363909 : myRetrievers.push_back(retriever); 240 : } 241 363909 : } 242 : 243 : 244 : void 245 829548 : MsgHandler::removeRetriever(OutputDevice* retriever) { 246 829548 : std::vector<OutputDevice*>::iterator i = find(myRetrievers.begin(), myRetrievers.end(), retriever); 247 829548 : if (i != myRetrievers.end()) { 248 356588 : myRetrievers.erase(i); 249 : } 250 829548 : } 251 : 252 : 253 : bool 254 616888 : MsgHandler::isRetriever(OutputDevice* retriever) const { 255 616888 : return std::find(myRetrievers.begin(), myRetrievers.end(), retriever) != myRetrievers.end(); 256 : } 257 : 258 : 259 : void 260 226623 : MsgHandler::removeRetrieverFromAllInstances(OutputDevice* out) { 261 226623 : if (myDebugInstance != nullptr) { 262 0 : myDebugInstance->removeRetriever(out); 263 : } 264 226623 : if (myGLDebugInstance != nullptr) { 265 0 : myGLDebugInstance->removeRetriever(out); 266 : } 267 226623 : if (myErrorInstance != nullptr) { 268 226623 : myErrorInstance->removeRetriever(out); 269 : } 270 226623 : if (myWarningInstance != nullptr) { 271 226623 : myWarningInstance->removeRetriever(out); 272 : } 273 226623 : if (myMessageInstance != nullptr) { 274 188220 : myMessageInstance->removeRetriever(out); 275 : } 276 226623 : } 277 : 278 : 279 : void 280 48368 : MsgHandler::setupI18n(const std::string& locale) { 281 : #ifdef HAVE_INTL 282 48368 : if (locale != "") { 283 : #ifdef WIN32 284 : _putenv_s("LANGUAGE", locale.data()); 285 : #else 286 48368 : setenv("LANGUAGE", locale.data(), true); 287 : #endif 288 : } 289 48368 : if (!setlocale(LC_MESSAGES, "")) { 290 0 : WRITE_WARNINGF(TL("Could not set locale to '%'."), locale); 291 : } 292 48368 : const char* sumoPath = getenv("SUMO_HOME"); 293 48368 : if (sumoPath == nullptr) { 294 32 : if (!bindtextdomain("sumo", nullptr)) { 295 0 : WRITE_WARNING(TL("Environment variable SUMO_HOME is not set, could not find localized messages.")); 296 0 : return; 297 : } 298 : } else { 299 96672 : const std::string path = sumoPath + std::string("/data/locale/"); 300 48336 : if (!bindtextdomain("sumo", path.data())) { 301 0 : WRITE_WARNING(TL("Could not find localized messages.")); 302 : return; 303 : } 304 : } 305 48368 : bind_textdomain_codeset("sumo", "UTF-8"); 306 48368 : textdomain("sumo"); 307 : #else 308 : UNUSED_PARAMETER(locale); 309 : #endif 310 48368 : myWarningPrefix = TL("Warning: "); 311 48368 : myErrorPrefix = TL("Error: "); 312 : } 313 : 314 : 315 : void 316 43150 : MsgHandler::initOutputOptions() { 317 : // initialize console properly 318 43150 : OutputDevice::getDevice("stdout"); 319 43150 : OutputDevice::getDevice("stderr"); 320 43150 : OptionsCont& oc = OptionsCont::getOptions(); 321 43150 : getWarningInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings")); 322 43150 : getErrorInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings")); 323 86300 : if (oc.getBool("no-warnings")) { 324 168 : getWarningInstance()->removeRetriever(&OutputDevice::getDevice("stderr")); 325 : } 326 : // build the logger if possible 327 86300 : if (oc.isSet("log", false)) { 328 794 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("log")); 329 397 : getErrorInstance()->addRetriever(logFile); 330 794 : if (!oc.getBool("no-warnings")) { 331 397 : getWarningInstance()->addRetriever(logFile); 332 : } 333 397 : getMessageInstance()->addRetriever(logFile); 334 794 : if (oc.getBool("log.timestamps")) { 335 1 : myWriteTimestamps = true; 336 : } 337 794 : if (oc.getBool("log.processid")) { 338 1 : myWriteProcessId = true; 339 : } 340 : } 341 86300 : if (oc.isSet("message-log", false)) { 342 13278 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("message-log")); 343 6639 : getMessageInstance()->addRetriever(logFile); 344 : } 345 86300 : if (oc.isSet("error-log", false)) { 346 13348 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("error-log")); 347 6674 : getErrorInstance()->addRetriever(logFile); 348 6674 : getWarningInstance()->addRetriever(logFile); 349 : } 350 86300 : if (oc.getBool("verbose")) { 351 9288 : getErrorInstance()->myInitialMessages.push_back("Repeating initial error messages:"); 352 : } else { 353 77012 : getMessageInstance()->removeRetriever(&OutputDevice::getDevice("stdout")); 354 : } 355 43150 : } 356 : 357 : 358 : void 359 121476 : MsgHandler::cleanupOnEnd() { 360 121476 : delete myMessageInstance; 361 121476 : myMessageInstance = nullptr; 362 121476 : delete myWarningInstance; 363 121476 : myWarningInstance = nullptr; 364 121476 : delete myErrorInstance; 365 121476 : myErrorInstance = nullptr; 366 121476 : delete myDebugInstance; 367 121476 : myDebugInstance = nullptr; 368 121476 : delete myGLDebugInstance; 369 121476 : myGLDebugInstance = nullptr; 370 121476 : } 371 : 372 : 373 : std::string 374 5 : MsgHandler::buildTimestampPrefix(void) const { 375 5 : std::stringstream prefix; 376 5 : const std::chrono::system_clock::time_point now_timestamp = std::chrono::system_clock::now(); 377 : const auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now_timestamp.time_since_epoch()) % 1000; 378 5 : const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now_timestamp); 379 : 380 : char timeString[21]; 381 5 : std::strftime(timeString, 21, "[%F %T", std::localtime(&now_time_t)); 382 10 : prefix << timeString << '.' << std::setfill('0') << std::setw(3) << milliseconds.count() << "] "; 383 5 : return prefix.str(); 384 5 : } 385 : 386 : 387 : std::string 388 5 : MsgHandler::buildProcessIdPrefix(void) const { 389 5 : std::stringstream prefix; 390 5 : prefix << "[PID: "; 391 : #ifdef WIN32 392 : prefix << GetCurrentProcessId(); 393 : #else 394 5 : prefix << getpid(); 395 : #endif 396 5 : prefix << "] "; 397 5 : return prefix.str(); 398 5 : } 399 : 400 : 401 292734 : MsgHandler::MsgHandler(MsgType type) : 402 292734 : myType(type), myWasInformed(false), myAggregationThreshold(-1) { 403 292734 : if (type == MsgType::MT_MESSAGE) { 404 143978 : addRetriever(&OutputDevice::getDevice("stdout")); 405 : } else { 406 441490 : addRetriever(&OutputDevice::getDevice("stderr")); 407 : } 408 292734 : } 409 : 410 : 411 495293 : MsgHandler::~MsgHandler() { 412 495293 : } 413 : 414 : 415 : bool 416 392367 : MsgHandler::wasInformed() const { 417 392367 : return myWasInformed; 418 : } 419 : 420 : 421 : /****************************************************************************/