220 lines
6.5 KiB
Markdown
220 lines
6.5 KiB
Markdown
# 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 <typename T> 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!
|
|
```
|
|
<!-- .slide: style="font-size: 0.74em" -->
|
|
<!-- .element: class="fragment fade-in" -->
|
|
___
|
|
|
|
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");
|
|
```
|
|
<!-- .slide: style="font-size: 0.75em" -->
|
|
___
|
|
|
|
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:
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```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!
|
|
```
|
|
<!-- .slide: style="font-size: 0.86em" -->
|
|
<!-- .element: class="fragment fade-in" -->
|
|
___
|
|
|
|
## 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;
|
|
}
|
|
```
|
|
<!-- .slide: style="font-size: 0.76em" -->
|
|
<!-- .element: class="fragment fade-in" -->
|
|
___
|
|
|
|
```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:
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
```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!
|
|
```
|
|
<!-- .slide: style="font-size: 0.86em" -->
|
|
<!-- .element: class="fragment fade-in" -->
|
|
___
|
|
|
|
## 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;
|
|
}
|
|
```
|
|
<!-- .slide: style="font-size: 0.74em" -->
|
|
<!-- .element: class="fragment fade-in" -->
|
|
___
|
|
|
|
```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
|
|
```
|
|
<!-- .slide: style="font-size: 0.76em" -->
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
___
|
|
|
|
## 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);
|
|
```
|