GCC Code Coverage Report


Directory: ./
File: basic/logger.cpp
Date: 2026-10-17 02:49:09
Exec Total Coverage
Lines: 247 407 60.7%
Functions: 42 60 70.0%
Branches: 162 517 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
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
510 delete rdbuf();
79 1020 }
80 6639 void lock() {
81 6639 Process::acquire_spinlock(lock_);
82 6639 }
83 6639 void unlock() {
84 6639 Process::release_spinlock(lock_);
85 6639 }
86 private:
87 Process::spinlock lock_;
88 class CERRStreamBuff : public std::stringbuf {
89 public:
90 255 CERRStreamBuff(CERRStream* stream) : stream_(stream) {
91 255 }
92 6639 int sync() override {
93
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();
94
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
13278 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
1/2
✓ Branch 1 taken 6019 times.
✗ Branch 2 not taken.
6019 std::string str(this->str());
110
1/2
✓ Branch 1 taken 6019 times.
✗ Branch 2 not taken.
6019 loggerStream_->notify(str);
111
2/4
✓ Branch 1 taken 6019 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6019 times.
✗ Branch 5 not taken.
12038 this->str("");
112 6019 return 0;
113 6019 }
114
115 /************************************************************************/
116
117 2040 LoggerStream::LoggerStream(Logger* logger) :
118 ✗ std::ostream(new LoggerStreamBuf(this)),
119
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) {
120 2040 }
121
122 2040 LoggerStream::~LoggerStream() {
123 2040 std::streambuf* buf = rdbuf();
124
1/2
✓ Branch 0 taken 1020 times.
✗ Branch 1 not taken.
2040 delete buf;
125 2040 }
126
127 6019 void LoggerStream::notify(const std::string& str) {
128 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
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);
146 299 }
147
148 5963 void ConsoleLogger::out(const std::string& str) {
149 5963 CmdLine::ui_message(str);
150 5963 }
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 56 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
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();
228 255 Environment::instance()->add_environment(instance_);
229 255 }
230
231 255 void Logger::terminate() {
232 255 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 1 taken 255 times.
✓ Branch 2 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 1 not taken.
✓ Branch 2 taken 13605 times.
13605 if(name == "log:minimal") {
249 ✗ set_minimal(String::to_bool(value));
250 ✗ return true;
251 }
252
253
2/2
✓ Branch 1 taken 255 times.
✓ Branch 2 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 1 taken 255 times.
✓ Branch 2 taken 13095 times.
13350 if(name == "log:file_name") {
259 255 log_file_name_ = value;
260
1/2
✗ Branch 1 not taken.
✓ Branch 2 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 1 taken 255 times.
✓ Branch 2 taken 12840 times.
13095 if(name == "log:features") {
267 255 std::vector<std::string> features;
268
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 String::split_string(value, ';', features);
269 255 log_features_.clear();
270
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] == "*") {
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 255 return true;
280 255 }
281
282
2/2
✓ Branch 1 taken 255 times.
✓ Branch 2 taken 12585 times.
12840 if(name == "log:features_exclude") {
283 255 std::vector<std::string> features;
284
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 String::split_string(value, ';', features);
285 255 log_features_exclude_.clear();
286
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 255 times.
255 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 255 return true;
291 255 }
292
293 12585 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 1 taken 105 times.
✓ Branch 2 taken 1907 times.
2012 if(name == "log:quiet") {
301
1/2
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
105 value = String::to_string(is_quiet());
302 105 return true;
303 }
304
305
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1907 times.
1907 if(name == "log:minimal") {
306 ✗ value = String::to_string(is_minimal());
307 ✗ return true;
308 }
309
310
2/2
✓ Branch 1 taken 109 times.
✓ Branch 2 taken 1798 times.
1907 if(name == "log:pretty") {
311
1/2
✓ Branch 2 taken 109 times.
✗ Branch 3 not taken.
109 value = String::to_string(is_pretty());
312 109 return true;
313 }
314
315
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 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 1 taken 360 times.
✓ Branch 2 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 360 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 1 taken 360 times.
✓ Branch 2 taken 973 times.
1333 if(name == "log:features_exclude") {
336 360 value = "";
337
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 360 times.
360 for(auto& it : log_features_exclude_) {
338 ✗ if(value.length() != 0) {
339 ✗ value += ';';
340 }
341 ✗ value += it;
342 }
343 360 return true;
344 }
345
346 973 return false;
347 }
348
349 255 void Logger::register_client(LoggerClient* c) {
350
1/2
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
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 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 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 2 taken 255 times.
✗ Branch 3 not taken.
510 indent_(0)
391 {
392 // Add a default client printing stuff to std::cout
393
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());
394 #ifdef GEO_DEBUG
395 255 quiet_ = false;
396 #endif
397
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;
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 27489 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 1 not taken.
✓ Branch 2 taken 27489 times.
27489 if(instance_ == nullptr) {
411 std::cerr
412 ✗ << "CRITICAL: Accessing uninitialized Logger instance"
413 ✗ << std::endl;
414 ✗ geo_abort();
415 }
416 27489 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
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()) ?
435
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) :
436
2/4
✓ Branch 1 taken 6639 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6639 times.
✗ Branch 5 not taken.
6639 (instance()->err_console() << " >>"
437
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));
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 299 current_feature_.clear();
472
1/2
✓ Branch 1 taken 299 times.
✗ Branch 2 not taken.
299 LoggerClients clients = clients_; // clients_ may be modified !
473
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) {
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 299 }
476 299 }
477 299 return out_;
478 }
479
480 5708 std::ostream& Logger::out_stream(const std::string& feature) {
481
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) {
482 2849 current_feature_changed_ = true;
483 2849 current_feature_ = feature;
484 }
485 5708 return out_;
486 }
487
488 43 std::ostream& Logger::err_stream(const std::string& feature) {
489
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) {
490 5 current_feature_changed_ = true;
491 5 current_feature_ = feature;
492 }
493 43 return err_;
494 }
495
496 13 std::ostream& Logger::warn_stream(const std::string& feature) {
497
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) {
498 4 current_feature_changed_ = true;
499 4 current_feature_ = feature;
500 }
501 13 return warn_;
502 }
503
504 ✗ std::ostream& Logger::status_stream() {
505 ✗ return status_;
506 }
507
508 5963 void Logger::notify_out(const std::string& message) {
509 5963 if(
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5963 times.
5963 (log_everything_ &&
511
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
5963 log_features_exclude_.find(current_feature_) ==
512 5963 log_features_exclude_.end())
513
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())
514 ) {
515 std::string feat_msg =
516
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
11926 CmdLine::ui_feature(current_feature_, current_feature_changed_)
517
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
5963 + message;
518
519
1/2
✓ Branch 1 taken 5963 times.
✗ Branch 2 not taken.
5963 LoggerClients clients = clients_; // clients_ may be modified !
520
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) {
521
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);
522 5963 }
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 5963 current_feature_changed_ = true;
529 5963 }
530 5963 }
531
532 13 void Logger::notify_warn(const std::string& message) {
533
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 std::string msg = "Warning: " + message;
534 std::string feat_msg =
535
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 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
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 LoggerClients clients = clients_; // clients_ may be modified !
539
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) {
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 13 }
543
544 13 current_feature_changed_ = false;
545 13 }
546
547 43 void Logger::notify_err(const std::string& message) {
548
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 std::string msg = "Error: " + message;
549 std::string feat_msg =
550
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
86 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 43 notifying_error_ = true;
558
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 LoggerClients clients = clients_; // clients_ may be modified !
559
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) {
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 43 }
563 43 notifying_error_ = false;
564 43 }
565
566 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/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()) {
581 ✗ return;
582 }
583
584
2/2
✓ Branch 0 taken 5963 times.
✓ Branch 1 taken 56 times.
6019 if(s == &out_) {
585 5963 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