GCC Code Coverage Report


Directory: ./
File: basic/logger.cpp
Date: 2026-10-17 02:37:39
Exec Total Coverage
Lines: 207 329 62.9%
Functions: 38 57 66.7%
Branches: 128 372 34.4%

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
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
1012 ~CERRStream() override{
78
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
506 delete rdbuf();
79 1012 }
80 void lock() {
81 Process::acquire_spinlock(lock_);
82 }
83 void unlock() {
84 Process::release_spinlock(lock_);
85 }
86 private:
87 Process::spinlock lock_;
88 class CERRStreamBuff : public std::stringbuf {
89 public:
90
1/2
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
253 CERRStreamBuff(CERRStream* stream) : stream_(stream) {
91 }
92 6639 int sync() override {
93 6639 std::cerr << this->str();
94
1/2
✓ Branch 2 taken 6639 times.
✗ Branch 3 not taken.
6639 this->str("");
95 6639 stream_->unlock();
96 6639 return 0;
97 }
98 private:
99 CERRStream* stream_;
100 };
101 };
102 }
103
104 namespace GEO {
105
106 /************************************************************************/
107
108 5933 int LoggerStreamBuf::sync() {
109 5933 std::string str(this->str());
110 5933 loggerStream_->notify(str);
111
4/8
✓ Branch 1 taken 5933 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5933 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1076 times.
✓ Branch 7 taken 4857 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
11866 this->str("");
112 5933 return 0;
113 }
114
115 /************************************************************************/
116
117 2024 LoggerStream::LoggerStream(Logger* logger) :
118
1/2
✓ Branch 1 taken 1012 times.
✗ Branch 2 not taken.
2024 std::ostream(new LoggerStreamBuf(this)),
119 2024 logger_(logger) {
120 2024 }
121
122
1/2
✓ Branch 0 taken 1012 times.
✗ Branch 1 not taken.
2024 LoggerStream::~LoggerStream() {
123 std::streambuf* buf = rdbuf();
124
1/2
✓ Branch 0 taken 1012 times.
✗ Branch 1 not taken.
2024 delete buf;
125 2024 }
126
127 ✗ void LoggerStream::notify(const std::string& str) {
128
1/2
✓ Branch 2 taken 5933 times.
✗ Branch 3 not taken.
5933 logger_->notify(this, str);
129 5933 }
130
131 /************************************************************************/
132
133 506 LoggerClient::~LoggerClient() {
134 506 }
135
136 /************************************************************************/
137
138 253 ConsoleLogger::ConsoleLogger() {
139 253 }
140
141 1012 ConsoleLogger::~ConsoleLogger() {
142 1012 }
143
144 297 void ConsoleLogger::div(const std::string& title) {
145
1/2
✓ Branch 2 taken 297 times.
✗ Branch 3 not taken.
297 CmdLine::ui_separator(title);
146 297 }
147
148 4412 void ConsoleLogger::out(const std::string& str) {
149 4412 CmdLine::ui_message(str);
150 4412 }
151
152 20 void ConsoleLogger::warn(const std::string& str) {
153 20 CmdLine::ui_message(str);
154 20 }
155
156 48 void ConsoleLogger::err(const std::string& str) {
157 48 CmdLine::ui_message(str);
158 48 }
159
160 68 void ConsoleLogger::status(const std::string& str) {
161 geo_argused(str);
162 68 }
163
164 /************************************************************************/
165
166 ✗ FileLogger::FileLogger() :
167 ✗ log_file_(nullptr) {
168 ✗ }
169
170 ✗ FileLogger::FileLogger(const std::string& file_name) :
171 ✗ log_file_(nullptr)
172 {
173 ✗ set_file_name(file_name);
174 ✗ }
175
176 ✗ FileLogger::~FileLogger() {
177 ✗ delete log_file_;
178 ✗ log_file_ = nullptr;
179 ✗ }
180
181 ✗ void FileLogger::set_file_name(const std::string& file_name) {
182 ✗ log_file_name_ = file_name;
183 ✗ if(log_file_ != nullptr) {
184 ✗ delete log_file_;
185 ✗ log_file_ = nullptr;
186 }
187 ✗ if(log_file_name_.length() != 0) {
188 ✗ log_file_ = new std::ofstream(log_file_name_.c_str());
189 }
190 ✗ }
191
192 ✗ void FileLogger::div(const std::string& title) {
193 ✗ if(log_file_ != nullptr) {
194 *log_file_
195 << "\n====[" << title << "]====\n"
196 << std::flush;
197 }
198 ✗ }
199
200 ✗ void FileLogger::out(const std::string& str) {
201 ✗ if(log_file_ != nullptr) {
202 *log_file_ << str << std::flush;
203 }
204 ✗ }
205
206 ✗ void FileLogger::warn(const std::string& str) {
207 ✗ if(log_file_ != nullptr) {
208 *log_file_ << str << std::flush;
209 }
210 ✗ }
211
212 ✗ void FileLogger::err(const std::string& str) {
213 ✗ if(log_file_ != nullptr) {
214 *log_file_ << str << std::flush;
215 }
216 ✗ }
217
218 ✗ void FileLogger::status(const std::string& str) {
219 geo_argused(str);
220 ✗ }
221
222 /************************************************************************/
223
224 SmartPointer<Logger> Logger::instance_;
225
226 253 void Logger::initialize() {
227
1/2
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
253 instance_ = new Logger();
228 253 Environment::instance()->add_environment(instance_);
229 253 }
230
231
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
253 void Logger::terminate() {
232 instance_.reset();
233 253 }
234
235 12616 bool Logger::is_initialized() {
236 12616 return (instance_ != nullptr);
237 }
238
239 13729 bool Logger::set_local_value(
240 const std::string& name, const std::string& value
241 ) {
242
243
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 13476 times.
13729 if(name == "log:quiet") {
244 253 set_quiet(String::to_bool(value));
245 253 return true;
246 }
247
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13476 times.
13476 if(name == "log:minimal") {
249 ✗ set_minimal(String::to_bool(value));
250 ✗ return true;
251 }
252
253
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 13223 times.
13476 if(name == "log:pretty") {
254 253 set_pretty(String::to_bool(value));
255 253 return true;
256 }
257
258
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 12970 times.
13223 if(name == "log:file_name") {
259 253 log_file_name_ = value;
260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 253 times.
253 if(!log_file_name_.empty()) {
261 ✗ register_client(new FileLogger(log_file_name_));
262 }
263 253 return true;
264 }
265
266
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 12717 times.
12970 if(name == "log:features") {
267 std::vector<std::string> features;
268
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 String::split_string(value, ';', features);
269 log_features_.clear();
270
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] == "*") {
271 253 log_everything_ = true;
272 } else {
273 ✗ log_everything_ = false;
274 ✗ for(size_t i = 0; i < features.size(); i++) {
275 log_features_.insert(features[i]);
276 }
277 }
278
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 notify_observers(name);
279 return true;
280 253 }
281
282
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 12464 times.
12717 if(name == "log:features_exclude") {
283 std::vector<std::string> features;
284
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 String::split_string(value, ';', features);
285 log_features_exclude_.clear();
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 253 times.
506 for(size_t i = 0; i < features.size(); i++) {
287 log_features_exclude_.insert(features[i]);
288 }
289
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 notify_observers(name);
290 return true;
291 253 }
292
293 return false;
294 }
295
296 1999 bool Logger::get_local_value(
297 const std::string& name, std::string& value
298 ) const {
299
300
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 1894 times.
1999 if(name == "log:quiet") {
301 105 value = String::to_string(is_quiet());
302 105 return true;
303 }
304
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1894 times.
1894 if(name == "log:minimal") {
306 ✗ value = String::to_string(is_minimal());
307 ✗ return true;
308 }
309
310
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 1785 times.
1894 if(name == "log:pretty") {
311 109 value = String::to_string(is_pretty());
312 109 return true;
313 }
314
315
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 1680 times.
1785 if(name == "log:file_name") {
316 105 value = log_file_name_;
317 105 return true;
318 }
319
320
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 1322 times.
1680 if(name == "log:features") {
321
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 if(log_everything_) {
322 value = "*";
323 } else {
324 value = "";
325 ✗ for(auto& it : log_features_) {
326 ✗ if(value.length() != 0) {
327 value += ';';
328 }
329 value += it;
330 }
331 }
332 358 return true;
333 }
334
335
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 964 times.
1322 if(name == "log:features_exclude") {
336 value = "";
337
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 for(auto& it : log_features_exclude_) {
338 ✗ if(value.length() != 0) {
339 value += ';';
340 }
341 value += it;
342 }
343 return true;
344 }
345
346 return false;
347 }
348
349
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
253 void Logger::register_client(LoggerClient* c) {
350 253 clients_.insert(c);
351 253 }
352
353 ✗ void Logger::unregister_client(LoggerClient* c) {
354 geo_debug_assert(clients_.find(c) != clients_.end());
355 ✗ clients_.erase(c);
356 ✗ }
357
358 ✗ void Logger::unregister_all_clients() {
359 clients_.clear();
360 ✗ }
361
362 ✗ bool Logger::is_client(LoggerClient* c) const {
363 ✗ return clients_.find(c) != clients_.end();
364 }
365
366 297 void Logger::set_quiet(bool flag) {
367 297 quiet_ = flag;
368 297 }
369
370 ✗ void Logger::set_minimal(bool flag) {
371 ✗ minimal_ = flag;
372 ✗ }
373
374 253 void Logger::set_pretty(bool flag) {
375 253 pretty_ = flag;
376 253 }
377
378
379
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 Logger::Logger() :
380 253 out_(this),
381
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 warn_(this),
382
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 err_(this),
383
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 status_(this),
384
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 log_everything_(true),
385 253 current_feature_changed_(false),
386 253 quiet_(true),
387 253 pretty_(true),
388 253 minimal_(false),
389 253 notifying_error_(false),
390
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 indent_(0)
391 {
392 // Add a default client printing stuff to std::cout
393
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());
394 #ifdef GEO_DEBUG
395 quiet_ = false;
396 #endif
397
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;
398 253 }
399
400 1012 Logger::~Logger() {
401
1/2
✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
506 delete err_console_;
402 506 err_console_ = nullptr;
403 1012 }
404
405
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24300 times.
24300 Logger* Logger::instance() {
406 // Do not use geo_assert here:
407 // if the instance is nullptr, geo_assert will
408 // call the Logger to print the assertion failure, thus ending in a
409 // infinite loop.
410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24300 times.
24300 if(instance_ == nullptr) {
411 std::cerr
412 << "CRITICAL: Accessing uninitialized Logger instance"
413 << std::endl;
414 ✗ geo_abort();
415 }
416 24300 return instance_;
417 }
418
419 6639 std::ostream& Logger::err_console() {
420 6639 static_cast<CERRStream*>(err_console_)->lock();
421 6639 return *err_console_;
422 }
423
424 297 std::ostream& Logger::div(const std::string& title) {
425 std::ostream& result =
426
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()) ?
427 297 instance()->div_stream(title) :
428 297 (instance()->err_console() << "=====" << title << std::endl);
429 297 return result;
430 }
431
432 12251 std::ostream& Logger::out(const std::string& feature) {
433 std::ostream& result =
434
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()) ?
435 5612 instance()->out_stream(feature) :
436 6639 (instance()->err_console() << " >>"
437 6639 << CmdLine::ui_feature(feature));
438
2/2
✓ Branch 1 taken 11686 times.
✓ Branch 2 taken 12251 times.
23937 for(index_t i=0; i<instance_->indent_; ++i) {
439 11686 result << "| ";
440 }
441 12251 return result;
442 }
443
444 48 std::ostream& Logger::err(const std::string& feature) {
445 std::ostream& result =
446
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()) ?
447 48 instance()->err_stream(feature) :
448 48 (instance()->err_console() << "(E)-[" << feature << "] ");
449 48 return result;
450 }
451
452 20 std::ostream& Logger::warn(const std::string& feature) {
453 std::ostream& result =
454
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()) ?
455 20 instance()->warn_stream(feature) :
456 20 (instance()->err_console() << "(W)-[" << feature << "] ");
457 20 return result;
458 }
459
460 ✗ std::ostream& Logger::status() {
461 std::ostream& result =
462 ✗ (is_initialized() && !Process::is_running_threads()) ?
463 ✗ instance()->status_stream() :
464 ✗ (instance()->err_console() << "[status] ");
465 ✗ return result;
466 }
467
468 297 std::ostream& Logger::div_stream(const std::string& title) {
469
1/2
✓ Branch 0 taken 297 times.
✗ Branch 1 not taken.
297 if(!quiet_) {
470 297 current_feature_changed_ = true;
471 current_feature_.clear();
472 LoggerClients clients = clients_; // clients_ may be modified !
473
2/2
✓ Branch 0 taken 297 times.
✓ Branch 1 taken 297 times.
594 for(auto it : clients) {
474
2/4
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 297 times.
✗ Branch 5 not taken.
297 it->div(title);
475 }
476 }
477 297 return out_;
478 }
479
480 5612 std::ostream& Logger::out_stream(const std::string& feature) {
481
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) {
482 2355 current_feature_changed_ = true;
483 current_feature_ = feature;
484 }
485 5612 return out_;
486 }
487
488 48 std::ostream& Logger::err_stream(const std::string& feature) {
489
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) {
490 10 current_feature_changed_ = true;
491 current_feature_ = feature;
492 }
493 48 return err_;
494 }
495
496 20 std::ostream& Logger::warn_stream(const std::string& feature) {
497
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) {
498 9 current_feature_changed_ = true;
499 current_feature_ = feature;
500 }
501 20 return warn_;
502 }
503
504 ✗ std::ostream& Logger::status_stream() {
505 ✗ return status_;
506 }
507
508 4412 void Logger::notify_out(const std::string& message) {
509 if(
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4412 times.
4412 (log_everything_ &&
511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4412 times.
4412 log_features_exclude_.find(current_feature_) ==
512 log_features_exclude_.end())
513
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())
514 ) {
515 std::string feat_msg =
516
1/2
✓ Branch 2 taken 4412 times.
✗ Branch 3 not taken.
8824 CmdLine::ui_feature(current_feature_, current_feature_changed_)
517
1/2
✓ Branch 1 taken 4412 times.
✗ Branch 2 not taken.
4412 + message;
518
519 LoggerClients clients = clients_; // clients_ may be modified !
520
2/2
✓ Branch 0 taken 4412 times.
✓ Branch 1 taken 4412 times.
8824 for(auto it : clients) {
521
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);
522 }
523
524 // There is a mechanism for not repeating feature when it is
525 // the same as in previous message, but finally I systematically
526 // display feature (else the log is not super easy to read,
527 // especially when there are nested Stopwatches).
528 4412 current_feature_changed_ = true;
529 }
530 4412 }
531
532 20 void Logger::notify_warn(const std::string& message) {
533 20 std::string msg = "Warning: " + message;
534 std::string feat_msg =
535
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_)
536
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 + msg;
537
538 LoggerClients clients = clients_; // clients_ may be modified !
539
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 for(auto it : clients) {
540
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);
541
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 it->status(msg);
542 }
543
544 20 current_feature_changed_ = false;
545 20 }
546
547 48 void Logger::notify_err(const std::string& message) {
548 48 std::string msg = "Error: " + message;
549 std::string feat_msg =
550
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_)
551
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 + msg;
552
553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if(notifying_error_) {
554 std::cerr << "Error while displaying error (!):"
555 << feat_msg << std::endl;
556 } else {
557
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 notifying_error_ = true;
558 LoggerClients clients = clients_; // clients_ may be modified !
559
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
96 for(auto it : clients) {
560
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);
561
2/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
48 it->status(msg);
562 }
563 48 notifying_error_ = false;
564 }
565
566
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 current_feature_changed_ = false;
567 48 }
568
569 ✗ void Logger::notify_status(const std::string& message) {
570 LoggerClients clients = clients_; // clients_ may be modified !
571 ✗ for(auto it : clients) {
572 ✗ it->status(message);
573 }
574
575 ✗ current_feature_changed_ = false;
576 ✗ }
577
578 5933 void Logger::notify(LoggerStream* s, const std::string& message) {
579
580
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()) {
581 return;
582 }
583
584
2/2
✓ Branch 0 taken 4412 times.
✓ Branch 1 taken 68 times.
4480 if(s == &out_) {
585 4412 notify_out(message);
586
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 48 times.
68 } else if(s == &warn_) {
587 20 notify_warn(message);
588
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 } else if(s == &err_) {
589 48 notify_err(message);
590 ✗ } else if(s == &status_) {
591 ✗ notify_status(message);
592 } else {
593 ✗ geo_assert_not_reached;
594 }
595 }
596
597 /************************************************************************/
598
599 }
600
601 extern "C" {
602
603 ✗ int geogram_printf(const char* format, ...) {
604
605 ✗ static std::string last_string;
606
607 va_list args;
608
609 // Get the number of characters to be printed.
610 ✗ va_start(args, format);
611 ✗ int nb = vsnprintf(nullptr, 0, format, args)+1; // +1, I don't know why.
612 ✗ va_end(args);
613
614 // Generate the output string
615 ✗ GEO::vector<char> buffer(GEO::index_t(nb+1));
616 ✗ va_start(args, format);
617 ✗ vsnprintf(buffer.data(),buffer.size()-1, format, args);
618 ✗ va_end(args);
619
620 // Find the lines in the generated string
621 GEO::vector<char*> lines;
622 ✗ lines.push_back(buffer.data());
623 char last_char = '\n';
624 ✗ for(char* ptr = buffer.data(); *ptr; ptr++) {
625 if(*ptr != '\0') {
626 last_char = *ptr;
627 }
628 ✗ if(*ptr == '\n') {
629 ✗ *ptr = '\0';
630 ✗ ptr++;
631 ✗ if(*ptr != '\0') {
632 lines.push_back(ptr);
633 }
634 }
635 }
636
637 // If last character is not a carriage return,
638 // memorize the last line for later.
639 ✗ if(last_char != '\n') {
640 ✗ last_string += *lines.rbegin();
641 lines.pop_back();
642 }
643
644 // Output all the lines.
645 // Prepend the optionally memorized previous strings to the
646 // first one.
647 ✗ for(GEO::index_t i=0; i<lines.size(); ++i) {
648 ✗ if(i == 0) {
649 ✗ GEO::Logger::out("") << last_string << lines[i] << std::endl;
650 last_string.clear();
651 } else {
652 ✗ GEO::Logger::out("") << lines[i] << std::endl;
653 }
654 }
655
656 ✗ return nb;
657 }
658
659 ✗ int geogram_fprintf(FILE* out, const char* format, ...) {
660
661
662 ✗ static std::string last_string;
663
664 va_list args;
665
666 // Get the number of characters to be printed.
667 ✗ va_start(args, format);
668 ✗ int nb = vsnprintf(nullptr, 0, format, args)+1; // +1, I don't know why...
669 ✗ va_end(args);
670
671 // Generate the output string
672 ✗ GEO::vector<char> buffer(GEO::index_t(nb+1));
673 ✗ va_start(args, format);
674 ✗ vsnprintf(buffer.data(),buffer.size()-1, format, args);
675 ✗ va_end(args);
676
677 // Find the lines in the generated string
678 GEO::vector<char*> lines;
679 ✗ lines.push_back(buffer.data());
680 char last_char = '\n';
681 ✗ for(char* ptr = buffer.data(); *ptr; ptr++) {
682 if(*ptr != '\0') {
683 last_char = *ptr;
684 }
685 ✗ if(*ptr == '\n') {
686 ✗ *ptr = '\0';
687 ✗ ptr++;
688 ✗ if(*ptr != '\0') {
689 lines.push_back(ptr);
690 }
691 }
692 }
693
694 // If last character is not a carriage return,
695 // memorize the last line for later.
696 ✗ if(last_char != '\n') {
697 ✗ last_string += *lines.rbegin();
698 lines.pop_back();
699 }
700
701 // Output all the lines.
702 // Prepend the optionally memorized previous strings to the
703 // first one.
704 ✗ for(GEO::index_t i=0; i<lines.size(); ++i) {
705 ✗ if(i == 0) {
706 ✗ if(out == stdout) {
707 ✗ GEO::Logger::out("") << last_string << lines[i] << std::endl;
708 ✗ } else if(out == stderr) {
709 ✗ GEO::Logger::err("") << last_string << lines[i] << std::endl;
710 } else {
711 ✗ fprintf(out, "%s%s", last_string.c_str(), lines[i]);
712 }
713 last_string.clear();
714 } else {
715 ✗ if(out == stdout) {
716 ✗ GEO::Logger::out("") << lines[i] << std::endl;
717 ✗ } else if(out == stderr) {
718 ✗ GEO::Logger::err("") << lines[i] << std::endl;
719 } else {
720 ✗ fprintf(out, "%s", lines[i]);
721 }
722 }
723 }
724
725 ✗ return nb;
726 }
727 }
728