GCC Code Coverage Report


Directory: ./
File: basic/logger.cpp
Date: 2026-10-17 02:51:58
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 253 CERRStream() :
75
4/10
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 253 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 253 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 253 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
253 std::ostream(new CERRStreamBuff(this)),lock_(GEOGRAM_SPINLOCK_INIT) {
76 253 }
77 1012 ~CERRStream() override{
78
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
506 delete rdbuf();
79 1012 }
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 253 CERRStreamBuff(CERRStream* stream) : stream_(stream) {
91 253 }
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 5933 int LoggerStreamBuf::sync() {
109
1/2
✓ Branch 1 taken 5933 times.
✗ Branch 2 not taken.
5933 std::string str(this->str());
110
1/2
✓ Branch 1 taken 5933 times.
✗ Branch 2 not taken.
5933 loggerStream_->notify(str);
111
2/4
✓ Branch 1 taken 5933 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5933 times.
✗ Branch 5 not taken.
11866 this->str("");
112 5933 return 0;
113 5933 }
114
115 /************************************************************************/
116
117 2024 LoggerStream::LoggerStream(Logger* logger) :
118 ✗ std::ostream(new LoggerStreamBuf(this)),
119
4/11
✓ Branch 2 taken 1012 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1012 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1012 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1012 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
2024 logger_(logger) {
120 2024 }
121
122 2024 LoggerStream::~LoggerStream() {
123 2024 std::streambuf* buf = rdbuf();
124
1/2
✓ Branch 0 taken 1012 times.
✗ Branch 1 not taken.
2024 delete buf;
125 2024 }
126
127 5933 void LoggerStream::notify(const std::string& str) {
128 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
2/4
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 297 times.
✗ Branch 5 not taken.
297 CmdLine::ui_separator(title);
146 297 }
147
148 5865 void ConsoleLogger::out(const std::string& str) {
149 5865 CmdLine::ui_message(str);
150 5865 }
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 68 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
3/8
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 253 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 253 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
253 instance_ = new Logger();
228 253 Environment::instance()->add_environment(instance_);
229 253 }
230
231 253 void Logger::terminate() {
232 253 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 1 taken 253 times.
✓ Branch 2 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 1 not taken.
✓ Branch 2 taken 13476 times.
13476 if(name == "log:minimal") {
249 ✗ set_minimal(String::to_bool(value));
250 ✗ return true;
251 }
252
253
2/2
✓ Branch 1 taken 253 times.
✓ Branch 2 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 1 taken 253 times.
✓ Branch 2 taken 12970 times.
13223 if(name == "log:file_name") {
259 253 log_file_name_ = value;
260
1/2
✗ Branch 1 not taken.
✓ Branch 2 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 1 taken 253 times.
✓ Branch 2 taken 12717 times.
12970 if(name == "log:features") {
267 253 std::vector<std::string> features;
268
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 String::split_string(value, ';', features);
269 253 log_features_.clear();
270
4/8
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 253 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 253 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 253 times.
✗ Branch 10 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 253 return true;
280 253 }
281
282
2/2
✓ Branch 1 taken 253 times.
✓ Branch 2 taken 12464 times.
12717 if(name == "log:features_exclude") {
283 253 std::vector<std::string> features;
284
1/2
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
253 String::split_string(value, ';', features);
285 253 log_features_exclude_.clear();
286
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 253 times.
253 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 253 return true;
291 253 }
292
293 12464 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 1 taken 105 times.
✓ Branch 2 taken 1894 times.
1999 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 1894 times.
1894 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 1785 times.
1894 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 1680 times.
1785 if(name == "log:file_name") {
316 105 value = log_file_name_;
317 105 return true;
318 }
319
320
2/2
✓ Branch 1 taken 358 times.
✓ Branch 2 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 358 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 1 taken 358 times.
✓ Branch 2 taken 964 times.
1322 if(name == "log:features_exclude") {
336 358 value = "";
337
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 358 times.
358 for(auto& it : log_features_exclude_) {
338 ✗ if(value.length() != 0) {
339 ✗ value += ';';
340 }
341 ✗ value += it;
342 }
343 358 return true;
344 }
345
346 964 return false;
347 }
348
349 253 void Logger::register_client(LoggerClient* c) {
350
1/2
✓ Branch 2 taken 253 times.
✗ Branch 3 not taken.
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 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 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 2 taken 253 times.
✗ Branch 3 not taken.
506 indent_(0)
391 {
392 // Add a default client printing stuff to std::cout
393
4/10
✓ 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.
✗ Branch 9 not taken.
✓ Branch 10 taken 253 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
253 register_client(new ConsoleLogger());
394 #ifdef GEO_DEBUG
395 253 quiet_ = false;
396 #endif
397
3/8
✓ Branch 1 taken 253 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 253 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 253 times.
✗ Branch 9 not taken.
✗ Branch 10 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 27206 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 27206 times.
27206 if(instance_ == nullptr) {
411 std::cerr
412 ✗ << "CRITICAL: Accessing uninitialized Logger instance"
413 ✗ << std::endl;
414 ✗ geo_abort();
415 }
416 27206 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
4/6
✓ Branch 0 taken 12251 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 12251 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5612 times.
✓ Branch 6 taken 6639 times.
12251 (is_initialized() && !Process::is_running_threads()) ?
435
2/4
✓ Branch 1 taken 5612 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5612 times.
✗ Branch 5 not taken.
5612 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 12251 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 5612 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
24502 << 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 297 current_feature_.clear();
472
1/2
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
297 LoggerClients clients = clients_; // clients_ may be modified !
473
3/4
✓ Branch 4 taken 297 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 297 times.
✓ Branch 9 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 297 }
476 297 }
477 297 return out_;
478 }
479
480 5612 std::ostream& Logger::out_stream(const std::string& feature) {
481
6/8
✓ Branch 0 taken 5612 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5612 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2796 times.
✓ Branch 6 taken 2816 times.
✓ Branch 7 taken 2796 times.
✓ Branch 8 taken 2816 times.
5612 if(!quiet_ && !minimal_ && current_feature_ != feature) {
482 2796 current_feature_changed_ = true;
483 2796 current_feature_ = feature;
484 }
485 5612 return out_;
486 }
487
488 48 std::ostream& Logger::err_stream(const std::string& feature) {
489
5/6
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 38 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 38 times.
48 if(!quiet_ && current_feature_ != feature) {
490 10 current_feature_changed_ = true;
491 10 current_feature_ = feature;
492 }
493 48 return err_;
494 }
495
496 20 std::ostream& Logger::warn_stream(const std::string& feature) {
497
5/6
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 11 times.
20 if(!quiet_ && current_feature_ != feature) {
498 9 current_feature_changed_ = true;
499 9 current_feature_ = feature;
500 }
501 20 return warn_;
502 }
503
504 ✗ std::ostream& Logger::status_stream() {
505 ✗ return status_;
506 }
507
508 5865 void Logger::notify_out(const std::string& message) {
509 5865 if(
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5865 times.
5865 (log_everything_ &&
511
1/2
✓ Branch 1 taken 5865 times.
✗ Branch 2 not taken.
5865 log_features_exclude_.find(current_feature_) ==
512 5865 log_features_exclude_.end())
513
2/8
✓ Branch 0 taken 5865 times.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 5865 times.
✗ Branch 10 not taken.
11730 || (log_features_.find(current_feature_) != log_features_.end())
514 ) {
515 std::string feat_msg =
516
1/2
✓ Branch 1 taken 5865 times.
✗ Branch 2 not taken.
11730 CmdLine::ui_feature(current_feature_, current_feature_changed_)
517
1/2
✓ Branch 1 taken 5865 times.
✗ Branch 2 not taken.
5865 + message;
518
519
1/2
✓ Branch 1 taken 5865 times.
✗ Branch 2 not taken.
5865 LoggerClients clients = clients_; // clients_ may be modified !
520
3/4
✓ Branch 4 taken 5865 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 5865 times.
✓ Branch 9 taken 5865 times.
11730 for(auto it : clients) {
521
2/4
✓ Branch 1 taken 5865 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5865 times.
✗ Branch 5 not taken.
5865 it->out(feat_msg);
522 5865 }
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 5865 current_feature_changed_ = true;
529 5865 }
530 5865 }
531
532 20 void Logger::notify_warn(const std::string& message) {
533
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 std::string msg = "Warning: " + message;
534 std::string feat_msg =
535
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 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
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 LoggerClients clients = clients_; // clients_ may be modified !
539
3/4
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 20 times.
✓ Branch 9 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 20 }
543
544 20 current_feature_changed_ = false;
545 20 }
546
547 48 void Logger::notify_err(const std::string& message) {
548
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 std::string msg = "Error: " + message;
549 std::string feat_msg =
550
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
96 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 48 notifying_error_ = true;
558
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 LoggerClients clients = clients_; // clients_ may be modified !
559
3/4
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 48 times.
✓ Branch 9 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 48 }
563 48 notifying_error_ = false;
564 48 }
565
566 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/10
✓ Branch 0 taken 5933 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5933 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 5933 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 5933 times.
5933 if(quiet_ || (minimal_ && s == &out_) || clients_.empty()) {
581 ✗ return;
582 }
583
584
2/2
✓ Branch 0 taken 5865 times.
✓ Branch 1 taken 68 times.
5933 if(s == &out_) {
585 5865 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