GCC Code Coverage Report


Directory: ./
File: basic/logger.cpp
Date: 2026-09-27 03:12:47
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 255 CERRStream() :
75
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
510 std::ostream(new CERRStreamBuff(this)),lock_(GEOGRAM_SPINLOCK_INIT) {
76 255 }
77 510 ~CERRStream() override{
78 510 }
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 255 times.
✗ Branch 3 not taken.
255 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 6019 int LoggerStreamBuf::sync() {
108 6019 std::string str(this->str());
109 6019 loggerStream_->notify(str);
110
4/8
✓ Branch 1 taken 6019 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6019 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1089 times.
✓ Branch 7 taken 4930 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
12038 this->str("");
111 6019 return 0;
112 }
113
114 /************************************************************************/
115
116 2040 LoggerStream::LoggerStream(Logger* logger) :
117
1/2
✓ Branch 1 taken 1020 times.
✗ Branch 2 not taken.
2040 std::ostream(new LoggerStreamBuf(this)),
118 2040 logger_(logger) {
119 2040 }
120
121
1/2
✓ Branch 0 taken 1020 times.
✗ Branch 1 not taken.
2040 LoggerStream::~LoggerStream() {
122 std::streambuf* buf = rdbuf();
123
1/2
✓ Branch 0 taken 1020 times.
✗ Branch 1 not taken.
2040 delete buf;
124 2040 }
125
126 ✗ void LoggerStream::notify(const std::string& str) {
127
1/2
✓ Branch 2 taken 6019 times.
✗ Branch 3 not taken.
6019 logger_->notify(this, str);
128 6019 }
129
130 /************************************************************************/
131
132 510 LoggerClient::~LoggerClient() {
133 510 }
134
135 /************************************************************************/
136
137 255 ConsoleLogger::ConsoleLogger() {
138 255 }
139
140 1020 ConsoleLogger::~ConsoleLogger() {
141 1020 }
142
143 299 void ConsoleLogger::div(const std::string& title) {
144
1/2
✓ Branch 2 taken 299 times.
✗ Branch 3 not taken.
299 CmdLine::ui_separator(title);
145 299 }
146
147 4498 void ConsoleLogger::out(const std::string& str) {
148 4498 CmdLine::ui_message(str);
149 4498 }
150
151 13 void ConsoleLogger::warn(const std::string& str) {
152 13 CmdLine::ui_message(str);
153 13 }
154
155 43 void ConsoleLogger::err(const std::string& str) {
156 43 CmdLine::ui_message(str);
157 43 }
158
159 56 void ConsoleLogger::status(const std::string& str) {
160 geo_argused(str);
161 56 }
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 255 void Logger::initialize() {
226
1/2
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
255 instance_ = new Logger();
227 255 Environment::instance()->add_environment(instance_);
228 255 }
229
230
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 void Logger::terminate() {
231 instance_.reset();
232 255 }
233
234 12702 bool Logger::is_initialized() {
235 12702 return (instance_ != nullptr);
236 }
237
238 13860 bool Logger::set_local_value(
239 const std::string& name, const std::string& value
240 ) {
241
242
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 13605 times.
13860 if(name == "log:quiet") {
243 255 set_quiet(String::to_bool(value));
244 255 return true;
245 }
246
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13605 times.
13605 if(name == "log:minimal") {
248 ✗ set_minimal(String::to_bool(value));
249 ✗ return true;
250 }
251
252
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 13350 times.
13605 if(name == "log:pretty") {
253 255 set_pretty(String::to_bool(value));
254 255 return true;
255 }
256
257
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 13095 times.
13350 if(name == "log:file_name") {
258 255 log_file_name_ = value;
259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 255 times.
255 if(!log_file_name_.empty()) {
260 ✗ register_client(new FileLogger(log_file_name_));
261 }
262 255 return true;
263 }
264
265
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 12840 times.
13095 if(name == "log:features") {
266 std::vector<std::string> features;
267
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 String::split_string(value, ';', features);
268 log_features_.clear();
269
2/4
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
255 if(features.size() == 1 && features[0] == "*") {
270 255 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 255 times.
✗ Branch 2 not taken.
255 notify_observers(name);
278 return true;
279 255 }
280
281
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 12585 times.
12840 if(name == "log:features_exclude") {
282 std::vector<std::string> features;
283
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 String::split_string(value, ';', features);
284 log_features_exclude_.clear();
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 255 times.
510 for(size_t i = 0; i < features.size(); i++) {
286 log_features_exclude_.insert(features[i]);
287 }
288
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 notify_observers(name);
289 return true;
290 255 }
291
292 return false;
293 }
294
295 2012 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 1907 times.
2012 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 1907 times.
1907 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 1798 times.
1907 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 1693 times.
1798 if(name == "log:file_name") {
315 105 value = log_file_name_;
316 105 return true;
317 }
318
319
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 1333 times.
1693 if(name == "log:features") {
320
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 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 360 return true;
332 }
333
334
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 973 times.
1333 if(name == "log:features_exclude") {
335 value = "";
336
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 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 255 times.
✗ Branch 1 not taken.
255 void Logger::register_client(LoggerClient* c) {
349 255 clients_.insert(c);
350 255 }
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 299 void Logger::set_quiet(bool flag) {
366 299 quiet_ = flag;
367 299 }
368
369 ✗ void Logger::set_minimal(bool flag) {
370 ✗ minimal_ = flag;
371 ✗ }
372
373 255 void Logger::set_pretty(bool flag) {
374 255 pretty_ = flag;
375 255 }
376
377
378
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 Logger::Logger() :
379 255 out_(this),
380
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 warn_(this),
381
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 err_(this),
382
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 status_(this),
383
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 log_everything_(true),
384 255 current_feature_changed_(false),
385 255 quiet_(true),
386 255 pretty_(true),
387 255 minimal_(false),
388 255 notifying_error_(false),
389
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 indent_(0)
390 {
391 // Add a default client printing stuff to std::cout
392
3/6
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 255 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 255 times.
✗ Branch 8 not taken.
255 register_client(new ConsoleLogger());
393 #ifdef GEO_DEBUG
394 quiet_ = false;
395 #endif
396
2/4
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 255 times.
✗ Branch 5 not taken.
255 err_console_ = new CERRStream;
397 255 }
398
399 1020 Logger::~Logger() {
400
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
510 delete err_console_;
401 510 err_console_ = nullptr;
402 1020 }
403
404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24559 times.
24559 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 24559 times.
24559 if(instance_ == nullptr) {
410 std::cerr
411 << "CRITICAL: Accessing uninitialized Logger instance"
412 << std::endl;
413 ✗ geo_abort();
414 }
415 24559 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 299 std::ostream& Logger::div(const std::string& title) {
424 std::ostream& result =
425
2/4
✓ Branch 1 taken 299 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 299 times.
✗ Branch 5 not taken.
299 (is_initialized() && !Process::is_running_threads()) ?
426 299 instance()->div_stream(title) :
427 299 (instance()->err_console() << "=====" << title << std::endl);
428 299 return result;
429 }
430
431 12347 std::ostream& Logger::out(const std::string& feature) {
432 std::ostream& result =
433
3/4
✓ Branch 1 taken 12347 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5708 times.
✓ Branch 5 taken 6639 times.
12347 (is_initialized() && !Process::is_running_threads()) ?
434 5708 instance()->out_stream(feature) :
435 6639 (instance()->err_console() << " >>"
436 6639 << CmdLine::ui_feature(feature));
437
2/2
✓ Branch 1 taken 11793 times.
✓ Branch 2 taken 12347 times.
24140 for(index_t i=0; i<instance_->indent_; ++i) {
438 11793 result << "| ";
439 }
440 12347 return result;
441 }
442
443 43 std::ostream& Logger::err(const std::string& feature) {
444 std::ostream& result =
445
2/4
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
43 (is_initialized() && !Process::is_running_threads()) ?
446 43 instance()->err_stream(feature) :
447 43 (instance()->err_console() << "(E)-[" << feature << "] ");
448 43 return result;
449 }
450
451 13 std::ostream& Logger::warn(const std::string& feature) {
452 std::ostream& result =
453
2/4
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
13 (is_initialized() && !Process::is_running_threads()) ?
454 13 instance()->warn_stream(feature) :
455 13 (instance()->err_console() << "(W)-[" << feature << "] ");
456 13 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 299 std::ostream& Logger::div_stream(const std::string& title) {
468
1/2
✓ Branch 0 taken 299 times.
✗ Branch 1 not taken.
299 if(!quiet_) {
469 299 current_feature_changed_ = true;
470 current_feature_.clear();
471 LoggerClients clients = clients_; // clients_ may be modified !
472
2/2
✓ Branch 0 taken 299 times.
✓ Branch 1 taken 299 times.
598 for(auto it : clients) {
473
2/4
✓ Branch 1 taken 299 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 299 times.
✗ Branch 5 not taken.
299 it->div(title);
474 }
475 }
476 299 return out_;
477 }
478
479 5708 std::ostream& Logger::out_stream(const std::string& feature) {
480
5/6
✓ Branch 0 taken 4498 times.
✓ Branch 1 taken 1210 times.
✓ Branch 2 taken 4498 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2404 times.
✓ Branch 5 taken 2094 times.
5708 if(!quiet_ && !minimal_ && current_feature_ != feature) {
481 2404 current_feature_changed_ = true;
482 current_feature_ = feature;
483 }
484 5708 return out_;
485 }
486
487 43 std::ostream& Logger::err_stream(const std::string& feature) {
488
3/4
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 38 times.
43 if(!quiet_ && current_feature_ != feature) {
489 5 current_feature_changed_ = true;
490 current_feature_ = feature;
491 }
492 43 return err_;
493 }
494
495 13 std::ostream& Logger::warn_stream(const std::string& feature) {
496
3/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 9 times.
13 if(!quiet_ && current_feature_ != feature) {
497 4 current_feature_changed_ = true;
498 current_feature_ = feature;
499 }
500 13 return warn_;
501 }
502
503 ✗ std::ostream& Logger::status_stream() {
504 ✗ return status_;
505 }
506
507 4498 void Logger::notify_out(const std::string& message) {
508 if(
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4498 times.
4498 (log_everything_ &&
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4498 times.
4498 log_features_exclude_.find(current_feature_) ==
511 log_features_exclude_.end())
512
1/4
✓ Branch 0 taken 4498 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4498 || (log_features_.find(current_feature_) != log_features_.end())
513 ) {
514 std::string feat_msg =
515
1/2
✓ Branch 2 taken 4498 times.
✗ Branch 3 not taken.
8996 CmdLine::ui_feature(current_feature_, current_feature_changed_)
516
1/2
✓ Branch 1 taken 4498 times.
✗ Branch 2 not taken.
4498 + message;
517
518 LoggerClients clients = clients_; // clients_ may be modified !
519
2/2
✓ Branch 0 taken 4498 times.
✓ Branch 1 taken 4498 times.
8996 for(auto it : clients) {
520
2/4
✓ Branch 1 taken 4498 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4498 times.
✗ Branch 5 not taken.
4498 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 4498 current_feature_changed_ = true;
528 }
529 4498 }
530
531 13 void Logger::notify_warn(const std::string& message) {
532 13 std::string msg = "Warning: " + message;
533 std::string feat_msg =
534
2/6
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
26 CmdLine::ui_feature(current_feature_, current_feature_changed_)
535
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 + msg;
536
537 LoggerClients clients = clients_; // clients_ may be modified !
538
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 13 times.
26 for(auto it : clients) {
539
2/4
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
13 it->warn(feat_msg);
540
2/4
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
13 it->status(msg);
541 }
542
543 13 current_feature_changed_ = false;
544 13 }
545
546 43 void Logger::notify_err(const std::string& message) {
547 43 std::string msg = "Error: " + message;
548 std::string feat_msg =
549
1/4
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
43 CmdLine::ui_feature(current_feature_, current_feature_changed_)
550
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 + msg;
551
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if(notifying_error_) {
553 std::cerr << "Error while displaying error (!):"
554 << feat_msg << std::endl;
555 } else {
556
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 notifying_error_ = true;
557 LoggerClients clients = clients_; // clients_ may be modified !
558
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 43 times.
86 for(auto it : clients) {
559
2/4
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
43 it->err(feat_msg);
560
2/4
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
43 it->status(msg);
561 }
562 43 notifying_error_ = false;
563 }
564
565
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 current_feature_changed_ = false;
566 43 }
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 6019 void Logger::notify(LoggerStream* s, const std::string& message) {
578
579
4/8
✓ Branch 0 taken 4554 times.
✓ Branch 1 taken 1465 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4554 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4554 times.
✗ Branch 7 not taken.
6019 if(quiet_ || (minimal_ && s == &out_) || clients_.empty()) {
580 return;
581 }
582
583
2/2
✓ Branch 0 taken 4498 times.
✓ Branch 1 taken 56 times.
4554 if(s == &out_) {
584 4498 notify_out(message);
585
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 43 times.
56 } else if(s == &warn_) {
586 13 notify_warn(message);
587
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 } else if(s == &err_) {
588 43 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