{"id":19024,"date":"2025-08-14T15:38:03","date_gmt":"2025-08-14T15:38:03","guid":{"rendered":"http:\/\/www.max-sperling.bplaced.net\/?p=19024"},"modified":"2025-08-22T09:40:22","modified_gmt":"2025-08-22T09:40:22","slug":"singelton-file-logger-with-concurrent_queue-c","status":"publish","type":"post","link":"http:\/\/www.max-sperling.bplaced.net\/?p=19024","title":{"rendered":"Singleton file logger with concurrent queue (C++)"},"content":{"rendered":"<h2>Advantages<\/h2>\n<ul>\n<li>&#8230; of a singleton is that you don&#8217;t have to pass it into every class you want to log something.<\/li>\n<li>&#8230; of a concurrent_queue is that you don&#8217;t block the calling thread with logging into the file.<\/li>\n<\/ul>\n<hr>\n<h2>Code<\/h2>\n<pre class=\"brush: cpp; gutter: false; title: ; notranslate\" title=\"\">\r\n#include &quot;tbb\/concurrent_queue.h&quot;\r\n\r\n#include &lt;atomic&gt;\r\n#include &lt;condition_variable&gt;\r\n#include &lt;fstream&gt;\r\n#include &lt;iostream&gt;\r\n#include &lt;mutex&gt;\r\n#include &lt;string&gt;\r\n#include &lt;thread&gt;\r\n\r\n#define FLOG(msg) FileLogger::instance().log(msg)\r\n\r\nclass FileLogger\r\n{\r\npublic:\r\n    static FileLogger&amp; instance(const std::string&amp; filename = &quot;&quot;)\r\n    {\r\n        static FileLogger instance(filename); \/\/ thread-safe since C++11\r\n        return instance;\r\n    }\r\n\r\n    void log(const std::string&amp; line)\r\n    {\r\n        queue_.push(line);\r\n        cv_.notify_one();\r\n    }\r\n\r\nprivate:\r\n    FileLogger(const std::string&amp; filename)\r\n    {\r\n        if (file_.is_open())\r\n        {\r\n            std::cout &lt;&lt; &quot;File already opened: &quot; &lt;&lt; filename &lt;&lt; &quot;\\n&quot;;\r\n            return;\r\n        }\r\n\r\n        file_.open(filename, std::ios::out | std::ios::trunc);\r\n        if (!file_.is_open())\r\n        {\r\n            std::cout &lt;&lt; &quot;Can&#039;t open file: &quot; &lt;&lt; filename &lt;&lt; &quot;\\n&quot;;\r\n            return;\r\n        }\r\n\r\n        running_.store(true);\r\n        thread_ = std::thread([this]\r\n        {\r\n            while (running_.load())\r\n            {\r\n                std::unique_lock&lt;std::mutex&gt; cv_lock(cv_mutex_);\r\n                cv_.wait(cv_lock, [this] {\r\n                    return !queue_.empty() || !running_.load();\r\n                });\r\n\r\n                std::string line;\r\n                while (running_.load() &amp;&amp; queue_.try_pop(line))\r\n                {\r\n                    file_ &lt;&lt; line &lt;&lt; &quot;\\n&quot;;\r\n                }\r\n            }\r\n        });\r\n    }\r\n\r\n    ~FileLogger()\r\n    {\r\n        if (!running_.load())\r\n        {\r\n            std::cout &lt;&lt; &quot;No logging happening\\n&quot;;\r\n        }\r\n        else\r\n        {\r\n            running_.store(false);\r\n            cv_.notify_all();\r\n            if (thread_.joinable())\r\n            {\r\n                thread_.join();\r\n            }\r\n\r\n            std::string line;\r\n            while (queue_.try_pop(line))\r\n            {\r\n                file_ &lt;&lt; line &lt;&lt; &quot;\\n&quot;;\r\n            }\r\n        }\r\n\r\n        if (!file_.is_open())\r\n        {\r\n            std::cout &lt;&lt; &quot;No file opened\\n&quot;;\r\n            return;\r\n        }\r\n        file_.close();\r\n    }\r\n\r\n    FileLogger(const FileLogger&amp;) = delete;\r\n    FileLogger&amp; operator=(const FileLogger&amp;) = delete;\r\n\r\nprivate:\r\n    std::ofstream file_;\r\n    std::atomic&lt;bool&gt; running_{};\r\n    std::thread thread_;\r\n    std::mutex cv_mutex_;\r\n    std::condition_variable cv_;\r\n    tbb::concurrent_queue&lt;std::string&gt; queue_;\r\n};\r\n\r\nint main()\r\n{\r\n    FileLogger::instance(&quot;log.txt&quot;);\r\n    FLOG(&quot;Log message 1&quot;);\r\n    FLOG(&quot;Log message 2&quot;);\r\n\r\n    return 0;\r\n}\r\n<\/pre>\n<pre>\r\n$ g++ main.cpp -ltbb -lpthread\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Advantages &#8230; of a singleton is that you don&#8217;t have to pass it into every class you want to log<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false},"categories":[63],"tags":[],"_links":{"self":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/19024"}],"collection":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=19024"}],"version-history":[{"count":15,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/19024\/revisions"}],"predecessor-version":[{"id":19083,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/19024\/revisions\/19083"}],"wp:attachment":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=19024"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=19024"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=19024"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}