GCC Code Coverage Report


Directory: ./
File: basic/logger.cpp
Date: 2026-09-27 03:24:14
Exec Total Coverage
Lines: 246 406 60.6%
Functions: 42 60 70.0%
Branches: 161 515 31.3%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/basic/logger.h>
41 #include <geogram/basic/assert.h>
42 #include <geogram/basic/command_line.h>
43 #include <geogram/basic/argused.h>
44 #include <geogram/basic/file_system.h>
45 #include <geogram/basic/process.h>
46
47
48 #include <stdlib.h>
49 #include <stdarg.h>
50
51 /*
52 Disables the warning caused by passing 'this' as an argument while
53 construction is not finished (in LoggerStream ctor).
54 As LoggerStreamBuf only stores the pointer for later use, so we can
55 ignore the fact that 'this' is not completely formed yet.
56 */
57 #ifdef GEO_OS_WINDOWS
58 #pragma warning(disable:4355)
59 #endif
60
61
62 namespace {
63 using namespace GEO;
64
65 /**
66 * \brief The output stream returned by Logger::err_console()
67 * \details It has a locking mechanism that avoids messages sent by different
68 * threads to be mixed.
69 * \see Logger::err_console(), Logger::out(), Logger::err(), Logger::warn(),
70 * Logger::status()
71 */
72 class CERRStream : public std::ostream {
73 public:
74 255 CERRStream() :
75
4/10
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 255 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 255 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 255 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
255 std::ostream(new CERRStreamBuff(this)),lock_(GEOGRAM_SPINLOCK_INIT) {
76 255 }
77 1020 ~CERRStream() override{
78 1020 }
79 6639 void lock() {
80 6639 Process::acquire_spinlock(lock_);
81 6639 }
82 6639 void unlock() {
83 6639 Process::release_spinlock(lock_);
84 6639 }
85 private:
86 Process::spinlock lock_;
87 class CERRStreamBuff : public std::stringbuf {
88 public:
89 255 CERRStreamBuff(CERRStream* stream) : stream_(stream) {
90 255 }
91 6639 int sync() override {
92
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
6639 std::cerr << this->str();
93
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
13278 this->str("");
94 6639 stream_->unlock();
95 6639 return 0;
96 }
97 private:
98 CERRStream* stream_;
99 };
100 };
101 }
102
103 namespace GEO {
104
105 /************************************************************************/
106
107 6019 int LoggerStreamBuf::sync() {
108
1/2
✓ Branch 1 taken 6019 times.
✗ Branch 2 not taken.
6019 std::string str(this->str());
109
1/2
✓ Branch 1 taken 6019 times.
✗ Branch 2 not taken.
6019 loggerStream_->notify(str);
110
2/4
✓ Branch 1 taken 6019 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6019 times.
✗ Branch 5 not taken.
12038 this->str("");
111 6019 return 0;
112 6019 }
113
114 /************************************************************************/
115
116 2040 LoggerStream::LoggerStream(Logger* logger) :
117 ✗ std::ostream(new LoggerStreamBuf(this)),
118
4/11
✓ Branch 2 taken 1020 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1020 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1020 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1020 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
2040 logger_(logger) {
119 2040 }
120
121 2040 LoggerStream::~LoggerStream() {
122 2040 std::streambuf* buf = rdbuf();
123
1/2
✓ Branch 0 taken 1020 times.
✗ Branch 1 not taken.
2040 delete buf;
124 2040 }
125
126 6019 void LoggerStream::notify(const std::string& str) {
127 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
2/4
✓ Branch 1 taken 299 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 299 times.
✗ Branch 5 not taken.
299 CmdLine::ui_separator(title);
145 299 }
146
147 5963 void ConsoleLogger::out(const std::string& str) {
148 5963 CmdLine::ui_message(str);
149 5963 }
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 56 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
3/8
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 255 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 255 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
255 instance_ = new Logger();
227 255 Environment::instance()->add_environment(instance_);
228 255 }
229
230 255 void Logger::terminate() {
231 255 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 1 taken 255 times.
✓ Branch 2 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 1 not taken.
✓ Branch 2 taken 13605 times.
13605 if(name == "log:minimal") {
248 ✗ set_minimal(String::to_bool(value));
249 ✗ return true;
250 }
251
252
2/2
✓ Branch 1 taken 255 times.
✓ Branch 2 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 1 taken 255 times.
✓ Branch 2 taken 13095 times.
13350 if(name == "log:file_name") {
258 255 log_file_name_ = value;
259
1/2
✗ Branch 1 not taken.
✓ Branch 2 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 1 taken 255 times.
✓ Branch 2 taken 12840 times.
13095 if(name == "log:features") {
266 255 std::vector<std::string> features;
267
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 String::split_string(value, ';', features);
268 255 log_features_.clear();
269
4/8
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 255 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 255 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 255 times.
✗ Branch 10 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 255 return true;
279 255 }
280
281
2/2
✓ Branch 1 taken 255 times.
✓ Branch 2 taken 12585 times.
12840 if(name == "log:features_exclude") {
282 255 std::vector<std::string> features;
283
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 String::split_string(value, ';', features);
284 255 log_features_exclude_.clear();
285
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 255 times.
255 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 255 return true;
290 255 }
291
292 12585 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 1 taken 105 times.
✓ Branch 2 taken 1907 times.
2012 if(name == "log:quiet") {
300
1/2
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
105 value = String::to_string(is_quiet());
301 105 return true;
302 }
303
304
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1907 times.
1907 if(name == "log:minimal") {
305 ✗ value = String::to_string(is_minimal());
306 ✗ return true;
307 }
308
309
2/2
✓ Branch 1 taken 109 times.
✓ Branch 2 taken 1798 times.
1907 if(name == "log:pretty") {
310
1/2
✓ Branch 2 taken 109 times.
✗ Branch 3 not taken.
109 value = String::to_string(is_pretty());
311 109 return true;
312 }
313
314
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 1693 times.
1798 if(name == "log:file_name") {
315 105 value = log_file_name_;
316 105 return true;
317 }
318
319
2/2
✓ Branch 1 taken 360 times.
✓ Branch 2 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 360 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 1 taken 360 times.
✓ Branch 2 taken 973 times.
1333 if(name == "log:features_exclude") {
335 360 value = "";
336
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 360 times.
360 for(auto& it : log_features_exclude_) {
337 ✗ if(value.length() != 0) {
338 ✗ value += ';';
339 }
340 ✗ value += it;
341 }
342 360 return true;
343 }
344
345 973 return false;
346 }
347
348 255 void Logger::register_client(LoggerClient* c) {
349
1/2
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
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 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 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 2 taken 255 times.
✗ Branch 3 not taken.
510 indent_(0)
390 {
391 // Add a default client printing stuff to std::cout
392
4/10
✓ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 255 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
255 register_client(new ConsoleLogger());
393 #ifdef GEO_DEBUG
394 255 quiet_ = false;
395 #endif
396
3/8
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 255 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 255 times.
✗ Branch 9 not taken.
✗ Branch 10 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 27489 Logger* Logger::instance() {
405 // Do not use geo_assert here:
406 // if the instance is nullptr, geo_assert will
407 // call the Logger to print the assertion failure, thus ending in a
408 // infinite loop.
409
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27489 times.
27489 if(instance_ == nullptr) {
410 std::cerr
411 ✗ << "CRITICAL: Accessing uninitialized Logger instance"
412 ✗ << std::endl;
413 ✗ geo_abort();
414 }
415 27489 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
4/6
✓ Branch 0 taken 12347 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 12347 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5708 times.
✓ Branch 6 taken 6639 times.
12347 (is_initialized() && !Process::is_running_threads()) ?
434
2/4
✓ Branch 1 taken 5708 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5708 times.
✗ Branch 5 not taken.
5708 instance()->out_stream(feature) :
435
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
6639 (instance()->err_console() << " >>"
436
6/12
✓ Branch 1 taken 12347 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6639 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6639 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6639 times.
✓ Branch 13 taken 5708 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
24694 << 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 299 current_feature_.clear();
471
1/2
✓ Branch 1 taken 299 times.
✗ Branch 2 not taken.
299 LoggerClients clients = clients_; // clients_ may be modified !
472
3/4
✓ Branch 4 taken 299 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 299 times.
✓ Branch 9 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 299 }
475 299 }
476 299 return out_;
477 }
478
479 5708 std::ostream& Logger::out_stream(const std::string& feature) {
480
6/8
✓ Branch 0 taken 5708 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5708 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2849 times.
✓ Branch 6 taken 2859 times.
✓ Branch 7 taken 2849 times.
✓ Branch 8 taken 2859 times.
5708 if(!quiet_ && !minimal_ && current_feature_ != feature) {
481 2849 current_feature_changed_ = true;
482 2849 current_feature_ = feature;
483 }
484 5708 return out_;
485 }
486
487 43 std::ostream& Logger::err_stream(const std::string& feature) {
488
5/6
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 38 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 38 times.
43 if(!quiet_ && current_feature_ != feature) {
489 5 current_feature_changed_ = true;
490 5 current_feature_ = feature;
491 }
492 43 return err_;
493 }
494
495 13 std::ostream& Logger::warn_stream(const std::string& feature) {
496
5/6
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 9 times.
13 if(!quiet_ && current_feature_ != feature) {
497 4 current_feature_changed_ = true;
498 4 current_feature_ = feature;
499 }
500 13 return warn_;
501 }
502
503 ✗ std::ostream& Logger::status_stream() {
504 ✗ return status_;
505 }
506
507 5963 void Logger::notify_out(const std::string& message) {
508 5963 if(
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5963 times.
5963 (log_everything_ &&
510
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
5963 log_features_exclude_.find(current_feature_) ==
511 5963 log_features_exclude_.end())
512
2/8
✓ Branch 0 taken 5963 times.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 5963 times.
✗ Branch 10 not taken.
11926 || (log_features_.find(current_feature_) != log_features_.end())
513 ) {
514 std::string feat_msg =
515
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
11926 CmdLine::ui_feature(current_feature_, current_feature_changed_)
516
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
5963 + message;
517
518
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
5963 LoggerClients clients = clients_; // clients_ may be modified !
519
3/4
✓ Branch 4 taken 5963 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 5963 times.
✓ Branch 9 taken 5963 times.
11926 for(auto it : clients) {
520
2/4
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5963 times.
✗ Branch 5 not taken.
5963 it->out(feat_msg);
521 5963 }
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 5963 current_feature_changed_ = true;
528 5963 }
529 5963 }
530
531 13 void Logger::notify_warn(const std::string& message) {
532
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 std::string msg = "Warning: " + message;
533 std::string feat_msg =
534
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 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
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 LoggerClients clients = clients_; // clients_ may be modified !
538
3/4
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 13 times.
✓ Branch 9 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 13 }
542
543 13 current_feature_changed_ = false;
544 13 }
545
546 43 void Logger::notify_err(const std::string& message) {
547
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 std::string msg = "Error: " + message;
548 std::string feat_msg =
549
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
86 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 43 notifying_error_ = true;
557
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 LoggerClients clients = clients_; // clients_ may be modified !
558
3/4
✓ Branch 4 taken 43 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 43 times.
✓ Branch 9 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 43 }
562 43 notifying_error_ = false;
563 43 }
564
565 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/10
✓ Branch 0 taken 6019 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6019 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 6019 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6019 times.
6019 if(quiet_ || (minimal_ && s == &out_) || clients_.empty()) {
580 ✗ return;
581 }
582
583
2/2
✓ Branch 0 taken 5963 times.
✓ Branch 1 taken 56 times.
6019 if(s == &out_) {
584 5963 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