merge
This commit is contained in:
		
						commit
						04c116315a
					
				
							
								
								
									
										67
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								README.md
									
									
									
									
									
								
							| @ -18,7 +18,9 @@ Just copy the files to your build tree and use a C++11 compiler | |||||||
| * Headers only. | * Headers only. | ||||||
| * No dependencies. | * No dependencies. | ||||||
| * Cross platform - Linux / Windows on 32/64 bits. | * Cross platform - Linux / Windows on 32/64 bits. | ||||||
| * Variadic-template/stream call styles: ```logger.info("variadic", x, y) << "or stream" << z;``` | * **new**! Feature rich [cppfromat call style](http://cppformat.readthedocs.org/en/stable/syntax.html) using the excellent [cppformat](http://cppformat.github.io/) library:```logger.info("Hello {} !!", "world");``` | ||||||
|  | * ostream call style ```logger.info() << "Hello << "logger";``` | ||||||
|  | * Mixed cppformat/ostream call style ```logger.info("{} + {} = ", 1, 2) << "?";``` | ||||||
| * [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting. | * [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting. | ||||||
| * Multi/Single threaded loggers. | * Multi/Single threaded loggers. | ||||||
| * Various log targets: | * Various log targets: | ||||||
| @ -27,12 +29,11 @@ Just copy the files to your build tree and use a C++11 compiler | |||||||
|     * Console logging. |     * Console logging. | ||||||
|     * Linux syslog. |     * Linux syslog. | ||||||
|     * Easily extendable with custom log targets  (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). |     * Easily extendable with custom log targets  (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). | ||||||
| * Optional async logging . | * Optional, (extremly fast!), async logging. | ||||||
| * Log levels. | * Log levels. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ## Benchmarks | ## Benchmarks | ||||||
| 
 | 
 | ||||||
| Below are some [benchmarks](bench) comparing the time needed to log 1,000,000 lines to file under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz (the best of 3 runs for each logger): | Below are some [benchmarks](bench) comparing the time needed to log 1,000,000 lines to file under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz (the best of 3 runs for each logger): | ||||||
| @ -43,8 +44,6 @@ Below are some [benchmarks](bench) comparing the time needed to log 1,000,000 li | |||||||
| |10|15.151ss|3.546s|3.500s|0.641s|0.199s| | |10|15.151ss|3.546s|3.500s|0.641s|0.199s| | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ## Usage Example | ## Usage Example | ||||||
| ```c++ | ```c++ | ||||||
| #include <iostream> | #include <iostream> | ||||||
| @ -52,33 +51,60 @@ Below are some [benchmarks](bench) comparing the time needed to log 1,000,000 li | |||||||
| 
 | 
 | ||||||
| int main(int, char* []) | int main(int, char* []) | ||||||
| { | { | ||||||
|  | 
 | ||||||
|     namespace spd = spdlog; |     namespace spd = spdlog; | ||||||
| 
 | 
 | ||||||
|     try |     try | ||||||
|     { |     { | ||||||
|         std::string filename = "spdlog_example"; |         std::string filename = "logs/spdlog_example"; | ||||||
|  |         // Set log level to all loggers to DEBUG and above | ||||||
|  |         spd::set_level(spd::level::DEBUG); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         //Create console, multithreaded logger | ||||||
|         auto console = spd::stdout_logger_mt("console"); |         auto console = spd::stdout_logger_mt("console"); | ||||||
|         console->info("Welcome to spdlog!") ; |         console->info("Welcome to spdlog!") ; | ||||||
|         console->info() << "Creating file " << filename << ".."; |         console->info("An info message example {}..", 1); | ||||||
|  |         console->info() << "Streams are supported too  " << 1; | ||||||
| 
 | 
 | ||||||
|  | 				 | ||||||
|  |         console->info("Easy padding in numbers like {:08d}", 12); | ||||||
|  |         console->info("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42); | ||||||
|  |         console->info("Support for floats {:03.2f}", 1.23456); | ||||||
|  |         console->info("Positional args are {1} {0}..", "too", "supported"); | ||||||
|  | 
 | ||||||
|  |         console->info("{:<30}", "left aligned"); | ||||||
|  |         console->info("{:>30}", "right aligned"); | ||||||
|  |         console->info("{:^30}", "centered"); | ||||||
|  |          | ||||||
|  |         //Create a file rotating logger with 5mb size max and 3 rotated files | ||||||
|         auto file_logger = spd::rotating_logger_mt("file_logger", filename, 1024 * 1024 * 5, 3); |         auto file_logger = spd::rotating_logger_mt("file_logger", filename, 1024 * 1024 * 5, 3); | ||||||
|         file_logger->info("Log file message number", 1); |         file_logger->info("Log file message number", 1); | ||||||
| 
 | 
 | ||||||
|         for (int i = 0; i < 100; ++i) |  | ||||||
|         { |  | ||||||
|             auto square = i*i; |  | ||||||
|             file_logger->info() << i << '*' << i << '=' << square << " (" << "0x" << std::hex << square << ")"; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         // Change log level to all loggers to warning and above |         spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); | ||||||
|         spd::set_level(spd::level::WARN); |         file_logger->info("This is another message with custom format"); | ||||||
|         console->info("This should not be displayed"); |  | ||||||
|         console->warn("This should!"); |  | ||||||
|         spd::set_level(spd::level::INFO); |  | ||||||
| 
 | 
 | ||||||
|         // Change format pattern to all loggers |         spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); | ||||||
|         spd::set_pattern(" **** %Y-%m-%d %H:%M:%S.%e %l **** %v"); | 
 | ||||||
|         spd::get("console")->info("This is another message with different format"); |         SPDLOG_TRACE(file_logger, "This is a trace message (only #ifdef _DEBUG)", 123); | ||||||
|  | 
 | ||||||
|  |         // | ||||||
|  |         // Asynchronous logging is easy.. | ||||||
|  |         // Just call spdlog::set_async_mode(max_q_size) and all created loggers from now on will be asynchronous.. | ||||||
|  |         // | ||||||
|  |         size_t max_q_size = 1048576; | ||||||
|  |         spdlog::set_async_mode(max_q_size); | ||||||
|  |         auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); | ||||||
|  |         async_file->info() << "This is async log.." << "Should be very fast!"; | ||||||
|  | 
 | ||||||
|  |         // | ||||||
|  |         // syslog example | ||||||
|  |         // | ||||||
|  | #ifdef __linux__ | ||||||
|  |         auto syslog_logger = spd::syslog_logger("syslog"); | ||||||
|  |         syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!"); | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
|     catch (const spd::spdlog_ex& ex) |     catch (const spd::spdlog_ex& ex) | ||||||
|     { |     { | ||||||
| @ -86,4 +112,5 @@ int main(int, char* []) | |||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
| ``` | ``` | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| CXX	= g++ | CXX	= g++ | ||||||
| CXXFLAGS	= -g -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed  -I../include  | CXXFLAGS	= -march=native -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed  -I../include  | ||||||
| CXX_RELEASE_FLAGS = -O3 -flto | CXX_RELEASE_FLAGS = -O3 -flto | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,80 +0,0 @@ | |||||||
| Running benchmakrs (all with 1000,000 writes to the logs folder |  | ||||||
| 
 |  | ||||||
| boost-bench (single thread).. |  | ||||||
| 
 |  | ||||||
| real	0m4.779s |  | ||||||
| user	0m4.256s |  | ||||||
| sys	0m0.044s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| glog-bench (single thread).. |  | ||||||
| 
 |  | ||||||
| real	0m1.109s |  | ||||||
| user	0m0.967s |  | ||||||
| sys	0m0.140s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| g2log-bench (single thread).. |  | ||||||
| 
 |  | ||||||
| Exiting, log location: logs/g2log-bench.g2log.20141124-233419.log |  | ||||||
| 
 |  | ||||||
| real	0m3.155s |  | ||||||
| user	0m4.255s |  | ||||||
| sys	0m0.874s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| spdlog-bench (single thread) |  | ||||||
| 
 |  | ||||||
| real	0m0.947s |  | ||||||
| user	0m0.914s |  | ||||||
| sys	0m0.032s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ------------------------------------ |  | ||||||
| Multithreaded benchmarks.. |  | ||||||
| ------------------------------------ |  | ||||||
| boost-bench-mt (10 threads, single logger).. |  | ||||||
| 
 |  | ||||||
| real	0m15.151s |  | ||||||
| user	0m35.555s |  | ||||||
| sys	0m7.453s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| glog-bench-mt (10 threads, single logger).. |  | ||||||
| 
 |  | ||||||
| real	0m3.546s |  | ||||||
| user	0m9.836s |  | ||||||
| sys	0m10.985s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| g2log-bench-mt (10 threads, single logger).. |  | ||||||
| 
 |  | ||||||
| Exiting, log location: logs/g2log-bench-mt.g2log.20141124-233502.log |  | ||||||
| 
 |  | ||||||
| real	0m3.500s |  | ||||||
| user	0m7.671s |  | ||||||
| sys	0m1.646s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| spdlog-bench-mt (10 threads, single logger).. |  | ||||||
| 
 |  | ||||||
| real	0m1.549s |  | ||||||
| user	0m6.994s |  | ||||||
| sys	0m2.969s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ------------------------------------ |  | ||||||
| Async  benchmarks.. |  | ||||||
| ------------------------------------ |  | ||||||
| spdlog-bench-async (single thread).. |  | ||||||
| 
 |  | ||||||
| real	0m1.455s |  | ||||||
| user	0m2.381s |  | ||||||
| sys	0m0.417s |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| spdlog-bench-mt-async (10 threads, single logger).. |  | ||||||
| 
 |  | ||||||
| real	0m2.040s |  | ||||||
| user	0m4.076s |  | ||||||
| sys	0m2.822s |  | ||||||
| @ -2,29 +2,29 @@ | |||||||
| echo "Running benchmakrs (all with 1000,000 writes to the logs folder)" | echo "Running benchmakrs (all with 1000,000 writes to the logs folder)" | ||||||
| echo | echo | ||||||
| echo "boost-bench (single thread).." | echo "boost-bench (single thread).." | ||||||
| time ./boost-bench | for i in {1..3}; do time ./boost-bench; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| 
 | 
 | ||||||
| echo "glog-bench (single thread).." | echo "glog-bench (single thread).." | ||||||
| time ./glog-bench | for i in {1..3}; do time ./glog-bench; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| 
 | 
 | ||||||
| echo "g2log-bench (single thread).." | echo "g2log-bench (single thread).." | ||||||
| time ./g2log-bench | for i in {1..3}; do time ./g2log-bench; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| 
 | 
 | ||||||
| echo "spdlog-bench (single thread)" | echo "spdlog-bench (single thread)" | ||||||
| time ./spdlog-bench | for i in {1..3}; do time ./spdlog-bench; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| @ -32,29 +32,29 @@ echo "------------------------------------" | |||||||
| echo "Multithreaded benchmarks.." | echo "Multithreaded benchmarks.." | ||||||
| echo "------------------------------------" | echo "------------------------------------" | ||||||
| echo "boost-bench-mt (10 threads, single logger)".. | echo "boost-bench-mt (10 threads, single logger)".. | ||||||
| time ./boost-bench-mt | for i in {1..3}; do ./boost-bench-mt; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| 
 | 
 | ||||||
| echo "glog-bench-mt (10 threads, single logger)".. | echo "glog-bench-mt (10 threads, single logger)".. | ||||||
| time ./glog-bench-mt | for i in {1..3}; do time ./glog-bench-mt; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| 
 | 
 | ||||||
| echo "g2log-bench-mt (10 threads, single logger)".. | echo "g2log-bench-mt (10 threads, single logger)".. | ||||||
| time ./g2log-bench-mt | for i in {1..3}; do time ./g2log-bench-mt; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| 
 | 
 | ||||||
| echo "spdlog-bench-mt (10 threads, single logger)".. | echo "spdlog-bench-mt (10 threads, single logger)".. | ||||||
| time ./spdlog-bench-mt | for i in {1..3}; do time ./spdlog-bench-mt; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| @ -64,14 +64,14 @@ echo "Async  benchmarks.." | |||||||
| echo "------------------------------------" | echo "------------------------------------" | ||||||
| 
 | 
 | ||||||
| echo "spdlog-bench-async (single thread)".. | echo "spdlog-bench-async (single thread)".. | ||||||
| time ./spdlog-bench-async | for i in {1..3}; do time ./spdlog-bench-async; done | ||||||
| rm logs/* | rm  -f logs/* | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| sleep 5 | sleep 5 | ||||||
| 
 | 
 | ||||||
| echo "spdlog-bench-mt-async (10 threads, single logger)".. | echo "spdlog-bench-mt-async (10 threads, single logger)".. | ||||||
| time ./spdlog-bench-mt-async | for i in {1..3}; do time ./spdlog-bench-mt-async; done | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,15 +4,19 @@ | |||||||
| 
 | 
 | ||||||
| int main(int, char* []) | int main(int, char* []) | ||||||
| { | { | ||||||
|     int howmany = 1000000; |     int howmany = 1048576; | ||||||
|     namespace spd = spdlog; |     namespace spd = spdlog; | ||||||
|    	spd::set_async_mode(2500, std::chrono::seconds(0)); |     spd::set_async_mode(howmany); | ||||||
|     ///Create a file rotating logger with 5mb size max and 3 rotated files
 |     ///Create a file rotating logger with 5mb size max and 3 rotated files
 | ||||||
|     auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5); |     auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5); | ||||||
| 
 | 
 | ||||||
|     logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); |     logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); | ||||||
|     for(int i  = 0 ; i < howmany; ++i) |     for(int i  = 0 ; i < howmany; ++i) | ||||||
|         logger->info() << "spdlog message #" << i << ": This is some text for your pleasure"; |         logger->info() << "spdlog message #" << i << ": This is some text for your pleasure"; | ||||||
|     spd::stop();      | 
 | ||||||
|  | 	 | ||||||
|  | 	//because spdlog async logger waits for the back thread logger to finish all messages upon destrcuting,
 | ||||||
|  | 	//and we want to measure only the time it took to push those messages to the backthread..
 | ||||||
|  | 	abort(); 	     | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,10 +14,10 @@ int main(int argc, char* argv[]) | |||||||
|     if(argc > 1) |     if(argc > 1) | ||||||
|         thread_count = atoi(argv[1]); |         thread_count = atoi(argv[1]); | ||||||
| 
 | 
 | ||||||
|     int howmany = 1000000; |     int howmany = 1048576; | ||||||
| 
 | 
 | ||||||
|     namespace spd = spdlog; |     namespace spd = spdlog; | ||||||
| 	spd::set_async_mode(2500, std::chrono::seconds(0)); |     spd::set_async_mode(howmany); | ||||||
|     ///Create a file rotating logger with 5mb size max and 3 rotated files
 |     ///Create a file rotating logger with 5mb size max and 3 rotated files
 | ||||||
|     auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5); |     auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5); | ||||||
| 
 | 
 | ||||||
| @ -45,6 +45,7 @@ int main(int argc, char* argv[]) | |||||||
|         t.join(); |         t.join(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| 	spd::stop();      | 	//because spdlog async logger waits for the back thread logger to finish all messages upon destrcuting,
 | ||||||
|     return 0; | 	//and we want to measure only the time it took to push those messages to the backthread..
 | ||||||
|  | 	abort(); 	     | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,8 +17,8 @@ int main(int argc, char* argv[]) | |||||||
|     int howmany = 1000000; |     int howmany = 1000000; | ||||||
| 
 | 
 | ||||||
|     namespace spd = spdlog; |     namespace spd = spdlog; | ||||||
|     ///Create a file rotating logger with 5mb size max and 5 rotated files
 |     ///Create a file rotating logger with 10mb size max and 5 rotated files
 | ||||||
|     auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5); |     auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5, false); | ||||||
| 
 | 
 | ||||||
|     logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); |     logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| CXX	= g++ | CXX	= g++ | ||||||
| CXXFLAGS	= -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed  -I../include  | CXXFLAGS	= -march=native -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed  -I../include  | ||||||
| CXX_RELEASE_FLAGS = -O3 -flto | CXX_RELEASE_FLAGS = -O3 -flto | ||||||
| CXX_DEBUG_FLAGS= -g  | CXX_DEBUG_FLAGS= -g  | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -50,9 +50,13 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count | |||||||
| int main(int argc, char* argv[]) | int main(int argc, char* argv[]) | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     int howmany = 1000000; |     int howmany = 1048576; | ||||||
|     int threads = 10; |     int threads = 10; | ||||||
|  | <<<<<<< HEAD | ||||||
| 	bool auto_flush = false; | 	bool auto_flush = false; | ||||||
|  | ======= | ||||||
|  |     bool auto_flush = false; | ||||||
|  | >>>>>>> cppformat | ||||||
|     int file_size = 30 * 1024 * 1024; |     int file_size = 30 * 1024 * 1024; | ||||||
|     int rotating_files = 5; |     int rotating_files = 5; | ||||||
| 
 | 
 | ||||||
| @ -64,6 +68,7 @@ int main(int argc, char* argv[]) | |||||||
|         if (argc > 2) |         if (argc > 2) | ||||||
|             threads =   atoi(argv[2]); |             threads =   atoi(argv[2]); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|         cout << "*******************************************************************************\n"; |         cout << "*******************************************************************************\n"; | ||||||
|         cout << "Single thread, " << format(howmany)  << " iterations, auto flush=" << auto_flush << endl; |         cout << "Single thread, " << format(howmany)  << " iterations, auto flush=" << auto_flush << endl; | ||||||
|         cout << "*******************************************************************************\n"; |         cout << "*******************************************************************************\n"; | ||||||
| @ -91,12 +96,14 @@ int main(int argc, char* argv[]) | |||||||
|         cout << "*******************************************************************************\n"; |         cout << "*******************************************************************************\n"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         spdlog::set_async_mode(2500); |         spdlog::set_async_mode(howmany); | ||||||
|         auto daily_st_async = spdlog::daily_logger_st("daily_async", "logs/daily_async", auto_flush); |  | ||||||
| 		bench_mt(howmany, daily_st_async, threads); |  | ||||||
| 	 |  | ||||||
|         spdlog::stop(); |  | ||||||
| 
 | 
 | ||||||
|  | 		for(int i = 0; i < 5; ++i) | ||||||
|  | 		{ | ||||||
|  | 			auto as = spdlog::daily_logger_st("as", "logs/daily_async", auto_flush); | ||||||
|  | 			bench_mt(howmany, as, threads);			 | ||||||
|  | 			spdlog::drop("as"); | ||||||
|  | 		} | ||||||
|     } |     } | ||||||
|     catch (std::exception &ex) |     catch (std::exception &ex) | ||||||
|     { |     { | ||||||
| @ -113,7 +120,7 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log) | |||||||
|     auto start = system_clock::now(); |     auto start = system_clock::now(); | ||||||
|     for (auto i = 0; i < howmany; ++i) |     for (auto i = 0; i < howmany; ++i) | ||||||
|     { |     { | ||||||
|         log->info("Hello logger: msg number ", i); |         log->info("Hello logger: msg number {}", i); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -138,7 +145,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count | |||||||
|             { |             { | ||||||
|                 int counter = ++msg_counter; |                 int counter = ++msg_counter; | ||||||
|                 if (counter > howmany) break; |                 if (counter > howmany) break; | ||||||
|                 log->info("Hello logger: msg number ") << counter; |                 log->info("Hello logger: msg number {}", counter); | ||||||
|             } |             } | ||||||
|         })); |         })); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ | |||||||
| // spdlog usage example
 | // spdlog usage example
 | ||||||
| //
 | //
 | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include "spdlog/spdlog.h" | #include "spdlog/spdlog.h"ls | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| int main(int, char* []) | int main(int, char* []) | ||||||
| @ -44,17 +44,23 @@ int main(int, char* []) | |||||||
|         //Create console, multithreaded logger
 |         //Create console, multithreaded logger
 | ||||||
|         auto console = spd::stdout_logger_mt("console"); |         auto console = spd::stdout_logger_mt("console"); | ||||||
|         console->info("Welcome to spdlog!") ; |         console->info("Welcome to spdlog!") ; | ||||||
|         console->info("An info message example", "...", 1, 2, 3.5); |         console->info("An info message example {}..", 1); | ||||||
|         console->info() << "Streams are supported too  " << std::setw(5) << std::setfill('0') << 1; |         console->info() << "Streams are supported too  " << 1; | ||||||
|  | 
 | ||||||
|  | 				 | ||||||
|  |         console->info("Easy padding in numbers like {:08d}", 12); | ||||||
|  |         console->info("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42); | ||||||
|  |         console->info("Support for floats {:03.2f}", 1.23456); | ||||||
|  |         console->info("Positional args are {1} {0}..", "too", "supported"); | ||||||
|  | 
 | ||||||
|  |         console->info("{:<30}", "left aligned"); | ||||||
|  |         console->info("{:>30}", "right aligned"); | ||||||
|  |         console->info("{:^30}", "centered"); | ||||||
|          |          | ||||||
|         //Create a file rotating logger with 5mb size max and 3 rotated files
 |         //Create a file rotating logger with 5mb size max and 3 rotated files
 | ||||||
|         auto file_logger = spd::rotating_logger_mt("file_logger", filename, 1024 * 1024 * 5, 3); |         auto file_logger = spd::rotating_logger_mt("file_logger", filename, 1024 * 1024 * 5, 3); | ||||||
|         file_logger->info("Log file message number", 1); |         file_logger->info("Log file message number", 1); | ||||||
| 
 | 
 | ||||||
|         for (int i = 0; i < 100; ++i) |  | ||||||
|         { |  | ||||||
|             file_logger->info(i, "in hex is", "0x") <<  std::hex << std::uppercase << i; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); |         spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); | ||||||
|         file_logger->info("This is another message with custom format"); |         file_logger->info("This is another message with custom format"); | ||||||
| @ -67,8 +73,7 @@ int main(int, char* []) | |||||||
|         // Asynchronous logging is easy..
 |         // Asynchronous logging is easy..
 | ||||||
|         // Just call spdlog::set_async_mode(max_q_size) and all created loggers from now on will be asynchronous..
 |         // Just call spdlog::set_async_mode(max_q_size) and all created loggers from now on will be asynchronous..
 | ||||||
|         //
 |         //
 | ||||||
| 
 |         size_t max_q_size = 1048576; | ||||||
|         size_t max_q_size = 100000; |  | ||||||
|         spdlog::set_async_mode(max_q_size); |         spdlog::set_async_mode(max_q_size); | ||||||
|         auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); |         auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); | ||||||
|         async_file->info() << "This is async log.." << "Should be very fast!"; |         async_file->info() << "This is async log.." << "Should be very fast!"; | ||||||
|  | |||||||
| @ -24,7 +24,6 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <iostream> |  | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| #include <locale> | #include <locale> | ||||||
|  | |||||||
| @ -24,10 +24,15 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| // Async logger
 | // Very fast asynchronous logger (millions of logs per second on an average desktop)
 | ||||||
|  | // Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
 | ||||||
|  | // Creates a single back thread to pop messages from the queue and log them.
 | ||||||
|  | //
 | ||||||
| // Upon each log write the logger:
 | // Upon each log write the logger:
 | ||||||
| //    1. Checks if its log level is enough to log the message
 | //    1. Checks if its log level is enough to log the message
 | ||||||
| // 2. Push a new copy of the message to a queue (uses sinks::async_sink for this)
 | //    2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
 | ||||||
|  | //    3. will throw spdlog_ex upon log exceptions
 | ||||||
|  | // Upong destruction, logs all remaining messages in the queue before destructing..
 | ||||||
| 
 | 
 | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include "common.h" | #include "common.h" | ||||||
| @ -37,18 +42,18 @@ | |||||||
| namespace spdlog | namespace spdlog | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| namespace sinks | namespace details | ||||||
| { | { | ||||||
| class async_sink; | class async_log_helper; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class async_logger :public logger | class async_logger :public logger | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     template<class It> |     template<class It> | ||||||
|     async_logger(const std::string& name, const It& begin, const It& end, size_t queue_size, const log_clock::duration& shutdown_duration); |     async_logger(const std::string& name, const It& begin, const It& end, size_t queue_size); | ||||||
|     async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const log_clock::duration& shutdown_duration); |     async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size); | ||||||
|     async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const log_clock::duration& shutdown_duration); |     async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
| @ -58,8 +63,7 @@ protected: | |||||||
|     void _stop() override; |     void _stop() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     log_clock::duration _shutdown_duration; |     std::unique_ptr<details::async_log_helper> _async_log_helper; | ||||||
|     std::unique_ptr<sinks::async_sink> _as; |  | ||||||
| }; | }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										289
									
								
								include/spdlog/details/async_log_helper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								include/spdlog/details/async_log_helper.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,289 @@ | |||||||
|  | /*************************************************************************/ | ||||||
|  | /* spdlog - an extremely fast and easy to use c++11 logging library.     */ | ||||||
|  | /* Copyright (c) 2014 Gabi Melman.                                       */ | ||||||
|  | /*                                                                       */ | ||||||
|  | /* Permission is hereby granted, free of charge, to any person obtaining */ | ||||||
|  | /* a copy of this software and associated documentation files (the       */ | ||||||
|  | /* "Software"), to deal in the Software without restriction, including   */ | ||||||
|  | /* without limitation the rights to use, copy, modify, merge, publish,   */ | ||||||
|  | /* distribute, sublicense, and/or sell copies of the Software, and to    */ | ||||||
|  | /* permit persons to whom the Software is furnished to do so, subject to */ | ||||||
|  | /* the following conditions:                                             */ | ||||||
|  | /*                                                                       */ | ||||||
|  | /* The above copyright notice and this permission notice shall be        */ | ||||||
|  | /* included in all copies or substantial portions of the Software.       */ | ||||||
|  | /*                                                                       */ | ||||||
|  | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ | ||||||
|  | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ | ||||||
|  | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ | ||||||
|  | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ | ||||||
|  | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ | ||||||
|  | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ | ||||||
|  | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | ||||||
|  | /*************************************************************************/ | ||||||
|  | 
 | ||||||
|  | // async log helper :
 | ||||||
|  | // Process logs asynchronously using a back thread.
 | ||||||
|  | //
 | ||||||
|  | // If the internal queue of log messages reaches its max size,
 | ||||||
|  | // then the client call will block until there is more room.
 | ||||||
|  | //
 | ||||||
|  | // If the back thread throws during logging, a spdlog::spdlog_ex exception
 | ||||||
|  | // will be thrown in client's thread when tries to log the next message
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <chrono> | ||||||
|  | #include <thread> | ||||||
|  | #include <atomic> | ||||||
|  | 
 | ||||||
|  | #include "../common.h" | ||||||
|  | #include "../sinks/sink.h" | ||||||
|  | #include "./mpmc_bounded_q.h" | ||||||
|  | #include "./log_msg.h" | ||||||
|  | #include "./format.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace spdlog | ||||||
|  | { | ||||||
|  | namespace details | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class async_log_helper | ||||||
|  | { | ||||||
|  |     // Async msg to move to/from the queue
 | ||||||
|  |     // Movable only. should never be copied
 | ||||||
|  |     struct async_msg | ||||||
|  |     { | ||||||
|  |         std::string logger_name; | ||||||
|  |         level::level_enum level; | ||||||
|  |         log_clock::time_point time; | ||||||
|  |         std::string txt; | ||||||
|  | 
 | ||||||
|  |         async_msg() = default; | ||||||
|  |         ~async_msg() = default; | ||||||
|  | 
 | ||||||
|  |         async_msg(const async_msg&) = delete; | ||||||
|  |         async_msg& operator=(async_msg& other) = delete; | ||||||
|  | 
 | ||||||
|  |         async_msg(const details::log_msg& m) : | ||||||
|  |             logger_name(m.logger_name), | ||||||
|  |             level(m.level), | ||||||
|  |             time(m.time), | ||||||
|  |             txt(m.raw.data(), m.raw.size()) | ||||||
|  |         {} | ||||||
|  | 
 | ||||||
|  |         async_msg(async_msg&& other) : | ||||||
|  |             logger_name(std::move(other.logger_name)), | ||||||
|  |             level(std::move(other.level)), | ||||||
|  |             time(std::move(other.time)), | ||||||
|  |             txt(std::move(other.txt)) | ||||||
|  |         {} | ||||||
|  | 
 | ||||||
|  |         async_msg& operator=(async_msg&& other) | ||||||
|  |         { | ||||||
|  |             logger_name = std::move(other.logger_name); | ||||||
|  |             level = other.level; | ||||||
|  |             time = std::move(other.time); | ||||||
|  |             txt = std::move(other.txt); | ||||||
|  |             return *this; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         void fill_log_msg(log_msg &msg) | ||||||
|  |         { | ||||||
|  |             msg.clear(); | ||||||
|  |             msg.logger_name = logger_name; | ||||||
|  |             msg.level = level; | ||||||
|  |             msg.time = time; | ||||||
|  |             msg.raw << txt; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  |     using item_type = async_msg; | ||||||
|  |     using q_type = details::mpmc_bounded_queue<item_type>; | ||||||
|  | 
 | ||||||
|  |     using clock = std::chrono::steady_clock; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     async_log_helper(formatter_ptr formatter, const std::vector<sink_ptr>& sinks, size_t queue_size); | ||||||
|  |     void log(const details::log_msg& msg); | ||||||
|  | 
 | ||||||
|  |     //Stop logging and join the back thread
 | ||||||
|  |     ~async_log_helper(); | ||||||
|  |     void set_formatter(formatter_ptr); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::atomic<bool> _active; | ||||||
|  |     formatter_ptr _formatter; | ||||||
|  |     std::vector<std::shared_ptr<sinks::sink>> _sinks; | ||||||
|  |     q_type _q; | ||||||
|  |     std::thread _worker_thread; | ||||||
|  | 
 | ||||||
|  |     // last exception thrown from the worker thread
 | ||||||
|  |     std::shared_ptr<spdlog_ex> _last_workerthread_ex; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // throw last worker thread exception or if worker thread is not active
 | ||||||
|  |     void throw_if_bad_worker(); | ||||||
|  | 
 | ||||||
|  |     // worker thread main loop
 | ||||||
|  |     void worker_loop(); | ||||||
|  | 
 | ||||||
|  |     //pop next message from the queue and process it
 | ||||||
|  |     //return true if a message was available (queue was not empty), will set the last_pop to the pop time
 | ||||||
|  |     bool process_next_msg(clock::time_point& last_pop); | ||||||
|  | 
 | ||||||
|  |     // guess how much to sleep if queue is empty/full using last succesful op time as hint
 | ||||||
|  |     static void sleep_or_yield(const clock::time_point& last_op_time); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // clear all remaining messages(if any), stop the _worker_thread and join it
 | ||||||
|  |     void join_worker(); | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // async_sink class implementation
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatter, const std::vector<sink_ptr>& sinks, size_t queue_size): | ||||||
|  |     _active(true), | ||||||
|  |     _formatter(formatter), | ||||||
|  |     _sinks(sinks), | ||||||
|  |     _q(queue_size), | ||||||
|  |     _worker_thread(&async_log_helper::worker_loop, this) | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::async_log_helper::~async_log_helper() | ||||||
|  | { | ||||||
|  |     join_worker(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //Try to push and block until succeeded
 | ||||||
|  | inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) | ||||||
|  | { | ||||||
|  |     throw_if_bad_worker(); | ||||||
|  |     async_msg new_msg(msg); | ||||||
|  |     if (!_q.enqueue(std::move(new_msg))) | ||||||
|  |     { | ||||||
|  |         auto last_op_time = clock::now(); | ||||||
|  |         do | ||||||
|  |         { | ||||||
|  |             sleep_or_yield(last_op_time); | ||||||
|  |         } | ||||||
|  |         while (!_q.enqueue(std::move(new_msg))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void spdlog::details::async_log_helper::worker_loop() | ||||||
|  | { | ||||||
|  |     clock::time_point last_pop = clock::now(); | ||||||
|  |     while (_active) | ||||||
|  |     { | ||||||
|  |         //Dont die if there are still messages in the q to process
 | ||||||
|  |         while(process_next_msg(last_pop)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_point& last_pop) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     async_msg incoming_async_msg; | ||||||
|  |     log_msg incoming_log_msg; | ||||||
|  | 
 | ||||||
|  |     if (_q.dequeue(incoming_async_msg)) | ||||||
|  |     { | ||||||
|  |         last_pop = clock::now(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             incoming_async_msg.fill_log_msg(incoming_log_msg); | ||||||
|  |             _formatter->format(incoming_log_msg); | ||||||
|  |             for (auto &s : _sinks) | ||||||
|  |                 s->log(incoming_log_msg); | ||||||
|  |         } | ||||||
|  |         catch (const std::exception& ex) | ||||||
|  |         { | ||||||
|  |             _last_workerthread_ex = std::make_shared<spdlog_ex>(std::string("async_logger worker thread exception: ") + ex.what()); | ||||||
|  |         } | ||||||
|  |         catch (...) | ||||||
|  |         { | ||||||
|  |             _last_workerthread_ex = std::make_shared<spdlog_ex>("async_logger worker thread exception"); | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     // sleep or yield if queue is empty.
 | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         sleep_or_yield(last_pop); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) | ||||||
|  | { | ||||||
|  |     _formatter = msg_formatter; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Sleep,yield or return immediatly using the time passed since last message as a hint
 | ||||||
|  | inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_point& last_op_time) | ||||||
|  | { | ||||||
|  |     using std::chrono::milliseconds; | ||||||
|  |     using namespace std::this_thread; | ||||||
|  | 
 | ||||||
|  |     auto time_since_op = clock::now() - last_op_time; | ||||||
|  | 
 | ||||||
|  |     //spin upto 1 ms
 | ||||||
|  |     if (time_since_op <= milliseconds(1)) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     // yield upto 10ms
 | ||||||
|  |     if (time_since_op <= milliseconds(10)) | ||||||
|  |         return yield(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // sleep for half of duration since last op
 | ||||||
|  |     if (time_since_op <= milliseconds(100)) | ||||||
|  |         return sleep_for(time_since_op / 2); | ||||||
|  | 
 | ||||||
|  |     return sleep_for(milliseconds(100)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //throw if the worker thread threw an exception or not active
 | ||||||
|  | inline void spdlog::details::async_log_helper::throw_if_bad_worker() | ||||||
|  | { | ||||||
|  |     if (_last_workerthread_ex) | ||||||
|  |     { | ||||||
|  |         auto ex = std::move(_last_workerthread_ex); | ||||||
|  |         _last_workerthread_ex.reset(); | ||||||
|  |         throw *ex; | ||||||
|  |     } | ||||||
|  |     if (!_active) | ||||||
|  |         throw(spdlog_ex("async logger is not active")); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | inline void spdlog::details::async_log_helper::join_worker() | ||||||
|  | { | ||||||
|  |     _active = false; | ||||||
|  | 
 | ||||||
|  |     try | ||||||
|  |     { | ||||||
|  |         _worker_thread.join(); | ||||||
|  |     } | ||||||
|  |     catch (const std::system_error&) //Dont crash if thread not joinable
 | ||||||
|  |     {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @ -25,8 +25,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include "./async_log_helper.h" | ||||||
| #include "../sinks/async_sink.h" |  | ||||||
| 
 | 
 | ||||||
| //
 | //
 | ||||||
| // Async Logger implementation
 | // Async Logger implementation
 | ||||||
| @ -35,38 +34,29 @@ | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| template<class It> | template<class It> | ||||||
| inline spdlog::async_logger::async_logger(const std::string& logger_name, const It& begin, const It& end, size_t queue_size, const log_clock::duration& shutdown_duration) : | inline spdlog::async_logger::async_logger(const std::string& logger_name, const It& begin, const It& end, size_t queue_size) : | ||||||
|     logger(logger_name, begin, end), |     logger(logger_name, begin, end), | ||||||
|     _shutdown_duration(shutdown_duration), |     _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size)) | ||||||
|     _as(std::unique_ptr<sinks::async_sink>(new sinks::async_sink(queue_size))) |  | ||||||
| { | { | ||||||
|     _as->set_formatter(_formatter); |  | ||||||
|     for (auto &s : _sinks) |  | ||||||
|         _as->add_sink(s); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline spdlog::async_logger::async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const log_clock::duration& shutdown_duration) : | inline spdlog::async_logger::async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size) : | ||||||
|     async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, shutdown_duration) {} |     async_logger(logger_name, sinks.begin(), sinks.end(), queue_size) {} | ||||||
| 
 | 
 | ||||||
| inline spdlog::async_logger::async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const log_clock::duration& shutdown_duration) : | inline spdlog::async_logger::async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size) : | ||||||
|     async_logger(logger_name, { single_sink }, queue_size, shutdown_duration) {} |     async_logger(logger_name, { single_sink }, queue_size) {} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| inline void spdlog::async_logger::_log_msg(details::log_msg& msg) |  | ||||||
| { |  | ||||||
|     _as->log(msg); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) | inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) | ||||||
| { | { | ||||||
|     _formatter = msg_formatter; |     _formatter = msg_formatter; | ||||||
|     _as->set_formatter(_formatter); |     _async_log_helper->set_formatter(_formatter); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline void spdlog::async_logger::_set_pattern(const std::string& pattern) | inline void spdlog::async_logger::_set_pattern(const std::string& pattern) | ||||||
| { | { | ||||||
|     _formatter = std::make_shared<pattern_formatter>(pattern); |     _formatter = std::make_shared<pattern_formatter>(pattern); | ||||||
|     _as->set_formatter(_formatter); |     _async_log_helper->set_formatter(_formatter); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -74,5 +64,9 @@ inline void spdlog::async_logger::_set_pattern(const std::string& pattern) | |||||||
| inline void spdlog::async_logger::_stop() | inline void spdlog::async_logger::_stop() | ||||||
| { | { | ||||||
|     set_level(level::OFF); |     set_level(level::OFF); | ||||||
|     _as->shutdown(_shutdown_duration); | } | ||||||
|  | 
 | ||||||
|  | inline void spdlog::async_logger::_log_msg(details::log_msg& msg) | ||||||
|  | { | ||||||
|  |     _async_log_helper->log(msg); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,155 +0,0 @@ | |||||||
| /*************************************************************************/ |  | ||||||
| /* spdlog - an extremely fast and easy to use c++11 logging library.     */ |  | ||||||
| /* Copyright (c) 2014 Gabi Melman.                                       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* Permission is hereby granted, free of charge, to any person obtaining */ |  | ||||||
| /* a copy of this software and associated documentation files (the       */ |  | ||||||
| /* "Software"), to deal in the Software without restriction, including   */ |  | ||||||
| /* without limitation the rights to use, copy, modify, merge, publish,   */ |  | ||||||
| /* distribute, sublicense, and/or sell copies of the Software, and to    */ |  | ||||||
| /* permit persons to whom the Software is furnished to do so, subject to */ |  | ||||||
| /* the following conditions:                                             */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* The above copyright notice and this permission notice shall be        */ |  | ||||||
| /* included in all copies or substantial portions of the Software.       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ |  | ||||||
| /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ |  | ||||||
| /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ |  | ||||||
| /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ |  | ||||||
| /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ |  | ||||||
| /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ |  | ||||||
| /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ |  | ||||||
| /*************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| // A blocking multi-consumer/multi-producer thread safe queue.
 |  | ||||||
| // Has max capacity and supports timeout on push or pop operations.
 |  | ||||||
| 
 |  | ||||||
| #include <chrono> |  | ||||||
| #include <memory> |  | ||||||
| #include <queue> |  | ||||||
| #include <mutex> |  | ||||||
| #include <condition_variable> |  | ||||||
| 
 |  | ||||||
| namespace spdlog |  | ||||||
| { |  | ||||||
| namespace details |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
| template<typename T> |  | ||||||
| class blocking_queue |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     using queue_type = std::queue<T>; |  | ||||||
|     using item_type = T; |  | ||||||
|     using size_type = typename queue_type::size_type; |  | ||||||
|     using clock = std::chrono::system_clock; |  | ||||||
| 
 |  | ||||||
|     explicit blocking_queue(size_type max_size) : |  | ||||||
|         _max_size(max_size), |  | ||||||
|         _q(), |  | ||||||
|         _mutex() |  | ||||||
|     { |  | ||||||
|     } |  | ||||||
|     blocking_queue(const blocking_queue&) = delete; |  | ||||||
|     blocking_queue& operator=(const blocking_queue&) = delete; |  | ||||||
|     ~blocking_queue() = default; |  | ||||||
| 
 |  | ||||||
|     size_type size() |  | ||||||
|     { |  | ||||||
|         std::lock_guard<std::mutex> lock(_mutex); |  | ||||||
|         return _q.size(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Push copy of item into the back of the queue.
 |  | ||||||
|     // If the queue is full, block the calling thread util there is room or timeout have passed.
 |  | ||||||
|     // Return: false on timeout, true on successful push.
 |  | ||||||
|     template<typename Duration_Rep, typename Duration_Period, typename TT> |  | ||||||
|     bool push(TT&& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout) |  | ||||||
|     { |  | ||||||
|         { |  | ||||||
|             std::unique_lock<std::mutex> ul(_mutex); |  | ||||||
|             if (is_full()) |  | ||||||
|             { |  | ||||||
|                 if (!_item_popped_cond.wait_until(ul, clock::now() + timeout, [this]() |  | ||||||
|             { |  | ||||||
|                 return !this->is_full(); |  | ||||||
|                 })) |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|             _q.push(std::forward<TT>(item)); |  | ||||||
|         } |  | ||||||
|         _item_pushed_cond.notify_one(); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| // Push copy of item into the back of the queue.
 |  | ||||||
| // If the queue is full, block the calling thread until there is room.
 |  | ||||||
|     template<typename TT> |  | ||||||
|     void push(TT&& item) |  | ||||||
|     { |  | ||||||
|         while (!push(std::forward<TT>(item), std::chrono::seconds(60))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| // Pop a copy of the front item in the queue into the given item ref.
 |  | ||||||
| // If the queue is empty, block the calling thread util there is item to pop or timeout have passed.
 |  | ||||||
| // Return: false on timeout , true on successful pop/
 |  | ||||||
|     template<class Duration_Rep, class Duration_Period> |  | ||||||
|     bool pop(T& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout) |  | ||||||
|     { |  | ||||||
|         { |  | ||||||
|             std::unique_lock<std::mutex> ul(_mutex); |  | ||||||
|             if (is_empty()) |  | ||||||
|             { |  | ||||||
|                 if (!_item_pushed_cond.wait_until(ul, clock::now() + timeout, [this]() |  | ||||||
|             { |  | ||||||
|                 return !this->is_empty(); |  | ||||||
|                 })) |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|             item = std::move(_q.front()); |  | ||||||
|             _q.pop(); |  | ||||||
|         } |  | ||||||
|         _item_popped_cond.notify_one(); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| // Pop a copy of the front item in the queue into the given item ref.
 |  | ||||||
| // If the queue is empty, block the calling thread util there is item to pop.
 |  | ||||||
|     void pop(T& item) |  | ||||||
|     { |  | ||||||
|         while (!pop(item, std::chrono::hours(1))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| // Clear the queue
 |  | ||||||
|     void clear() |  | ||||||
|     { |  | ||||||
|         { |  | ||||||
|             std::unique_lock<std::mutex> ul(_mutex); |  | ||||||
|             queue_type().swap(_q); |  | ||||||
|         } |  | ||||||
|         _item_popped_cond.notify_all(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     size_type _max_size; |  | ||||||
|     std::queue<T> _q; |  | ||||||
|     std::mutex _mutex; |  | ||||||
|     std::condition_variable _item_pushed_cond; |  | ||||||
|     std::condition_variable _item_popped_cond; |  | ||||||
| 
 |  | ||||||
|     inline bool is_full() |  | ||||||
|     { |  | ||||||
|         return _q.size() >= _max_size; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     inline bool is_empty() |  | ||||||
|     { |  | ||||||
|         return _q.size() == 0; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| } |  | ||||||
| @ -1,130 +0,0 @@ | |||||||
| /*************************************************************************/ |  | ||||||
| /* spdlog - an extremely fast and easy to use c++11 logging library.     */ |  | ||||||
| /* Copyright (c) 2014 Gabi Melman.                                       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* Permission is hereby granted, free of charge, to any person obtaining */ |  | ||||||
| /* a copy of this software and associated documentation files (the       */ |  | ||||||
| /* "Software"), to deal in the Software without restriction, including   */ |  | ||||||
| /* without limitation the rights to use, copy, modify, merge, publish,   */ |  | ||||||
| /* distribute, sublicense, and/or sell copies of the Software, and to    */ |  | ||||||
| /* permit persons to whom the Software is furnished to do so, subject to */ |  | ||||||
| /* the following conditions:                                             */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* The above copyright notice and this permission notice shall be        */ |  | ||||||
| /* included in all copies or substantial portions of the Software.       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ |  | ||||||
| /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ |  | ||||||
| /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ |  | ||||||
| /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ |  | ||||||
| /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ |  | ||||||
| /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ |  | ||||||
| /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ |  | ||||||
| /*************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| #include <string> |  | ||||||
| 
 |  | ||||||
| //Fast to int to string
 |  | ||||||
| //Base on :http://stackoverflow.com/a/4351484/192001
 |  | ||||||
| //Modified version to pad zeros according to padding arg
 |  | ||||||
| 
 |  | ||||||
| namespace spdlog |  | ||||||
| { |  | ||||||
| namespace details |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
| const char digit_pairs[201] = |  | ||||||
| { |  | ||||||
|     "00010203040506070809" |  | ||||||
|     "10111213141516171819" |  | ||||||
|     "20212223242526272829" |  | ||||||
|     "30313233343536373839" |  | ||||||
|     "40414243444546474849" |  | ||||||
|     "50515253545556575859" |  | ||||||
|     "60616263646566676869" |  | ||||||
|     "70717273747576777879" |  | ||||||
|     "80818283848586878889" |  | ||||||
|     "90919293949596979899" |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| inline std::string& fast_itostr(int n, std::string& s, size_t padding) |  | ||||||
| { |  | ||||||
|     if (n == 0) |  | ||||||
|     { |  | ||||||
|         s = std::string(padding, '0'); |  | ||||||
|         return s; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int sign = -(n < 0); |  | ||||||
|     unsigned int val = static_cast<unsigned int>((n^sign) - sign); |  | ||||||
| 
 |  | ||||||
|     size_t size; |  | ||||||
|     if (val >= 10000) |  | ||||||
|     { |  | ||||||
|         if (val >= 10000000) |  | ||||||
|         { |  | ||||||
|             if (val >= 1000000000) |  | ||||||
|                 size = 10; |  | ||||||
|             else if (val >= 100000000) |  | ||||||
|                 size = 9; |  | ||||||
|             else |  | ||||||
|                 size = 8; |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             if (val >= 1000000) |  | ||||||
|                 size = 7; |  | ||||||
|             else if (val >= 100000) |  | ||||||
|                 size = 6; |  | ||||||
|             else |  | ||||||
|                 size = 5; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         if (val >= 100) |  | ||||||
|         { |  | ||||||
|             if (val >= 1000) |  | ||||||
|                 size = 4; |  | ||||||
|             else |  | ||||||
|                 size = 3; |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             if (val >= 10) |  | ||||||
|                 size = 2; |  | ||||||
|             else |  | ||||||
|                 size = 1; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     size -= sign; |  | ||||||
|     if (size < padding) |  | ||||||
|         size = padding; |  | ||||||
| 
 |  | ||||||
|     s.resize(size); |  | ||||||
|     char* c = &s[0]; |  | ||||||
|     if (sign) |  | ||||||
|         *c = '-'; |  | ||||||
| 
 |  | ||||||
|     c += size - 1; |  | ||||||
|     while (val >= 100) |  | ||||||
|     { |  | ||||||
|         size_t pos = val % 100; |  | ||||||
|         val /= 100; |  | ||||||
|         *(short*)(c - 1) = *(short*)(digit_pairs + 2 * pos); |  | ||||||
|         c -= 2; |  | ||||||
|     } |  | ||||||
|     while (val > 0) |  | ||||||
|     { |  | ||||||
|         *c-- = static_cast<char>('0' + (val % 10)); |  | ||||||
|         val /= 10; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     while (c >= s.data()) |  | ||||||
|         *c-- = '0'; |  | ||||||
|     return s; |  | ||||||
| } |  | ||||||
| } |  | ||||||
| } |  | ||||||
| @ -1,189 +0,0 @@ | |||||||
| /*************************************************************************/ |  | ||||||
| /* spdlog - an extremely fast and easy to use c++11 logging library.     */ |  | ||||||
| /* Copyright (c) 2014 Gabi Melman.                                       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* Permission is hereby granted, free of charge, to any person obtaining */ |  | ||||||
| /* a copy of this software and associated documentation files (the       */ |  | ||||||
| /* "Software"), to deal in the Software without restriction, including   */ |  | ||||||
| /* without limitation the rights to use, copy, modify, merge, publish,   */ |  | ||||||
| /* distribute, sublicense, and/or sell copies of the Software, and to    */ |  | ||||||
| /* permit persons to whom the Software is furnished to do so, subject to */ |  | ||||||
| /* the following conditions:                                             */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* The above copyright notice and this permission notice shall be        */ |  | ||||||
| /* included in all copies or substantial portions of the Software.       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ |  | ||||||
| /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ |  | ||||||
| /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ |  | ||||||
| /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ |  | ||||||
| /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ |  | ||||||
| /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ |  | ||||||
| /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ |  | ||||||
| /*************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| // A faster-than-ostringstream class
 |  | ||||||
| // uses stack_buf as the underlying buffer (upto 256 bytes before using the heap)
 |  | ||||||
| 
 |  | ||||||
| #include <ostream> |  | ||||||
| #include <iomanip> |  | ||||||
| #include "fast_istostr.h" |  | ||||||
| #include "stack_buf.h" |  | ||||||
| #include<iostream> |  | ||||||
| 
 |  | ||||||
| namespace spdlog |  | ||||||
| { |  | ||||||
| namespace details |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
| class stack_devicebuf :public std::streambuf |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     static const unsigned short stack_size = 256; |  | ||||||
|     using stackbuf_t = stack_buf<stack_size>; |  | ||||||
| 
 |  | ||||||
|     stack_devicebuf() = default; |  | ||||||
|     ~stack_devicebuf() = default; |  | ||||||
| 
 |  | ||||||
|     stack_devicebuf(const stack_devicebuf& other) :std::basic_streambuf<char>(), _stackbuf(other._stackbuf) |  | ||||||
|     {} |  | ||||||
| 
 |  | ||||||
|     stack_devicebuf(stack_devicebuf&& other): |  | ||||||
|         std::basic_streambuf<char>(), |  | ||||||
|         _stackbuf(std::move(other._stackbuf)) |  | ||||||
|     { |  | ||||||
|         other.clear(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     stack_devicebuf& operator=(stack_devicebuf other) |  | ||||||
|     { |  | ||||||
|         std::swap(_stackbuf, other._stackbuf); |  | ||||||
|         return *this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const stackbuf_t& buf() const |  | ||||||
|     { |  | ||||||
|         return _stackbuf; |  | ||||||
|     } |  | ||||||
|     std::size_t size() const |  | ||||||
|     { |  | ||||||
|         return _stackbuf.size(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void clear() |  | ||||||
|     { |  | ||||||
|         _stackbuf.clear(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| protected: |  | ||||||
|     // copy the give buffer into the accumulated fast buffer
 |  | ||||||
|     std::streamsize xsputn(const char_type* s, std::streamsize count) override |  | ||||||
|     { |  | ||||||
|         _stackbuf.append(s, static_cast<unsigned int>(count)); |  | ||||||
|         return count; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int_type overflow(int_type ch) override |  | ||||||
|     { |  | ||||||
|         if (traits_type::not_eof(ch)) |  | ||||||
|         { |  | ||||||
|             char c = traits_type::to_char_type(ch); |  | ||||||
|             xsputn(&c, 1); |  | ||||||
|         } |  | ||||||
|         return ch; |  | ||||||
|     } |  | ||||||
| private: |  | ||||||
|     stackbuf_t _stackbuf; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class fast_oss :public std::ostream |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     fast_oss() :std::ostream(&_dev) {} |  | ||||||
|     ~fast_oss() = default; |  | ||||||
| 
 |  | ||||||
|     fast_oss(const fast_oss& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(other._dev) |  | ||||||
|     {} |  | ||||||
| 
 |  | ||||||
|     fast_oss(fast_oss&& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(std::move(other._dev)) |  | ||||||
|     { |  | ||||||
|         other.clear(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     fast_oss& operator=(fast_oss other) |  | ||||||
|     { |  | ||||||
|         swap(*this, other); |  | ||||||
|         return *this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void swap(fast_oss& first, fast_oss& second) // nothrow
 |  | ||||||
|     { |  | ||||||
|         using std::swap; |  | ||||||
|         swap(first._dev, second._dev); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::string str() const |  | ||||||
|     { |  | ||||||
|         auto& buffer = _dev.buf(); |  | ||||||
|         const char*data = buffer.data(); |  | ||||||
|         return std::string(data, data+buffer.size()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const stack_devicebuf::stackbuf_t& buf() const |  | ||||||
|     { |  | ||||||
|         return _dev.buf(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     std::size_t size() const |  | ||||||
|     { |  | ||||||
|         return _dev.size(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void clear() |  | ||||||
|     { |  | ||||||
|         _dev.clear(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     //
 |  | ||||||
|     // The following were added because they significantly boost to perfromance
 |  | ||||||
|     //
 |  | ||||||
|     void putc(char c) |  | ||||||
|     { |  | ||||||
|         _dev.sputc(c); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // put int and pad with zeroes if smalled than min_width
 |  | ||||||
|     void put_int(int n, size_t padding) |  | ||||||
|     { |  | ||||||
|         std::string s; |  | ||||||
|         details::fast_itostr(n, s, padding); |  | ||||||
|         _dev.sputn(s.data(), s.size()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void put_data(const char* p, std::size_t data_size) |  | ||||||
|     { |  | ||||||
|         _dev.sputn(p, data_size); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void put_str(const std::string& s) |  | ||||||
|     { |  | ||||||
|         _dev.sputn(s.data(), s.size()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void put_fast_oss(const fast_oss& oss) |  | ||||||
|     { |  | ||||||
|         auto& buffer = oss.buf(); |  | ||||||
|         _dev.sputn(buffer.data(), buffer.size()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     stack_devicebuf _dev; |  | ||||||
| }; |  | ||||||
| } |  | ||||||
| } |  | ||||||
| @ -99,9 +99,10 @@ public: | |||||||
| 
 | 
 | ||||||
|     void write(const log_msg& msg) |     void write(const log_msg& msg) | ||||||
|     { |     { | ||||||
|         auto& buf = msg.formatted.buf(); | 
 | ||||||
|         size_t size = buf.size(); |         size_t size = msg.formatted.size(); | ||||||
|         if(std::fwrite(buf.data(), sizeof(char), size, _fd) != size) |         auto data = msg.formatted.data(); | ||||||
|  |         if(std::fwrite(data, 1, size, _fd) != size) | ||||||
|             throw spdlog_ex("Failed writing to file " + _filename); |             throw spdlog_ex("Failed writing to file " + _filename); | ||||||
| 
 | 
 | ||||||
|         if(_auto_flush) |         if(_auto_flush) | ||||||
|  | |||||||
							
								
								
									
										1171
									
								
								include/spdlog/details/format.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1171
									
								
								include/spdlog/details/format.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2875
									
								
								include/spdlog/details/format.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2875
									
								
								include/spdlog/details/format.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -26,7 +26,6 @@ | |||||||
| 
 | 
 | ||||||
| #include "../common.h" | #include "../common.h" | ||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "fast_oss.h" |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // Line logger class - aggregates operator<< calls to fast ostream
 | // Line logger class - aggregates operator<< calls to fast ostream
 | ||||||
| @ -66,7 +65,6 @@ public: | |||||||
|         { |         { | ||||||
|             _log_msg.logger_name = _callback_logger->name(); |             _log_msg.logger_name = _callback_logger->name(); | ||||||
|             _log_msg.time = log_clock::now(); |             _log_msg.time = log_clock::now(); | ||||||
|             _log_msg.tm_time = details::os::localtime(log_clock::to_time_t(_log_msg.time)); |  | ||||||
|             _callback_logger->_log_msg(_log_msg); |             _callback_logger->_log_msg(_log_msg); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -80,6 +78,12 @@ public: | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     void write(const std::string& fmt, const Args&... args) | ||||||
|  |     { | ||||||
|  |         _log_msg.raw.write(fmt, args...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     template<typename T> |     template<typename T> | ||||||
|     line_logger& operator<<(const T& what) |     line_logger& operator<<(const T& what) | ||||||
|     { |     { | ||||||
| @ -87,6 +91,7 @@ public: | |||||||
|         return *this; |         return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     void disable() |     void disable() | ||||||
|     { |     { | ||||||
|         _enabled = false; |         _enabled = false; | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "../common.h" | #include "../common.h" | ||||||
| #include "./fast_oss.h" | #include "./format.h" | ||||||
| 
 | 
 | ||||||
| namespace spdlog | namespace spdlog | ||||||
| { | { | ||||||
| @ -38,25 +38,31 @@ struct log_msg | |||||||
|         logger_name(), |         logger_name(), | ||||||
|         level(l), |         level(l), | ||||||
|         time(), |         time(), | ||||||
|         tm_time(), |  | ||||||
|         raw(), |         raw(), | ||||||
|         formatted() {} |         formatted() {} | ||||||
| 
 | 
 | ||||||
|     log_msg(const log_msg& other): | 
 | ||||||
|  |     log_msg(const log_msg& other) : | ||||||
|         logger_name(other.logger_name), |         logger_name(other.logger_name), | ||||||
|         level(other.level), |         level(other.level), | ||||||
|         time(other.time), |         time(other.time) | ||||||
|         tm_time(other.tm_time), |     { | ||||||
|         raw(other.raw), |         if (other.raw.size()) | ||||||
|         formatted(other.formatted) {} |             raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size()); | ||||||
|  |         if (other.formatted.size()) | ||||||
|  |             formatted << fmt::BasicStringRef<char>(other.formatted.data(), other.formatted.size()); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     log_msg(log_msg&& other) : |     log_msg(log_msg&& other) : | ||||||
|         logger_name(std::move(other.logger_name)), |         logger_name(std::move(other.logger_name)), | ||||||
|         level(other.level), |         level(other.level), | ||||||
|         time(std::move(other.time)), |         time(std::move(other.time)), | ||||||
|         tm_time(other.tm_time), |  | ||||||
|         raw(std::move(other.raw)), |         raw(std::move(other.raw)), | ||||||
|         formatted(std::move(other.formatted)) {} |         formatted(std::move(other.formatted)) | ||||||
|  |     { | ||||||
|  |         other.clear(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     log_msg& operator=(log_msg&& other) |     log_msg& operator=(log_msg&& other) | ||||||
|     { |     { | ||||||
| @ -66,16 +72,15 @@ struct log_msg | |||||||
|         logger_name = std::move(other.logger_name); |         logger_name = std::move(other.logger_name); | ||||||
|         level = other.level; |         level = other.level; | ||||||
|         time = std::move(other.time); |         time = std::move(other.time); | ||||||
|         tm_time = other.tm_time; |  | ||||||
|         raw = std::move(other.raw); |         raw = std::move(other.raw); | ||||||
|         formatted = std::move(other.formatted); |         formatted = std::move(other.formatted); | ||||||
|  |         other.clear(); | ||||||
|         return *this; |         return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     void clear() |     void clear() | ||||||
|     { |     { | ||||||
|  |         level = level::OFF; | ||||||
|         raw.clear(); |         raw.clear(); | ||||||
|         formatted.clear(); |         formatted.clear(); | ||||||
|     } |     } | ||||||
| @ -83,9 +88,8 @@ struct log_msg | |||||||
|     std::string logger_name; |     std::string logger_name; | ||||||
|     level::level_enum level; |     level::level_enum level; | ||||||
|     log_clock::time_point time; |     log_clock::time_point time; | ||||||
|     std::tm tm_time; |     fmt::MemoryWriter raw; | ||||||
|     fast_oss raw; |     fmt::MemoryWriter formatted; | ||||||
|     fast_oss formatted; |  | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -63,78 +63,157 @@ inline void spdlog::logger::set_pattern(const std::string& pattern) | |||||||
|     _set_pattern(pattern); |     _set_pattern(pattern); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | //
 | ||||||
| 
 | // cppformat API of the form logger.info("hello {} {}", "world", 1);
 | ||||||
|  | //
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::log(level::level_enum lvl, const Args&... args) | inline spdlog::details::line_logger spdlog::logger::_log(level::level_enum lvl, const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     bool msg_enabled = should_log(lvl); |     bool msg_enabled = should_log(lvl); | ||||||
|     details::line_logger l(this, lvl, msg_enabled); |     details::line_logger l(this, lvl, msg_enabled); | ||||||
|     if (msg_enabled) |     if (msg_enabled) | ||||||
|         _variadic_log(l, args...); |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             l.write(fmt, args...); | ||||||
|  |         } | ||||||
|  |         catch(const fmt::FormatError& e) | ||||||
|  |         { | ||||||
|  |             throw spdlog_ex(fmt::format("formatting error while processing format string '{}': {}", fmt, e.what())); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|     return l; |     return l; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::log(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::log(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::ALWAYS, args...); |     return _log(level::ALWAYS, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::trace(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::trace(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::TRACE, args...); |     return _log(level::TRACE, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::debug(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::debug(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::DEBUG, args...); |     return _log(level::DEBUG, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::info(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::info(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::INFO, args...); |     return _log(level::INFO, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::notice(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::notice(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::NOTICE, args...); |     return _log(level::NOTICE, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::warn(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::warn(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::WARN, args...); |     return _log(level::WARN, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::error(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::error(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::ERR, args...); |     return _log(level::ERR, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::critical(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::critical(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::CRITICAL, args...); |     return _log(level::CRITICAL, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::alert(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::alert(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::ALERT, args...); |     return _log(level::ALERT, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename... Args> | template <typename... Args> | ||||||
| inline spdlog::details::line_logger spdlog::logger::emerg(const Args&... args) | inline spdlog::details::line_logger spdlog::logger::emerg(const std::string& fmt, const Args&... args) | ||||||
| { | { | ||||||
|     return log(level::EMERG, args...); |     return _log(level::EMERG, fmt, args...); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // //API to support logger.info() << ".." calls
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::_log(level::level_enum lvl) | ||||||
|  | { | ||||||
|  |     bool msg_enabled = should_log(lvl); | ||||||
|  |     details::line_logger l(this, lvl, msg_enabled); | ||||||
|  |     return l; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::log() | ||||||
|  | { | ||||||
|  |     return _log(level::ALWAYS); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::trace() | ||||||
|  | { | ||||||
|  |     return _log(level::TRACE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::debug() | ||||||
|  | { | ||||||
|  |     return _log(level::DEBUG); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::info() | ||||||
|  | { | ||||||
|  |     return _log(level::INFO); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::notice() | ||||||
|  | { | ||||||
|  |     return _log(level::NOTICE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::warn() | ||||||
|  | { | ||||||
|  |     return _log(level::WARN); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::error() | ||||||
|  | { | ||||||
|  |     return _log(level::ERR); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::critical() | ||||||
|  | { | ||||||
|  |     return _log(level::CRITICAL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::alert() | ||||||
|  | { | ||||||
|  |     return _log(level::ALERT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline spdlog::details::line_logger spdlog::logger::emerg() | ||||||
|  | { | ||||||
|  |     return _log(level::EMERG); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
| inline const std::string& spdlog::logger::name() const | inline const std::string& spdlog::logger::name() const | ||||||
| { | { | ||||||
|     return _name; |     return _name; | ||||||
| @ -182,24 +261,7 @@ inline void spdlog::logger::_stop() | |||||||
|     set_level(level::OFF); |     set_level(level::OFF); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* private functions */ |  | ||||||
| inline void spdlog::logger::_variadic_log(spdlog::details::line_logger&) {} |  | ||||||
| 
 |  | ||||||
| template <typename Last> |  | ||||||
| inline void spdlog::logger::_variadic_log(spdlog::details::line_logger& l, const Last& last) |  | ||||||
| { |  | ||||||
|     l.write(last); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| template <typename First, typename... Rest> |  | ||||||
| inline void spdlog::logger::_variadic_log(spdlog::details::line_logger& l, const First& first, const Rest&... rest) |  | ||||||
| { |  | ||||||
|     l.write(first); |  | ||||||
|     l.write(' '); |  | ||||||
|     _variadic_log(l, rest...); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										175
									
								
								include/spdlog/details/mpmc_bounded_q.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								include/spdlog/details/mpmc_bounded_q.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | |||||||
|  | /*
 | ||||||
|  | A modified version of Bounded MPMC queue by Dmitry Vyukov. | ||||||
|  | 
 | ||||||
|  | Original code from: | ||||||
|  | http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
 | ||||||
|  | 
 | ||||||
|  | licensed by Dmitry Vyukov under the terms below: | ||||||
|  | 
 | ||||||
|  | Simplified BSD license | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. | ||||||
|  | Redistribution and use in source and binary forms, with or without modification, | ||||||
|  | are permitted provided that the following conditions are met: | ||||||
|  | 1. Redistributions of source code must retain the above copyright notice, this list of | ||||||
|  | conditions and the following disclaimer. | ||||||
|  | 
 | ||||||
|  | 2. Redistributions in binary form must reproduce the above copyright notice, this list | ||||||
|  | of conditions and the following disclaimer in the documentation and/or other materials | ||||||
|  | provided with the distribution. | ||||||
|  | 
 | ||||||
|  | THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED | ||||||
|  | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT | ||||||
|  | SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||||
|  | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | ||||||
|  | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||||
|  | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | ||||||
|  | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||||||
|  | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | 
 | ||||||
|  | The views and conclusions contained in the software and documentation are those of the authors and | ||||||
|  | should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | The code in its current form adds the license below: | ||||||
|  | 
 | ||||||
|  | spdlog - an extremely fast and easy to use c++11 logging library. | ||||||
|  | Copyright (c) 2014 Gabi Melman. | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining | ||||||
|  | a copy of this software and associated documentation files (the | ||||||
|  | "Software"), to deal in the Software without restriction, including | ||||||
|  | without limitation the rights to use, copy, modify, merge, publish, | ||||||
|  | distribute, sublicense, and/or sell copies of the Software, and to | ||||||
|  | permit persons to whom the Software is furnished to do so, subject to | ||||||
|  | the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be | ||||||
|  | included in all copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||||
|  | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||||
|  | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||||
|  | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||||
|  | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||||
|  | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||||
|  | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <atomic> | ||||||
|  | #include "../common.h" | ||||||
|  | 
 | ||||||
|  | namespace spdlog | ||||||
|  | { | ||||||
|  | namespace details | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | template<typename T> | ||||||
|  | class mpmc_bounded_queue | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  | 	using item_type = T; | ||||||
|  | 	mpmc_bounded_queue(size_t buffer_size) | ||||||
|  | 		: buffer_(new cell_t [buffer_size]), | ||||||
|  | 		  buffer_mask_(buffer_size - 1) | ||||||
|  | 	{ | ||||||
|  | 		//queue size must be power of two
 | ||||||
|  | 		if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) | ||||||
|  | 			throw spdlog_ex("async logger queue size must be power of two"); | ||||||
|  | 
 | ||||||
|  | 		for (size_t i = 0; i != buffer_size; i += 1) | ||||||
|  | 			buffer_[i].sequence_.store(i, std::memory_order_relaxed); | ||||||
|  | 		enqueue_pos_.store(0, std::memory_order_relaxed); | ||||||
|  | 		dequeue_pos_.store(0, std::memory_order_relaxed); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	~mpmc_bounded_queue() | ||||||
|  | 	{		 | ||||||
|  | 		delete [] buffer_; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	bool enqueue(T&& data) | ||||||
|  | 	{ | ||||||
|  | 		cell_t* cell; | ||||||
|  | 		size_t pos = enqueue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 		for (;;) | ||||||
|  | 		{		 | ||||||
|  | 			cell = &buffer_[pos & buffer_mask_]; | ||||||
|  | 			size_t seq = cell->sequence_.load(std::memory_order_acquire); | ||||||
|  | 			intptr_t dif = (intptr_t)seq - (intptr_t)pos; | ||||||
|  | 			if (dif == 0) | ||||||
|  | 			{			 | ||||||
|  | 				if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
|  | 			else if (dif < 0) | ||||||
|  | 			{				 | ||||||
|  | 				return false; | ||||||
|  | 			}			 | ||||||
|  | 			else | ||||||
|  | 			{		 | ||||||
|  | 				pos = enqueue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 			} | ||||||
|  | 		}		 | ||||||
|  | 		cell->data_ = std::move(data); | ||||||
|  | 		cell->sequence_.store(pos + 1, std::memory_order_release); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool dequeue(T& data) | ||||||
|  | 	{ | ||||||
|  | 		cell_t* cell; | ||||||
|  | 		size_t pos = dequeue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 		for (;;) | ||||||
|  | 		{ | ||||||
|  | 			cell = &buffer_[pos & buffer_mask_]; | ||||||
|  | 			size_t seq = | ||||||
|  | 				cell->sequence_.load(std::memory_order_acquire); | ||||||
|  | 			intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1); | ||||||
|  | 			if (dif == 0) | ||||||
|  | 			{ | ||||||
|  | 				if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
|  | 			else if (dif < 0) | ||||||
|  | 				return false; | ||||||
|  | 			else | ||||||
|  | 				pos = dequeue_pos_.load(std::memory_order_relaxed); | ||||||
|  | 		} | ||||||
|  | 		data = std::move(cell->data_); | ||||||
|  | 		cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release); | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	struct cell_t | ||||||
|  | 	{ | ||||||
|  | 		std::atomic<size_t>   sequence_; | ||||||
|  | 		T                     data_; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static size_t const     cacheline_size = 64; | ||||||
|  | 	typedef char            cacheline_pad_t [cacheline_size]; | ||||||
|  | 
 | ||||||
|  | 	cacheline_pad_t         pad0_; | ||||||
|  | 	cell_t* const           buffer_; | ||||||
|  | 	size_t const            buffer_mask_; | ||||||
|  | 	cacheline_pad_t         pad1_; | ||||||
|  | 	std::atomic<size_t>     enqueue_pos_; | ||||||
|  | 	cacheline_pad_t         pad2_; | ||||||
|  | 	std::atomic<size_t>     dequeue_pos_; | ||||||
|  | 	cacheline_pad_t         pad3_; | ||||||
|  | 
 | ||||||
|  | 	mpmc_bounded_queue(mpmc_bounded_queue const&); | ||||||
|  | 	void operator = (mpmc_bounded_queue const&); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // ns details
 | ||||||
|  | } // ns spdlog
 | ||||||
| @ -30,9 +30,9 @@ | |||||||
| #include <vector> | #include <vector> | ||||||
| #include <thread> | #include <thread> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| #include "../formatter.h" | #include "../formatter.h" | ||||||
| #include "./log_msg.h" | #include "./log_msg.h" | ||||||
| #include "./fast_oss.h" |  | ||||||
| #include "./os.h" | #include "./os.h" | ||||||
| 
 | 
 | ||||||
| namespace spdlog | namespace spdlog | ||||||
| @ -43,7 +43,7 @@ class flag_formatter | |||||||
| { | { | ||||||
| public: | public: | ||||||
|     virtual ~flag_formatter() {} |     virtual ~flag_formatter() {} | ||||||
|     virtual void format(details::log_msg& msg) = 0; |     virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| ///////////////////////////////////////////////////////////////////////
 | ///////////////////////////////////////////////////////////////////////
 | ||||||
| @ -53,7 +53,7 @@ namespace | |||||||
| { | { | ||||||
| class name_formatter :public flag_formatter | class name_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm&) override | ||||||
|     { |     { | ||||||
|         msg.formatted << msg.logger_name; |         msg.formatted << msg.logger_name; | ||||||
|     } |     } | ||||||
| @ -63,7 +63,7 @@ class name_formatter :public flag_formatter | |||||||
| // log level appender
 | // log level appender
 | ||||||
| class level_formatter :public flag_formatter | class level_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm&) override | ||||||
|     { |     { | ||||||
|         msg.formatted << level::to_str(msg.level); |         msg.formatted << level::to_str(msg.level); | ||||||
|     } |     } | ||||||
| @ -87,9 +87,9 @@ static int to12h(const tm& t) | |||||||
| static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | ||||||
| class a_formatter :public flag_formatter | class a_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_str(days[msg.tm_time.tm_wday]); |         msg.formatted << days[tm_time.tm_wday]; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -97,9 +97,9 @@ class a_formatter :public flag_formatter | |||||||
| static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; | static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; | ||||||
| class A_formatter :public flag_formatter | class A_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_str(full_days[msg.tm_time.tm_wday]); |         msg.formatted << full_days[tm_time.tm_wday]; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -107,9 +107,9 @@ class A_formatter :public flag_formatter | |||||||
| static const std::string  months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; | static const std::string  months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; | ||||||
| class b_formatter :public flag_formatter | class b_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_str(months[msg.tm_time.tm_mon]); |         msg.formatted<< months[tm_time.tm_mon]; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -117,28 +117,35 @@ class b_formatter :public flag_formatter | |||||||
| static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; | static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; | ||||||
| class B_formatter :public flag_formatter | class B_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_str(full_months[msg.tm_time.tm_mon]); |         msg.formatted << full_months[tm_time.tm_mon]; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | //write 2 ints seperated by sep with padding of 2
 | ||||||
|  | static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep) | ||||||
|  | { | ||||||
|  |     w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0'); | ||||||
|  |     return w; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //write 3 ints seperated by sep with padding of 2
 | ||||||
|  | static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep) | ||||||
|  | { | ||||||
|  |     w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0'); | ||||||
|  |     return w; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| //Date and time representation (Thu Aug 23 15:35:46 2014)
 | //Date and time representation (Thu Aug 23 15:35:46 2014)
 | ||||||
| class c_formatter :public flag_formatter | class c_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_str(days[msg.tm_time.tm_wday]); |         msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' '; | ||||||
|         msg.formatted.putc(' '); |         pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900; | ||||||
|         msg.formatted.put_str(months[msg.tm_time.tm_mon]); |  | ||||||
|         msg.formatted.putc(' '); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_mday, 2); |  | ||||||
|         msg.formatted.putc(' '); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_hour, 2); |  | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_min, 2); |  | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_sec, 2); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -146,9 +153,9 @@ class c_formatter :public flag_formatter | |||||||
| // year - 2 digit
 | // year - 2 digit
 | ||||||
| class C_formatter :public flag_formatter | class C_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_year % 100, 2); |         msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -157,13 +164,9 @@ class C_formatter :public flag_formatter | |||||||
| // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
 | // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
 | ||||||
| class D_formatter :public flag_formatter | class D_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2); |         pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/'); | ||||||
|         msg.formatted.putc('/'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_mday, 2); |  | ||||||
|         msg.formatted.putc('/'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_year % 100, 2); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -171,83 +174,83 @@ class D_formatter :public flag_formatter | |||||||
| // year - 4 digit
 | // year - 4 digit
 | ||||||
| class Y_formatter :public flag_formatter | class Y_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_year + 1900, 4); |         msg.formatted << tm_time.tm_year + 1900; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // month 1-12
 | // month 1-12
 | ||||||
| class m_formatter :public flag_formatter | class m_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2); |         msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // day of month 1-31
 | // day of month 1-31
 | ||||||
| class d_formatter :public flag_formatter | class d_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_mday, 2); |         msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // hours in 24 format  0-23
 | // hours in 24 format  0-23
 | ||||||
| class H_formatter :public flag_formatter | class H_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_hour, 2); |         msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // hours in 12 format  1-12
 | // hours in 12 format  1-12
 | ||||||
| class I_formatter :public flag_formatter | class I_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(to12h(msg.tm_time), 2); |         msg.formatted << fmt::pad(to12h(tm_time), 2, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // ninutes 0-59
 | // ninutes 0-59
 | ||||||
| class M_formatter :public flag_formatter | class M_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_min, 2); |         msg.formatted << fmt::pad(tm_time.tm_min, 2, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // seconds 0-59
 | // seconds 0-59
 | ||||||
| class S_formatter :public flag_formatter | class S_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_sec, 2); |         msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // milliseconds
 | // milliseconds
 | ||||||
| class e_formatter :public flag_formatter | class e_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm&) override | ||||||
|     { |     { | ||||||
|         auto duration = msg.time.time_since_epoch(); |         auto duration = msg.time.time_since_epoch(); | ||||||
|         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; |         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; | ||||||
|         msg.formatted.put_int(static_cast<int>(millis), 3); |         msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0'); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // AM/PM
 | // AM/PM
 | ||||||
| class p_formatter :public flag_formatter | class p_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_data(ampm(msg.tm_time), 2); |         msg.formatted << ampm(tm_time); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -255,100 +258,92 @@ class p_formatter :public flag_formatter | |||||||
| // 12 hour clock 02:55:02 pm
 | // 12 hour clock 02:55:02 pm
 | ||||||
| class r_formatter :public flag_formatter | class r_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(to12h(msg.tm_time), 2); |         pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time); | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_min, 2); |  | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_sec, 2); |  | ||||||
|         msg.formatted.putc(' '); |  | ||||||
|         msg.formatted.put_data(ampm(msg.tm_time), 2); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 24-hour HH:MM time, equivalent to %H:%M
 | // 24-hour HH:MM time, equivalent to %H:%M
 | ||||||
| class R_formatter :public flag_formatter | class R_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_hour, 2); |         pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':'); | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_min, 2); |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
 | // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
 | ||||||
| class T_formatter :public flag_formatter | class T_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_hour, 2); |         pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':'); | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_min, 2); |  | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_sec, 2); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // ISO 8601 offset from UTC in timezone (HH:MM)
 | 
 | ||||||
|  | // ISO 8601 offset from UTC in timezone (+-HH:MM)
 | ||||||
| class z_formatter :public flag_formatter | class z_formatter :public flag_formatter | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     z_formatter() {} |     const std::chrono::seconds cache_refresh = std::chrono::seconds(5); | ||||||
|  | 
 | ||||||
|  |     z_formatter() :_last_update(std::chrono::seconds(0)) {} | ||||||
|     z_formatter(const z_formatter&) = delete; |     z_formatter(const z_formatter&) = delete; | ||||||
|     z_formatter& operator=(const z_formatter&) = delete; |     z_formatter& operator=(const z_formatter&) = delete; | ||||||
| 
 | 
 | ||||||
|     void format(log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> l(_mutex); | #ifdef _WIN32 | ||||||
|         using namespace std::chrono; |         int total_minutes = get_cached_offset(msg, tm_time); | ||||||
|         auto diff = msg.time - _last_update; | #else | ||||||
|         auto secs_diff = std::abs((duration_cast<seconds>(diff)).count()); |         // No need to chache under gcc,
 | ||||||
|         if (secs_diff >= 2) |         // it is very fast (already stored in tm.tm_gmtoff)
 | ||||||
|         { |         int total_minutes = os::utc_minutes_offset(tm_time); | ||||||
|             _value = get_value(msg); | #endif | ||||||
|             _last_update = msg.time; | 
 | ||||||
|         } |         int h = total_minutes / 60; | ||||||
|         msg.formatted.put_str(_value); |         int m = total_minutes % 60; | ||||||
|  |         char sign = h >= 0 ? '+' : '-'; | ||||||
|  |         msg.formatted << sign; | ||||||
|  |         pad_n_join(msg.formatted, h, m, ':'); | ||||||
|     } |     } | ||||||
| private: | private: | ||||||
|     log_clock::time_point _last_update; |     log_clock::time_point _last_update; | ||||||
|     std::string _value; |     int _offset_minutes; | ||||||
|     std::mutex _mutex; |     std::mutex _mutex; | ||||||
| 
 | 
 | ||||||
|     std::string get_value(const log_msg& msg) |     int get_cached_offset(const log_msg& msg, const std::tm& tm_time) | ||||||
|     { |     { | ||||||
|         int total_minutes = os::utc_minutes_offset(msg.tm_time); |         using namespace std::chrono; | ||||||
|         int h = total_minutes / 60; |         std::lock_guard<std::mutex> l(_mutex); | ||||||
|         int m = total_minutes % 60; |         if (msg.time - _last_update >= cache_refresh) | ||||||
|         fast_oss oss; |         { | ||||||
|         oss.putc(h < 0 ? '-' : '+'); |             _offset_minutes = os::utc_minutes_offset(tm_time); | ||||||
|         oss.put_int(h, 2); |             _last_update = msg.time; | ||||||
|         oss.putc(':'); |         } | ||||||
|         oss.put_int(m, 2); |         return _offset_minutes; | ||||||
|         return oss.str(); |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| //Thread id
 | //Thread id
 | ||||||
| class t_formatter :public flag_formatter | class t_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm&) override | ||||||
|     { |     { | ||||||
|         msg.formatted << std::this_thread::get_id(); |         msg.formatted << std::hash<std::thread::id>()(std::this_thread::get_id()); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class v_formatter :public flag_formatter | class v_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm&) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_fast_oss(msg.raw); |         msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -357,9 +352,9 @@ class ch_formatter :public flag_formatter | |||||||
| public: | public: | ||||||
|     explicit ch_formatter(char ch) : _ch(ch) |     explicit ch_formatter(char ch) : _ch(ch) | ||||||
|     {} |     {} | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm&) override | ||||||
|     { |     { | ||||||
|         msg.formatted.putc(_ch); |         msg.formatted << _ch; | ||||||
|     } |     } | ||||||
| private: | private: | ||||||
|     char _ch; |     char _ch; | ||||||
| @ -376,9 +371,9 @@ public: | |||||||
|     { |     { | ||||||
|         _str += ch; |         _str += ch; | ||||||
|     } |     } | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm&) override | ||||||
|     { |     { | ||||||
|         msg.formatted.put_str(_str); |         msg.formatted << _str; | ||||||
|     } |     } | ||||||
| private: | private: | ||||||
|     std::string _str; |     std::string _str; | ||||||
| @ -388,29 +383,35 @@ private: | |||||||
| // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
 | // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
 | ||||||
| class full_formatter :public flag_formatter | class full_formatter :public flag_formatter | ||||||
| { | { | ||||||
|     void format(details::log_msg& msg) override |     void format(details::log_msg& msg, const std::tm& tm_time) override | ||||||
|     { |     { | ||||||
|         msg.formatted.putc('['); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_year+1900, 4); |  | ||||||
|         msg.formatted.putc('-'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_mon+ 1, 2); |  | ||||||
|         msg.formatted.putc('-'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_mday, 2); |  | ||||||
|         msg.formatted.putc(' '); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_hour, 2); |  | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_min, 2); |  | ||||||
|         msg.formatted.putc(':'); |  | ||||||
|         msg.formatted.put_int(msg.tm_time.tm_sec, 2); |  | ||||||
|         //millis
 |  | ||||||
|         msg.formatted.putc('.'); |  | ||||||
|         auto duration = msg.time.time_since_epoch(); |         auto duration = msg.time.time_since_epoch(); | ||||||
|         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; |         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; | ||||||
|         msg.formatted.put_int(static_cast<int>(millis), 3); |  | ||||||
|         msg.formatted.putc(']'); |  | ||||||
|         msg.formatted << " [" << msg.logger_name << "] [" << level::to_str(msg.level) << "] "; |  | ||||||
|         msg.formatted.put_fast_oss(msg.raw); |  | ||||||
| 
 | 
 | ||||||
|  |         /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
 | ||||||
|  |         msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ", | ||||||
|  |         tm_time.tm_year + 1900, | ||||||
|  |         tm_time.tm_mon + 1, | ||||||
|  |         tm_time.tm_mday, | ||||||
|  |         tm_time.tm_hour, | ||||||
|  |         tm_time.tm_min, | ||||||
|  |         tm_time.tm_sec, | ||||||
|  |         static_cast<int>(millis), | ||||||
|  |         msg.logger_name, | ||||||
|  |         level::to_str(msg.level), | ||||||
|  |         msg.raw.str());*/ | ||||||
|  | 
 | ||||||
|  |         // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
 | ||||||
|  |         msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-' | ||||||
|  |                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-' | ||||||
|  |                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' ' | ||||||
|  |                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':' | ||||||
|  |                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':' | ||||||
|  |                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.' | ||||||
|  |                       << fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] "; | ||||||
|  | 
 | ||||||
|  |         msg.formatted << '[' << msg.logger_name << "] [" << level::to_str(msg.level) << "] "; | ||||||
|  |         msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -571,10 +572,18 @@ inline void spdlog::pattern_formatter::handle_flag(char flag) | |||||||
| 
 | 
 | ||||||
| inline void spdlog::pattern_formatter::format(details::log_msg& msg) | inline void spdlog::pattern_formatter::format(details::log_msg& msg) | ||||||
| { | { | ||||||
|  |     try | ||||||
|  |     { | ||||||
|  |         auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); | ||||||
|         for (auto &f : _formatters) |         for (auto &f : _formatters) | ||||||
|         { |         { | ||||||
|         f->format(msg); |             f->format(msg, tm_time); | ||||||
|         } |         } | ||||||
|         //write eol
 |         //write eol
 | ||||||
|     msg.formatted.write(details::os::eol(), details::os::eol_size()); |         msg.formatted << details::os::eol(); | ||||||
|  |     } | ||||||
|  |     catch(const fmt::FormatError& e) | ||||||
|  |     { | ||||||
|  |         throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what())); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ public: | |||||||
|             return found->second; |             return found->second; | ||||||
|         std::shared_ptr<logger> new_logger; |         std::shared_ptr<logger> new_logger; | ||||||
|         if (_async_mode) |         if (_async_mode) | ||||||
|             new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _async_shutdown_duration); |             new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size); | ||||||
|         else |         else | ||||||
|             new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end); |             new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end); | ||||||
| 
 | 
 | ||||||
| @ -114,12 +114,11 @@ public: | |||||||
|             l.second->set_level(log_level); |             l.second->set_level(log_level); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_async_mode(size_t q_size, const log_clock::duration& shutdown_duration) |     void set_async_mode(size_t q_size) | ||||||
|     { |     { | ||||||
|         std::lock_guard<std::mutex> lock(_mutex); |         std::lock_guard<std::mutex> lock(_mutex); | ||||||
|         _async_mode = true; |         _async_mode = true; | ||||||
|         _async_q_size = q_size; |         _async_q_size = q_size; | ||||||
|         _async_shutdown_duration = shutdown_duration; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_sync_mode() |     void set_sync_mode() | ||||||
| @ -153,7 +152,6 @@ private: | |||||||
|     level::level_enum _level = level::INFO; |     level::level_enum _level = level::INFO; | ||||||
|     bool _async_mode = false; |     bool _async_mode = false; | ||||||
|     size_t _async_q_size = 0; |     size_t _async_q_size = 0; | ||||||
|     log_clock::duration _async_shutdown_duration; |  | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -133,9 +133,9 @@ inline void spdlog::set_level(level::level_enum log_level) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| inline void spdlog::set_async_mode(size_t queue_size, const log_clock::duration& shutdown_duration) | inline void spdlog::set_async_mode(size_t queue_size) | ||||||
| { | { | ||||||
|     details::registry::instance().set_async_mode(queue_size, shutdown_duration); |     details::registry::instance().set_async_mode(queue_size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline void spdlog::set_sync_mode() | inline void spdlog::set_sync_mode() | ||||||
|  | |||||||
| @ -1,134 +0,0 @@ | |||||||
| /*************************************************************************/ |  | ||||||
| /* spdlog - an extremely fast and easy to use c++11 logging library.     */ |  | ||||||
| /* Copyright (c) 2014 Gabi Melman.                                       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* Permission is hereby granted, free of charge, to any person obtaining */ |  | ||||||
| /* a copy of this software and associated documentation files (the       */ |  | ||||||
| /* "Software"), to deal in the Software without restriction, including   */ |  | ||||||
| /* without limitation the rights to use, copy, modify, merge, publish,   */ |  | ||||||
| /* distribute, sublicense, and/or sell copies of the Software, and to    */ |  | ||||||
| /* permit persons to whom the Software is furnished to do so, subject to */ |  | ||||||
| /* the following conditions:                                             */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* The above copyright notice and this permission notice shall be        */ |  | ||||||
| /* included in all copies or substantial portions of the Software.       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ |  | ||||||
| /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ |  | ||||||
| /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ |  | ||||||
| /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ |  | ||||||
| /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ |  | ||||||
| /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ |  | ||||||
| /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ |  | ||||||
| /*************************************************************************/ |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <algorithm> |  | ||||||
| #include <array> |  | ||||||
| #include <vector> |  | ||||||
| #include <cstring> |  | ||||||
| 
 |  | ||||||
| namespace spdlog |  | ||||||
| { |  | ||||||
| namespace details |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
| // Fast memory storage on the stack when possible or in std::vector
 |  | ||||||
| template<unsigned short STACK_SIZE> |  | ||||||
| class stack_buf |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     static const unsigned short stack_size = STACK_SIZE; |  | ||||||
|     stack_buf() :_v(), _stack_size(0) {} |  | ||||||
|     ~stack_buf() = default; |  | ||||||
|     stack_buf(const stack_buf& other) :stack_buf(other, delegate_copy_or_move {}) |  | ||||||
|     {} |  | ||||||
| 
 |  | ||||||
|     stack_buf(stack_buf&& other) :stack_buf(other, delegate_copy_or_move {}) |  | ||||||
|     { |  | ||||||
|         other.clear(); |  | ||||||
|     } |  | ||||||
|     template<class T1> |  | ||||||
|     stack_buf& operator=(T1&& other) |  | ||||||
|     { |  | ||||||
|         _stack_size = other._stack_size; |  | ||||||
|         if (other.vector_used()) |  | ||||||
|             _v = std::forward<T1>(other)._v; |  | ||||||
|         else |  | ||||||
|             std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin()); |  | ||||||
|         return *this; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void append(const char* buf, std::size_t buf_size) |  | ||||||
|     { |  | ||||||
|         //If we are aleady using _v, forget about the stack
 |  | ||||||
|         if (vector_used()) |  | ||||||
|         { |  | ||||||
|             _v.insert(_v.end(), buf, buf + buf_size); |  | ||||||
|         } |  | ||||||
|         //Try use the stack
 |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             if (_stack_size + buf_size <= STACK_SIZE) |  | ||||||
|             { |  | ||||||
|                 std::memcpy(&_stack_array[_stack_size], buf, buf_size); |  | ||||||
|                 _stack_size += buf_size; |  | ||||||
|             } |  | ||||||
|             //Not enough stack space. Copy all to _v
 |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 _v.reserve(_stack_size + buf_size); |  | ||||||
|                 _v.insert(_v.end(), _stack_array.begin(), _stack_array.begin() + _stack_size); |  | ||||||
|                 _v.insert(_v.end(), buf, buf + buf_size); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     void clear() |  | ||||||
|     { |  | ||||||
|         _stack_size = 0; |  | ||||||
|         _v.clear(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const char* data() const |  | ||||||
|     { |  | ||||||
|         if (vector_used()) |  | ||||||
|             return _v.data(); |  | ||||||
|         else |  | ||||||
|             return _stack_array.data(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::size_t size() const |  | ||||||
|     { |  | ||||||
|         if (vector_used()) |  | ||||||
|             return _v.size(); |  | ||||||
|         else |  | ||||||
|             return _stack_size; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     struct delegate_copy_or_move {}; |  | ||||||
|     template<class T1> |  | ||||||
|     stack_buf(T1&& other, delegate_copy_or_move) |  | ||||||
|     { |  | ||||||
|         _stack_size = other._stack_size; |  | ||||||
|         if (other.vector_used()) |  | ||||||
|             _v = std::forward<T1>(other)._v; |  | ||||||
|         else |  | ||||||
|             std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     inline bool vector_used() const |  | ||||||
|     { |  | ||||||
|         return !(_v.empty()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::vector<char> _v; |  | ||||||
|     std::array<char, STACK_SIZE> _stack_array; |  | ||||||
|     std::size_t _stack_size; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| } //namespace spdlog { namespace details {
 |  | ||||||
| @ -34,7 +34,6 @@ | |||||||
| #include<vector> | #include<vector> | ||||||
| #include<memory> | #include<memory> | ||||||
| #include "sinks/base_sink.h" | #include "sinks/base_sink.h" | ||||||
| #include "sinks/async_sink.h" |  | ||||||
| #include "common.h" | #include "common.h" | ||||||
| 
 | 
 | ||||||
| namespace spdlog | namespace spdlog | ||||||
| @ -66,17 +65,29 @@ public: | |||||||
|     //Stop logging
 |     //Stop logging
 | ||||||
|     void stop(); |     void stop(); | ||||||
| 
 | 
 | ||||||
|     template <typename... Args> details::line_logger log(level::level_enum lvl, const Args&... args); |     template <typename... Args> details::line_logger log(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger log(const Args&... args); |     template <typename... Args> details::line_logger trace(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger trace(const Args&... args); |     template <typename... Args> details::line_logger debug(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger debug(const Args&... args); |     template <typename... Args> details::line_logger info(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger info(const Args&... args); |     template <typename... Args> details::line_logger notice(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger notice(const Args&... args); |     template <typename... Args> details::line_logger warn(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger warn(const Args&... args); |     template <typename... Args> details::line_logger error(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger error(const Args&... args); |     template <typename... Args> details::line_logger critical(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger critical(const Args&... args); |     template <typename... Args> details::line_logger alert(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger alert(const Args&... args); |     template <typename... Args> details::line_logger emerg(const std::string& fmt, const Args&... args); | ||||||
|     template <typename... Args> details::line_logger emerg(const Args&... args); | 
 | ||||||
|  |     //API to support logger.info() << ".." calls
 | ||||||
|  | 
 | ||||||
|  |     details::line_logger log(); | ||||||
|  |     details::line_logger trace(); | ||||||
|  |     details::line_logger debug(); | ||||||
|  |     details::line_logger info(); | ||||||
|  |     details::line_logger notice(); | ||||||
|  |     details::line_logger warn(); | ||||||
|  |     details::line_logger error(); | ||||||
|  |     details::line_logger critical(); | ||||||
|  |     details::line_logger alert(); | ||||||
|  |     details::line_logger emerg(); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     void set_pattern(const std::string&); |     void set_pattern(const std::string&); | ||||||
| @ -84,10 +95,12 @@ public: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     virtual void _log_msg(details::log_msg& msg); |     virtual void _log_msg(details::log_msg&); | ||||||
|     virtual void _set_pattern(const std::string&); |     virtual void _set_pattern(const std::string&); | ||||||
|     virtual void _set_formatter(formatter_ptr); |     virtual void _set_formatter(formatter_ptr); | ||||||
|     virtual void _stop(); |     virtual void _stop(); | ||||||
|  |     details::line_logger _log(level::level_enum lvl); | ||||||
|  |     template <typename... Args> details::line_logger _log(level::level_enum lvl, const std::string& fmt, const Args&... args); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     friend details::line_logger; |     friend details::line_logger; | ||||||
| @ -96,13 +109,6 @@ protected: | |||||||
|     formatter_ptr _formatter; |     formatter_ptr _formatter; | ||||||
|     std::atomic_int _level; |     std::atomic_int _level; | ||||||
| 
 | 
 | ||||||
| private: |  | ||||||
|     void _variadic_log(details::line_logger& l); |  | ||||||
|     template <typename Last> |  | ||||||
|     inline void _variadic_log(spdlog::details::line_logger& l, const Last& last); |  | ||||||
|     template <typename First, typename... Rest> |  | ||||||
|     void _variadic_log(details::line_logger&l, const First& first, const Rest&... rest); |  | ||||||
| 
 |  | ||||||
| }; | }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,209 +0,0 @@ | |||||||
| /*************************************************************************/ |  | ||||||
| /* spdlog - an extremely fast and easy to use c++11 logging library.     */ |  | ||||||
| /* Copyright (c) 2014 Gabi Melman.                                       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* Permission is hereby granted, free of charge, to any person obtaining */ |  | ||||||
| /* a copy of this software and associated documentation files (the       */ |  | ||||||
| /* "Software"), to deal in the Software without restriction, including   */ |  | ||||||
| /* without limitation the rights to use, copy, modify, merge, publish,   */ |  | ||||||
| /* distribute, sublicense, and/or sell copies of the Software, and to    */ |  | ||||||
| /* permit persons to whom the Software is furnished to do so, subject to */ |  | ||||||
| /* the following conditions:                                             */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* The above copyright notice and this permission notice shall be        */ |  | ||||||
| /* included in all copies or substantial portions of the Software.       */ |  | ||||||
| /*                                                                       */ |  | ||||||
| /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ |  | ||||||
| /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ |  | ||||||
| /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ |  | ||||||
| /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ |  | ||||||
| /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ |  | ||||||
| /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ |  | ||||||
| /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ |  | ||||||
| /*************************************************************************/ |  | ||||||
| 
 |  | ||||||
| // async sink:
 |  | ||||||
| // Process logs asynchronously using a back thread.
 |  | ||||||
| //
 |  | ||||||
| // If the internal queue of log messages reaches its max size,
 |  | ||||||
| // then the client call will block until there is more room.
 |  | ||||||
| //
 |  | ||||||
| // If the back thread throws during logging, a spdlog::spdlog_ex exception
 |  | ||||||
| // will be thrown in client's thread when tries to log the next message
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <thread> |  | ||||||
| #include <chrono> |  | ||||||
| #include <atomic> |  | ||||||
| 
 |  | ||||||
| #include "./base_sink.h" |  | ||||||
| #include "../logger.h" |  | ||||||
| #include "../details/blocking_queue.h" |  | ||||||
| #include "../details/null_mutex.h" |  | ||||||
| #include "../details/log_msg.h" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| namespace spdlog |  | ||||||
| { |  | ||||||
| namespace sinks |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class async_sink : public base_sink < details::null_mutex > //single worker thread so null_mutex
 |  | ||||||
| { |  | ||||||
| public: |  | ||||||
|     using q_type = details::blocking_queue < std::unique_ptr<details::log_msg> > ; |  | ||||||
| 
 |  | ||||||
|     explicit async_sink(const q_type::size_type max_queue_size); |  | ||||||
| 
 |  | ||||||
|     //Stop logging and join the back thread
 |  | ||||||
|     ~async_sink(); |  | ||||||
|     void add_sink(sink_ptr sink); |  | ||||||
|     void remove_sink(sink_ptr sink_ptr); |  | ||||||
|     void set_formatter(formatter_ptr); |  | ||||||
|     //Wait to remaining items (if any) in the queue to be written and shutdown
 |  | ||||||
|     void shutdown(const log_clock::duration& timeout); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| protected: |  | ||||||
|     void _sink_it(const details::log_msg& msg) override; |  | ||||||
|     void _thread_loop(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     std::vector<std::shared_ptr<sink>> _sinks; |  | ||||||
|     std::atomic<bool> _active; |  | ||||||
|     q_type _q; |  | ||||||
|     std::thread _back_thread; |  | ||||||
|     std::mutex _mutex; |  | ||||||
|     //Last exception thrown from the back thread
 |  | ||||||
|     std::shared_ptr<spdlog_ex> _last_backthread_ex; |  | ||||||
| 
 |  | ||||||
|     formatter_ptr _formatter; |  | ||||||
| 
 |  | ||||||
|     //will throw last back thread exception or if backthread no active
 |  | ||||||
|     void _push_sentry(); |  | ||||||
| 
 |  | ||||||
|     //Clear all remaining messages(if any), stop the _back_thread and join it
 |  | ||||||
|     void _join(); |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
| } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ///////////////////////////////////////////////////////////////////////////////
 |  | ||||||
| // async_sink class implementation
 |  | ||||||
| ///////////////////////////////////////////////////////////////////////////////
 |  | ||||||
| inline spdlog::sinks::async_sink::async_sink(const q_type::size_type max_queue_size) |  | ||||||
|     :_sinks(), |  | ||||||
|      _active(true), |  | ||||||
|      _q(max_queue_size), |  | ||||||
|      _back_thread(&async_sink::_thread_loop, this) |  | ||||||
| {} |  | ||||||
| 
 |  | ||||||
| inline spdlog::sinks::async_sink::~async_sink() |  | ||||||
| { |  | ||||||
|     _join(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::_sink_it(const details::log_msg& msg) |  | ||||||
| { |  | ||||||
|     _push_sentry(); |  | ||||||
|     _q.push(std::unique_ptr<details::log_msg>(new details::log_msg(msg))); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::_thread_loop() |  | ||||||
| { |  | ||||||
|     std::chrono::seconds  pop_timeout { 1 }; |  | ||||||
| 
 |  | ||||||
|     while (_active) |  | ||||||
|     { |  | ||||||
|         q_type::item_type msg; |  | ||||||
|         if (_q.pop(msg, pop_timeout)) |  | ||||||
|         { |  | ||||||
|             if (!_active) |  | ||||||
|                 return; |  | ||||||
|             _formatter->format(*msg); |  | ||||||
|             for (auto &s : _sinks) |  | ||||||
|             { |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     s->log(*msg); |  | ||||||
|                 } |  | ||||||
|                 catch (const std::exception& ex) |  | ||||||
|                 { |  | ||||||
|                     _last_backthread_ex = std::make_shared<spdlog_ex>(ex.what()); |  | ||||||
|                 } |  | ||||||
|                 catch (...) |  | ||||||
|                 { |  | ||||||
|                     _last_backthread_ex = std::make_shared<spdlog_ex>("Unknown exception"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::add_sink(spdlog::sink_ptr s) |  | ||||||
| { |  | ||||||
|     std::lock_guard<std::mutex> guard(_mutex); |  | ||||||
|     _sinks.push_back(s); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::remove_sink(spdlog::sink_ptr s) |  | ||||||
| { |  | ||||||
|     std::lock_guard<std::mutex> guard(_mutex); |  | ||||||
|     _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), s), _sinks.end()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::set_formatter(formatter_ptr msg_formatter) |  | ||||||
| { |  | ||||||
|     _formatter = msg_formatter; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::shutdown(const log_clock::duration& timeout) |  | ||||||
| { |  | ||||||
|     if (timeout > std::chrono::milliseconds::zero()) |  | ||||||
|     { |  | ||||||
|         auto until = log_clock::now() + timeout; |  | ||||||
|         while (_q.size() > 0 && log_clock::now() < until) |  | ||||||
|         { |  | ||||||
|             std::this_thread::sleep_for(std::chrono::milliseconds(5)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     _join(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::_push_sentry() |  | ||||||
| { |  | ||||||
|     if (_last_backthread_ex) |  | ||||||
|     { |  | ||||||
|         auto ex = std::move(_last_backthread_ex); |  | ||||||
|         _last_backthread_ex.reset(); |  | ||||||
|         throw *ex; |  | ||||||
|     } |  | ||||||
|     if (!_active) |  | ||||||
|         throw(spdlog_ex("async_sink not active")); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| inline void spdlog::sinks::async_sink::_join() |  | ||||||
| { |  | ||||||
|     _active = false; |  | ||||||
|     if (_back_thread.joinable()) |  | ||||||
|     { |  | ||||||
|         try |  | ||||||
|         { |  | ||||||
|             _back_thread.join(); |  | ||||||
|         } |  | ||||||
|         catch (const std::system_error&) //Dont crash if thread not joinable
 |  | ||||||
|         {} |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @ -28,8 +28,7 @@ | |||||||
| #include "base_sink.h" | #include "base_sink.h" | ||||||
| #include "../details/null_mutex.h" | #include "../details/null_mutex.h" | ||||||
| #include "../details/file_helper.h" | #include "../details/file_helper.h" | ||||||
| #include "../details/fast_oss.h" | #include "../details/format.h" | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace spdlog | namespace spdlog | ||||||
| @ -100,12 +99,12 @@ protected: | |||||||
| private: | private: | ||||||
|     static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension) |     static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension) | ||||||
|     { |     { | ||||||
|         details::fast_oss oss; |         fmt::MemoryWriter w; | ||||||
|         if (index) |         if (index) | ||||||
|             oss << filename << "." << index << "." << extension; |             w.write("{}.{}.{}", filename, index, extension); | ||||||
|         else |         else | ||||||
|             oss << filename << "." << extension; |             w.write("{}.{}", filename, extension); | ||||||
|         return oss.str(); |         return w.str(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -197,11 +196,9 @@ private: | |||||||
|     static std::string calc_filename(const std::string& basename, const std::string& extension) |     static std::string calc_filename(const std::string& basename, const std::string& extension) | ||||||
|     { |     { | ||||||
|         std::tm tm = spdlog::details::os::localtime(); |         std::tm tm = spdlog::details::os::localtime(); | ||||||
|         details::fast_oss oss; |         fmt::MemoryWriter w; | ||||||
|         oss << basename << '.'; |         w.write("{}.{:04d}-{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension); | ||||||
|         oss << tm.tm_year + 1900 << '-' << std::setw(2) << std::setfill('0') << tm.tm_mon + 1 << '-' << tm.tm_mday; |         return w.str(); | ||||||
|         oss << '.' << extension; |  | ||||||
|         return oss.str(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string _base_filename; |     std::string _base_filename; | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <iostream> | #include <ostream> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 | 
 | ||||||
| @ -47,8 +47,7 @@ public: | |||||||
| protected: | protected: | ||||||
|     virtual void _sink_it(const details::log_msg& msg) override |     virtual void _sink_it(const details::log_msg& msg) override | ||||||
|     { |     { | ||||||
|         auto& buf = msg.formatted.buf(); |         _ostream.write(msg.formatted.data(), msg.formatted.size()); | ||||||
|         _ostream.write(buf.data(), buf.size()); |  | ||||||
|     } |     } | ||||||
|     std::ostream& _ostream; |     std::ostream& _ostream; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -65,9 +65,8 @@ void set_level(level::level_enum log_level); | |||||||
| //
 | //
 | ||||||
| 
 | 
 | ||||||
| // Turn on async mode and set the queue size for each async_logger
 | // Turn on async mode and set the queue size for each async_logger
 | ||||||
| // shutdown_duration indicates max time to wait for the worker thread to log its messages before terminating.
 |  | ||||||
| 
 | 
 | ||||||
| void set_async_mode(size_t queue_size, const log_clock::duration& shutdown_duration = std::chrono::seconds(5)); | void set_async_mode(size_t queue_size); | ||||||
| // Turn off async mode
 | // Turn off async mode
 | ||||||
| void set_sync_mode(); | void set_sync_mode(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user