Recently, I sorted out the code and saw the company's code on log. I gained a lot, so I sorted out the instructions for the use of spdlog library.
https://github.com/gabime/spdlog.git
This paper mainly arranges the following aspects. The main basis for reference is the official routine, which is located in github
example\example.cpp
1. Log level division and several functions related to log level
The log is divided into the following levels. The specific level is the meaning, and the most serious time is
critical level, the least serious is trace
/* spdlog::level::trace */ /* spdlog::level::debug */ /* spdlog::level::info */ /* spdlog::level::warn */ /* spdlog::level::err */ /* spdlog::level::critical */ /* spdlog::level::off */
In actual use, this method can be used to directly output logs
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); //[2021-04-07 22:46:35.840] [info] Welcome to spdlog version 1.8.5 !
It will be used in development
m_logger->info("xxxxxxxxxxxxxxx")
Output in this form. Adding pointers is mainly to facilitate the output of different logs to different locations, such as M_ Output the content corresponding to Log1 to file 1; m_ Output the contents of log2 to file 2;
1.1 spdlog::set_level(spdlog::level::trace)
This function is mainly used to set the output level of the log. In the debugging stage, we want to output more debugging information. When the program is stable, we only need to output a small amount of important information, such as error reporting. At this time, you need to set the log output level. spdlog::set_level is a function that sets the output level.
1.2 logger->flush_on(spdlog::level::err);
When an error or above is encountered, the cached buffer will be written to the file immediately. The underlying call is std::fflush(_fd)
2. Introduction to log character format
Because a lot of template functions are used in spdlog, the use of functions is very flexible
All format parameters can be replaced by {}.
2.1 disorder the order of parameters
The variables in brackets can be divided into two parts. If only one number is filled in, it represents the sequence number of the parameter
spdlog::info("Positional args are {1} {0}..", "too", "supported"); //[2021-04-07 23:49:24.916] [info] Positional args are supported too..
In the above example, the order of parameters is obtained according to the sequence number in parentheses.
2.2 formatted digital output
Like printf, spdlog also supports formatted character output.
spdlog::warn("Easy padding in numbers like {:08d}", 12); // [2021-04-07 23:49:24.916] [warning] Easy padding in numbers like 00000012 spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); // [2021-04-07 23:49:24.916] [critical] Support for int: 42; hex: 2a; oct: 52; bin: 101010 spdlog::info("Support for floats {1:06.4f}", 1.23456,9.8754321); // [2021-04-07 23:49:24.916] [info] Support for floats 9.8754 spdlog::info("Positional args are {1} {0}..", "too", "supported"); // [2021-04-07 23:49:24.916] [info] Positional args are supported too.. spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left"); // [2021-04-07 23:49:24.916] [info] right aligned, left aligned
2.3 method of removing log time
Do you think the log printing time takes up too much storage space, In this way, the time of log printing can be removed
m_log->set_pattern("%v");
2.4 some methods of recording hexadecimal digits
The following is from the official readme file
// many types of std::container<char> types can be used. // ranges are supported too. // format flags: // {:X} - print in uppercase. // {:s} - don't separate each byte with space. // {:p} - don't print the position on each line start. // {:n} - don't split the output to lines. // {:a} - show ASCII if :n is not set. #include "spdlog/fmt/bin_to_hex.h" void binary_example() { auto console = spdlog::get("console"); std::array<char, 80> buf; console->info("Binary example: {}", spdlog::to_hex(buf)); console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); // more examples: // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); }
2.5 output format of time, thread, etc
The big guys on the Internet here write very well
https://blog.csdn.net/shizheng163/article/details/79418190
In addition to the above boss's summary, I also found the code segment about format analysis. You can directly see the meaning of format from the code!
// The code is taken from include\spdlog\pattern_formatter-inl.h position of about 1100 rows switch (flag) { case ('+'): // default formatter formatters_.push_back(details::make_unique<details::full_formatter>(padding)); break; case 'n': // logger name formatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding)); break; case 'l': // level formatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding)); break; case 'L': // short level formatters_.push_back(details::make_unique<details::short_level_formatter<Padder>>(padding)); break; case ('t'): // thread id formatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding)); break; case ('v'): // the message text formatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding)); break; case ('a'): // weekday formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding)); break; case ('A'): // short weekday formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding)); break; case ('b'): case ('h'): // month formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding)); break; case ('B'): // short month formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding)); break; case ('c'): // datetime formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding)); break; case ('C'): // year 2 digits formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding)); break; case ('Y'): // year 4 digits formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding)); break; case ('D'): case ('x'): // datetime MM/DD/YY formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding)); break; case ('m'): // month 1-12 formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding)); break; case ('d'): // day of month 1-31 formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding)); break; case ('H'): // hours 24 formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding)); break; case ('I'): // hours 12 formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding)); break; case ('M'): // minutes formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding)); break; case ('S'): // seconds formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding)); break; case ('e'): // milliseconds formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding)); break; case ('f'): // microseconds formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding)); break; case ('F'): // nanoseconds formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding)); break; case ('E'): // seconds since epoch formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding)); break; case ('p'): // am/pm formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding)); break; case ('r'): // 12 hour clock 02:55:02 pm formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding)); break; case ('R'): // 24-hour HH:MM time formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding)); break; case ('T'): case ('X'): // ISO 8601 time format (HH:MM:SS) formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding)); break; case ('z'): // timezone formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding)); break; case ('P'): // pid formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding)); break; case ('^'): // color range start formatters_.push_back(details::make_unique<details::color_start_formatter>(padding)); break; case ('$'): // color range end formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding)); break; case ('@'): // source location (filename:filenumber) formatters_.push_back(details::make_unique<details::source_location_formatter<Padder>>(padding)); break; case ('s'): // short source filename - without directory name formatters_.push_back(details::make_unique<details::short_filename_formatter<Padder>>(padding)); break; case ('g'): // full source filename formatters_.push_back(details::make_unique<details::source_filename_formatter<Padder>>(padding)); break; case ('#'): // source line number formatters_.push_back(details::make_unique<details::source_linenum_formatter<Padder>>(padding)); break; case ('!'): // source funcname formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding)); break; case ('%'): // % char formatters_.push_back(details::make_unique<details::ch_formatter>('%')); break; case ('u'): // elapsed time since last log message in nanos formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding)); break; case ('i'): // elapsed time since last log message in micros formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding)); break; case ('o'): // elapsed time since last log message in millis formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding)); break; case ('O'): // elapsed time since last log message in seconds formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding)); break;
3. Output form of log: file / print / remote print
After solving the problem of the format of the previous log generation, let's analyze the destination of the log.
spdlog provides a variety of log destinations. In order to be compatible with different platforms, it implements log destinations under different platforms, but these are roughly divided into these categories
- Print local console directly
- Print to remote console
- Write to file
- Custom type (you can define where your log should be output according to the standard format!!!)
For the first type of printing to the console, it is the simplest one. The most direct is to use printf. spdlog also realizes color printing for us, that is, the color information of the console is attached to the printing information, which can control the color of the printed characters!
Their corresponding implementation class is
include\spdlog\sinks\stdout_color_sinks.h # Color printout include\spdlog\sinks\stdout_sinks.h # Colorless printout
For the second category, spdlog mainly provides an example of a tcp client, which can write local logs to remote tcp clients
His realization is
include\spdlog\sinks\tcp_sink.h # Note that the old version does not have this file. The author is in version 1.8.5
For the third kind of writing files, the realization is more diversified.
① The simplest and crudest way is to write a log file directly without limiting the size. This has great defects, but it is also simple.
# include\spdlog\sinks\basic_file_sink.h
② Rolling file type: set the maximum size and the maximum number of files of a file, and the system can automatically write the file. If the file size exceeds the set maximum size of a single file, the current file will be renamed, and then another file will continue to write the log.
//include\spdlog\sinks\rotating_file_sink.h // Rotate files: // log.txt -> log.1.txt // log.1.txt -> log.2.txt // log.2.txt -> log.3.txt // log.3.txt -> delete
③ Is to write files regularly, and then name them by time. According to the cycle, it is divided into daily update and hourly update
# include\spdlog\sinks\daily_file_sink.h # Generator of daily log file names in format basename.YYYY-MM-DD.ext # include\spdlog\sinks\hourly_file_sink.h # Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
④ Finally, simply record the customized log type. This log library is used in the project, but we hope that the product of the project can be used as the TCP server. When our debugger is connected to the product, the product will print the real-time log to the debugger (computer).
In fact, the main thing is to realize it_ sink_it () and_ flush () function, I won't go into detail here.
4 . Multiple output of logs
When it is necessary to use the log output function on the console, it is also necessary to use the log output function.
This is mentioned in the default demo
// spdlog\example\example.cpp // A logger with multiple sinks (stdout and file) - each with a different format and log level. void multi_sink_example() { auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); console_sink->set_level(spdlog::level::warn); console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true); file_sink->set_level(spdlog::level::trace); spdlog::logger logger("multi_sink", {console_sink, file_sink}); logger.set_level(spdlog::level::debug); logger.warn("this should appear in both console and file"); logger.info("this message should not appear in the console, only in the file"); }
I also need to learn more skills.