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 1023950 : MsgHandler::getMessageInstance() {
67 1023950 : if (myMessageInstance == nullptr) {
68 85086 : if (myFactory == nullptr) {
69 43792 : myMessageInstance = new MsgHandler(MsgType::MT_MESSAGE);
70 : } else {
71 41294 : myMessageInstance = myFactory(MsgType::MT_MESSAGE);
72 : }
73 : }
74 1023950 : return myMessageInstance;
75 : }
76 :
77 :
78 : MsgHandler*
79 949281 : MsgHandler::getWarningInstance() {
80 949281 : if (myWarningInstance == nullptr) {
81 126359 : if (myFactory == nullptr) {
82 73729 : myWarningInstance = new MsgHandler(MsgType::MT_WARNING);
83 : } else {
84 52630 : myWarningInstance = myFactory(MsgType::MT_WARNING);
85 : }
86 : }
87 949281 : return myWarningInstance;
88 : }
89 :
90 :
91 : MsgHandler*
92 1401713 : MsgHandler::getErrorInstance() {
93 1401713 : if (myErrorInstance == nullptr) {
94 126347 : myErrorInstance = new MsgHandler(MsgType::MT_ERROR);
95 : }
96 1401713 : 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 0 : 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 0 : return msg;
150 : }
151 :
152 :
153 : void
154 1103601 : MsgHandler::inform(std::string msg, bool addType) {
155 1103601 : if (addType && !myInitialMessages.empty() && myInitialMessages.size() < 5) {
156 94 : myInitialMessages.push_back(msg);
157 : }
158 : // beautify progress output
159 1103601 : if (myAmProcessingProcess) {
160 742 : myAmProcessingProcess = false;
161 1484 : MsgHandler::getMessageInstance()->inform("");
162 : }
163 2207202 : msg = build(msg, addType);
164 : // inform all receivers
165 1693780 : for (auto i : myRetrievers) {
166 590179 : i->inform(msg);
167 : }
168 : // set the information that something occurred
169 1103601 : myWasInformed = true;
170 1103601 : }
171 :
172 :
173 : void
174 138739 : MsgHandler::beginProcessMsg(std::string msg, bool addType) {
175 277478 : msg = build(msg, addType);
176 : // inform all other receivers
177 167349 : for (auto i : myRetrievers) {
178 28610 : i->inform(msg, true);
179 28610 : myAmProcessingProcess = true;
180 : }
181 : // set the information that something occurred
182 138739 : myWasInformed = true;
183 138739 : }
184 :
185 :
186 : void
187 137664 : MsgHandler::endProcessMsg2(bool success, long duration) {
188 137664 : if (success) {
189 136937 : if (duration > -1) {
190 230832 : endProcessMsg(TLF(" done (%ms).", toString(duration)));
191 : } else {
192 43042 : endProcessMsg(TL(" done."));
193 : }
194 : } else {
195 1454 : endProcessMsg(TL(" failed."));
196 : }
197 137664 : }
198 :
199 :
200 : void
201 138255 : MsgHandler::endProcessMsg(std::string msg) {
202 : // inform all other receivers
203 166672 : for (auto i : myRetrievers) {
204 28417 : i->inform(msg);
205 : }
206 : // set the information that something occurred
207 138255 : myWasInformed = true;
208 138255 : myAmProcessingProcess = false;
209 138255 : }
210 :
211 :
212 : void
213 401556 : MsgHandler::clear(bool resetInformed) {
214 401556 : if (myAggregationThreshold >= 0) {
215 9575 : for (const auto& i : myAggregationCount) {
216 1666 : if (i.second > myAggregationThreshold) {
217 993 : inform(toString(i.second) + " total messages of type: " + i.first);
218 : }
219 : }
220 : }
221 : myAggregationCount.clear();
222 401556 : 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 401556 : if (resetInformed) {
231 399238 : myWasInformed = false;
232 : }
233 401556 : }
234 :
235 :
236 : void
237 411461 : MsgHandler::addRetriever(OutputDevice* retriever) {
238 411461 : if (!isRetriever(retriever)) {
239 411461 : myRetrievers.push_back(retriever);
240 : }
241 411461 : }
242 :
243 :
244 : void
245 997248 : MsgHandler::removeRetriever(OutputDevice* retriever) {
246 997248 : std::vector<OutputDevice*>::iterator i = find(myRetrievers.begin(), myRetrievers.end(), retriever);
247 997248 : if (i != myRetrievers.end()) {
248 403829 : myRetrievers.erase(i);
249 : }
250 997248 : }
251 :
252 :
253 : bool
254 713904 : MsgHandler::isRetriever(OutputDevice* retriever) const {
255 713904 : return std::find(myRetrievers.begin(), myRetrievers.end(), retriever) != myRetrievers.end();
256 : }
257 :
258 :
259 : void
260 277370 : MsgHandler::removeRetrieverFromAllInstances(OutputDevice* out) {
261 277370 : if (myDebugInstance != nullptr) {
262 0 : myDebugInstance->removeRetriever(out);
263 : }
264 277370 : if (myGLDebugInstance != nullptr) {
265 0 : myGLDebugInstance->removeRetriever(out);
266 : }
267 277370 : if (myErrorInstance != nullptr) {
268 277370 : myErrorInstance->removeRetriever(out);
269 : }
270 277370 : if (myWarningInstance != nullptr) {
271 277370 : myWarningInstance->removeRetriever(out);
272 : }
273 277370 : if (myMessageInstance != nullptr) {
274 236085 : myMessageInstance->removeRetriever(out);
275 : }
276 277370 : }
277 :
278 :
279 : void
280 58064 : MsgHandler::setupI18n(const std::string& locale) {
281 : #ifdef HAVE_INTL
282 58064 : if (locale != "") {
283 : #ifdef WIN32
284 : _putenv_s("LANGUAGE", locale.data());
285 : #else
286 58064 : setenv("LANGUAGE", locale.data(), true);
287 : #endif
288 : }
289 58064 : if (!setlocale(LC_MESSAGES, "")) {
290 0 : WRITE_WARNINGF(TL("Could not set locale to '%'."), locale);
291 : }
292 58064 : const char* sumoPath = getenv("SUMO_HOME");
293 58064 : if (sumoPath == nullptr) {
294 16 : 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 116096 : const std::string path = sumoPath + std::string("/data/locale/");
300 58048 : if (!bindtextdomain("sumo", path.data())) {
301 0 : WRITE_WARNING(TL("Could not find localized messages."));
302 : return;
303 : }
304 : }
305 58064 : bind_textdomain_codeset("sumo", "UTF-8");
306 58064 : textdomain("sumo");
307 : #else
308 : UNUSED_PARAMETER(locale);
309 : #endif
310 58064 : myWarningPrefix = TL("Warning: ");
311 58064 : myErrorPrefix = TL("Error: ");
312 : }
313 :
314 :
315 : void
316 55259 : MsgHandler::initOutputOptions() {
317 : // initialize console properly
318 55259 : OutputDevice::getDevice("stdout");
319 55259 : OutputDevice::getDevice("stderr");
320 55259 : OptionsCont& oc = OptionsCont::getOptions();
321 55259 : getWarningInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings"));
322 55259 : getErrorInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings"));
323 110518 : if (oc.getBool("no-warnings")) {
324 88 : getWarningInstance()->removeRetriever(&OutputDevice::getDevice("stderr"));
325 : }
326 : // build the logger if possible
327 110518 : if (oc.isSet("log", false)) {
328 237 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("log"));
329 237 : getErrorInstance()->addRetriever(logFile);
330 474 : if (!oc.getBool("no-warnings")) {
331 237 : getWarningInstance()->addRetriever(logFile);
332 : }
333 237 : getMessageInstance()->addRetriever(logFile);
334 474 : if (oc.getBool("log.timestamps")) {
335 1 : myWriteTimestamps = true;
336 : }
337 474 : if (oc.getBool("log.processid")) {
338 1 : myWriteProcessId = true;
339 : }
340 : }
341 110518 : if (oc.isSet("message-log", false)) {
342 6965 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("message-log"));
343 6965 : getMessageInstance()->addRetriever(logFile);
344 : }
345 110518 : if (oc.isSet("error-log", false)) {
346 6983 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("error-log"));
347 6983 : getErrorInstance()->addRetriever(logFile);
348 6983 : getWarningInstance()->addRetriever(logFile);
349 : }
350 110518 : if (oc.getBool("verbose")) {
351 8766 : getErrorInstance()->myInitialMessages.push_back("Repeating initial error messages:");
352 : } else {
353 101752 : getMessageInstance()->removeRetriever(&OutputDevice::getDevice("stdout"));
354 : }
355 55259 : }
356 :
357 :
358 : void
359 137957 : MsgHandler::cleanupOnEnd() {
360 137957 : delete myMessageInstance;
361 137957 : myMessageInstance = nullptr;
362 137957 : delete myWarningInstance;
363 137957 : myWarningInstance = nullptr;
364 137957 : delete myErrorInstance;
365 137957 : myErrorInstance = nullptr;
366 137957 : delete myDebugInstance;
367 137957 : myDebugInstance = nullptr;
368 137957 : delete myGLDebugInstance;
369 137957 : myGLDebugInstance = nullptr;
370 137957 : }
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 337792 : MsgHandler::MsgHandler(MsgType type) :
402 337792 : myType(type), myWasInformed(false), myAggregationThreshold(-1) {
403 337792 : if (type == MsgType::MT_MESSAGE) {
404 170172 : addRetriever(&OutputDevice::getDevice("stdout"));
405 : } else {
406 505412 : addRetriever(&OutputDevice::getDevice("stderr"));
407 : }
408 337792 : }
409 :
410 :
411 581606 : MsgHandler::~MsgHandler() {
412 581606 : }
413 :
414 :
415 : bool
416 332131 : MsgHandler::wasInformed() const {
417 332131 : return myWasInformed;
418 : }
419 :
420 :
421 : /****************************************************************************/
|