cppthread 1.1.16
C++ Thread Library
log.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2025 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/project/cppthread
4// contact@m2osw.com
5//
6// This program is free software; you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation; either version 2 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License along
17// with this program; if not, write to the Free Software Foundation, Inc.,
18// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
42// self
43//
44#include "cppthread/log.h"
45
46#include "cppthread/exception.h"
47
48
49// C++
50//
51#include <cstring>
52#include <iostream>
53
54
55// last include
56//
57#include <snapdev/poison.h>
58
59
60
61namespace cppthread
62{
63
64
76extern void create_system_mutex();
77
78
91
92
93namespace
94{
95
96
106log_callback g_log_callback = nullptr;
107
108
115pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER;
116
117
124bool g_log_locked = false;
125
126
135bool g_log_recursive_initialized = false;
136
137
150pthread_mutex_t g_log_recursive_mutex;
151
152
153} // no name namespace
154
155
156
170void set_log_callback(log_callback callback)
171{
172 pthread_mutex_lock(&g_log_mutex);
173 g_log_callback = callback;
174 pthread_mutex_unlock(&g_log_mutex);
175}
176
177
208{
209 try
210 {
212 }
213 catch(...)
214 {
215 std::cerr << "fatal: could not create system mutex."
216 << std::endl;
217 std::terminate();
218 }
219}
220
221
239{
240 int err(pthread_mutex_lock(&g_log_mutex));
241 if(err != 0)
242 {
243 std::cerr << "fatal: the mutex lock in cppthread::logger::lock() generated error #"
244 << err
245 << std::endl;
246 pthread_mutex_unlock(&g_log_mutex);
247 std::terminate();
248 }
249
250 if(!g_log_recursive_initialized)
251 {
252 g_log_recursive_initialized = true;
253
254 pthread_mutexattr_t mattr;
255 err = pthread_mutexattr_init(&mattr);
256 if(err != 0)
257 {
258 std::cerr << "fatal: a mutex attribute structure could not be initialized, error #"
259 << err
260 << std::endl;
261 pthread_mutex_unlock(&g_log_mutex);
262 std::terminate();
263 }
264 err = pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
265 if(err != 0)
266 {
267 std::cerr << "fatal: a mutex attribute structure type could not be setup, error #"
268 << err
269 << std::endl;
270 pthread_mutex_unlock(&g_log_mutex);
271 std::terminate();
272 }
273 err = pthread_mutex_init(&g_log_recursive_mutex, &mattr);
274 if(err != 0)
275 {
276 std::cerr << "fatal: a mutex structure could not be initialized, error #"
277 << err
278 << std::endl;
279 pthread_mutex_unlock(&g_log_mutex);
280 std::terminate();
281 }
282 err = pthread_mutexattr_destroy(&mattr);
283 if(err != 0)
284 {
285 std::cerr << "fatal: a mutex attribute structure could not be destroyed, error #"
286 << err
287 << std::endl;
288 pthread_mutex_unlock(&g_log_mutex);
289 std::terminate();
290 }
291 }
292 err = pthread_mutex_unlock(&g_log_mutex);
293 if(err != 0)
294 {
295 std::cerr << "fatal: a mutex unlock generated error #"
296 << err
297 << std::endl;
298 std::terminate();
299 }
300
301 // we have to lock only once so we use a flag to know whether we're
302 // already locked, if so, we unlock immediately (but we still have
303 // one lock in place)
304 //
305 err = pthread_mutex_lock(&g_log_recursive_mutex);
306 if(err != 0)
307 {
308 std::cerr << "fatal: a mutex lock generated error #"
309 << err
310 << std::endl;
311 std::terminate();
312 }
313
314 if(g_log_locked)
315 {
316 // it was already locked, so unlock (i.e. we only want to keep one
317 // lock turned on at a time)
318 //
319 err = pthread_mutex_unlock(&g_log_recursive_mutex);
320 if(err != 0)
321 {
322 std::cerr << "fatal: a mutex unlock generated error #"
323 << err
324 << std::endl;
325 std::terminate();
326 }
327 }
328 else
329 {
330 g_log_locked = true;
331 }
332}
333
334
347{
348 if(!g_log_locked)
349 {
350 std::cerr << "fatal: logger::unlock() called with g_log_locked == false"
351 << std::endl;
352 std::terminate();
353 }
354
355 g_log_locked = false;
356
357 int err(pthread_mutex_unlock(&g_log_recursive_mutex));
358 if(err != 0)
359 {
360 std::cerr << "fatal: a mutex unlock generated error #"
361 << err
362 << std::endl;
363 pthread_mutex_unlock(&g_log_recursive_mutex);
364 std::terminate();
365 }
366}
367
368
408{
409 if(level < log_level_t::debug
410 || level > log_level_t::fatal)
411 {
412 throw invalid_log_level(
413 "unknown log level ("
414 + std::to_string(static_cast<int>(level))
415 + ").");
416 }
417
418 lock();
419 if(f_log.tellp() != 0)
420 {
421 unlock();
422 throw invalid_log_level("log level specified when logger buffer is not empty.");
423 }
424 f_level = level;
425 ++f_counters[static_cast<int>(level)];
426 return *this;
427}
428
429
442{
443 func(*this);
444 return *this;
445}
446
447
458{
459 std::memset(f_counters, 0, sizeof(f_counters));
460}
461
462
483std::uint32_t logger::get_counter(log_level_t level) const
484{
485 if(level < log_level_t::debug
486 || level > log_level_t::fatal)
487 {
488 throw invalid_log_level(
489 "unknown log level ("
490 + std::to_string(static_cast<int>(level))
491 + ").");
492 }
493
494 return f_counters[static_cast<int>(level)];
495}
496
497
511std::uint32_t logger::get_errors() const
512{
513 return f_counters[static_cast<int>(log_level_t::error)]
514 + f_counters[static_cast<int>(log_level_t::fatal)];
515}
516
517
531std::uint32_t logger::get_warnings() const
532{
533 return f_counters[static_cast<int>(log_level_t::warning)];
534}
535
536
555{
556 // the logger is still locked at this point so:
557 // 1. capture a copy of the data
558 // 2. reset the message for next time
559 // 3. unlock
560 // 4. display the message or call the callback
561 //
562 log_callback callback;
563 log_level_t level;
564 std::string msg;
565 try
566 {
567 callback = g_log_callback;
568 level = f_level;
569 msg = f_log.str();
570 f_log.str(std::string());
571 unlock();
572 }
573 catch(...)
574 {
575 unlock();
576 throw;
577 }
578
579 if(callback != nullptr)
580 {
581 callback(level, msg);
582 }
583 else if(level >= log_level_t::info)
584 {
585 // the std::cerr requires a lock when multiple << are used
586 // so instead create one string with the entire message first
587 // including the newline
588 //
589 msg = to_string(level) + ": " + msg + "\n";
590 std::cerr << msg;
591 }
592
593 return *this;
594}
595
596
610std::string to_string(log_level_t level)
611{
612 switch(level)
613 {
614 case log_level_t::debug:
615 return "debug";
616
617 case log_level_t::info:
618 return "info";
619
620 case log_level_t::warning:
621 return "warning";
622
623 case log_level_t::error:
624 return "error";
625
626 case log_level_t::fatal:
627 return "fatal";
628
629 case log_level_t::LOG_LEVEL_SIZE:
630 break;
631
632 }
633
634 throw invalid_error("unknown log level ("
635 + std::to_string(static_cast<int>(level))
636 + ")");
637}
638
639
730} // namespace cppthread
731// vim: ts=4 sw=4 et
The cppthread logger.
Definition log.h:59
std::uint32_t get_errors() const
Get the number of errors that occurred so far.
Definition log.cpp:511
static void lock()
Lock the system so a log can be emitted properly.
Definition log.cpp:238
void reset_counters()
Reset all the log message counters to zero.
Definition log.cpp:457
std::uint32_t get_counter(log_level_t level) const
Get one of the level counters.
Definition log.cpp:483
logger & end()
End the logger's message.
Definition log.cpp:554
static void unlock()
Unlock the logger once we are done with it.
Definition log.cpp:346
logger()
Initialize the logger.
Definition log.cpp:207
logger & operator<<(log_level_t const &level)
Save the level at which to log this message.
Definition log.cpp:407
std::stringstream f_log
The log message.
Definition log.h:85
std::uint32_t get_warnings() const
Get the number of warnings that occurred so far.
Definition log.cpp:531
log_level_t f_level
The level of this message.
Definition log.h:84
Exceptions for the thread environment.
void create_system_mutex()
Function used to initialize the system mutex.
Definition mutex.cpp:926
void set_log_callback(log_callback callback)
Set a callback function.
Definition log.cpp:170
logger log
The logger object used to send logs out.
Definition log.cpp:90
std::string to_string(log_level_t level)
Convert a log level to a string.
Definition log.cpp:610
Declaration of the log class used to send error messages.
void(* log_callback)(log_level_t level, std::string const &message)
The log callback type definition.
Definition log.h:53
log_level_t
The log level or severity.
Definition log.h:40

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.