Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 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 : #ifdef WIN32
30 : #define NOMINMAX
31 : #include <windows.h>
32 : #undef NOMINMAX
33 : #else
34 : #include <unistd.h>
35 : #endif
36 : #include <utils/options/OptionsCont.h>
37 : #include <utils/iodevices/OutputDevice.h>
38 : #include <utils/common/UtilExceptions.h>
39 : #include "MsgHandler.h"
40 :
41 :
42 : // ===========================================================================
43 : // static member variables
44 : // ===========================================================================
45 : MsgHandler::Factory MsgHandler::myFactory = nullptr;
46 : MsgHandler* MsgHandler::myErrorInstance = nullptr;
47 : MsgHandler* MsgHandler::myWarningInstance = nullptr;
48 : MsgHandler* MsgHandler::myMessageInstance = nullptr;
49 : bool MsgHandler::myAmProcessingProcess = false;
50 : bool MsgHandler::myWriteDebugMessages = false;
51 : bool MsgHandler::myWriteDebugGLMessages = false;
52 : bool MsgHandler::myWriteTimestamps = false;
53 : bool MsgHandler::myWriteProcessId = false;
54 : std::string MsgHandler::myErrorPrefix = "Error: ";
55 : std::string MsgHandler::myWarningPrefix = "Warning: ";
56 :
57 :
58 : // ===========================================================================
59 : // method definitions
60 : // ===========================================================================
61 :
62 : MsgHandler*
63 1024777 : MsgHandler::getMessageInstance() {
64 1024777 : if (myMessageInstance == nullptr) {
65 77612 : if (myFactory == nullptr) {
66 33667 : myMessageInstance = new MsgHandler(MsgType::MT_MESSAGE);
67 : } else {
68 43945 : myMessageInstance = myFactory(MsgType::MT_MESSAGE);
69 : }
70 : }
71 1024777 : return myMessageInstance;
72 : }
73 :
74 :
75 : MsgHandler*
76 1412639 : MsgHandler::getWarningInstance() {
77 1412639 : if (myWarningInstance == nullptr) {
78 121774 : if (myFactory == nullptr) {
79 65725 : myWarningInstance = new MsgHandler(MsgType::MT_WARNING);
80 : } else {
81 56049 : myWarningInstance = myFactory(MsgType::MT_WARNING);
82 : }
83 : }
84 1412639 : return myWarningInstance;
85 : }
86 :
87 :
88 : MsgHandler*
89 5717779 : MsgHandler::getErrorInstance() {
90 5717779 : if (myErrorInstance == nullptr) {
91 117423 : myErrorInstance = new MsgHandler(MsgType::MT_ERROR);
92 : }
93 5717779 : return myErrorInstance;
94 : }
95 :
96 :
97 : void
98 0 : MsgHandler::enableDebugMessages(bool enable) {
99 0 : myWriteDebugMessages = enable;
100 0 : }
101 :
102 : void
103 0 : MsgHandler::enableDebugGLMessages(bool enable) {
104 0 : myWriteDebugGLMessages = enable;
105 0 : }
106 :
107 :
108 : std::string
109 0 : MsgHandler::insertLineBreaks(std::string msg, int lineWidth) {
110 : // TODO: check what FXFont::getTextWidth can do
111 : //int textWidth = getApp()->getNormalFont()->getTextWidth
112 0 : if ((int)msg.size() <= lineWidth) {
113 0 : return msg;
114 : }
115 : size_t pos = 0;
116 0 : size_t nextLineBreak = msg.find('\n');
117 0 : size_t spaceAfterLine = msg.find(' ', lineWidth);
118 0 : while (spaceAfterLine != std::string::npos) {
119 0 : if (nextLineBreak == std::string::npos || nextLineBreak > spaceAfterLine) {
120 : msg = msg.replace(spaceAfterLine, 1, "\n");
121 0 : pos = spaceAfterLine + 1;
122 : } else {
123 0 : pos = nextLineBreak + 1;
124 : }
125 0 : spaceAfterLine = msg.find(' ', pos + lineWidth);
126 0 : nextLineBreak = msg.find('\n', pos);
127 : }
128 0 : return msg;
129 : }
130 :
131 :
132 : void
133 5609307 : MsgHandler::inform(std::string msg, bool addType) {
134 5609307 : if (addType && !myInitialMessages.empty() && myInitialMessages.size() < 5) {
135 226 : myInitialMessages.push_back(msg);
136 : }
137 : // beautify progress output
138 5609307 : if (myAmProcessingProcess) {
139 794 : myAmProcessingProcess = false;
140 1588 : MsgHandler::getMessageInstance()->inform("");
141 : }
142 11218614 : msg = build(msg, addType);
143 : // inform all receivers
144 10641385 : for (auto i : myRetrievers) {
145 5032078 : i->inform(msg);
146 : }
147 : // set the information that something occurred
148 5609307 : myWasInformed = true;
149 5609307 : }
150 :
151 :
152 : void
153 129078 : MsgHandler::beginProcessMsg(std::string msg, bool addType) {
154 258156 : msg = build(msg, addType);
155 : // inform all other receivers
156 159773 : for (auto i : myRetrievers) {
157 30695 : i->inform(msg, true);
158 30695 : myAmProcessingProcess = true;
159 : }
160 : // set the information that something occurred
161 129078 : myWasInformed = true;
162 129078 : }
163 :
164 :
165 : void
166 127996 : MsgHandler::endProcessMsg2(bool success, long duration) {
167 127996 : if (success) {
168 127269 : if (duration > -1) {
169 234450 : endProcessMsg(TLF(" done (%ms).", toString(duration)));
170 : } else {
171 20088 : endProcessMsg(TL(" done."));
172 : }
173 : } else {
174 1454 : endProcessMsg(TL(" failed."));
175 : }
176 127996 : }
177 :
178 :
179 : void
180 128650 : MsgHandler::endProcessMsg(std::string msg) {
181 : // inform all other receivers
182 159175 : for (auto i : myRetrievers) {
183 30525 : i->inform(msg);
184 : }
185 : // set the information that something occurred
186 128650 : myWasInformed = true;
187 128650 : myAmProcessingProcess = false;
188 128650 : }
189 :
190 :
191 : void
192 366035 : MsgHandler::clear(bool resetInformed) {
193 366035 : if (myAggregationThreshold >= 0) {
194 41129 : for (const auto& i : myAggregationCount) {
195 5196 : if (i.second > myAggregationThreshold) {
196 11412 : inform(toString(i.second) + " total messages of type: " + i.first);
197 : }
198 : }
199 : }
200 : myAggregationCount.clear();
201 366035 : if (!resetInformed && myInitialMessages.size() > 1) {
202 3 : const bool wasInformed = myWasInformed;
203 15 : for (const std::string& msg : myInitialMessages) {
204 24 : inform(msg, false);
205 : }
206 : myInitialMessages.clear();
207 3 : myWasInformed = wasInformed;
208 : }
209 366035 : if (resetInformed) {
210 363596 : myWasInformed = false;
211 : }
212 366035 : }
213 :
214 :
215 : void
216 395600 : MsgHandler::addRetriever(OutputDevice* retriever) {
217 395600 : if (!isRetriever(retriever)) {
218 395600 : myRetrievers.push_back(retriever);
219 : }
220 395600 : }
221 :
222 :
223 : void
224 990220 : MsgHandler::removeRetriever(OutputDevice* retriever) {
225 990220 : std::vector<OutputDevice*>::iterator i = find(myRetrievers.begin(), myRetrievers.end(), retriever);
226 990220 : if (i != myRetrievers.end()) {
227 383081 : myRetrievers.erase(i);
228 : }
229 990220 : }
230 :
231 :
232 : bool
233 700400 : MsgHandler::isRetriever(OutputDevice* retriever) const {
234 700400 : return std::find(myRetrievers.begin(), myRetrievers.end(), retriever) != myRetrievers.end();
235 : }
236 :
237 :
238 : void
239 274548 : MsgHandler::removeRetrieverFromAllInstances(OutputDevice* out) {
240 274548 : if (myErrorInstance != nullptr) {
241 274548 : myErrorInstance->removeRetriever(out);
242 : }
243 274548 : if (myWarningInstance != nullptr) {
244 274548 : myWarningInstance->removeRetriever(out);
245 : }
246 274548 : if (myMessageInstance != nullptr) {
247 234708 : myMessageInstance->removeRetriever(out);
248 : }
249 274548 : }
250 :
251 :
252 : void
253 48736 : MsgHandler::setupI18n(const std::string& locale) {
254 : #ifdef HAVE_INTL
255 48736 : if (locale != "") {
256 : #ifdef WIN32
257 : _putenv_s("LANGUAGE", locale.data());
258 : #else
259 48736 : setenv("LANGUAGE", locale.data(), true);
260 : #endif
261 : }
262 48736 : if (!setlocale(LC_MESSAGES, "")) {
263 0 : WRITE_WARNINGF(TL("Could not set locale to '%'."), locale);
264 : }
265 48736 : const char* sumoPath = getenv("SUMO_HOME");
266 48736 : if (sumoPath == nullptr) {
267 16 : if (!bindtextdomain("sumo", nullptr)) {
268 0 : WRITE_WARNING(TL("Environment variable SUMO_HOME is not set, could not find localized messages."));
269 0 : return;
270 : }
271 : } else {
272 97440 : const std::string path = sumoPath + std::string("/data/locale/");
273 48720 : if (!bindtextdomain("sumo", path.data())) {
274 0 : WRITE_WARNING(TL("Could not find localized messages."));
275 : return;
276 : }
277 : }
278 48736 : bind_textdomain_codeset("sumo", "UTF-8");
279 48736 : textdomain("sumo");
280 : #ifdef WIN32
281 : SetConsoleOutputCP(CP_UTF8);
282 : #endif
283 : #else
284 : UNUSED_PARAMETER(locale);
285 : #endif
286 48736 : myWarningPrefix = TL("Warning: ");
287 48736 : myErrorPrefix = TL("Error: ");
288 48736 : gLocaleInitialized = true;
289 : }
290 :
291 :
292 : void
293 45800 : MsgHandler::initOutputOptions() {
294 : // initialize console properly
295 45800 : OutputDevice::getDevice("stdout");
296 45800 : OutputDevice::getDevice("stderr");
297 45800 : OptionsCont& oc = OptionsCont::getOptions();
298 45800 : getWarningInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings"));
299 45800 : getErrorInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings"));
300 91600 : if (oc.getBool("no-warnings")) {
301 570 : getWarningInstance()->removeRetriever(&OutputDevice::getDevice("stderr"));
302 : }
303 : // build the logger if possible
304 91600 : if (oc.isSet("log", false)) {
305 377 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("log"));
306 377 : getErrorInstance()->addRetriever(logFile);
307 754 : if (!oc.getBool("no-warnings")) {
308 377 : getWarningInstance()->addRetriever(logFile);
309 : }
310 377 : getMessageInstance()->addRetriever(logFile);
311 754 : if (oc.getBool("log.timestamps")) {
312 1 : myWriteTimestamps = true;
313 : }
314 754 : if (oc.getBool("log.processid")) {
315 1 : myWriteProcessId = true;
316 : }
317 : }
318 91600 : if (oc.isSet("message-log", false)) {
319 7410 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("message-log"));
320 7410 : getMessageInstance()->addRetriever(logFile);
321 : }
322 91600 : if (oc.isSet("error-log", false)) {
323 7441 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("error-log"));
324 7441 : getErrorInstance()->addRetriever(logFile);
325 7441 : getWarningInstance()->addRetriever(logFile);
326 : }
327 91600 : if (oc.getBool("verbose")) {
328 9856 : getErrorInstance()->myInitialMessages.push_back("Repeating initial error messages:");
329 : } else {
330 81744 : getMessageInstance()->removeRetriever(&OutputDevice::getDevice("stdout"));
331 : }
332 45800 : }
333 :
334 :
335 : void
336 129810 : MsgHandler::cleanupOnEnd() {
337 129810 : delete myMessageInstance;
338 129810 : myMessageInstance = nullptr;
339 129810 : delete myWarningInstance;
340 129810 : myWarningInstance = nullptr;
341 129810 : delete myErrorInstance;
342 129810 : myErrorInstance = nullptr;
343 129810 : }
344 :
345 :
346 : std::string
347 6 : MsgHandler::buildProcessIdPrefix() const {
348 6 : std::stringstream prefix;
349 6 : prefix << "[PID: ";
350 : #ifdef WIN32
351 : prefix << GetCurrentProcessId();
352 : #else
353 6 : prefix << getpid();
354 : #endif
355 6 : prefix << "] ";
356 6 : return prefix.str();
357 6 : }
358 :
359 :
360 316809 : MsgHandler::MsgHandler(MsgType type) :
361 316809 : myType(type), myWasInformed(false), myAggregationThreshold(-1) {
362 316809 : if (type == MsgType::MT_MESSAGE) {
363 155224 : addRetriever(&OutputDevice::getDevice("stdout"));
364 : } else {
365 478394 : addRetriever(&OutputDevice::getDevice("stderr"));
366 : }
367 316809 : }
368 :
369 :
370 533512 : MsgHandler::~MsgHandler() {
371 533512 : }
372 :
373 :
374 : bool
375 453014 : MsgHandler::wasInformed() const {
376 453014 : return myWasInformed;
377 : }
378 :
379 :
380 : /****************************************************************************/
|