# logger ___ ## source_loaction Since C++20 we got a nice functionality to perform easy and efficient logging. ```C++ void log(const std::string_view message, const std::source_location location = std::source_location::current()) { std::cout << std::format("{}({}:{}) '{}':{}\n", location.file_name(), location.line(), location.column(), location.function_name(), message); } template void fun(T x) { log(x); } int main(int, char*[]) { log("Hello world!"); fun("Hello C++20!"); } ``` ```bash prog.cc(24:8) 'int main(int, char**)': Hello world! prog.cc(19:8) 'void fun(T) [with T = const char*]': Hello C++20! ``` ___ It's easy to rebuild a little this solution and create own logger: ```C++ enum LogType {INF, WRN, ERR}; struct Logger { Logger(LogType logType, std::source_location location = std::source_location::current()): logType_{logType}, location_{location} {} Logger& operator<<(std::string_view message) { file_ << std::format("[{}]({}): {}({}:{}) `{}`: {}\n", toString(logType_), std::chrono::system_clock::now(), location_.file_name(), location_.line(), location_.column(), location_.function_name(), message); file_ << std::flush; return *this; } private: std::string_view toString(LogType logType) { switch (logType) { case INF: return "I"; case WRN: return "W"; case ERR: return "E"; default: return "UNKNOWN!"; } } LogType logType_; std::source_location location_; static std::ofstream file_; }; std::ofstream Logger::file_("log.txt"); ``` ___ Ok now is time to log sth ```C++ int main() { Logger(INF) << "Hello!" << "And Hi" << "And Dzien dobry!"; Logger(WRN) << "Ojej!"; Logger(ERR) << "Critical!!!" << "UPS!" << "Bad bad!"; } ``` And in file `log.txt` we got: ```C++ [I](2024-03-17 15:29:34.248747710): prog.cc(55:15) `int main()`: Hello! [I](2024-03-17 15:29:34.248817593): prog.cc(55:15) `int main()`: And Hi [I](2024-03-17 15:29:34.248832174): prog.cc(55:15) `int main()`: And Dzien dobry! [W](2024-03-17 15:29:34.248843859): prog.cc(56:15) `int main()`: Ojej! [E](2024-03-17 15:29:34.248855275): prog.cc(57:15) `int main()`: Critical!!! [E](2024-03-17 15:29:34.248866476): prog.cc(57:15) `int main()`: UPS! [E](2024-03-17 15:29:34.248877615): prog.cc(57:15) `int main()`: Bad bad! ``` ___ ## Second version We can also write a whole output when the logger will be destroyed, this allows to log only one line. ```C++ Logger::Logger(LogType logType, std::source_location location) : logType_{logType}, location_{location} { file_ << std::format("[{}]({}): {}({}:{}) `{}`:", toString(logType_), std::chrono::system_clock::now(), location_.file_name(), location_.line(), location_.column(), location_.function_name()); } Logger::~Logger() { file_ << std::endl; // flush and new line } Logger& Logger::operator<<(const std::string& str) { file_ << " " << str; return *this; } ``` ___ ```C++ int main() { Logger(INF) << "Hello!" << "And Hi" << "And Dzien dobry!"; Logger(WRN) << "Ojej!"; Logger(ERR) << "Critical!!!" << "UPS!" << "Bad bad!"; } ``` And in file `log.txt` we got: ```C++ [I](2024-03-17 15:29:34.248747710): prog.cc(55:15) `int main()`: Hello! And Hi And Dzien dobry! [W](2024-03-17 15:29:34.248843859): prog.cc(56:15) `int main()`: Ojej! [E](2024-03-17 15:29:34.248855275): prog.cc(57:15) `int main()`: Critical!!! UPS! Bad bad! ``` ___ ## Ok but this is not thread safe! Now we need to make the code a thread-safe and put the `id` of the thread. We need to lock a mutex before writing output to the file and keep it until we destroy a logger. Ofc we risk that someone will create a logger instance and don't destroy them in the following line, but we assume that we use it correctly. We can add a special function that wraps this for use (the most common is macro). ```C++ Logger::Logger(LogType logType, std::source_location location): logType_{logType}, location_{location} { formatted_ = std::format("[{}][th:{}]({}): {}({}:{}) `{}`:", toString(logType_), std::this_thread::get_id(), std::chrono::system_clock::now(), location_.file_name(), location_.line(), location_.column(), location_.function_name()); } Logger& Logger::operator<<(const std::string& str) { formatted_ = std::format("{} {}", std::move(formatted_), str); return *this; } Logger::~Logger() { std::lock_guard lg(loggMutex_); loggFile_ << formatted_ << std::endl; } ``` ___ ```C++ #define LOG_INF Logger(INF) #define LOG_ERR Logger(ERR) #define LOG_WRN Logger(WRN) int main() { LOG_INF << "This" << "is" << "an" << "info"; LOG_WRN << "This" << "is" << "a" << "warning"; LOG_ERR << "This" << "is" << "an" << "error"; } ``` ```C++ [I][th:1](2024-03-17 15:53:04.743470912): prog.cc(86:5) `int main()`: This is an info [W][th:1](2024-03-17 15:53:04.743541096): prog.cc(87:5) `int main()`: This is a warning [E][th:1](2024-03-17 15:53:04.743558245): prog.cc(88:5) `int main()`: This is an error ``` ___ ## Cleanup At the beginning of the program, we should also clear all logged data, this is easy to achieve: ```C++ // Always clear file when program start std::ofstream Logger::loggFile_("log.txt", std::ofstream::trunc); ```