GCC Code Coverage Report


Directory: ./
File: basic/logger.cpp
Date: 2026-09-27 03:10:11
Exec Total Coverage
Lines: 206 328 62.8%
Functions: 38 57 66.7%
Branches: 126 368 34.2%

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 253 CERRStream() :
75
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
506 std::ostream(new CERRStreamBuff(this)),lock_(GEOGRAM_SPINLOCK_INIT) {
76 253 }
77 506 ~CERRStream() override{
78 506 }
79 void lock() {
80 Process::acquire_spinlock(lock_);
81 }
82 void unlock() {
83 Process::release_spinlock(lock_);
84 }
85 private:
86 Process::spinlock lock_;
87 class CERRStreamBuff : public std::stringbuf {
88 public:
89
1/2
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
253 CERRStreamBuff(CERRStream* stream) : stream_(stream) {
90 }
91 6639 int sync() override {
92 6639 std::cerr << this->str();
93
1/2
✓ Branch 2 taken 6639 times.
✗ Branch 3 not taken.
6639 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 5933 int LoggerStreamBuf::sync() {
108 5933 std::string str(this->str());
109 5933 loggerStream_->notify(str);
110
4/8
✓ Branch 1 taken 5933 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5933 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1069 times.
✓ Branch 7 taken 4864 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
11866 this->str("");
111 5933 return 0;
112 }
113
114 /************************************************************************/
115
116 2024 LoggerStream::LoggerStream(Logger* logger) :
117
1/2
✓ Branch 1 taken 1012 times.
✗ Branch 2 not taken.
2024 std::ostream(new LoggerStreamBuf(this)),
118 2024 logger_(logger) {
119 2024 }
120
121
1/2
✓ Branch 0 taken 1012 times.
✗ Branch 1 not taken.
2024 LoggerStream::~LoggerStream() {
122 std::streambuf* buf = rdbuf();
123
1/2
✓ Branch 0 taken 1012 times.
✗ Branch 1 not taken.
2024 delete buf;
124 2024 }
125
126 ✗ void LoggerStream::notify(const std::string& str) {
127
1/2
✓ Branch 2 taken 5933 times.
✗ Branch 3 not taken.
5933 logger_->notify(this, str);
128 5933 }
129
130 /************************************************************************/
131
132 506 LoggerClient::~LoggerClient() {
133 506 }
134
135 /************************************************************************/
136
137 253 ConsoleLogger::ConsoleLogger() {
138 253 }
139
140 1012 ConsoleLogger::~ConsoleLogger() {
141 1012 }
142
143 297 void ConsoleLogger::div(const std::string& title) {
144
1/2
✓ Branch 2 taken 297 times.
✗ Branch 3 not taken.
297 CmdLine::ui_separator(title);
145 297 }
146
147 4412 void ConsoleLogger::out(const std::string& str) {
148 4412 CmdLine::ui_message(str);
149 4412 }
150
151 20 void ConsoleLogger::warn(const std::string& str) {
152 20 CmdLine::ui_message(str);
153 20 }
154
155 48 void ConsoleLogger::err(const std::string& str) {
156 48 CmdLine::ui_message(str);
157 48 }
158
159 68 void ConsoleLogger::status(const std::string& str) {
160 geo_argused(str);
161 68 }
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 253 void Logger::initialize() {
226
1/2
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
253 instance_ = new Logger();
227 253 Environment::instance()->add_environment(instance_);
228 253 }
229
230
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
253 void Logger::terminate() {
231 instance_.reset();
232 253 }
233
234 12616 bool Logger::is_initialized() {
235 12616 return (instance_ != nullptr);
236 }
237
238 13729 bool Logger::set_local_value(
239 const std::string& name, const std::string& value
240 ) {
241
242
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 13476 times.
13729 if(name == "log:quiet") {
243 253 set_quiet(String::to_bool(value));
244 253 return true;
245 }
246
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13476 times.
13476 if(name == "log:minimal") {
248 ✗ set_minimal(String::to_bool(value));
249 ✗ return true;
250 }
251
252
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 13223 times.
13476 if(name == "log:pretty") {
253 253 set_pretty(String::to_bool(value));
254 253 return true;
255 }
256
257
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 12970 times.
13223 if(name == "log:file_name") {
258 253 log_file_name_ = value;
259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 253 times.
253 if(!log_file_name_.empty()) {
260 ✗ register_client(new FileLogger(log_file_name_));
261 }
262 253 return true;
263 }
264
265
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 12717 times.
12970 if(name == "log:features") {
266 std::vector<std::string> features;
267
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 String::split_string(value, ';', features);
268 log_features_.clear();
269
2/4
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
253 if(features.size() == 1 && features[0] == "*") {
270 253 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 253 times.
✗ Branch 2 not taken.
253 notify_observers(name);
278 return true;
279 253 }
280
281
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 12464 times.
12717 if(name == "log:features_exclude") {
282 std::vector<std::string> features;
283
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 String::split_string(value, ';', features);
284 log_features_exclude_.clear();
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 253 times.
506 for(size_t i = 0; i < features.size(); i++) {
286 log_features_exclude_.insert(features[i]);
287 }
288
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 notify_observers(name);
289 return true;
290 253 }
291
292 return false;
293 }
294
295 1999 bool Logger::get_local_value(
296 const std::string& name, std::string& value
297 ) const {
298
299
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 1894 times.
1999 if(name == "log:quiet") {
300 105 value = String::to_string(is_quiet());
301 105 return true;
302 }
303
304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1894 times.
1894 if(name == "log:minimal") {
305 ✗ value = String::to_string(is_minimal());
306 ✗ return true;
307 }
308
309
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 1785 times.
1894 if(name == "log:pretty") {
310 109 value = String::to_string(is_pretty());
311 109 return true;
312 }
313
314
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 1680 times.
1785 if(name == "log:file_name") {
315 105 value = log_file_name_;
316 105 return true;
317 }
318
319
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 1322 times.
1680 if(name == "log:features") {
320
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 if(log_everything_) {
321 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 358 return true;
332 }
333
334
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 964 times.
1322 if(name == "log:features_exclude") {
335 value = "";
336
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 for(auto& it : log_features_exclude_) {
337 ✗ if(value.length() != 0) {
338 value += ';';
339 }
340 value += it;
341 }
342 return true;
343 }
344
345 return false;
346 }
347
348
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
253 void Logger::register_client(LoggerClient* c) {
349 253 clients_.insert(c);
350 253 }
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 297 void Logger::set_quiet(bool flag) {
366 297 quiet_ = flag;
367 297 }
368
369 ✗ void Logger::set_minimal(bool flag) {
370 ✗ minimal_ = flag;
371 ✗ }
372
373 253 void Logger::set_pretty(bool flag) {
374 253 pretty_ = flag;
375 253 }
376
377
378
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 Logger::Logger() :
379 253 out_(this),
380
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 warn_(this),
381
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 err_(this),
382
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 status_(this),
383
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 log_everything_(true),
384 253 current_feature_changed_(false),
385 253 quiet_(true),
386 253 pretty_(true),
387 253 minimal_(false),
388 253 notifying_error_(false),
389
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 indent_(0)
390 {
391 // Add a default client printing stuff to std::cout
392
3/6
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 253 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 253 times.
✗ Branch 8 not taken.
253 register_client(new ConsoleLogger());
393 #ifdef GEO_DEBUG
394 quiet_ = false;
395 #endif
396
2/4
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 253 times.
✗ Branch 5 not taken.
253 err_console_ = new CERRStream;
397 253 }
398
399 1012 Logger::~Logger() {
400
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
506 delete err_console_;
401 506 err_console_ = nullptr;
402 1012 }
403
404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24300 times.
24300 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 0 not taken.
✓ Branch 1 taken 24300 times.
24300 if(instance_ == nullptr) {
410 std::cerr
411 << "CRITICAL: Accessing uninitialized Logger instance"
412 << std::endl;
413 ✗ geo_abort();
414 }
415 24300 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 297 std::ostream& Logger::div(const std::string& title) {
424 std::ostream& result =
425
2/4
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 297 times.
✗ Branch 5 not taken.
297 (is_initialized() && !Process::is_running_threads()) ?
426 297 instance()->div_stream(title) :
427 297 (instance()->err_console() << "=====" << title << std::endl);
428 297 return result;
429 }
430
431 12251 std::ostream& Logger::out(const std::string& feature) {
432 std::ostream& result =
433
3/4
✓ Branch 1 taken 12251 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5612 times.
✓ Branch 5 taken 6639 times.
12251 (is_initialized() && !Process::is_running_threads()) ?
434 5612 instance()->out_stream(feature) :
435 6639 (instance()->err_console() << " >>"
436 6639 << CmdLine::ui_feature(feature));
437
2/2
✓ Branch 1 taken 11686 times.
✓ Branch 2 taken 12251 times.
23937 for(index_t i=0; i<instance_->indent_; ++i) {
438 11686 result << "| ";
439 }
440 12251 return result;
441 }
442
443 48 std::ostream& Logger::err(const std::string& feature) {
444 std::ostream& result =
445
2/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
48 (is_initialized() && !Process::is_running_threads()) ?
446 48 instance()->err_stream(feature) :
447 48 (instance()->err_console() << "(E)-[" << feature << "] ");
448 48 return result;
449 }
450
451 20 std::ostream& Logger::warn(const std::string& feature) {
452 std::ostream& result =
453
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 (is_initialized() && !Process::is_running_threads()) ?
454 20 instance()->warn_stream(feature) :
455 20 (instance()->err_console() << "(W)-[" << feature << "] ");
456 20 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 297 std::ostream& Logger::div_stream(const std::string& title) {
468
1/2
✓ Branch 0 taken 297 times.
✗ Branch 1 not taken.
297 if(!quiet_) {
469 297 current_feature_changed_ = true;
470 current_feature_.clear();
471 LoggerClients clients = clients_; // clients_ may be modified !
472
2/2
✓ Branch 0 taken 297 times.
✓ Branch 1 taken 297 times.
594 for(auto it : clients) {
473
2/4
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 297 times.
✗ Branch 5 not taken.
297 it->div(title);
474 }
475 }
476 297 return out_;
477 }
478
479 5612 std::ostream& Logger::out_stream(const std::string& feature) {
480
5/6
✓ Branch 0 taken 4412 times.
✓ Branch 1 taken 1200 times.
✓ Branch 2 taken 4412 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2355 times.
✓ Branch 5 taken 2057 times.
5612 if(!quiet_ && !minimal_ && current_feature_ != feature) {
481 2355 current_feature_changed_ = true;
482 current_feature_ = feature;
483 }
484 5612 return out_;
485 }
486
487 48 std::ostream& Logger::err_stream(const std::string& feature) {
488
3/4
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 38 times.
48 if(!quiet_ && current_feature_ != feature) {
489 10 current_feature_changed_ = true;
490 current_feature_ = feature;
491 }
492 48 return err_;
493 }
494
495 20 std::ostream& Logger::warn_stream(const std::string& feature) {
496
3/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 11 times.
20 if(!quiet_ && current_feature_ != feature) {
497 9 current_feature_changed_ = true;
498 current_feature_ = feature;
499 }
500 20 return warn_;
501 }
502
503 ✗ std::ostream& Logger::status_stream() {
504 ✗ return status_;
505 }
506
507 4412 void Logger::notify_out(const std::string& message) {
508 if(
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4412 times.
4412 (log_everything_ &&
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4412 times.
4412 log_features_exclude_.find(current_feature_) ==
511 log_features_exclude_.end())
512
1/4
✓ Branch 0 taken 4412 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4412 || (log_features_.find(current_feature_) != log_features_.end())
513 ) {
514 std::string feat_msg =
515
1/2
✓ Branch 2 taken 4412 times.
✗ Branch 3 not taken.
8824 CmdLine::ui_feature(current_feature_, current_feature_changed_)
516
1/2
✓ Branch 1 taken 4412 times.
✗ Branch 2 not taken.
4412 + message;
517
518 LoggerClients clients = clients_; // clients_ may be modified !
519
2/2
✓ Branch 0 taken 4412 times.
✓ Branch 1 taken 4412 times.
8824 for(auto it : clients) {
520
2/4
✓ Branch 1 taken 4412 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4412 times.
✗ Branch 5 not taken.
4412 it->out(feat_msg);
521 }
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 4412 current_feature_changed_ = true;
528 }
529 4412 }
530
531 20 void Logger::notify_warn(const std::string& message) {
532 20 std::string msg = "Warning: " + message;
533 std::string feat_msg =
534
2/6
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
40 CmdLine::ui_feature(current_feature_, current_feature_changed_)
535
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 + msg;
536
537 LoggerClients clients = clients_; // clients_ may be modified !
538
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 for(auto it : clients) {
539
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 it->warn(feat_msg);
540
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 it->status(msg);
541 }
542
543 20 current_feature_changed_ = false;
544 20 }
545
546 48 void Logger::notify_err(const std::string& message) {
547 48 std::string msg = "Error: " + message;
548 std::string feat_msg =
549
1/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
48 CmdLine::ui_feature(current_feature_, current_feature_changed_)
550
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 + msg;
551
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if(notifying_error_) {
553 std::cerr << "Error while displaying error (!):"
554 << feat_msg << std::endl;
555 } else {
556
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 notifying_error_ = true;
557 LoggerClients clients = clients_; // clients_ may be modified !
558
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
96 for(auto it : clients) {
559
2/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
48 it->err(feat_msg);
560
2/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
48 it->status(msg);
561 }
562 48 notifying_error_ = false;
563 }
564
565
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 current_feature_changed_ = false;
566 48 }
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 5933 void Logger::notify(LoggerStream* s, const std::string& message) {
578
579
4/8
✓ Branch 0 taken 4480 times.
✓ Branch 1 taken 1453 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4480 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4480 times.
✗ Branch 7 not taken.
5933 if(quiet_ || (minimal_ && s == &out_) || clients_.empty()) {
580 return;
581 }
582
583
2/2
✓ Branch 0 taken 4412 times.
✓ Branch 1 taken 68 times.
4480 if(s == &out_) {
584 4412 notify_out(message);
585
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 48 times.
68 } else if(s == &warn_) {
586 20 notify_warn(message);
587
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 } else if(s == &err_) {
588 48 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