GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/logger.cpp
Date: 2026-09-07 02:36:43
Exec Total Coverage
Lines: 246 406 60.6%
Functions: 42 60 70.0%
Branches: 161 515 31.3%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/basic/logger.h>
41 #include <geogram/basic/assert.h>
42 #include <geogram/basic/command_line.h>
43 #include <geogram/basic/argused.h>
44 #include <geogram/basic/file_system.h>
45 #include <geogram/basic/process.h>
46
47
48 #include <stdlib.h>
49 #include <stdarg.h>
50
51 /*
52 Disables the warning caused by passing 'this' as an argument while
53 construction is not finished (in LoggerStream ctor).
54 As LoggerStreamBuf only stores the pointer for later use, so we can
55 ignore the fact that 'this' is not completely formed yet.
56 */
57 #ifdef GEO_OS_WINDOWS
58 #pragma warning(disable:4355)
59 #endif
60
61
62 namespace {
63 using namespace GEO;
64
65 /**
66 * \brief The output stream returned by Logger::err_console()
67 * \details It has a locking mechanism that avoids messages sent by different
68 * threads to be mixed.
69 * \see Logger::err_console(), Logger::out(), Logger::err(), Logger::warn(),
70 * Logger::status()
71 */
72 class CERRStream : public std::ostream {
73 public:
74 249 CERRStream() :
75
4/10
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 249 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 249 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 249 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
249 std::ostream(new CERRStreamBuff(this)),lock_(GEOGRAM_SPINLOCK_INIT) {
76 249 }
77 996 ~CERRStream() override{
78 996 }
79 6639 void lock() {
80 6639 Process::acquire_spinlock(lock_);
81 6639 }
82 6639 void unlock() {
83 6639 Process::release_spinlock(lock_);
84 6639 }
85 private:
86 Process::spinlock lock_;
87 class CERRStreamBuff : public std::stringbuf {
88 public:
89 249 CERRStreamBuff(CERRStream* stream) : stream_(stream) {
90 249 }
91 6639 int sync() override {
92
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
6639 std::cerr << this->str();
93
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
13278 this->str("");
94 6639 stream_->unlock();
95 6639 return 0;
96 }
97 private:
98 CERRStream* stream_;
99 };
100 };
101 }
102
103 namespace GEO {
104
105 /************************************************************************/
106
107 5736 int LoggerStreamBuf::sync() {
108
1/2
✓ Branch 1 taken 5736 times.
✗ Branch 2 not taken.
5736 std::string str(this->str());
109
1/2
✓ Branch 1 taken 5736 times.
✗ Branch 2 not taken.
5736 loggerStream_->notify(str);
110
2/4
✓ Branch 1 taken 5736 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5736 times.
✗ Branch 5 not taken.
11472 this->str("");
111 5736 return 0;
112 5736 }
113
114 /************************************************************************/
115
116 1992 LoggerStream::LoggerStream(Logger* logger) :
117 std::ostream(new LoggerStreamBuf(this)),
118
4/11
✓ Branch 2 taken 996 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 996 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 996 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 996 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
1992 logger_(logger) {
119 1992 }
120
121 1992 LoggerStream::~LoggerStream() {
122 1992 std::streambuf* buf = rdbuf();
123
1/2
✓ Branch 0 taken 996 times.
✗ Branch 1 not taken.
1992 delete buf;
124 1992 }
125
126 5736 void LoggerStream::notify(const std::string& str) {
127 5736 logger_->notify(this, str);
128 5736 }
129
130 /************************************************************************/
131
132 498 LoggerClient::~LoggerClient() {
133 498 }
134
135 /************************************************************************/
136
137 249 ConsoleLogger::ConsoleLogger() {
138 249 }
139
140 996 ConsoleLogger::~ConsoleLogger() {
141 996 }
142
143 291 void ConsoleLogger::div(const std::string& title) {
144
2/4
✓ Branch 1 taken 291 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 291 times.
✗ Branch 5 not taken.
291 CmdLine::ui_separator(title);
145 291 }
146
147 5670 void ConsoleLogger::out(const std::string& str) {
148 5670 CmdLine::ui_message(str);
149 5670 }
150
151 19 void ConsoleLogger::warn(const std::string& str) {
152 19 CmdLine::ui_message(str);
153 19 }
154
155 47 void ConsoleLogger::err(const std::string& str) {
156 47 CmdLine::ui_message(str);
157 47 }
158
159 66 void ConsoleLogger::status(const std::string& str) {
160 66 geo_argused(str);
161 66 }
162
163 /************************************************************************/
164
165 FileLogger::FileLogger() :
166 log_file_(nullptr) {
167 }
168
169 FileLogger::FileLogger(const std::string& file_name) :
170 log_file_(nullptr)
171 {
172 set_file_name(file_name);
173 }
174
175 FileLogger::~FileLogger() {
176 delete log_file_;
177 log_file_ = nullptr;
178 }
179
180 void FileLogger::set_file_name(const std::string& file_name) {
181 log_file_name_ = file_name;
182 if(log_file_ != nullptr) {
183 delete log_file_;
184 log_file_ = nullptr;
185 }
186 if(log_file_name_.length() != 0) {
187 log_file_ = new std::ofstream(log_file_name_.c_str());
188 }
189 }
190
191 void FileLogger::div(const std::string& title) {
192 if(log_file_ != nullptr) {
193 *log_file_
194 << "\n====[" << title << "]====\n"
195 << std::flush;
196 }
197 }
198
199 void FileLogger::out(const std::string& str) {
200 if(log_file_ != nullptr) {
201 *log_file_ << str << std::flush;
202 }
203 }
204
205 void FileLogger::warn(const std::string& str) {
206 if(log_file_ != nullptr) {
207 *log_file_ << str << std::flush;
208 }
209 }
210
211 void FileLogger::err(const std::string& str) {
212 if(log_file_ != nullptr) {
213 *log_file_ << str << std::flush;
214 }
215 }
216
217 void FileLogger::status(const std::string& str) {
218 geo_argused(str);
219 }
220
221 /************************************************************************/
222
223 SmartPointer<Logger> Logger::instance_;
224
225 249 void Logger::initialize() {
226
3/8
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 249 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 249 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
249 instance_ = new Logger();
227 249 Environment::instance()->add_environment(instance_);
228 249 }
229
230 249 void Logger::terminate() {
231 249 instance_.reset();
232 249 }
233
234 12417 bool Logger::is_initialized() {
235 12417 return (instance_ != nullptr);
236 }
237
238 13501 bool Logger::set_local_value(
239 const std::string& name, const std::string& value
240 ) {
241
242
2/2
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 13252 times.
13501 if(name == "log:quiet") {
243 249 set_quiet(String::to_bool(value));
244 249 return true;
245 }
246
247
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13252 times.
13252 if(name == "log:minimal") {
248 set_minimal(String::to_bool(value));
249 return true;
250 }
251
252
2/2
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 13003 times.
13252 if(name == "log:pretty") {
253 249 set_pretty(String::to_bool(value));
254 249 return true;
255 }
256
257
2/2
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 12754 times.
13003 if(name == "log:file_name") {
258 249 log_file_name_ = value;
259
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
249 if(!log_file_name_.empty()) {
260 register_client(new FileLogger(log_file_name_));
261 }
262 249 return true;
263 }
264
265
2/2
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 12505 times.
12754 if(name == "log:features") {
266 249 std::vector<std::string> features;
267
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 String::split_string(value, ';', features);
268 249 log_features_.clear();
269
4/8
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 249 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 249 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 249 times.
✗ Branch 10 not taken.
249 if(features.size() == 1 && features[0] == "*") {
270 249 log_everything_ = true;
271 } else {
272 log_everything_ = false;
273 for(size_t i = 0; i < features.size(); i++) {
274 log_features_.insert(features[i]);
275 }
276 }
277
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 notify_observers(name);
278 249 return true;
279 249 }
280
281
2/2
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 12256 times.
12505 if(name == "log:features_exclude") {
282 249 std::vector<std::string> features;
283
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 String::split_string(value, ';', features);
284 249 log_features_exclude_.clear();
285
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
249 for(size_t i = 0; i < features.size(); i++) {
286 log_features_exclude_.insert(features[i]);
287 }
288
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 notify_observers(name);
289 249 return true;
290 249 }
291
292 12256 return false;
293 }
294
295 1971 bool Logger::get_local_value(
296 const std::string& name, std::string& value
297 ) const {
298
299
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 1866 times.
1971 if(name == "log:quiet") {
300
1/2
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
105 value = String::to_string(is_quiet());
301 105 return true;
302 }
303
304
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1866 times.
1866 if(name == "log:minimal") {
305 value = String::to_string(is_minimal());
306 return true;
307 }
308
309
2/2
✓ Branch 1 taken 109 times.
✓ Branch 2 taken 1757 times.
1866 if(name == "log:pretty") {
310
1/2
✓ Branch 2 taken 109 times.
✗ Branch 3 not taken.
109 value = String::to_string(is_pretty());
311 109 return true;
312 }
313
314
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 1652 times.
1757 if(name == "log:file_name") {
315 105 value = log_file_name_;
316 105 return true;
317 }
318
319
2/2
✓ Branch 1 taken 354 times.
✓ Branch 2 taken 1298 times.
1652 if(name == "log:features") {
320
1/2
✓ Branch 0 taken 354 times.
✗ Branch 1 not taken.
354 if(log_everything_) {
321 354 value = "*";
322 } else {
323 value = "";
324 for(auto& it : log_features_) {
325 if(value.length() != 0) {
326 value += ';';
327 }
328 value += it;
329 }
330 }
331 354 return true;
332 }
333
334
2/2
✓ Branch 1 taken 354 times.
✓ Branch 2 taken 944 times.
1298 if(name == "log:features_exclude") {
335 354 value = "";
336
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 354 times.
354 for(auto& it : log_features_exclude_) {
337 if(value.length() != 0) {
338 value += ';';
339 }
340 value += it;
341 }
342 354 return true;
343 }
344
345 944 return false;
346 }
347
348 249 void Logger::register_client(LoggerClient* c) {
349
1/2
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 clients_.insert(c);
350 249 }
351
352 void Logger::unregister_client(LoggerClient* c) {
353 geo_debug_assert(clients_.find(c) != clients_.end());
354 clients_.erase(c);
355 }
356
357 void Logger::unregister_all_clients() {
358 clients_.clear();
359 }
360
361 bool Logger::is_client(LoggerClient* c) const {
362 return clients_.find(c) != clients_.end();
363 }
364
365 293 void Logger::set_quiet(bool flag) {
366 293 quiet_ = flag;
367 293 }
368
369 void Logger::set_minimal(bool flag) {
370 minimal_ = flag;
371 }
372
373 249 void Logger::set_pretty(bool flag) {
374 249 pretty_ = flag;
375 249 }
376
377
378 249 Logger::Logger() :
379 249 out_(this),
380
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 warn_(this),
381
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 err_(this),
382
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 status_(this),
383 249 log_everything_(true),
384 249 current_feature_changed_(false),
385 249 quiet_(true),
386 249 pretty_(true),
387 249 minimal_(false),
388 249 notifying_error_(false),
389
1/2
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
498 indent_(0)
390 {
391 // Add a default client printing stuff to std::cout
392
4/10
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 249 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 249 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
249 register_client(new ConsoleLogger());
393 #ifdef GEO_DEBUG
394 249 quiet_ = false;
395 #endif
396
3/8
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 249 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
249 err_console_ = new CERRStream;
397 249 }
398
399 996 Logger::~Logger() {
400
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
498 delete err_console_;
401 498 err_console_ = nullptr;
402 996 }
403
404 26521 Logger* Logger::instance() {
405 // Do not use geo_assert here:
406 // if the instance is nullptr, geo_assert will
407 // call the Logger to print the assertion failure, thus ending in a
408 // infinite loop.
409
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26521 times.
26521 if(instance_ == nullptr) {
410 std::cerr
411 << "CRITICAL: Accessing uninitialized Logger instance"
412 << std::endl;
413 geo_abort();
414 }
415 26521 return instance_;
416 }
417
418 6639 std::ostream& Logger::err_console() {
419 6639 static_cast<CERRStream*>(err_console_)->lock();
420 6639 return *err_console_;
421 }
422
423 291 std::ostream& Logger::div(const std::string& title) {
424 std::ostream& result =
425
2/4
✓ Branch 1 taken 291 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 291 times.
✗ Branch 5 not taken.
291 (is_initialized() && !Process::is_running_threads()) ?
426 291 instance()->div_stream(title) :
427 291 (instance()->err_console() << "=====" << title << std::endl);
428 291 return result;
429 }
430
431 12060 std::ostream& Logger::out(const std::string& feature) {
432 std::ostream& result =
433
4/6
✓ Branch 0 taken 12060 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 12060 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5421 times.
✓ Branch 6 taken 6639 times.
12060 (is_initialized() && !Process::is_running_threads()) ?
434
2/4
✓ Branch 1 taken 5421 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5421 times.
✗ Branch 5 not taken.
5421 instance()->out_stream(feature) :
435
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
6639 (instance()->err_console() << " >>"
436
6/12
✓ Branch 1 taken 12060 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6639 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6639 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6639 times.
✓ Branch 13 taken 5421 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
24120 << CmdLine::ui_feature(feature));
437
2/2
✓ Branch 1 taken 11441 times.
✓ Branch 2 taken 12060 times.
23501 for(index_t i=0; i<instance_->indent_; ++i) {
438 11441 result << "| ";
439 }
440 12060 return result;
441 }
442
443 47 std::ostream& Logger::err(const std::string& feature) {
444 std::ostream& result =
445
2/4
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
47 (is_initialized() && !Process::is_running_threads()) ?
446 47 instance()->err_stream(feature) :
447 47 (instance()->err_console() << "(E)-[" << feature << "] ");
448 47 return result;
449 }
450
451 19 std::ostream& Logger::warn(const std::string& feature) {
452 std::ostream& result =
453
2/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
19 (is_initialized() && !Process::is_running_threads()) ?
454 19 instance()->warn_stream(feature) :
455 19 (instance()->err_console() << "(W)-[" << feature << "] ");
456 19 return result;
457 }
458
459 std::ostream& Logger::status() {
460 std::ostream& result =
461 (is_initialized() && !Process::is_running_threads()) ?
462 instance()->status_stream() :
463 (instance()->err_console() << "[status] ");
464 return result;
465 }
466
467 291 std::ostream& Logger::div_stream(const std::string& title) {
468
1/2
✓ Branch 0 taken 291 times.
✗ Branch 1 not taken.
291 if(!quiet_) {
469 291 current_feature_changed_ = true;
470 291 current_feature_.clear();
471
1/2
✓ Branch 1 taken 291 times.
✗ Branch 2 not taken.
291 LoggerClients clients = clients_; // clients_ may be modified !
472
3/4
✓ Branch 4 taken 291 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 291 times.
✓ Branch 9 taken 291 times.
582 for(auto it : clients) {
473
2/4
✓ Branch 1 taken 291 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 291 times.
✗ Branch 5 not taken.
291 it->div(title);
474 291 }
475 291 }
476 291 return out_;
477 }
478
479 5421 std::ostream& Logger::out_stream(const std::string& feature) {
480
6/8
✓ Branch 0 taken 5421 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5421 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2712 times.
✓ Branch 6 taken 2709 times.
✓ Branch 7 taken 2712 times.
✓ Branch 8 taken 2709 times.
5421 if(!quiet_ && !minimal_ && current_feature_ != feature) {
481 2712 current_feature_changed_ = true;
482 2712 current_feature_ = feature;
483 }
484 5421 return out_;
485 }
486
487 47 std::ostream& Logger::err_stream(const std::string& feature) {
488
5/6
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 38 times.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 38 times.
47 if(!quiet_ && current_feature_ != feature) {
489 9 current_feature_changed_ = true;
490 9 current_feature_ = feature;
491 }
492 47 return err_;
493 }
494
495 19 std::ostream& Logger::warn_stream(const std::string& feature) {
496
5/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 11 times.
19 if(!quiet_ && current_feature_ != feature) {
497 8 current_feature_changed_ = true;
498 8 current_feature_ = feature;
499 }
500 19 return warn_;
501 }
502
503 std::ostream& Logger::status_stream() {
504 return status_;
505 }
506
507 5670 void Logger::notify_out(const std::string& message) {
508 5670 if(
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5670 times.
5670 (log_everything_ &&
510
1/2
✓ Branch 1 taken 5670 times.
✗ Branch 2 not taken.
5670 log_features_exclude_.find(current_feature_) ==
511 5670 log_features_exclude_.end())
512
2/8
✓ Branch 0 taken 5670 times.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 5670 times.
✗ Branch 10 not taken.
11340 || (log_features_.find(current_feature_) != log_features_.end())
513 ) {
514 std::string feat_msg =
515
1/2
✓ Branch 1 taken 5670 times.
✗ Branch 2 not taken.
11340 CmdLine::ui_feature(current_feature_, current_feature_changed_)
516
1/2
✓ Branch 1 taken 5670 times.
✗ Branch 2 not taken.
5670 + message;
517
518
1/2
✓ Branch 1 taken 5670 times.
✗ Branch 2 not taken.
5670 LoggerClients clients = clients_; // clients_ may be modified !
519
3/4
✓ Branch 4 taken 5670 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 5670 times.
✓ Branch 9 taken 5670 times.
11340 for(auto it : clients) {
520
2/4
✓ Branch 1 taken 5670 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5670 times.
✗ Branch 5 not taken.
5670 it->out(feat_msg);
521 5670 }
522
523 // There is a mechanism for not repeating feature when it is
524 // the same as in previous message, but finally I systematically
525 // display feature (else the log is not super easy to read,
526 // especially when there are nested Stopwatches).
527 5670 current_feature_changed_ = true;
528 5670 }
529 5670 }
530
531 19 void Logger::notify_warn(const std::string& message) {
532
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 std::string msg = "Warning: " + message;
533 std::string feat_msg =
534
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
38 CmdLine::ui_feature(current_feature_, current_feature_changed_)
535
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 + msg;
536
537
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 LoggerClients clients = clients_; // clients_ may be modified !
538
3/4
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 19 times.
✓ Branch 9 taken 19 times.
38 for(auto it : clients) {
539
2/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
19 it->warn(feat_msg);
540
2/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
19 it->status(msg);
541 19 }
542
543 19 current_feature_changed_ = false;
544 19 }
545
546 47 void Logger::notify_err(const std::string& message) {
547
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 std::string msg = "Error: " + message;
548 std::string feat_msg =
549
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
94 CmdLine::ui_feature(current_feature_, current_feature_changed_)
550
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 + msg;
551
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
47 if(notifying_error_) {
553 std::cerr << "Error while displaying error (!):"
554 << feat_msg << std::endl;
555 } else {
556 47 notifying_error_ = true;
557
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
47 LoggerClients clients = clients_; // clients_ may be modified !
558
3/4
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 47 times.
✓ Branch 9 taken 47 times.
94 for(auto it : clients) {
559
2/4
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
47 it->err(feat_msg);
560
2/4
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
47 it->status(msg);
561 47 }
562 47 notifying_error_ = false;
563 47 }
564
565 47 current_feature_changed_ = false;
566 47 }
567
568 void Logger::notify_status(const std::string& message) {
569 LoggerClients clients = clients_; // clients_ may be modified !
570 for(auto it : clients) {
571 it->status(message);
572 }
573
574 current_feature_changed_ = false;
575 }
576
577 5736 void Logger::notify(LoggerStream* s, const std::string& message) {
578
579
4/10
✓ Branch 0 taken 5736 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5736 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5736 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5736 times.
5736 if(quiet_ || (minimal_ && s == &out_) || clients_.empty()) {
580 return;
581 }
582
583
2/2
✓ Branch 0 taken 5670 times.
✓ Branch 1 taken 66 times.
5736 if(s == &out_) {
584 5670 notify_out(message);
585
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 47 times.
66 } else if(s == &warn_) {
586 19 notify_warn(message);
587
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 } else if(s == &err_) {
588 47 notify_err(message);
589 } else if(s == &status_) {
590 notify_status(message);
591 } else {
592 geo_assert_not_reached;
593 }
594 }
595
596 /************************************************************************/
597
598 }
599
600 extern "C" {
601
602 int geogram_printf(const char* format, ...) {
603
604 static std::string last_string;
605
606 va_list args;
607
608 // Get the number of characters to be printed.
609 va_start(args, format);
610 int nb = vsnprintf(nullptr, 0, format, args)+1; // +1, I don't know why.
611 va_end(args);
612
613 // Generate the output string
614 GEO::vector<char> buffer(GEO::index_t(nb+1));
615 va_start(args, format);
616 vsnprintf(buffer.data(),buffer.size()-1, format, args);
617 va_end(args);
618
619 // Find the lines in the generated string
620 GEO::vector<char*> lines;
621 lines.push_back(buffer.data());
622 char last_char = '\n';
623 for(char* ptr = buffer.data(); *ptr; ptr++) {
624 if(*ptr != '\0') {
625 last_char = *ptr;
626 }
627 if(*ptr == '\n') {
628 *ptr = '\0';
629 ptr++;
630 if(*ptr != '\0') {
631 lines.push_back(ptr);
632 }
633 }
634 }
635
636 // If last character is not a carriage return,
637 // memorize the last line for later.
638 if(last_char != '\n') {
639 last_string += *lines.rbegin();
640 lines.pop_back();
641 }
642
643 // Output all the lines.
644 // Prepend the optionally memorized previous strings to the
645 // first one.
646 for(GEO::index_t i=0; i<lines.size(); ++i) {
647 if(i == 0) {
648 GEO::Logger::out("") << last_string << lines[i] << std::endl;
649 last_string.clear();
650 } else {
651 GEO::Logger::out("") << lines[i] << std::endl;
652 }
653 }
654
655 return nb;
656 }
657
658 int geogram_fprintf(FILE* out, const char* format, ...) {
659
660
661 static std::string last_string;
662
663 va_list args;
664
665 // Get the number of characters to be printed.
666 va_start(args, format);
667 int nb = vsnprintf(nullptr, 0, format, args)+1; // +1, I don't know why...
668 va_end(args);
669
670 // Generate the output string
671 GEO::vector<char> buffer(GEO::index_t(nb+1));
672 va_start(args, format);
673 vsnprintf(buffer.data(),buffer.size()-1, format, args);
674 va_end(args);
675
676 // Find the lines in the generated string
677 GEO::vector<char*> lines;
678 lines.push_back(buffer.data());
679 char last_char = '\n';
680 for(char* ptr = buffer.data(); *ptr; ptr++) {
681 if(*ptr != '\0') {
682 last_char = *ptr;
683 }
684 if(*ptr == '\n') {
685 *ptr = '\0';
686 ptr++;
687 if(*ptr != '\0') {
688 lines.push_back(ptr);
689 }
690 }
691 }
692
693 // If last character is not a carriage return,
694 // memorize the last line for later.
695 if(last_char != '\n') {
696 last_string += *lines.rbegin();
697 lines.pop_back();
698 }
699
700 // Output all the lines.
701 // Prepend the optionally memorized previous strings to the
702 // first one.
703 for(GEO::index_t i=0; i<lines.size(); ++i) {
704 if(i == 0) {
705 if(out == stdout) {
706 GEO::Logger::out("") << last_string << lines[i] << std::endl;
707 } else if(out == stderr) {
708 GEO::Logger::err("") << last_string << lines[i] << std::endl;
709 } else {
710 fprintf(out, "%s%s", last_string.c_str(), lines[i]);
711 }
712 last_string.clear();
713 } else {
714 if(out == stdout) {
715 GEO::Logger::out("") << lines[i] << std::endl;
716 } else if(out == stderr) {
717 GEO::Logger::err("") << lines[i] << std::endl;
718 } else {
719 fprintf(out, "%s", lines[i]);
720 }
721 }
722 }
723
724 return nb;
725 }
726 }
727