FlatImage
A configurable Linux containerization system
Loading...
Searching...
No Matches
log.hpp
Go to the documentation of this file.
1
121
122#pragma once
123
124#include <pthread.h>
125#include <filesystem>
126#include <iostream>
127#include <fstream>
128#include <print>
129#include <ranges>
130#include <unistd.h>
131
132#include "../std/concept.hpp"
133#include "../std/string.hpp"
134
144namespace ns_log
145{
146
147enum class Level : int
148{
149 CRITICAL,
150 ERROR,
151 WARN,
152 INFO,
153 DEBUG,
154};
155
156namespace
157{
158
159namespace fs = std::filesystem;
160
162{
163 private:
164 std::ofstream m_sink;
165 Level m_level;
166 pid_t m_pid;
167 public:
168 Logger();
169 Logger(Logger const&) = delete;
170 Logger(Logger&&) = delete;
171 Logger& operator=(Logger const&) = delete;
172 Logger& operator=(Logger&&) = delete;
173 void set_level(Level level);
174 [[nodiscard]] Level get_level() const;
175 [[nodiscard]] pid_t get_pid() const;
176 void set_sink_file(fs::path const& path_file_sink);
177 void set_as_fork();
178 void flush();
179 [[nodiscard]] std::ofstream& get_sink_file();
180};
181
188thread_local Logger logger;
189
190
200static void fork_handler_parent()
201{
202 std::cout.flush();
203 std::cerr.flush();
204 ::fflush(nullptr);
205}
206
219{
220 logger.set_sink_file("/dev/null");
221}
222
228 : m_sink("/dev/null")
229 , m_level(Level::CRITICAL)
230 , m_pid(getpid())
231{
232 static thread_local bool registered = false;
233 if (!registered)
234 {
235 pthread_atfork(fork_handler_parent, nullptr, fork_handler_child);
236 registered = true;
237 }
238}
239
245inline void Logger::set_sink_file(fs::path const& path_file_sink)
246{
247 // File to save logs into
248 if ( const char* var = std::getenv("FIM_DEBUG"); var && std::string_view{var} == "1" )
249 {
250 if(path_file_sink != "/dev/null")
251 {
252 std::println("D::Logger file: {}", path_file_sink.string());
253 }
254 }
255 // File output stream
256 m_sink = std::ofstream(path_file_sink, std::ios::out | std::ios::trunc);
257 // Check if file was opened successfully
258 if(not m_sink.is_open())
259 {
260 std::println("E::Could not open file '{}'", path_file_sink.string());
261 }
262}
263
269inline std::ofstream& Logger::get_sink_file()
270{
271 return m_sink;
272}
273
277inline void Logger::flush()
278{
279 if (auto& file = this->m_sink)
280 {
281 file.flush();
282 }
283}
284
290inline void Logger::set_level(Level level)
291{
292 m_level = level;
293}
294
300inline Level Logger::get_level() const
301{
302 return m_level;
303}
304
314inline pid_t Logger::get_pid() const
315{
316 return m_pid;
317}
318
328{
329 m_pid = 0;
330}
331
332}
333
339inline void set_level(Level level)
340{
341 logger.set_level(level);
342}
343
349inline Level get_level()
350{
351 return logger.get_level();
352}
353
359inline void set_sink_file(fs::path const& path_file_sink)
360{
361 logger.set_sink_file(path_file_sink);
362}
363
390inline void set_as_fork()
391{
392 logger.set_as_fork();
393}
394
403{
404 std::string_view m_str_file;
405 uint32_t m_line;
406
413 consteval Location(const char* str_file = __builtin_FILE() , uint32_t str_line = __builtin_LINE())
414 : m_str_file(str_file)
415 , m_line(str_line)
416 {
417 m_str_file = m_str_file.substr(m_str_file.find_last_of("/")+1);
418 }
419
425 constexpr auto get() const
426 {
427 return std::format("{}::{}", m_str_file, m_line);
428 }
429};
430
439template<typename... Ts>
440std::string vformat(std::string_view fmt, Ts&&... ts)
441{
442 return std::vformat(fmt, std::make_format_args(ts...));
443}
444
452{
453 private:
454 Location m_loc;
455 Level m_level;
456 std::string m_prefix;
457 std::reference_wrapper<std::ostream> ostream;
458
459 public:
468 Writer(Location const& location, Level const& level, std::string prefix, std::ostream& ostream)
469 : m_loc(location)
470 , m_level(level)
471 , m_prefix(prefix)
472 , ostream(ostream)
473 {}
474
482 template<ns_concept::StringRepresentable T, typename... Args>
484 void operator()(T&& format, Args&&... args)
485 {
486 // Get current PID to detect if this is a forked child process
487 pid_t pid = getpid();
488 // Create string
489 std::string line;
490 // Parent: show "PREFIX::file::line::", Child: show "PREFIX::child_pid::"
491 line += (pid == logger.get_pid())? std::format("{}::{}::", m_prefix, m_loc.get())
492 : std::format("{}::{}::", m_prefix, pid);
493 // Append format and args
494 line += vformat(ns_string::to_string(format), ns_string::to_string(args)...)
495 // Remove new lines to avoid printing without a prefix
496 | std::views::filter([](char c){ return c != '\n'; })
497 // Make it into a string
498 | std::ranges::to<std::string>();
499 // Append a single newline
500 line += '\n';
501 // Push to file
502 if(std::ofstream& sink = logger.get_sink_file(); sink.is_open())
503 {
504 sink << line;
505 }
506 // Push to stream
507 if(logger.get_level() >= m_level)
508 {
509 ostream.get() << line;
510 }
511 logger.flush();
512 }
513};
514
521class debug final : public Writer
522{
523 private:
524 Location m_loc;
525 public:
530 debug(Location location = Location())
531 : Writer(location, Level::DEBUG, "D", std::cout)
532 , m_loc(location)
533 {}
534};
535
542class info : public Writer
543{
544 private:
545 Location m_loc;
546 public:
551 info(Location location = Location())
552 : Writer(location, Level::INFO, "I", std::cout)
553 , m_loc(location)
554 {}
555};
556
563class warn : public Writer
564{
565 private:
566 Location m_loc;
567 public:
572 warn(Location location = Location())
573 : Writer(location, Level::WARN, "W", std::cerr)
574 , m_loc(location)
575 {}
576};
577
584class error : public Writer
585{
586 private:
587 Location m_loc;
588 public:
593 error(Location location = Location())
594 : Writer(location, Level::ERROR, "E", std::cerr)
595 , m_loc(location)
596 {}
597};
598
605class critical : public Writer
606{
607 private:
608 Location m_loc;
609 public:
615 : Writer(location, Level::CRITICAL, "C", std::cerr)
616 , m_loc(location)
617 {}
618};
619
682#define logger(fmt, ...) ::ns_log::impl_log<fmt>(::ns_log::Location{})(__VA_ARGS__)
683
727#define logger_loc(loc, fmt, ...) ::ns_log::impl_log<fmt>(loc)(__VA_ARGS__)
728
736template<ns_string::static_string str>
737constexpr decltype(auto) impl_log(Location loc)
738{
739 return [=]<typename... Ts>(Ts&&... ts)
740 {
741 constexpr std::string_view sv = str.data;
742 if constexpr (sv.starts_with("I::"))
743 {
744 info{loc}(sv.substr(3), std::forward<Ts>(ts)...);
745 }
746 else if constexpr (sv.starts_with("W::"))
747 {
748 warn{loc}(sv.substr(3), std::forward<Ts>(ts)...);
749 }
750 else if constexpr (sv.starts_with("E::"))
751 {
752 error{loc}(sv.substr(3), std::forward<Ts>(ts)...);
753 }
754 else if constexpr (sv.starts_with("C::"))
755 {
756 critical{loc}(sv.substr(3), std::forward<Ts>(ts)...);
757 }
758 else if constexpr (sv.starts_with("Q::"))
759 {
760 // Discard
761 }
762 else
763 {
764 // It is necessary to repeat all conditions here due to how the compile-time language works
765 static_assert(sv.starts_with("D::")
766 or sv.starts_with("I::")
767 or sv.starts_with("W::")
768 or sv.starts_with("E::")
769 or sv.starts_with("C::")
770 or sv.starts_with("Q::")
771 );
772 ns_log::debug{loc}(sv.substr(3), std::forward<Ts>(ts)...);
773 }
774 };
775}
776
777} // namespace ns_log
778
779/* vim: set expandtab fdm=marker ts=2 sw=2 tw=100 et :*/
void operator()(T &&format, Args &&... args)
Writes a formatted log message.
Definition log.hpp:484
Writer(Location const &location, Level const &level, std::string prefix, std::ostream &ostream)
Constructs a Writer with location and level information.
Definition log.hpp:468
std::ofstream & get_sink_file()
Gets the sink file of the logger.
Definition log.hpp:269
Logger()
Construct a new Logger:: Logger object.
Definition log.hpp:227
Level get_level() const
Get current verbosity level of the logger.
Definition log.hpp:300
void set_as_fork()
Marks the logger as being in a forked child process.
Definition log.hpp:327
pid_t get_pid() const
Get the PID that was active when this logger was initialized.
Definition log.hpp:314
void set_level(Level level)
Sets the logging verbosity (CRITICAL,ERROR,INFO,DEBUG)
Definition log.hpp:290
void set_sink_file(fs::path const &path_file_sink)
Sets the sink file of the logger.
Definition log.hpp:245
void flush()
Flushes the buffer content to the log file if it is defined.
Definition log.hpp:277
Critical-level logger (highest priority, always shown)
Definition log.hpp:606
critical(Location location=Location())
Constructs a critical logger.
Definition log.hpp:614
Debug-level logger (lowest priority)
Definition log.hpp:522
debug(Location location=Location())
Constructs a debug logger.
Definition log.hpp:530
Error-level logger (recoverable errors)
Definition log.hpp:585
error(Location location=Location())
Constructs an error logger.
Definition log.hpp:593
Info-level logger (informational messages)
Definition log.hpp:543
info(Location location=Location())
Constructs an info logger.
Definition log.hpp:551
Warning-level logger (potential issues)
Definition log.hpp:564
warn(Location location=Location())
Constructs a warning logger.
Definition log.hpp:572
Custom C++ concepts for type constraints and compile-time validation.
Concept for iterable containers (has begin() and end())
Definition concept.hpp:227
Concept for types that can be represented as a string.
Definition concept.hpp:442
#define logger(fmt,...)
Compile-time log level dispatch macro with automatic location capture.
Definition log.hpp:682
void fork_handler_child()
Fork handler that resets the logger in child processes.
Definition log.hpp:218
thread_local Logger logger
Thread-local logger instance.
Definition log.hpp:188
Multi-level logging system with file and stdout sinks.
constexpr decltype(auto) impl_log(Location loc)
Implementation function for compile-time log level dispatch.
Definition log.hpp:737
void set_as_fork()
Marks the logger as being in a forked child process.
Definition log.hpp:390
std::string vformat(std::string_view fmt, Ts &&... ts)
Workaround make_format_args only taking references.
Definition log.hpp:440
void set_level(Level level)
Sets the logging verbosity (CRITICAL,ERROR,INFO,DEBUG)
Definition log.hpp:339
Level get_level()
Get current verbosity level of the logger.
Definition log.hpp:349
void set_sink_file(fs::path const &path_file_sink)
Sets the sink file of the logger.
Definition log.hpp:359
std::string to_string(T &&t) noexcept
Converts a type to a string.
Definition string.hpp:97
String helpers.
Source code location information for log messages.
Definition log.hpp:403
consteval Location(const char *str_file=__builtin_FILE(), uint32_t str_line=__builtin_LINE())
Constructs a Location with automatic file/line capture.
Definition log.hpp:413
std::string_view m_str_file
Source file name (basename only)
Definition log.hpp:404
constexpr auto get() const
Formats location as "filename::line".
Definition log.hpp:425
uint32_t m_line
Source line number.
Definition log.hpp:405