The queue is unlimited.
/* * * Author, Copyright: Oleg Borodin <onborodin@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #include <iostream> #include <fstream> #include <sstream> #include <functional> #include <condition_variable> #include <queue> #include <thread> #include <mutex> #include <chrono> #include <memory> #include <stdexcept> #include <iomanip> class logger { private: class message { public: std::string content; message(std::string msg) : content(msg) {} }; std::queue<message> queue; std::condition_variable cv; std::mutex mutex; std::vector<std::shared_ptr<std::thread>> threads; std::string path; std::shared_ptr<std::ofstream> file; bool shutdown = false; std::string timestamp() { std::time_t now = std::time(0); std::stringstream ss; ss << std::put_time(std::gmtime(&now), "%Y-%m-%d %T %Z"); return ss.str(); } public: logger(std::string path, int num_threads = 1) : path(path) { using mode = std::fstream; file = std::make_shared<std::ofstream>( path, mode::out | mode::app ); if (!file->is_open()) { throw std::runtime_error("cannot open log file"); } auto worker = [&]{ while(true) { std::unique_lock<std::mutex> lock(mutex); cv.wait(lock, [&]{ return !queue.empty() || shutdown; }); if (shutdown) { file->close(); return; } while(!queue.empty()) { auto msg = queue.front(); queue.pop(); cv.notify_one(); *file << msg.content << std::endl; std::cerr << msg.content << std::endl; } } }; for (std::size_t i = 0; i < num_threads; ++i) { auto t = new std::thread(std::move(worker)); std::shared_ptr<std::thread> thread(std::move(t)); threads.push_back(thread); } for (std::size_t i = 0; i < threads.size(); ++i) { threads[i]->detach(); } } void stop() { shutdown = true; cv.notify_all(); } void log(std::string msg) { message m(timestamp() + " " + msg); auto func = [&]{ std::unique_lock<std::mutex> lock(mutex); queue.push(m); }; if (!shutdown) { func(); } cv.notify_all(); } }; int main(int argc, char **argv) { try { logger l("./std.log"); l.log("init hello"); int i = 0; while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); l.log("hello"); if (i > 10) { l.stop(); break; } i++; } } catch (std::exception& e) { std::cerr << "#exception: " << e.what() << ", exit." << std::endl; return 1; } return 0; }
$ c++ -O -pthread -o logger logger.cpp $ ./logger 2019-04-25 08:17:10 UTC init hello 2019-04-25 08:17:11 UTC hello 2019-04-25 08:17:12 UTC hello 2019-04-25 08:17:13 UTC hello 2019-04-25 08:17:14 UTC hello 2019-04-25 08:17:15 UTC hello 2019-04-25 08:17:16 UTC hello 2019-04-25 08:17:17 UTC hello 2019-04-25 08:17:18 UTC hello 2019-04-25 08:17:19 UTC hello 2019-04-25 08:17:20 UTC hello 2019-04-25 08:17:21 UTC hello $