GCC Code Coverage Report


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