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 1044435 : MsgHandler::getMessageInstance() {
64 1044435 : if (myMessageInstance == nullptr) {
65 79008 : if (myFactory == nullptr) {
66 34925 : myMessageInstance = new MsgHandler(MsgType::MT_MESSAGE);
67 : } else {
68 44083 : myMessageInstance = myFactory(MsgType::MT_MESSAGE);
69 : }
70 : }
71 1044435 : return myMessageInstance;
72 : }
73 :
74 :
75 : MsgHandler*
76 1496691 : MsgHandler::getWarningInstance() {
77 1496691 : if (myWarningInstance == nullptr) {
78 124996 : if (myFactory == nullptr) {
79 68771 : myWarningInstance = new MsgHandler(MsgType::MT_WARNING);
80 : } else {
81 56225 : myWarningInstance = myFactory(MsgType::MT_WARNING);
82 : }
83 : }
84 1496691 : return myWarningInstance;
85 : }
86 :
87 :
88 : MsgHandler*
89 5499422 : MsgHandler::getErrorInstance() {
90 5499422 : if (myErrorInstance == nullptr) {
91 120636 : myErrorInstance = new MsgHandler(MsgType::MT_ERROR);
92 : }
93 5499422 : 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 5445424 : MsgHandler::inform(std::string msg, bool addType) {
134 5445424 : if (addType && !myInitialMessages.empty() && myInitialMessages.size() < 5) {
135 223 : myInitialMessages.push_back(msg);
136 : }
137 : // beautify progress output
138 5445424 : if (myAmProcessingProcess) {
139 801 : myAmProcessingProcess = false;
140 1602 : MsgHandler::getMessageInstance()->inform("");
141 : }
142 10890848 : msg = build(msg, addType);
143 : // inform all receivers
144 10305657 : for (auto i : myRetrievers) {
145 4860233 : i->inform(msg);
146 : }
147 : // set the information that something occurred
148 5445424 : myWasInformed = true;
149 5445424 : }
150 :
151 :
152 : void
153 133444 : MsgHandler::beginProcessMsg(std::string msg, bool addType) {
154 266888 : msg = build(msg, addType);
155 : // inform all other receivers
156 164227 : for (auto i : myRetrievers) {
157 30783 : i->inform(msg, true);
158 30783 : myAmProcessingProcess = true;
159 : }
160 : // set the information that something occurred
161 133444 : myWasInformed = true;
162 133444 : }
163 :
164 :
165 : void
166 132361 : MsgHandler::endProcessMsg2(bool success, long duration) {
167 132361 : if (success) {
168 131634 : if (duration > -1) {
169 243122 : endProcessMsg(TLF(" done (%ms).", toString(duration)));
170 : } else {
171 20146 : endProcessMsg(TL(" done."));
172 : }
173 : } else {
174 1454 : endProcessMsg(TL(" failed."));
175 : }
176 132361 : }
177 :
178 :
179 : void
180 133025 : MsgHandler::endProcessMsg(std::string msg) {
181 : // inform all other receivers
182 163644 : for (auto i : myRetrievers) {
183 30619 : i->inform(msg);
184 : }
185 : // set the information that something occurred
186 133025 : myWasInformed = true;
187 133025 : myAmProcessingProcess = false;
188 133025 : }
189 :
190 :
191 : void
192 380391 : MsgHandler::clear(bool resetInformed) {
193 380391 : if (myAggregationThreshold >= 0) {
194 41003 : for (const auto& i : myAggregationCount) {
195 5038 : if (i.second > myAggregationThreshold) {
196 10938 : inform(toString(i.second) + " total messages of type: " + i.first);
197 : }
198 : }
199 : }
200 : myAggregationCount.clear();
201 380391 : 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 380391 : if (resetInformed) {
210 377952 : myWasInformed = false;
211 : }
212 380391 : }
213 :
214 :
215 : void
216 403675 : MsgHandler::addRetriever(OutputDevice* retriever) {
217 403675 : if (!isRetriever(retriever)) {
218 403675 : myRetrievers.push_back(retriever);
219 : }
220 403675 : }
221 :
222 :
223 : void
224 1018210 : MsgHandler::removeRetriever(OutputDevice* retriever) {
225 1018210 : std::vector<OutputDevice*>::iterator i = find(myRetrievers.begin(), myRetrievers.end(), retriever);
226 1018210 : if (i != myRetrievers.end()) {
227 391137 : myRetrievers.erase(i);
228 : }
229 1018210 : }
230 :
231 :
232 : bool
233 719034 : MsgHandler::isRetriever(OutputDevice* retriever) const {
234 719034 : return std::find(myRetrievers.begin(), myRetrievers.end(), retriever) != myRetrievers.end();
235 : }
236 :
237 :
238 : void
239 283882 : MsgHandler::removeRetrieverFromAllInstances(OutputDevice* out) {
240 283882 : if (myErrorInstance != nullptr) {
241 283882 : myErrorInstance->removeRetriever(out);
242 : }
243 283882 : if (myWarningInstance != nullptr) {
244 283882 : myWarningInstance->removeRetriever(out);
245 : }
246 283882 : if (myMessageInstance != nullptr) {
247 242225 : myMessageInstance->removeRetriever(out);
248 : }
249 283882 : }
250 :
251 :
252 : void
253 50030 : MsgHandler::setupI18n(const std::string& locale) {
254 : #ifdef HAVE_INTL
255 50030 : if (locale != "") {
256 : #ifdef WIN32
257 : _putenv_s("LANGUAGE", locale.data());
258 : #else
259 50030 : setenv("LANGUAGE", locale.data(), true);
260 : #endif
261 : }
262 50030 : if (!setlocale(LC_MESSAGES, "")) {
263 0 : WRITE_WARNINGF(TL("Could not set locale to '%'."), locale);
264 : }
265 50030 : const char* sumoPath = getenv("SUMO_HOME");
266 50030 : 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 100028 : const std::string path = sumoPath + std::string("/data/locale/");
273 50014 : if (!bindtextdomain("sumo", path.data())) {
274 0 : WRITE_WARNING(TL("Could not find localized messages."));
275 : return;
276 : }
277 : }
278 50030 : bind_textdomain_codeset("sumo", "UTF-8");
279 50030 : textdomain("sumo");
280 : #ifdef WIN32
281 : SetConsoleOutputCP(CP_UTF8);
282 : #endif
283 : #else
284 : UNUSED_PARAMETER(locale);
285 : #endif
286 50030 : myWarningPrefix = TL("Warning: ");
287 50030 : myErrorPrefix = TL("Error: ");
288 50030 : gLocaleInitialized = true;
289 : }
290 :
291 :
292 : void
293 47092 : MsgHandler::initOutputOptions() {
294 : // initialize console properly
295 47092 : OutputDevice::getDevice("stdout");
296 47092 : OutputDevice::getDevice("stderr");
297 47092 : OptionsCont& oc = OptionsCont::getOptions();
298 47092 : getWarningInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings"));
299 47092 : getErrorInstance()->setAggregationThreshold(oc.getInt("aggregate-warnings"));
300 94184 : if (oc.getBool("no-warnings")) {
301 564 : getWarningInstance()->removeRetriever(&OutputDevice::getDevice("stderr"));
302 : }
303 : // build the logger if possible
304 94184 : 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 94184 : if (oc.isSet("message-log", false)) {
319 7433 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("message-log"));
320 7433 : getMessageInstance()->addRetriever(logFile);
321 : }
322 94184 : if (oc.isSet("error-log", false)) {
323 7464 : OutputDevice* logFile = &OutputDevice::getDevice(oc.getString("error-log"));
324 7464 : getErrorInstance()->addRetriever(logFile);
325 7464 : getWarningInstance()->addRetriever(logFile);
326 : }
327 94184 : if (oc.getBool("verbose")) {
328 9904 : getErrorInstance()->myInitialMessages.push_back("Repeating initial error messages:");
329 : } else {
330 84280 : getMessageInstance()->removeRetriever(&OutputDevice::getDevice("stdout"));
331 : }
332 47092 : }
333 :
334 :
335 : void
336 133060 : MsgHandler::cleanupOnEnd() {
337 133060 : delete myMessageInstance;
338 133060 : myMessageInstance = nullptr;
339 133060 : delete myWarningInstance;
340 133060 : myWarningInstance = nullptr;
341 133060 : delete myErrorInstance;
342 133060 : myErrorInstance = nullptr;
343 133060 : }
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 324640 : MsgHandler::MsgHandler(MsgType type) :
361 324640 : myType(type), myWasInformed(false), myAggregationThreshold(-1) {
362 324640 : if (type == MsgType::MT_MESSAGE) {
363 158016 : addRetriever(&OutputDevice::getDevice("stdout"));
364 : } else {
365 491264 : addRetriever(&OutputDevice::getDevice("stderr"));
366 : }
367 324640 : }
368 :
369 :
370 548872 : MsgHandler::~MsgHandler() {
371 548872 : }
372 :
373 :
374 : bool
375 460268 : MsgHandler::wasInformed() const {
376 460268 : return myWasInformed;
377 : }
378 :
379 :
380 : /****************************************************************************/
|